diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 75461d072394..7a745d5a01d5 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -16,6 +16,8 @@ tasks: name: "Coverage" platform: ubuntu1804 shell_commands: + - "sudo apt -y update && sudo apt -y install automake autotools-dev cmake libtool m4 ninja-build" + - "wget https://apt.llvm.org/llvm.sh && sudo bash llvm.sh 10" - "bazel/setup_clang.sh /usr/lib/llvm-10" test_targets: - "//test/common/common/..." diff --git a/.bazelrc b/.bazelrc index 4a9e2f9c319b..9efa5409050d 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 @@ -47,6 +50,7 @@ build:sanitizer --test_tag_filters=-no_san # Common flags for Clang build:clang --action_env=BAZEL_COMPILER=clang +build:clang --action_env=CC=clang --action_env=CXX=clang++ build:clang --linkopt=-fuse-ld=lld # Flags for Clang + PCH @@ -74,16 +78,19 @@ build:asan --copt -O1 build:asan --copt -fno-optimize-sibling-calls # Clang ASAN/UBSAN +build:clang-asan --config=clang build:clang-asan --config=asan build:clang-asan --linkopt -fuse-ld=lld build:clang-asan --linkopt --rtlib=compiler-rt build:clang-asan --linkopt --unwindlib=libgcc -# macOS ASAN/UBSAN +# macOS build:macos --cxxopt=-std=c++17 build:macos --action_env=PATH=/usr/bin:/bin:/opt/homebrew/bin:/usr/local/bin:/opt/local/bin build:macos --host_action_env=PATH=/usr/bin:/bin:/opt/homebrew/bin:/usr/local/bin:/opt/local/bin +build:macos --define tcmalloc=disabled +# macOS ASAN/UBSAN build:macos-asan --config=asan # Workaround, see https://github.com/bazelbuild/bazel/issues/6932 build:macos-asan --copt -Wno-macro-redefined @@ -316,7 +323,7 @@ build:plain-fuzzer --copt=-fsanitize=fuzzer-no-link build:plain-fuzzer --linkopt=-fsanitize=fuzzer-no-link build:asan-fuzzer --config=plain-fuzzer -build:asan-fuzzer --config=asan +build:asan-fuzzer --config=clang-asan build:asan-fuzzer --copt=-fno-omit-frame-pointer # Remove UBSAN halt_on_error to avoid crashing on protobuf errors. build:asan-fuzzer --test_env=UBSAN_OPTIONS=print_stacktrace=1 diff --git a/.github/actions/pr_notifier/requirements.txt b/.github/actions/pr_notifier/requirements.txt index fb21f429db9f..c8f43486f223 100644 --- a/.github/actions/pr_notifier/requirements.txt +++ b/.github/actions/pr_notifier/requirements.txt @@ -111,9 +111,9 @@ six==1.16.0 \ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 # via pynacl -slack_sdk==3.11.2 \ - --hash=sha256:131bf605894525c2d66da064677eabc19f53f02ce0f82a3f2fa130d4ec3bc1b0 \ - --hash=sha256:35245ec34c8549fbb5c43ccc17101afd725b3508bb784da46530b214f496bf93 +slack_sdk==3.13.0 \ + --hash=sha256:54f2a5f7419f1ab932af9e3200f7f2f93db96e0f0eb8ad7d3b4214aa9f124641 \ + --hash=sha256:aae6ce057e286a5e7fe7a9f256e85b886eee556def8e04b82b08f699e64d7f67 # via -r requirements.in urllib3==1.26.6 \ --hash=sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4 \ diff --git a/.github/workflows/codeql-daily.yml b/.github/workflows/codeql-daily.yml index 358695c84f61..920108ff42ee 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 d79e7a011d34..26b66486367b 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 88759f9f1414..fee32f3c49e8 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: diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 017518fe4f05..eea5c277249c 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Prune Stale - uses: actions/stale@v3.0.19 + uses: actions/stale@v4.1.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} # Different amounts of days for issues/PRs are not currently supported but there is a PR diff --git a/CODEOWNERS b/CODEOWNERS index 4b8e4a2ca5cb..c8c3fa0e50a0 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -6,6 +6,7 @@ /api/ @envoyproxy/api-shepherds # access loggers /*/extensions/access_loggers/common @auni53 @zuercher +/*/extensions/access_loggers/filters/cel @dio @douglas-reid /*/extensions/access_loggers/open_telemetry @itamarkam @yanavlasov /*/extensions/access_loggers/stream @mattklein123 @davinci26 # compression extensions @@ -40,6 +41,8 @@ extensions/filters/common/original_src @snowp @klarose /*/extensions/filters/http/header_to_metadata @rgs1 @zuercher # alts transport socket extension /*/extensions/transport_sockets/alts @antoniovicente @asraa @yangminzhu +# tcp_stats transport socket extension +/*/extensions/transport_sockets/tcp_stats @ggreenway @mattklein123 # tls transport socket extension /*/extensions/transport_sockets/tls @lizan @asraa @ggreenway # tls SPIFFE certificate validator extension diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9b6e47913f07..20e8389e3ba6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -197,7 +197,7 @@ Runtime features are set true by default by inclusion in There are four suggested options for testing new runtime features: 1. Create a per-test Runtime::LoaderSingleton as done in [DeprecatedFieldsTest.IndividualFieldDisallowedWithRuntimeOverride](https://github.com/envoyproxy/envoy/blob/main/test/common/protobuf/utility_test.cc) -2. Create a [parameterized test](https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#how-to-write-value-parameterized-tests) +2. Create a [parameterized test](https://github.com/google/googletest/blob/master/docs/advanced.md#how-to-write-value-parameterized-tests) where the set up of the test sets the new runtime value explicitly to GetParam() as outlined in (1). 3. Set up integration tests with custom runtime defaults as documented in the diff --git a/OWNERS.md b/OWNERS.md index 66fb84068b2c..68236b952f57 100644 --- a/OWNERS.md +++ b/OWNERS.md @@ -43,8 +43,6 @@ routing PRs, questions, etc. to the right place. * Windows port and CI build, `bazel/foreign_cc` build and dependencies liaison. * Antonio Vicente ([antoniovicente](https://github.com/antoniovicente)) (avd@google.com) * Event management, security, performance, data plane. -* Sotiris Nanopoulos ([davinci26](https://github.com/davinci26)) (Sotiris.Nanopoulos@microsoft.com) - * Windows, low level networking. * Dmitry Rozhkov ([rojkov](https://github.com/rojkov)) (dmitry.rozhkov@intel.com) * Scalability and performance. * Ryan Hamilton ([RyanTheOptimist](https://github.com/ryantheoptimist)) (rch@google.com) @@ -67,6 +65,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/README.md b/README.md index 210048e28753..f7c222723fcb 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ involved and how Envoy plays a role, read the CNCF * [Official documentation](https://www.envoyproxy.io/) * [FAQ](https://www.envoyproxy.io/docs/envoy/latest/faq/overview) -* [Unofficial Chinese documentation](https://www.servicemesher.com/envoy/) +* [Unofficial Chinese documentation](https://cloudnative.to/envoy/) * Watch [a video overview of Envoy](https://www.youtube.com/watch?v=RVZX4CwKhGE) ([transcript](https://www.microservices.com/talks/lyfts-envoy-monolith-service-mesh-matt-klein/)) to find out more about the origin story and design philosophy of Envoy diff --git a/RELEASES.md b/RELEASES.md index 1619b0d22d72..4f086eb33ef0 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 diff --git a/api/BUILD b/api/BUILD index 0f60195fb6dd..5f5acd1e4055 100644 --- a/api/BUILD +++ b/api/BUILD @@ -18,17 +18,31 @@ proto_library( "//envoy/api/v2/ratelimit:pkg", "//envoy/api/v2/route:pkg", "//envoy/config/bootstrap/v2:pkg", + "//envoy/config/cluster/dynamic_forward_proxy/v2alpha:pkg", + "//envoy/config/common/dynamic_forward_proxy/v2alpha:pkg", + "//envoy/config/common/tap/v2alpha:pkg", "//envoy/config/filter/accesslog/v2:pkg", "//envoy/config/filter/fault/v2:pkg", + "//envoy/config/filter/http/compressor/v2:pkg", + "//envoy/config/filter/http/dynamic_forward_proxy/v2alpha:pkg", + "//envoy/config/filter/http/gzip/v2:pkg", + "//envoy/config/filter/http/rate_limit/v2:pkg", + "//envoy/config/filter/http/rbac/v2:pkg", + "//envoy/config/filter/network/dubbo_proxy/v2alpha1:pkg", "//envoy/config/filter/network/http_connection_manager/v2:pkg", + "//envoy/config/filter/network/rate_limit/v2:pkg", + "//envoy/config/filter/network/rbac/v2:pkg", "//envoy/config/filter/network/redis_proxy/v2:pkg", "//envoy/config/filter/network/tcp_proxy/v2:pkg", "//envoy/config/filter/network/thrift_proxy/v2alpha1:pkg", + "//envoy/config/filter/thrift/rate_limit/v2alpha1:pkg", "//envoy/config/filter/thrift/router/v2alpha1:pkg", "//envoy/config/health_checker/redis/v2:pkg", "//envoy/config/listener/v2:pkg", "//envoy/config/metrics/v2:pkg", "//envoy/config/overload/v2alpha:pkg", + "//envoy/config/ratelimit/v2:pkg", + "//envoy/config/rbac/v2:pkg", "//envoy/config/resource_monitor/fixed_heap/v2alpha:pkg", "//envoy/config/resource_monitor/injected_resource/v2alpha:pkg", "//envoy/config/retry/omit_canary_hosts/v2:pkg", @@ -36,6 +50,7 @@ proto_library( "//envoy/config/trace/v2:pkg", "//envoy/config/trace/v2alpha:pkg", "//envoy/config/transport_socket/alts/v2alpha:pkg", + "//envoy/config/transport_socket/tap/v2alpha:pkg", "//envoy/data/accesslog/v2:pkg", "//envoy/data/tap/v2alpha:pkg", "//envoy/service/accesslog/v2:pkg", @@ -97,6 +112,7 @@ proto_library( "//envoy/data/dns/v3:pkg", "//envoy/data/tap/v3:pkg", "//envoy/extensions/access_loggers/file/v3:pkg", + "//envoy/extensions/access_loggers/filters/cel/v3:pkg", "//envoy/extensions/access_loggers/grpc/v3:pkg", "//envoy/extensions/access_loggers/open_telemetry/v3:pkg", "//envoy/extensions/access_loggers/stream/v3:pkg", @@ -171,6 +187,9 @@ proto_library( "//envoy/extensions/filters/network/ext_authz/v3:pkg", "//envoy/extensions/filters/network/http_connection_manager/v3:pkg", "//envoy/extensions/filters/network/local_ratelimit/v3:pkg", + "//envoy/extensions/filters/network/meta_protocol_proxy/matcher/action/v3:pkg", + "//envoy/extensions/filters/network/meta_protocol_proxy/matcher/v3:pkg", + "//envoy/extensions/filters/network/meta_protocol_proxy/v3:pkg", "//envoy/extensions/filters/network/mongo_proxy/v3:pkg", "//envoy/extensions/filters/network/ratelimit/v3:pkg", "//envoy/extensions/filters/network/rbac/v3:pkg", @@ -178,6 +197,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", @@ -221,6 +241,7 @@ proto_library( "//envoy/extensions/transport_sockets/s2a/v3:pkg", "//envoy/extensions/transport_sockets/starttls/v3:pkg", "//envoy/extensions/transport_sockets/tap/v3:pkg", + "//envoy/extensions/transport_sockets/tcp_stats/v3:pkg", "//envoy/extensions/transport_sockets/tls/v3:pkg", "//envoy/extensions/upstreams/http/generic/v3:pkg", "//envoy/extensions/upstreams/http/http/v3:pkg", diff --git a/api/bazel/external_deps.bzl b/api/bazel/external_deps.bzl index e8283e4fee10..d541512ce98b 100644 --- a/api/bazel/external_deps.bzl +++ b/api/bazel/external_deps.bzl @@ -71,7 +71,7 @@ USE_CATEGORIES = [ USE_CATEGORIES_WITH_CPE_OPTIONAL = ["build", "other", "test_only", "api"] def _fail_missing_attribute(attr, key): - fail("The '%s' attribute must be defined for external dependecy " % attr + key) + fail("The '%s' attribute must be defined for external dependency " % attr + key) # Method for verifying content of the repository location specifications. # diff --git a/api/bazel/repository_locations.bzl b/api/bazel/repository_locations.bzl index 0e5d85aa3318..0784bdb23244 100644 --- a/api/bazel/repository_locations.bzl +++ b/api/bazel/repository_locations.bzl @@ -14,9 +14,9 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "protoc-gen-validate (PGV)", project_desc = "protoc plugin to generate polyglot message validators", project_url = "https://github.com/envoyproxy/protoc-gen-validate", - version = "0.6.1", - sha256 = "c695fc5a2e5a1b52904cd8a58ce7a1c3a80f7f50719496fd606e551685c01101", - release_date = "2021-04-26", + version = "0.6.2", + sha256 = "b02da533c77023238c556982507b9a71afc850478b637a7a13ec13f311efa5c0", + release_date = "2021-10-21", strip_prefix = "protoc-gen-validate-{version}", urls = ["https://github.com/envoyproxy/protoc-gen-validate/archive/v{version}.tar.gz"], use_category = ["api"], @@ -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.4", + sha256 = "f3d86864a37663ad2303d8781e3f76e86b7e948cc6a3beae01da71cad2e302bd", + release_date = "2021-12-03", strip_prefix = "buildtools-{version}", urls = ["https://github.com/bazelbuild/buildtools/archive/{version}.tar.gz"], use_category = ["api"], diff --git a/api/contrib/envoy/extensions/filters/http/squash/v3/squash.proto b/api/contrib/envoy/extensions/filters/http/squash/v3/squash.proto index f9bc9cceceb9..6f178e1a9f8d 100644 --- a/api/contrib/envoy/extensions/filters/http/squash/v3/squash.proto +++ b/api/contrib/envoy/extensions/filters/http/squash/v3/squash.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.squash.v3"; option java_outer_classname = "SquashProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/squash/v3;squashv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Squash] diff --git a/api/contrib/envoy/extensions/filters/http/sxg/v3alpha/sxg.proto b/api/contrib/envoy/extensions/filters/http/sxg/v3alpha/sxg.proto index b9efc278e6de..3b34026329bc 100644 --- a/api/contrib/envoy/extensions/filters/http/sxg/v3alpha/sxg.proto +++ b/api/contrib/envoy/extensions/filters/http/sxg/v3alpha/sxg.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.sxg.v3alpha"; option java_outer_classname = "SxgProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/sxg/v3alpha"; option (udpa.annotations.file_status).work_in_progress = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/contrib/envoy/extensions/filters/network/kafka_broker/v3/kafka_broker.proto b/api/contrib/envoy/extensions/filters/network/kafka_broker/v3/kafka_broker.proto index 0fac07427d0c..83fdd27b378c 100644 --- a/api/contrib/envoy/extensions/filters/network/kafka_broker/v3/kafka_broker.proto +++ b/api/contrib/envoy/extensions/filters/network/kafka_broker/v3/kafka_broker.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.kafka_broker.v3"; option java_outer_classname = "KafkaBrokerProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/kafka_broker/v3;kafka_brokerv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Kafka Broker] diff --git a/api/contrib/envoy/extensions/filters/network/kafka_mesh/v3alpha/kafka_mesh.proto b/api/contrib/envoy/extensions/filters/network/kafka_mesh/v3alpha/kafka_mesh.proto index 88fd46c3a856..4a906cdeebc6 100644 --- a/api/contrib/envoy/extensions/filters/network/kafka_mesh/v3alpha/kafka_mesh.proto +++ b/api/contrib/envoy/extensions/filters/network/kafka_mesh/v3alpha/kafka_mesh.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.kafka_mesh.v3alpha"; option java_outer_classname = "KafkaMeshProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/kafka_mesh/v3alpha"; option (udpa.annotations.file_status).package_version_status = ACTIVE; option (xds.annotations.v3.file_status).work_in_progress = true; diff --git a/api/contrib/envoy/extensions/filters/network/mysql_proxy/v3/mysql_proxy.proto b/api/contrib/envoy/extensions/filters/network/mysql_proxy/v3/mysql_proxy.proto index 9dfdb14d3f11..b1bce3bc931c 100644 --- a/api/contrib/envoy/extensions/filters/network/mysql_proxy/v3/mysql_proxy.proto +++ b/api/contrib/envoy/extensions/filters/network/mysql_proxy/v3/mysql_proxy.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.mysql_proxy.v3"; option java_outer_classname = "MysqlProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/mysql_proxy/v3;mysql_proxyv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: MySQL proxy] diff --git a/api/contrib/envoy/extensions/filters/network/postgres_proxy/v3alpha/postgres_proxy.proto b/api/contrib/envoy/extensions/filters/network/postgres_proxy/v3alpha/postgres_proxy.proto index 8fe98f269626..114e97ca7e48 100644 --- a/api/contrib/envoy/extensions/filters/network/postgres_proxy/v3alpha/postgres_proxy.proto +++ b/api/contrib/envoy/extensions/filters/network/postgres_proxy/v3alpha/postgres_proxy.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.postgres_proxy.v3alpha"; option java_outer_classname = "PostgresProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/postgres_proxy/v3alpha"; option (udpa.annotations.file_status).work_in_progress = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/contrib/envoy/extensions/filters/network/rocketmq_proxy/v3/rocketmq_proxy.proto b/api/contrib/envoy/extensions/filters/network/rocketmq_proxy/v3/rocketmq_proxy.proto index 12438751fada..dc8de3aaeec7 100644 --- a/api/contrib/envoy/extensions/filters/network/rocketmq_proxy/v3/rocketmq_proxy.proto +++ b/api/contrib/envoy/extensions/filters/network/rocketmq_proxy/v3/rocketmq_proxy.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.rocketmq_proxy.v3"; option java_outer_classname = "RocketmqProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/rocketmq_proxy/v3;rocketmq_proxyv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: RocketMQ Proxy] diff --git a/api/contrib/envoy/extensions/filters/network/rocketmq_proxy/v3/route.proto b/api/contrib/envoy/extensions/filters/network/rocketmq_proxy/v3/route.proto index 6ec6c71c5627..ff76d9d344d6 100644 --- a/api/contrib/envoy/extensions/filters/network/rocketmq_proxy/v3/route.proto +++ b/api/contrib/envoy/extensions/filters/network/rocketmq_proxy/v3/route.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.rocketmq_proxy.v3"; option java_outer_classname = "RouteProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/rocketmq_proxy/v3;rocketmq_proxyv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Rocketmq Proxy Route Configuration] diff --git a/api/contrib/envoy/extensions/filters/network/sip_proxy/router/v3alpha/router.proto b/api/contrib/envoy/extensions/filters/network/sip_proxy/router/v3alpha/router.proto index 4b7accacf406..15a3137499a8 100644 --- a/api/contrib/envoy/extensions/filters/network/sip_proxy/router/v3alpha/router.proto +++ b/api/contrib/envoy/extensions/filters/network/sip_proxy/router/v3alpha/router.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.sip_proxy.router.v3alpha"; option java_outer_classname = "RouterProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/sip_proxy/router/v3alpha"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Router] diff --git a/api/contrib/envoy/extensions/filters/network/sip_proxy/v3alpha/route.proto b/api/contrib/envoy/extensions/filters/network/sip_proxy/v3alpha/route.proto index 03c17a8ede82..11bcdac33ad5 100644 --- a/api/contrib/envoy/extensions/filters/network/sip_proxy/v3alpha/route.proto +++ b/api/contrib/envoy/extensions/filters/network/sip_proxy/v3alpha/route.proto @@ -8,6 +8,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.sip_proxy.v3alpha"; option java_outer_classname = "RouteProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/sip_proxy/v3alpha"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Sip Proxy Route Configuration] diff --git a/api/contrib/envoy/extensions/filters/network/sip_proxy/v3alpha/sip_proxy.proto b/api/contrib/envoy/extensions/filters/network/sip_proxy/v3alpha/sip_proxy.proto index 380ee714f40c..5347eb770575 100644 --- a/api/contrib/envoy/extensions/filters/network/sip_proxy/v3alpha/sip_proxy.proto +++ b/api/contrib/envoy/extensions/filters/network/sip_proxy/v3alpha/sip_proxy.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.sip_proxy.v3alpha"; option java_outer_classname = "SipProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/sip_proxy/v3alpha"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Sip Proxy] diff --git a/api/contrib/envoy/extensions/private_key_providers/cryptomb/v3alpha/cryptomb.proto b/api/contrib/envoy/extensions/private_key_providers/cryptomb/v3alpha/cryptomb.proto index aa2d8cd2fb82..f92406493bdc 100644 --- a/api/contrib/envoy/extensions/private_key_providers/cryptomb/v3alpha/cryptomb.proto +++ b/api/contrib/envoy/extensions/private_key_providers/cryptomb/v3alpha/cryptomb.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.private_key_providers.cryptomb.v3alpha"; option java_outer_classname = "CryptombProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/private_key_providers/cryptomb/v3alpha"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: CryptoMb private key provider] diff --git a/api/contrib/envoy/extensions/vcl/v3alpha/vcl_socket_interface.proto b/api/contrib/envoy/extensions/vcl/v3alpha/vcl_socket_interface.proto index d8219df358f4..e93fecaded81 100644 --- a/api/contrib/envoy/extensions/vcl/v3alpha/vcl_socket_interface.proto +++ b/api/contrib/envoy/extensions/vcl/v3alpha/vcl_socket_interface.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.vcl.v3alpha"; option java_outer_classname = "VclSocketInterfaceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/vcl/v3alpha"; option (udpa.annotations.file_status).work_in_progress = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/admin/v2alpha/certs.proto b/api/envoy/admin/v2alpha/certs.proto index c7b568ca1e58..b20f84fe43f2 100644 --- a/api/envoy/admin/v2alpha/certs.proto +++ b/api/envoy/admin/v2alpha/certs.proto @@ -9,6 +9,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.admin.v2alpha"; option java_outer_classname = "CertsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/admin/v2alpha"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Certificates] diff --git a/api/envoy/admin/v2alpha/clusters.proto b/api/envoy/admin/v2alpha/clusters.proto index 3b7ec029aa63..b3f579c35476 100644 --- a/api/envoy/admin/v2alpha/clusters.proto +++ b/api/envoy/admin/v2alpha/clusters.proto @@ -13,6 +13,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.admin.v2alpha"; option java_outer_classname = "ClustersProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/admin/v2alpha"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Clusters] diff --git a/api/envoy/admin/v2alpha/config_dump.proto b/api/envoy/admin/v2alpha/config_dump.proto index 833c015fb474..0946a7246de7 100644 --- a/api/envoy/admin/v2alpha/config_dump.proto +++ b/api/envoy/admin/v2alpha/config_dump.proto @@ -12,6 +12,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.admin.v2alpha"; option java_outer_classname = "ConfigDumpProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/admin/v2alpha"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: ConfigDump] diff --git a/api/envoy/admin/v2alpha/listeners.proto b/api/envoy/admin/v2alpha/listeners.proto index ca7b736521d0..ee385de793c7 100644 --- a/api/envoy/admin/v2alpha/listeners.proto +++ b/api/envoy/admin/v2alpha/listeners.proto @@ -9,6 +9,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.admin.v2alpha"; option java_outer_classname = "ListenersProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/admin/v2alpha"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Listeners] diff --git a/api/envoy/admin/v2alpha/memory.proto b/api/envoy/admin/v2alpha/memory.proto index 85fd2169d6d7..a96257b395c9 100644 --- a/api/envoy/admin/v2alpha/memory.proto +++ b/api/envoy/admin/v2alpha/memory.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.admin.v2alpha"; option java_outer_classname = "MemoryProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/admin/v2alpha"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Memory] diff --git a/api/envoy/admin/v2alpha/metrics.proto b/api/envoy/admin/v2alpha/metrics.proto index 15ad219c13e5..0a27b0456248 100644 --- a/api/envoy/admin/v2alpha/metrics.proto +++ b/api/envoy/admin/v2alpha/metrics.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.admin.v2alpha"; option java_outer_classname = "MetricsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/admin/v2alpha"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Metrics] diff --git a/api/envoy/admin/v2alpha/mutex_stats.proto b/api/envoy/admin/v2alpha/mutex_stats.proto index 22c65f3de5a6..8cb9ad145447 100644 --- a/api/envoy/admin/v2alpha/mutex_stats.proto +++ b/api/envoy/admin/v2alpha/mutex_stats.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.admin.v2alpha"; option java_outer_classname = "MutexStatsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/admin/v2alpha"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: MutexStats] diff --git a/api/envoy/admin/v2alpha/server_info.proto b/api/envoy/admin/v2alpha/server_info.proto index b9db6bbc1e1f..53411641e2f0 100644 --- a/api/envoy/admin/v2alpha/server_info.proto +++ b/api/envoy/admin/v2alpha/server_info.proto @@ -10,6 +10,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.admin.v2alpha"; option java_outer_classname = "ServerInfoProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/admin/v2alpha"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Server State] diff --git a/api/envoy/admin/v2alpha/tap.proto b/api/envoy/admin/v2alpha/tap.proto index 6335b4db6284..7a25ce856a67 100644 --- a/api/envoy/admin/v2alpha/tap.proto +++ b/api/envoy/admin/v2alpha/tap.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.admin.v2alpha"; option java_outer_classname = "TapProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/admin/v2alpha"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Tap] diff --git a/api/envoy/admin/v3/BUILD b/api/envoy/admin/v3/BUILD index 548dc41982ab..507cffde4ade 100644 --- a/api/envoy/admin/v3/BUILD +++ b/api/envoy/admin/v3/BUILD @@ -6,7 +6,6 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ - "//envoy/admin/v2alpha:pkg", "//envoy/config/bootstrap/v3:pkg", "//envoy/config/cluster/v3:pkg", "//envoy/config/core/v3:pkg", diff --git a/api/envoy/admin/v3/certs.proto b/api/envoy/admin/v3/certs.proto index 5580bb5ef17d..b19cec7c3499 100644 --- a/api/envoy/admin/v3/certs.proto +++ b/api/envoy/admin/v3/certs.proto @@ -10,6 +10,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.admin.v3"; option java_outer_classname = "CertsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/admin/v3;adminv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Certificates] diff --git a/api/envoy/admin/v3/clusters.proto b/api/envoy/admin/v3/clusters.proto index 509280f46624..1395700a84cf 100644 --- a/api/envoy/admin/v3/clusters.proto +++ b/api/envoy/admin/v3/clusters.proto @@ -15,6 +15,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.admin.v3"; option java_outer_classname = "ClustersProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/admin/v3;adminv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Clusters] diff --git a/api/envoy/admin/v3/config_dump.proto b/api/envoy/admin/v3/config_dump.proto index ddafb56b3936..336d5b13eec1 100644 --- a/api/envoy/admin/v3/config_dump.proto +++ b/api/envoy/admin/v3/config_dump.proto @@ -13,6 +13,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.admin.v3"; option java_outer_classname = "ConfigDumpProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/admin/v3;adminv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: ConfigDump] diff --git a/api/envoy/admin/v3/init_dump.proto b/api/envoy/admin/v3/init_dump.proto index 0c2eb738c431..13a374dcc1bb 100644 --- a/api/envoy/admin/v3/init_dump.proto +++ b/api/envoy/admin/v3/init_dump.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.admin.v3"; option java_outer_classname = "InitDumpProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/admin/v3;adminv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: InitDump] diff --git a/api/envoy/admin/v3/listeners.proto b/api/envoy/admin/v3/listeners.proto index 6197a44e4243..86e35890bd67 100644 --- a/api/envoy/admin/v3/listeners.proto +++ b/api/envoy/admin/v3/listeners.proto @@ -10,6 +10,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.admin.v3"; option java_outer_classname = "ListenersProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/admin/v3;adminv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Listeners] diff --git a/api/envoy/admin/v3/memory.proto b/api/envoy/admin/v3/memory.proto index bcf9f271748d..522bf5e7888a 100644 --- a/api/envoy/admin/v3/memory.proto +++ b/api/envoy/admin/v3/memory.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.admin.v3"; option java_outer_classname = "MemoryProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/admin/v3;adminv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Memory] diff --git a/api/envoy/admin/v3/metrics.proto b/api/envoy/admin/v3/metrics.proto index 71592ac1e9ec..7280d955c9f9 100644 --- a/api/envoy/admin/v3/metrics.proto +++ b/api/envoy/admin/v3/metrics.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.admin.v3"; option java_outer_classname = "MetricsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/admin/v3;adminv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Metrics] diff --git a/api/envoy/admin/v3/mutex_stats.proto b/api/envoy/admin/v3/mutex_stats.proto index 49965d87ae80..8a3003f07cae 100644 --- a/api/envoy/admin/v3/mutex_stats.proto +++ b/api/envoy/admin/v3/mutex_stats.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.admin.v3"; option java_outer_classname = "MutexStatsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/admin/v3;adminv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: MutexStats] diff --git a/api/envoy/admin/v3/server_info.proto b/api/envoy/admin/v3/server_info.proto index 15b2c74e8831..851642990487 100644 --- a/api/envoy/admin/v3/server_info.proto +++ b/api/envoy/admin/v3/server_info.proto @@ -12,6 +12,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.admin.v3"; option java_outer_classname = "ServerInfoProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/admin/v3;adminv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Server State] @@ -58,7 +59,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 +190,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/api/envoy/admin/v3/tap.proto b/api/envoy/admin/v3/tap.proto index 934170b2deea..49eeb86039be 100644 --- a/api/envoy/admin/v3/tap.proto +++ b/api/envoy/admin/v3/tap.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.admin.v3"; option java_outer_classname = "TapProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/admin/v3;adminv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Tap] diff --git a/api/envoy/annotations/deprecation.proto b/api/envoy/annotations/deprecation.proto index ce02ab98a8dc..c9a96f1ae27d 100644 --- a/api/envoy/annotations/deprecation.proto +++ b/api/envoy/annotations/deprecation.proto @@ -1,6 +1,7 @@ syntax = "proto3"; package envoy.annotations; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/annotations"; import "google/protobuf/descriptor.proto"; diff --git a/api/envoy/annotations/resource.proto b/api/envoy/annotations/resource.proto index bd794c68dd7a..3877afc7fe36 100644 --- a/api/envoy/annotations/resource.proto +++ b/api/envoy/annotations/resource.proto @@ -1,6 +1,7 @@ syntax = "proto3"; package envoy.annotations; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/annotations"; import "google/protobuf/descriptor.proto"; diff --git a/api/envoy/api/v2/auth/cert.proto b/api/envoy/api/v2/auth/cert.proto index 6a9cbddd2508..81e2672d9b61 100644 --- a/api/envoy/api/v2/auth/cert.proto +++ b/api/envoy/api/v2/auth/cert.proto @@ -11,5 +11,6 @@ import public "envoy/api/v2/auth/tls.proto"; option java_package = "io.envoyproxy.envoy.api.v2.auth"; option java_outer_classname = "CertProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.transport_sockets.tls.v3"; diff --git a/api/envoy/api/v2/auth/common.proto b/api/envoy/api/v2/auth/common.proto index c8122f401029..23e5ceb1a3e1 100644 --- a/api/envoy/api/v2/auth/common.proto +++ b/api/envoy/api/v2/auth/common.proto @@ -17,6 +17,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2.auth"; option java_outer_classname = "CommonProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.transport_sockets.tls.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/auth/secret.proto b/api/envoy/api/v2/auth/secret.proto index 3a6d8cf7dcb6..4a4ab3bf169d 100644 --- a/api/envoy/api/v2/auth/secret.proto +++ b/api/envoy/api/v2/auth/secret.proto @@ -13,6 +13,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.api.v2.auth"; option java_outer_classname = "SecretProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.transport_sockets.tls.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/auth/tls.proto b/api/envoy/api/v2/auth/tls.proto index 201973a2b9de..911ada77d3fb 100644 --- a/api/envoy/api/v2/auth/tls.proto +++ b/api/envoy/api/v2/auth/tls.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2.auth"; option java_outer_classname = "TlsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.transport_sockets.tls.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; @@ -81,10 +82,9 @@ message DownstreamTlsContext { bool disable_stateless_session_resumption = 7; } - // If specified, session_timeout will change maximum lifetime (in seconds) of TLS session - // Currently this value is used as a hint to `TLS session ticket lifetime (for TLSv1.2) - // ` - // only seconds could be specified (fractional seconds are going to be ignored). + // If specified, ``session_timeout`` will change the maximum lifetime (in seconds) of the TLS session. + // Currently this value is used as a hint for the `TLS session ticket lifetime (for TLSv1.2) `_. + // Only seconds can be specified (fractional seconds are ignored). google.protobuf.Duration session_timeout = 6 [(validate.rules).duration = { lt {seconds: 4294967296} gte {} diff --git a/api/envoy/api/v2/cds.proto b/api/envoy/api/v2/cds.proto index 0b657a0fa452..38f7c3c19ecc 100644 --- a/api/envoy/api/v2/cds.proto +++ b/api/envoy/api/v2/cds.proto @@ -15,6 +15,7 @@ import public "envoy/api/v2/cluster.proto"; option java_package = "io.envoyproxy.envoy.api.v2"; option java_outer_classname = "CdsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2;apiv2"; option java_generic_services = true; option (udpa.annotations.file_migrate).move_to_package = "envoy.service.cluster.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/cluster.proto b/api/envoy/api/v2/cluster.proto index fab95f71b763..b1b6751de4b6 100644 --- a/api/envoy/api/v2/cluster.proto +++ b/api/envoy/api/v2/cluster.proto @@ -27,6 +27,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2"; option java_outer_classname = "ClusterProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2;apiv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.cluster.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/cluster/circuit_breaker.proto b/api/envoy/api/v2/cluster/circuit_breaker.proto index 510619b26429..9b11be6accb6 100644 --- a/api/envoy/api/v2/cluster/circuit_breaker.proto +++ b/api/envoy/api/v2/cluster/circuit_breaker.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2.cluster"; option java_outer_classname = "CircuitBreakerProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/cluster"; option csharp_namespace = "Envoy.Api.V2.ClusterNS"; option ruby_package = "Envoy.Api.V2.ClusterNS"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.cluster.v3"; diff --git a/api/envoy/api/v2/cluster/filter.proto b/api/envoy/api/v2/cluster/filter.proto index b87ad79d8f35..828fe5a9da91 100644 --- a/api/envoy/api/v2/cluster/filter.proto +++ b/api/envoy/api/v2/cluster/filter.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2.cluster"; option java_outer_classname = "FilterProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/cluster"; option csharp_namespace = "Envoy.Api.V2.ClusterNS"; option ruby_package = "Envoy.Api.V2.ClusterNS"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.cluster.v3"; diff --git a/api/envoy/api/v2/cluster/outlier_detection.proto b/api/envoy/api/v2/cluster/outlier_detection.proto index 6cf35e41ff15..a7f75e05fb4e 100644 --- a/api/envoy/api/v2/cluster/outlier_detection.proto +++ b/api/envoy/api/v2/cluster/outlier_detection.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2.cluster"; option java_outer_classname = "OutlierDetectionProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/cluster"; option csharp_namespace = "Envoy.Api.V2.ClusterNS"; option ruby_package = "Envoy.Api.V2.ClusterNS"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.cluster.v3"; diff --git a/api/envoy/api/v2/core/address.proto b/api/envoy/api/v2/core/address.proto index fdcb4e7d94f9..3399538be105 100644 --- a/api/envoy/api/v2/core/address.proto +++ b/api/envoy/api/v2/core/address.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2.core"; option java_outer_classname = "AddressProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.core.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/core/backoff.proto b/api/envoy/api/v2/core/backoff.proto index e45c71e39be8..845dfce39e07 100644 --- a/api/envoy/api/v2/core/backoff.proto +++ b/api/envoy/api/v2/core/backoff.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2.core"; option java_outer_classname = "BackoffProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.core.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/core/base.proto b/api/envoy/api/v2/core/base.proto index 32cd90b4ee1b..94b346bc3e81 100644 --- a/api/envoy/api/v2/core/base.proto +++ b/api/envoy/api/v2/core/base.proto @@ -21,6 +21,7 @@ import public "envoy/api/v2/core/socket_option.proto"; option java_package = "io.envoyproxy.envoy.api.v2.core"; option java_outer_classname = "BaseProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.core.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/core/config_source.proto b/api/envoy/api/v2/core/config_source.proto index 6cf44dbe9bbd..b3b400ae64c8 100644 --- a/api/envoy/api/v2/core/config_source.proto +++ b/api/envoy/api/v2/core/config_source.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2.core"; option java_outer_classname = "ConfigSourceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.core.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/core/event_service_config.proto b/api/envoy/api/v2/core/event_service_config.proto index f822f8c6b630..12ec25d4d41c 100644 --- a/api/envoy/api/v2/core/event_service_config.proto +++ b/api/envoy/api/v2/core/event_service_config.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2.core"; option java_outer_classname = "EventServiceConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.core.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/core/grpc_method_list.proto b/api/envoy/api/v2/core/grpc_method_list.proto index 3d646484b359..b4dbe69c7f5c 100644 --- a/api/envoy/api/v2/core/grpc_method_list.proto +++ b/api/envoy/api/v2/core/grpc_method_list.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2.core"; option java_outer_classname = "GrpcMethodListProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.core.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/core/grpc_service.proto b/api/envoy/api/v2/core/grpc_service.proto index dd789644e1d7..faafb7f0f7f0 100644 --- a/api/envoy/api/v2/core/grpc_service.proto +++ b/api/envoy/api/v2/core/grpc_service.proto @@ -17,6 +17,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2.core"; option java_outer_classname = "GrpcServiceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.core.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/core/health_check.proto b/api/envoy/api/v2/core/health_check.proto index bc4ae3e5c866..347ac9c96b90 100644 --- a/api/envoy/api/v2/core/health_check.proto +++ b/api/envoy/api/v2/core/health_check.proto @@ -21,6 +21,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2.core"; option java_outer_classname = "HealthCheckProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.core.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/core/http_uri.proto b/api/envoy/api/v2/core/http_uri.proto index cd1a0660e330..cb95125b90c8 100644 --- a/api/envoy/api/v2/core/http_uri.proto +++ b/api/envoy/api/v2/core/http_uri.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2.core"; option java_outer_classname = "HttpUriProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.core.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/core/protocol.proto b/api/envoy/api/v2/core/protocol.proto index ae1a86424cf0..3b7fe3589643 100644 --- a/api/envoy/api/v2/core/protocol.proto +++ b/api/envoy/api/v2/core/protocol.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2.core"; option java_outer_classname = "ProtocolProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.core.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/core/socket_option.proto b/api/envoy/api/v2/core/socket_option.proto index 39678ad1b8bc..da8140596ddf 100644 --- a/api/envoy/api/v2/core/socket_option.proto +++ b/api/envoy/api/v2/core/socket_option.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2.core"; option java_outer_classname = "SocketOptionProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.core.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/discovery.proto b/api/envoy/api/v2/discovery.proto index da2690f867fc..fc5370688a7e 100644 --- a/api/envoy/api/v2/discovery.proto +++ b/api/envoy/api/v2/discovery.proto @@ -13,6 +13,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.api.v2"; option java_outer_classname = "DiscoveryProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2;apiv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.service.discovery.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/eds.proto b/api/envoy/api/v2/eds.proto index d757f17fc2f3..4bd923555513 100644 --- a/api/envoy/api/v2/eds.proto +++ b/api/envoy/api/v2/eds.proto @@ -15,6 +15,7 @@ import public "envoy/api/v2/endpoint.proto"; option java_package = "io.envoyproxy.envoy.api.v2"; option java_outer_classname = "EdsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2;apiv2"; option java_generic_services = true; option (udpa.annotations.file_migrate).move_to_package = "envoy.service.endpoint.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/endpoint.proto b/api/envoy/api/v2/endpoint.proto index 70bac3c6c4f6..13e90521b63d 100644 --- a/api/envoy/api/v2/endpoint.proto +++ b/api/envoy/api/v2/endpoint.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2"; option java_outer_classname = "EndpointProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2;apiv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.endpoint.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/endpoint/endpoint.proto b/api/envoy/api/v2/endpoint/endpoint.proto index 247c9ae265a5..2c2e9daa5c0e 100644 --- a/api/envoy/api/v2/endpoint/endpoint.proto +++ b/api/envoy/api/v2/endpoint/endpoint.proto @@ -7,3 +7,4 @@ import public "envoy/api/v2/endpoint/endpoint_components.proto"; option java_package = "io.envoyproxy.envoy.api.v2.endpoint"; option java_outer_classname = "EndpointProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/endpoint"; diff --git a/api/envoy/api/v2/endpoint/endpoint_components.proto b/api/envoy/api/v2/endpoint/endpoint_components.proto index 78d45e2e08d0..86a533bf0e6a 100644 --- a/api/envoy/api/v2/endpoint/endpoint_components.proto +++ b/api/envoy/api/v2/endpoint/endpoint_components.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2.endpoint"; option java_outer_classname = "EndpointComponentsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/endpoint"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.endpoint.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/endpoint/load_report.proto b/api/envoy/api/v2/endpoint/load_report.proto index 928aed6102df..09dda612e4b1 100644 --- a/api/envoy/api/v2/endpoint/load_report.proto +++ b/api/envoy/api/v2/endpoint/load_report.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2.endpoint"; option java_outer_classname = "LoadReportProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/endpoint"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.endpoint.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/lds.proto b/api/envoy/api/v2/lds.proto index 01d9949777dd..9c66e5426daf 100644 --- a/api/envoy/api/v2/lds.proto +++ b/api/envoy/api/v2/lds.proto @@ -15,6 +15,7 @@ import public "envoy/api/v2/listener.proto"; option java_package = "io.envoyproxy.envoy.api.v2"; option java_outer_classname = "LdsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2;apiv2"; option java_generic_services = true; option (udpa.annotations.file_migrate).move_to_package = "envoy.service.listener.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/listener.proto b/api/envoy/api/v2/listener.proto index 1fdd202de42a..139816dc2867 100644 --- a/api/envoy/api/v2/listener.proto +++ b/api/envoy/api/v2/listener.proto @@ -20,6 +20,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2"; option java_outer_classname = "ListenerProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2;apiv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.listener.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/listener/listener.proto b/api/envoy/api/v2/listener/listener.proto index 273b29cb5dd3..5f6b85090500 100644 --- a/api/envoy/api/v2/listener/listener.proto +++ b/api/envoy/api/v2/listener/listener.proto @@ -7,5 +7,6 @@ import public "envoy/api/v2/listener/listener_components.proto"; option java_package = "io.envoyproxy.envoy.api.v2.listener"; option java_outer_classname = "ListenerProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/listener"; option csharp_namespace = "Envoy.Api.V2.ListenerNS"; option ruby_package = "Envoy.Api.V2.ListenerNS"; diff --git a/api/envoy/api/v2/listener/listener_components.proto b/api/envoy/api/v2/listener/listener_components.proto index 08738962c5ee..e02642d21921 100644 --- a/api/envoy/api/v2/listener/listener_components.proto +++ b/api/envoy/api/v2/listener/listener_components.proto @@ -18,6 +18,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2.listener"; option java_outer_classname = "ListenerComponentsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/listener"; option csharp_namespace = "Envoy.Api.V2.ListenerNS"; option ruby_package = "Envoy.Api.V2.ListenerNS"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.listener.v3"; diff --git a/api/envoy/api/v2/listener/quic_config.proto b/api/envoy/api/v2/listener/quic_config.proto index 2a4616bb09c9..d56797a33123 100644 --- a/api/envoy/api/v2/listener/quic_config.proto +++ b/api/envoy/api/v2/listener/quic_config.proto @@ -11,6 +11,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.api.v2.listener"; option java_outer_classname = "QuicConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/listener"; option csharp_namespace = "Envoy.Api.V2.ListenerNS"; option ruby_package = "Envoy.Api.V2.ListenerNS"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.listener.v3"; diff --git a/api/envoy/api/v2/listener/udp_listener_config.proto b/api/envoy/api/v2/listener/udp_listener_config.proto index d4d29531f3aa..49e14698176c 100644 --- a/api/envoy/api/v2/listener/udp_listener_config.proto +++ b/api/envoy/api/v2/listener/udp_listener_config.proto @@ -11,6 +11,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.api.v2.listener"; option java_outer_classname = "UdpListenerConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/listener"; option csharp_namespace = "Envoy.Api.V2.ListenerNS"; option ruby_package = "Envoy.Api.V2.ListenerNS"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.listener.v3"; diff --git a/api/envoy/api/v2/ratelimit/ratelimit.proto b/api/envoy/api/v2/ratelimit/ratelimit.proto index 5ac72c69a6fb..7245692a1e77 100644 --- a/api/envoy/api/v2/ratelimit/ratelimit.proto +++ b/api/envoy/api/v2/ratelimit/ratelimit.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2.ratelimit"; option java_outer_classname = "RatelimitProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/ratelimit"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.common.ratelimit.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/rds.proto b/api/envoy/api/v2/rds.proto index faa5fdcf3194..2ac30541aefd 100644 --- a/api/envoy/api/v2/rds.proto +++ b/api/envoy/api/v2/rds.proto @@ -15,6 +15,7 @@ import public "envoy/api/v2/route.proto"; option java_package = "io.envoyproxy.envoy.api.v2"; option java_outer_classname = "RdsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2;apiv2"; option java_generic_services = true; option (udpa.annotations.file_migrate).move_to_package = "envoy.service.route.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/route.proto b/api/envoy/api/v2/route.proto index 549f134a7f43..4f9e40a44099 100644 --- a/api/envoy/api/v2/route.proto +++ b/api/envoy/api/v2/route.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2"; option java_outer_classname = "RouteProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2;apiv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.route.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/route/route.proto b/api/envoy/api/v2/route/route.proto index ec13e9e5c801..0c52d051dd00 100644 --- a/api/envoy/api/v2/route/route.proto +++ b/api/envoy/api/v2/route/route.proto @@ -7,3 +7,4 @@ import public "envoy/api/v2/route/route_components.proto"; option java_package = "io.envoyproxy.envoy.api.v2.route"; option java_outer_classname = "RouteProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/route"; diff --git a/api/envoy/api/v2/route/route_components.proto b/api/envoy/api/v2/route/route_components.proto index d73fbb8674c9..062e73231d6a 100644 --- a/api/envoy/api/v2/route/route_components.proto +++ b/api/envoy/api/v2/route/route_components.proto @@ -22,6 +22,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2.route"; option java_outer_classname = "RouteComponentsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2/route"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.route.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; @@ -675,8 +676,8 @@ message RouteAction { message FilterState { // The name of the Object in the per-request filterState, which is an - // Envoy::Http::Hashable object. If there is no data associated with the key, - // or the stored object is not Envoy::Http::Hashable, no hash will be produced. + // Envoy::Hashable object. If there is no data associated with the key, + // or the stored object is not Envoy::Hashable, no hash will be produced. string key = 1 [(validate.rules).string = {min_bytes: 1}]; } diff --git a/api/envoy/api/v2/scoped_route.proto b/api/envoy/api/v2/scoped_route.proto index 0841bd08723c..f3902d9d9e7e 100644 --- a/api/envoy/api/v2/scoped_route.proto +++ b/api/envoy/api/v2/scoped_route.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.api.v2"; option java_outer_classname = "ScopedRouteProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2;apiv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.route.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/api/v2/srds.proto b/api/envoy/api/v2/srds.proto index 0edb99a1eccb..4f0ecab76574 100644 --- a/api/envoy/api/v2/srds.proto +++ b/api/envoy/api/v2/srds.proto @@ -15,6 +15,7 @@ import public "envoy/api/v2/scoped_route.proto"; option java_package = "io.envoyproxy.envoy.api.v2"; option java_outer_classname = "SrdsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/api/v2;apiv2"; option java_generic_services = true; option (udpa.annotations.file_migrate).move_to_package = "envoy.service.route.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/accesslog/v2/als.proto b/api/envoy/config/accesslog/v2/als.proto index 5b4106af106e..38f5edff8492 100644 --- a/api/envoy/config/accesslog/v2/als.proto +++ b/api/envoy/config/accesslog/v2/als.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.accesslog.v2"; option java_outer_classname = "AlsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v2;accesslogv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.access_loggers.grpc.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/accesslog/v2/file.proto b/api/envoy/config/accesslog/v2/file.proto index 9b8671c81358..0619493362c2 100644 --- a/api/envoy/config/accesslog/v2/file.proto +++ b/api/envoy/config/accesslog/v2/file.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.accesslog.v2"; option java_outer_classname = "FileProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v2;accesslogv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.access_loggers.file.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/accesslog/v3/BUILD b/api/envoy/config/accesslog/v3/BUILD index 27f7828529fd..7e5e77fc70f5 100644 --- a/api/envoy/config/accesslog/v3/BUILD +++ b/api/envoy/config/accesslog/v3/BUILD @@ -7,7 +7,6 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ "//envoy/config/core/v3:pkg", - "//envoy/config/filter/accesslog/v2:pkg", "//envoy/config/route/v3:pkg", "//envoy/type/matcher/v3:pkg", "//envoy/type/v3:pkg", diff --git a/api/envoy/config/accesslog/v3/accesslog.proto b/api/envoy/config/accesslog/v3/accesslog.proto index bb53286380c9..a29fa839ea5a 100644 --- a/api/envoy/config/accesslog/v3/accesslog.proto +++ b/api/envoy/config/accesslog/v3/accesslog.proto @@ -17,6 +17,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.accesslog.v3"; option java_outer_classname = "AccesslogProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v3;accesslogv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Common access log types] @@ -83,6 +84,7 @@ message AccessLogFilter { GrpcStatusFilter grpc_status_filter = 10; // Extension filter. + // [#extension-category: envoy.access_loggers.extension_filters] ExtensionFilter extension_filter = 11; // Metadata Filter diff --git a/api/envoy/config/bootstrap/v2/bootstrap.proto b/api/envoy/config/bootstrap/v2/bootstrap.proto index 30c276f24276..68a08bc37ac9 100644 --- a/api/envoy/config/bootstrap/v2/bootstrap.proto +++ b/api/envoy/config/bootstrap/v2/bootstrap.proto @@ -25,6 +25,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.bootstrap.v2"; option java_outer_classname = "BootstrapProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v2;bootstrapv2"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Bootstrap] diff --git a/api/envoy/config/bootstrap/v3/BUILD b/api/envoy/config/bootstrap/v3/BUILD index 48e8fb522c99..23067e57ca46 100644 --- a/api/envoy/config/bootstrap/v3/BUILD +++ b/api/envoy/config/bootstrap/v3/BUILD @@ -8,7 +8,6 @@ api_proto_package( deps = [ "//envoy/annotations:pkg", "//envoy/config/accesslog/v3:pkg", - "//envoy/config/bootstrap/v2:pkg", "//envoy/config/cluster/v3:pkg", "//envoy/config/core/v3:pkg", "//envoy/config/listener/v3:pkg", diff --git a/api/envoy/config/bootstrap/v3/bootstrap.proto b/api/envoy/config/bootstrap/v3/bootstrap.proto index 0cb494e16323..bde4d5c3965b 100644 --- a/api/envoy/config/bootstrap/v3/bootstrap.proto +++ b/api/envoy/config/bootstrap/v3/bootstrap.proto @@ -32,6 +32,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.bootstrap.v3"; option java_outer_classname = "BootstrapProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v3;bootstrapv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Bootstrap] @@ -40,7 +41,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // ` for more detail. // Bootstrap :ref:`configuration overview `. -// [#next-free-field: 33] +// [#next-free-field: 34] message Bootstrap { option (udpa.annotations.versioning).previous_message_type = "envoy.config.bootstrap.v2.Bootstrap"; @@ -325,11 +326,15 @@ message Bootstrap { // // Note that the 'set-cookie' header cannot be registered as inline header. repeated CustomInlineHeader inline_headers = 32; + + // Optional path to a file with performance tracing data created by "Perfetto" SDK in binary + // ProtoBuf format. The default value is "envoy.pftrace". + string perf_tracing_file_path = 33; } // Administration interface :ref:`operations documentation // `. -// [#next-free-field: 6] +// [#next-free-field: 7] message Admin { option (udpa.annotations.versioning).previous_message_type = "envoy.config.bootstrap.v2.Admin"; @@ -355,6 +360,10 @@ message Admin { // Additional socket options that may not be present in Envoy source code or // precompiled binaries. repeated core.v3.SocketOption socket_options = 4; + + // Indicates whether :ref:`global_downstream_max_connections ` + // should apply to the admin interface or not. + bool ignore_global_conn_limit = 6; } // Cluster manager :ref:`architecture overview `. diff --git a/api/envoy/config/cluster/aggregate/v2alpha/cluster.proto b/api/envoy/config/cluster/aggregate/v2alpha/cluster.proto index a0fdadd75724..3a6506eb8dc2 100644 --- a/api/envoy/config/cluster/aggregate/v2alpha/cluster.proto +++ b/api/envoy/config/cluster/aggregate/v2alpha/cluster.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.cluster.aggregate.v2alpha"; option java_outer_classname = "ClusterProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/cluster/aggregate/v2alpha"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.clusters.aggregate.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/cluster/dynamic_forward_proxy/v2alpha/cluster.proto b/api/envoy/config/cluster/dynamic_forward_proxy/v2alpha/cluster.proto index 33f5ffe057e3..fb3fc8338e9d 100644 --- a/api/envoy/config/cluster/dynamic_forward_proxy/v2alpha/cluster.proto +++ b/api/envoy/config/cluster/dynamic_forward_proxy/v2alpha/cluster.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.cluster.dynamic_forward_proxy.v2alpha"; option java_outer_classname = "ClusterProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/cluster/dynamic_forward_proxy/v2alpha"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.clusters.dynamic_forward_proxy.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/cluster/redis/redis_cluster.proto b/api/envoy/config/cluster/redis/redis_cluster.proto index abe88f76a6ff..78baa8337119 100644 --- a/api/envoy/config/cluster/redis/redis_cluster.proto +++ b/api/envoy/config/cluster/redis/redis_cluster.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.cluster.redis"; option java_outer_classname = "RedisClusterProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/cluster/redis"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Redis Cluster Configuration] diff --git a/api/envoy/config/cluster/v3/BUILD b/api/envoy/config/cluster/v3/BUILD index 92530e0fa197..21528bbc7bfb 100644 --- a/api/envoy/config/cluster/v3/BUILD +++ b/api/envoy/config/cluster/v3/BUILD @@ -7,8 +7,6 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ "//envoy/annotations:pkg", - "//envoy/api/v2:pkg", - "//envoy/api/v2/cluster:pkg", "//envoy/config/core/v3:pkg", "//envoy/config/endpoint/v3:pkg", "//envoy/type/v3:pkg", diff --git a/api/envoy/config/cluster/v3/circuit_breaker.proto b/api/envoy/config/cluster/v3/circuit_breaker.proto index 82cd329b91a7..3cb1904ebd4e 100644 --- a/api/envoy/config/cluster/v3/circuit_breaker.proto +++ b/api/envoy/config/cluster/v3/circuit_breaker.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.cluster.v3"; option java_outer_classname = "CircuitBreakerProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3;clusterv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Circuit breakers] @@ -59,10 +60,12 @@ message CircuitBreakers { // The maximum number of pending requests that Envoy will allow to the // upstream cluster. If not specified, the default is 1024. + // This limit is applied as a connection limit for non-HTTP traffic. google.protobuf.UInt32Value max_pending_requests = 3; // The maximum number of parallel requests that Envoy will make to the // upstream cluster. If not specified, the default is 1024. + // This limit does not apply to non-HTTP traffic. google.protobuf.UInt32Value max_requests = 4; // The maximum number of parallel retries that Envoy will allow to the diff --git a/api/envoy/config/cluster/v3/cluster.proto b/api/envoy/config/cluster/v3/cluster.proto index 34a8608e9e0e..704cfd3347d2 100644 --- a/api/envoy/config/cluster/v3/cluster.proto +++ b/api/envoy/config/cluster/v3/cluster.proto @@ -32,6 +32,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.cluster.v3"; option java_outer_classname = "ClusterProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3;clusterv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Cluster configuration] @@ -112,9 +113,9 @@ message Cluster { // Use the new :ref:`load_balancing_policy // ` field to determine the LB policy. - // [#next-major-version: In the v3 API, we should consider deprecating the lb_policy field - // and instead using the new load_balancing_policy field as the one and only mechanism for - // configuring this.] + // This has been deprecated in favor of using the :ref:`load_balancing_policy + // ` field without + // setting any value in :ref:`lb_policy`. LOAD_BALANCING_POLICY_CONFIG = 7; } @@ -129,6 +130,8 @@ message Cluster { // If V4_PREFERRED is specified, the DNS resolver will first perform a lookup for addresses in the // IPv4 family and fallback to a lookup for addresses in the IPv6 family. i.e., the callback // target will only get v6 addresses if there were NO v4 addresses to return. + // If ALL is specified, the DNS resolver will perform a lookup for both IPv4 and IPv6 families, + // and return all resolved addresses. // For cluster types other than // :ref:`STRICT_DNS` and // :ref:`LOGICAL_DNS`, @@ -140,6 +143,7 @@ message Cluster { V4_ONLY = 1; V6_ONLY = 2; V4_PREFERRED = 3; + ALL = 4; } enum ClusterProtocolSelection { @@ -1044,9 +1048,8 @@ message Cluster { // servers of this cluster. repeated Filter filters = 40; - // New mechanism for LB policy configuration. Used only if the - // :ref:`lb_policy` field has the value - // :ref:`LOAD_BALANCING_POLICY_CONFIG`. + // If this field is set and is supported by the client, it will supersede the value of + // :ref:`lb_policy`. LoadBalancingPolicy load_balancing_policy = 41; // [#not-implemented-hide:] diff --git a/api/envoy/config/cluster/v3/filter.proto b/api/envoy/config/cluster/v3/filter.proto index 7d11b87bcd5d..a98674b72f01 100644 --- a/api/envoy/config/cluster/v3/filter.proto +++ b/api/envoy/config/cluster/v3/filter.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.cluster.v3"; option java_outer_classname = "FilterProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3;clusterv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Upstream filters] diff --git a/api/envoy/config/cluster/v3/outlier_detection.proto b/api/envoy/config/cluster/v3/outlier_detection.proto index b19e95db99b7..28dd52085fad 100644 --- a/api/envoy/config/cluster/v3/outlier_detection.proto +++ b/api/envoy/config/cluster/v3/outlier_detection.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.cluster.v3"; option java_outer_classname = "OutlierDetectionProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3;clusterv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Outlier detection] diff --git a/api/envoy/config/common/dynamic_forward_proxy/v2alpha/dns_cache.proto b/api/envoy/config/common/dynamic_forward_proxy/v2alpha/dns_cache.proto index 3941c20aeb80..d04754bd45c8 100644 --- a/api/envoy/config/common/dynamic_forward_proxy/v2alpha/dns_cache.proto +++ b/api/envoy/config/common/dynamic_forward_proxy/v2alpha/dns_cache.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.common.dynamic_forward_proxy.v2alpha"; option java_outer_classname = "DnsCacheProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/common/dynamic_forward_proxy/v2alpha"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.common.dynamic_forward_proxy.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/common/key_value/v3/config.proto b/api/envoy/config/common/key_value/v3/config.proto index 8d62c0986308..566461fb084c 100644 --- a/api/envoy/config/common/key_value/v3/config.proto +++ b/api/envoy/config/common/key_value/v3/config.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.common.key_value.v3"; option java_outer_classname = "ConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/common/key_value/v3;key_valuev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Key Value Store storage plugin] diff --git a/api/envoy/config/common/matcher/v3/matcher.proto b/api/envoy/config/common/matcher/v3/matcher.proto index 1fb8c83ec3ef..5b9da519687a 100644 --- a/api/envoy/config/common/matcher/v3/matcher.proto +++ b/api/envoy/config/common/matcher/v3/matcher.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.common.matcher.v3"; option java_outer_classname = "MatcherProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/common/matcher/v3;matcherv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Unified Matcher API] diff --git a/api/envoy/config/common/tap/v2alpha/common.proto b/api/envoy/config/common/tap/v2alpha/common.proto index 6db1ecceddc4..23b7eeeda6d0 100644 --- a/api/envoy/config/common/tap/v2alpha/common.proto +++ b/api/envoy/config/common/tap/v2alpha/common.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.common.tap.v2alpha"; option java_outer_classname = "CommonProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/common/tap/v2alpha"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.common.tap.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/core/v3/BUILD b/api/envoy/config/core/v3/BUILD index 3fbb6b0e1f18..812e942fc3b0 100644 --- a/api/envoy/config/core/v3/BUILD +++ b/api/envoy/config/core/v3/BUILD @@ -7,7 +7,6 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ "//envoy/annotations:pkg", - "//envoy/api/v2/core:pkg", "//envoy/type/matcher/v3:pkg", "//envoy/type/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", diff --git a/api/envoy/config/core/v3/address.proto b/api/envoy/config/core/v3/address.proto index 06876d5f8e41..df5001bdd4f2 100644 --- a/api/envoy/config/core/v3/address.proto +++ b/api/envoy/config/core/v3/address.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.core.v3"; option java_outer_classname = "AddressProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/core/v3;corev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Network addresses] diff --git a/api/envoy/config/core/v3/backoff.proto b/api/envoy/config/core/v3/backoff.proto index 3ffa97bb0299..1899d1abf114 100644 --- a/api/envoy/config/core/v3/backoff.proto +++ b/api/envoy/config/core/v3/backoff.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.core.v3"; option java_outer_classname = "BackoffProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/core/v3;corev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Backoff Strategy] diff --git a/api/envoy/config/core/v3/base.proto b/api/envoy/config/core/v3/base.proto index c9921f25efe3..f8d94a49dd62 100644 --- a/api/envoy/config/core/v3/base.proto +++ b/api/envoy/config/core/v3/base.proto @@ -23,6 +23,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.core.v3"; option java_outer_classname = "BaseProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/core/v3;corev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Common types] diff --git a/api/envoy/config/core/v3/config_source.proto b/api/envoy/config/core/v3/config_source.proto index 43519c010b75..07898774b95c 100644 --- a/api/envoy/config/core/v3/config_source.proto +++ b/api/envoy/config/core/v3/config_source.proto @@ -17,6 +17,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.core.v3"; option java_outer_classname = "ConfigSourceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/core/v3;corev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Configuration sources] diff --git a/api/envoy/config/core/v3/event_service_config.proto b/api/envoy/config/core/v3/event_service_config.proto index b3552e3975a3..68c8df4076ea 100644 --- a/api/envoy/config/core/v3/event_service_config.proto +++ b/api/envoy/config/core/v3/event_service_config.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.core.v3"; option java_outer_classname = "EventServiceConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/core/v3;corev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#not-implemented-hide:] diff --git a/api/envoy/config/core/v3/extension.proto b/api/envoy/config/core/v3/extension.proto index adb7cb1ba400..12a37cd8c01f 100644 --- a/api/envoy/config/core/v3/extension.proto +++ b/api/envoy/config/core/v3/extension.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.core.v3"; option java_outer_classname = "ExtensionProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/core/v3;corev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Extension configuration] diff --git a/api/envoy/config/core/v3/grpc_method_list.proto b/api/envoy/config/core/v3/grpc_method_list.proto index e79ec24e0201..8242b425e5da 100644 --- a/api/envoy/config/core/v3/grpc_method_list.proto +++ b/api/envoy/config/core/v3/grpc_method_list.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.core.v3"; option java_outer_classname = "GrpcMethodListProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/core/v3;corev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: gRPC method list] diff --git a/api/envoy/config/core/v3/grpc_service.proto b/api/envoy/config/core/v3/grpc_service.proto index a7f29c8f5298..4fb609558057 100644 --- a/api/envoy/config/core/v3/grpc_service.proto +++ b/api/envoy/config/core/v3/grpc_service.proto @@ -18,6 +18,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.core.v3"; option java_outer_classname = "GrpcServiceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/core/v3;corev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: gRPC services] diff --git a/api/envoy/config/core/v3/health_check.proto b/api/envoy/config/core/v3/health_check.proto index 81a9b6c7f153..fcae6eb697c4 100644 --- a/api/envoy/config/core/v3/health_check.proto +++ b/api/envoy/config/core/v3/health_check.proto @@ -20,6 +20,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.core.v3"; option java_outer_classname = "HealthCheckProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/core/v3;corev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Health check] diff --git a/api/envoy/config/core/v3/http_uri.proto b/api/envoy/config/core/v3/http_uri.proto index 5d1fc239e07e..ec0f71f90558 100644 --- a/api/envoy/config/core/v3/http_uri.proto +++ b/api/envoy/config/core/v3/http_uri.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.core.v3"; option java_outer_classname = "HttpUriProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/core/v3;corev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: HTTP Service URI ] diff --git a/api/envoy/config/core/v3/protocol.proto b/api/envoy/config/core/v3/protocol.proto index dfa50ded9290..4c6577c6533d 100644 --- a/api/envoy/config/core/v3/protocol.proto +++ b/api/envoy/config/core/v3/protocol.proto @@ -18,6 +18,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.core.v3"; option java_outer_classname = "ProtocolProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/core/v3;corev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Protocol options] @@ -28,11 +29,38 @@ message TcpProtocolOptions { "envoy.api.v2.core.TcpProtocolOptions"; } +// Config for keepalive probes in a QUIC connection. +// Note that QUIC keep-alive probing packets work differently from HTTP/2 keep-alive PINGs in a sense that the probing packet +// itself doesn't timeout waiting for a probing response. Quic has a shorter idle timeout than TCP, so it doesn't rely on such probing to discover dead connections. If the peer fails to respond, the connection will idle timeout eventually. Thus, they are configured differently from :ref:`connection_keepalive `. +message QuicKeepAliveSettings { + // The max interval for a connection to send keep-alive probing packets (with PING or PATH_RESPONSE). The value should be smaller than :ref:`connection idle_timeout ` to prevent idle timeout while not less than 1s to avoid throttling the connection or flooding the peer with probes. + // + // If :ref:`initial_interval ` is absent or zero, a client connection will use this value to start probing. + // + // If zero, disable keepalive probing. + // If absent, use the QUICHE default interval to probe. + google.protobuf.Duration max_interval = 1 [(validate.rules).duration = { + lte {} + gte {seconds: 1} + }]; + + // The interval to send the first few keep-alive probing packets to prevent connection from hitting the idle timeout. Subsequent probes will be sent, each one with an interval exponentially longer than previous one, till it reaches :ref:`max_interval `. And the probes afterwards will always use :ref:`max_interval `. + // + // The value should be smaller than :ref:`connection idle_timeout ` to prevent idle timeout and smaller than max_interval to take effect. + // + // If absent or zero, disable keepalive probing for a server connection. For a client connection, if :ref:`max_interval ` is also zero, do not keepalive, otherwise use max_interval or QUICHE default to probe all the time. + google.protobuf.Duration initial_interval = 2 [(validate.rules).duration = { + lte {} + gte {seconds: 1} + }]; +} + // QUIC protocol options which apply to both downstream and upstream connections. +// [#next-free-field: 6] message QuicProtocolOptions { // Maximum number of streams that the client can negotiate per connection. 100 // if not specified. - google.protobuf.UInt32Value max_concurrent_streams = 1; + google.protobuf.UInt32Value max_concurrent_streams = 1 [(validate.rules).uint32 = {gte: 1}]; // `Initial stream-level flow-control receive window // `_ size. Valid values range from @@ -56,15 +84,16 @@ 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. + QuicKeepAliveSettings connection_keepalive = 5; } message UpstreamHttpProtocolOptions { @@ -261,7 +290,7 @@ message Http1ProtocolOptions { // Allows Envoy to process requests/responses with both `Content-Length` and `Transfer-Encoding` // headers set. By default such messages are rejected, but if option is enabled - Envoy will // remove Content-Length header and process message. - // See `RFC7230, sec. 3.3.3 ` for details. + // See `RFC7230, sec. 3.3.3 `_ for details. // // .. attention:: // Enabling this option might lead to request smuggling vulnerability, especially if traffic @@ -299,6 +328,8 @@ message KeepaliveSettings { // If this is zero, this type of PING will not be sent. // If an interval ping is outstanding, a second ping will not be sent as the // interval ping will determine if the connection is dead. + // + // The same feature for HTTP/3 is given by inheritance from QUICHE which uses :ref:`connection idle_timeout ` and the current PTO of the connection to decide whether to probe before sending a new request. google.protobuf.Duration connection_idle_interval = 4 [(validate.rules).duration = {gte {nanos: 1000000}}]; } @@ -378,8 +409,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, @@ -387,8 +416,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 @@ -397,8 +424,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 @@ -412,8 +437,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 @@ -430,8 +453,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/api/envoy/config/core/v3/proxy_protocol.proto b/api/envoy/config/core/v3/proxy_protocol.proto index 40b33f33ff5b..9cfdbe5f66cd 100644 --- a/api/envoy/config/core/v3/proxy_protocol.proto +++ b/api/envoy/config/core/v3/proxy_protocol.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.core.v3"; option java_outer_classname = "ProxyProtocolProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/core/v3;corev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Proxy Protocol] diff --git a/api/envoy/config/core/v3/resolver.proto b/api/envoy/config/core/v3/resolver.proto index 2ca4eaab0e5d..f4d103ab038e 100644 --- a/api/envoy/config/core/v3/resolver.proto +++ b/api/envoy/config/core/v3/resolver.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.core.v3"; option java_outer_classname = "ResolverProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/core/v3;corev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Resolver] diff --git a/api/envoy/config/core/v3/socket_option.proto b/api/envoy/config/core/v3/socket_option.proto index b22169b86aeb..b165a1c740d2 100644 --- a/api/envoy/config/core/v3/socket_option.proto +++ b/api/envoy/config/core/v3/socket_option.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.core.v3"; option java_outer_classname = "SocketOptionProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/core/v3;corev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Socket Option ] diff --git a/api/envoy/config/core/v3/substitution_format_string.proto b/api/envoy/config/core/v3/substitution_format_string.proto index b2a1c5e13ee4..c3a213a1ca00 100644 --- a/api/envoy/config/core/v3/substitution_format_string.proto +++ b/api/envoy/config/core/v3/substitution_format_string.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.core.v3"; option java_outer_classname = "SubstitutionFormatStringProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/core/v3;corev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Substitution format string] diff --git a/api/envoy/config/core/v3/udp_socket_config.proto b/api/envoy/config/core/v3/udp_socket_config.proto index 00033eabdb8a..ec9f77f06872 100644 --- a/api/envoy/config/core/v3/udp_socket_config.proto +++ b/api/envoy/config/core/v3/udp_socket_config.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.core.v3"; option java_outer_classname = "UdpSocketConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/core/v3;corev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: UDP socket config] diff --git a/api/envoy/config/endpoint/v3/BUILD b/api/envoy/config/endpoint/v3/BUILD index 420cd0878233..ad2fc9a9a84f 100644 --- a/api/envoy/config/endpoint/v3/BUILD +++ b/api/envoy/config/endpoint/v3/BUILD @@ -6,8 +6,6 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ - "//envoy/api/v2:pkg", - "//envoy/api/v2/endpoint:pkg", "//envoy/config/core/v3:pkg", "//envoy/type/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", diff --git a/api/envoy/config/endpoint/v3/endpoint.proto b/api/envoy/config/endpoint/v3/endpoint.proto index b22a644eeaeb..7edfb66c9a82 100644 --- a/api/envoy/config/endpoint/v3/endpoint.proto +++ b/api/envoy/config/endpoint/v3/endpoint.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.endpoint.v3"; option java_outer_classname = "EndpointProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3;endpointv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Endpoint configuration] diff --git a/api/envoy/config/endpoint/v3/endpoint_components.proto b/api/envoy/config/endpoint/v3/endpoint_components.proto index 1faf64e20c2c..23b5d2174885 100644 --- a/api/envoy/config/endpoint/v3/endpoint_components.proto +++ b/api/envoy/config/endpoint/v3/endpoint_components.proto @@ -16,6 +16,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.endpoint.v3"; option java_outer_classname = "EndpointComponentsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3;endpointv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Endpoints] diff --git a/api/envoy/config/endpoint/v3/load_report.proto b/api/envoy/config/endpoint/v3/load_report.proto index c114fa726622..85ecae7f2d2c 100644 --- a/api/envoy/config/endpoint/v3/load_report.proto +++ b/api/envoy/config/endpoint/v3/load_report.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.endpoint.v3"; option java_outer_classname = "LoadReportProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3;endpointv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Load Report] diff --git a/api/envoy/config/filter/accesslog/v2/accesslog.proto b/api/envoy/config/filter/accesslog/v2/accesslog.proto index 25d27bfbd106..7f38515421ce 100644 --- a/api/envoy/config/filter/accesslog/v2/accesslog.proto +++ b/api/envoy/config/filter/accesslog/v2/accesslog.proto @@ -16,6 +16,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.accesslog.v2"; option java_outer_classname = "AccesslogProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/accesslog/v2;accesslogv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.accesslog.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/dubbo/router/v2alpha1/router.proto b/api/envoy/config/filter/dubbo/router/v2alpha1/router.proto index 2e35bb7f7c5b..0c92db6074d7 100644 --- a/api/envoy/config/filter/dubbo/router/v2alpha1/router.proto +++ b/api/envoy/config/filter/dubbo/router/v2alpha1/router.proto @@ -8,6 +8,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.filter.dubbo.router.v2alpha1"; option java_outer_classname = "RouterProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/dubbo/router/v2alpha1"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.dubbo_proxy.router.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/fault/v2/fault.proto b/api/envoy/config/filter/fault/v2/fault.proto index 016140d10f84..d23e50b1916b 100644 --- a/api/envoy/config/filter/fault/v2/fault.proto +++ b/api/envoy/config/filter/fault/v2/fault.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.fault.v2"; option java_outer_classname = "FaultProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/fault/v2;faultv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.common.fault.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/adaptive_concurrency/v2alpha/adaptive_concurrency.proto b/api/envoy/config/filter/http/adaptive_concurrency/v2alpha/adaptive_concurrency.proto index 6860b6d6ef2b..26d5dbffdd42 100644 --- a/api/envoy/config/filter/http/adaptive_concurrency/v2alpha/adaptive_concurrency.proto +++ b/api/envoy/config/filter/http/adaptive_concurrency/v2alpha/adaptive_concurrency.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.adaptive_concurrency.v2alpha"; option java_outer_classname = "AdaptiveConcurrencyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/adaptive_concurrency/v2alpha"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.adaptive_concurrency.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/aws_lambda/v2alpha/aws_lambda.proto b/api/envoy/config/filter/http/aws_lambda/v2alpha/aws_lambda.proto index 43823286286a..41315785ffaf 100644 --- a/api/envoy/config/filter/http/aws_lambda/v2alpha/aws_lambda.proto +++ b/api/envoy/config/filter/http/aws_lambda/v2alpha/aws_lambda.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.aws_lambda.v2alpha"; option java_outer_classname = "AwsLambdaProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/aws_lambda/v2alpha"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.aws_lambda.v3"; option (udpa.annotations.file_status).work_in_progress = true; diff --git a/api/envoy/config/filter/http/aws_request_signing/v2alpha/aws_request_signing.proto b/api/envoy/config/filter/http/aws_request_signing/v2alpha/aws_request_signing.proto index 5ebb92c01dfa..d76ea9d6bd82 100644 --- a/api/envoy/config/filter/http/aws_request_signing/v2alpha/aws_request_signing.proto +++ b/api/envoy/config/filter/http/aws_request_signing/v2alpha/aws_request_signing.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.aws_request_signing.v2alpha"; option java_outer_classname = "AwsRequestSigningProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/aws_request_signing/v2alpha"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.aws_request_signing.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/buffer/v2/buffer.proto b/api/envoy/config/filter/http/buffer/v2/buffer.proto index 56961d22fe09..3be47dac275b 100644 --- a/api/envoy/config/filter/http/buffer/v2/buffer.proto +++ b/api/envoy/config/filter/http/buffer/v2/buffer.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.buffer.v2"; option java_outer_classname = "BufferProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/buffer/v2;bufferv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.buffer.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/cache/v2alpha/cache.proto b/api/envoy/config/filter/http/cache/v2alpha/cache.proto index 98035c05d45a..3d743357de3f 100644 --- a/api/envoy/config/filter/http/cache/v2alpha/cache.proto +++ b/api/envoy/config/filter/http/cache/v2alpha/cache.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v2alpha"; option java_outer_classname = "CacheProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/cache/v2alpha"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.cache.v3alpha"; option (udpa.annotations.file_status).work_in_progress = true; diff --git a/api/envoy/config/filter/http/compressor/v2/compressor.proto b/api/envoy/config/filter/http/compressor/v2/compressor.proto index d62d0d7a42fa..441edffd81a1 100644 --- a/api/envoy/config/filter/http/compressor/v2/compressor.proto +++ b/api/envoy/config/filter/http/compressor/v2/compressor.proto @@ -12,6 +12,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.compressor.v2"; option java_outer_classname = "CompressorProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/compressor/v2;compressorv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.compressor.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/cors/v2/cors.proto b/api/envoy/config/filter/http/cors/v2/cors.proto index 9060a9c38fda..80c91de989cd 100644 --- a/api/envoy/config/filter/http/cors/v2/cors.proto +++ b/api/envoy/config/filter/http/cors/v2/cors.proto @@ -8,6 +8,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.cors.v2"; option java_outer_classname = "CorsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/cors/v2;corsv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.cors.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/csrf/v2/csrf.proto b/api/envoy/config/filter/http/csrf/v2/csrf.proto index 3c2c9110e9fe..d2e02dc415e0 100644 --- a/api/envoy/config/filter/http/csrf/v2/csrf.proto +++ b/api/envoy/config/filter/http/csrf/v2/csrf.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.csrf.v2"; option java_outer_classname = "CsrfProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/csrf/v2;csrfv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.csrf.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/dynamic_forward_proxy/v2alpha/dynamic_forward_proxy.proto b/api/envoy/config/filter/http/dynamic_forward_proxy/v2alpha/dynamic_forward_proxy.proto index 436bb6bf4616..4e28b9bbe17d 100644 --- a/api/envoy/config/filter/http/dynamic_forward_proxy/v2alpha/dynamic_forward_proxy.proto +++ b/api/envoy/config/filter/http/dynamic_forward_proxy/v2alpha/dynamic_forward_proxy.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.dynamic_forward_proxy.v2alpha"; option java_outer_classname = "DynamicForwardProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/dynamic_forward_proxy/v2alpha"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.dynamic_forward_proxy.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/dynamo/v2/dynamo.proto b/api/envoy/config/filter/http/dynamo/v2/dynamo.proto index 011d22f768c8..5aa885cc05fb 100644 --- a/api/envoy/config/filter/http/dynamo/v2/dynamo.proto +++ b/api/envoy/config/filter/http/dynamo/v2/dynamo.proto @@ -8,6 +8,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.dynamo.v2"; option java_outer_classname = "DynamoProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/dynamo/v2;dynamov2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.dynamo.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/ext_authz/v2/ext_authz.proto b/api/envoy/config/filter/http/ext_authz/v2/ext_authz.proto index b9a807d82edb..abf7ddd93311 100644 --- a/api/envoy/config/filter/http/ext_authz/v2/ext_authz.proto +++ b/api/envoy/config/filter/http/ext_authz/v2/ext_authz.proto @@ -16,6 +16,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.ext_authz.v2"; option java_outer_classname = "ExtAuthzProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/ext_authz/v2;ext_authzv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.ext_authz.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/fault/v2/fault.proto b/api/envoy/config/filter/http/fault/v2/fault.proto index cb99b0d71bbd..109dfb4cfbee 100644 --- a/api/envoy/config/filter/http/fault/v2/fault.proto +++ b/api/envoy/config/filter/http/fault/v2/fault.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.fault.v2"; option java_outer_classname = "FaultProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/fault/v2;faultv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.fault.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/grpc_http1_bridge/v2/config.proto b/api/envoy/config/filter/http/grpc_http1_bridge/v2/config.proto index b4331dad5031..18c057e289e6 100644 --- a/api/envoy/config/filter/http/grpc_http1_bridge/v2/config.proto +++ b/api/envoy/config/filter/http/grpc_http1_bridge/v2/config.proto @@ -8,6 +8,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.grpc_http1_bridge.v2"; option java_outer_classname = "ConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/grpc_http1_bridge/v2;grpc_http1_bridgev2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.grpc_http1_bridge.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/grpc_http1_reverse_bridge/v2alpha1/config.proto b/api/envoy/config/filter/http/grpc_http1_reverse_bridge/v2alpha1/config.proto index 8b916d327e19..0200ec899f4a 100644 --- a/api/envoy/config/filter/http/grpc_http1_reverse_bridge/v2alpha1/config.proto +++ b/api/envoy/config/filter/http/grpc_http1_reverse_bridge/v2alpha1/config.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.grpc_http1_reverse_bridge.v2alpha1"; option java_outer_classname = "ConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/grpc_http1_reverse_bridge/v2alpha1"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.grpc_http1_reverse_bridge.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/grpc_stats/v2alpha/config.proto b/api/envoy/config/filter/http/grpc_stats/v2alpha/config.proto index fea48e6bb64f..e15a276ad6b2 100644 --- a/api/envoy/config/filter/http/grpc_stats/v2alpha/config.proto +++ b/api/envoy/config/filter/http/grpc_stats/v2alpha/config.proto @@ -12,6 +12,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.grpc_stats.v2alpha"; option java_outer_classname = "ConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/grpc_stats/v2alpha"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.grpc_stats.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/grpc_web/v2/grpc_web.proto b/api/envoy/config/filter/http/grpc_web/v2/grpc_web.proto index be23b4d87b58..0be39a18c03d 100644 --- a/api/envoy/config/filter/http/grpc_web/v2/grpc_web.proto +++ b/api/envoy/config/filter/http/grpc_web/v2/grpc_web.proto @@ -8,6 +8,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.grpc_web.v2"; option java_outer_classname = "GrpcWebProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/grpc_web/v2;grpc_webv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.grpc_web.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/gzip/v2/gzip.proto b/api/envoy/config/filter/http/gzip/v2/gzip.proto index 0c134c6208b1..0ff02f09571d 100644 --- a/api/envoy/config/filter/http/gzip/v2/gzip.proto +++ b/api/envoy/config/filter/http/gzip/v2/gzip.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.gzip.v2"; option java_outer_classname = "GzipProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/gzip/v2;gzipv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.gzip.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/header_to_metadata/v2/header_to_metadata.proto b/api/envoy/config/filter/http/header_to_metadata/v2/header_to_metadata.proto index 30de69d98b1c..c9ff092601ec 100644 --- a/api/envoy/config/filter/http/header_to_metadata/v2/header_to_metadata.proto +++ b/api/envoy/config/filter/http/header_to_metadata/v2/header_to_metadata.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.header_to_metadata.v2"; option java_outer_classname = "HeaderToMetadataProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/header_to_metadata/v2;header_to_metadatav2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.header_to_metadata.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/health_check/v2/health_check.proto b/api/envoy/config/filter/http/health_check/v2/health_check.proto index 7f2a486b2618..8e42ff719db7 100644 --- a/api/envoy/config/filter/http/health_check/v2/health_check.proto +++ b/api/envoy/config/filter/http/health_check/v2/health_check.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.health_check.v2"; option java_outer_classname = "HealthCheckProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/health_check/v2;health_checkv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.health_check.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/ip_tagging/v2/ip_tagging.proto b/api/envoy/config/filter/http/ip_tagging/v2/ip_tagging.proto index f99b18a12c71..79ca502b2641 100644 --- a/api/envoy/config/filter/http/ip_tagging/v2/ip_tagging.proto +++ b/api/envoy/config/filter/http/ip_tagging/v2/ip_tagging.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.ip_tagging.v2"; option java_outer_classname = "IpTaggingProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/ip_tagging/v2;ip_taggingv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.ip_tagging.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/jwt_authn/v2alpha/config.proto b/api/envoy/config/filter/http/jwt_authn/v2alpha/config.proto index 847e36f163ba..e87c9478db63 100644 --- a/api/envoy/config/filter/http/jwt_authn/v2alpha/config.proto +++ b/api/envoy/config/filter/http/jwt_authn/v2alpha/config.proto @@ -16,6 +16,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.jwt_authn.v2alpha"; option java_outer_classname = "ConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/jwt_authn/v2alpha"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.jwt_authn.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/lua/v2/lua.proto b/api/envoy/config/filter/http/lua/v2/lua.proto index 068b5e255df5..820011eef2fd 100644 --- a/api/envoy/config/filter/http/lua/v2/lua.proto +++ b/api/envoy/config/filter/http/lua/v2/lua.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.lua.v2"; option java_outer_classname = "LuaProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/lua/v2;luav2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.lua.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/on_demand/v2/on_demand.proto b/api/envoy/config/filter/http/on_demand/v2/on_demand.proto index 4c5aadf442cf..1dc02a9efcde 100644 --- a/api/envoy/config/filter/http/on_demand/v2/on_demand.proto +++ b/api/envoy/config/filter/http/on_demand/v2/on_demand.proto @@ -8,6 +8,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.on_demand.v2"; option java_outer_classname = "OnDemandProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/on_demand/v2;on_demandv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.on_demand.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/original_src/v2alpha1/original_src.proto b/api/envoy/config/filter/http/original_src/v2alpha1/original_src.proto index 8dfb4354d238..d713438e413e 100644 --- a/api/envoy/config/filter/http/original_src/v2alpha1/original_src.proto +++ b/api/envoy/config/filter/http/original_src/v2alpha1/original_src.proto @@ -8,6 +8,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.original_src.v2alpha1"; option java_outer_classname = "OriginalSrcProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/original_src/v2alpha1"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.original_src.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/rate_limit/v2/rate_limit.proto b/api/envoy/config/filter/http/rate_limit/v2/rate_limit.proto index b9361476bcfd..029fe76ff08d 100644 --- a/api/envoy/config/filter/http/rate_limit/v2/rate_limit.proto +++ b/api/envoy/config/filter/http/rate_limit/v2/rate_limit.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.rate_limit.v2"; option java_outer_classname = "RateLimitProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/rate_limit/v2;rate_limitv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.ratelimit.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/rbac/v2/rbac.proto b/api/envoy/config/filter/http/rbac/v2/rbac.proto index 87d76a8f913e..8266c0295604 100644 --- a/api/envoy/config/filter/http/rbac/v2/rbac.proto +++ b/api/envoy/config/filter/http/rbac/v2/rbac.proto @@ -10,6 +10,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.rbac.v2"; option java_outer_classname = "RbacProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/rbac/v2;rbacv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.rbac.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/router/v2/router.proto b/api/envoy/config/filter/http/router/v2/router.proto index c95500cf8168..e47e73f8c7ab 100644 --- a/api/envoy/config/filter/http/router/v2/router.proto +++ b/api/envoy/config/filter/http/router/v2/router.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.router.v2"; option java_outer_classname = "RouterProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/router/v2;routerv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.router.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/squash/v2/squash.proto b/api/envoy/config/filter/http/squash/v2/squash.proto index a7ae625d2ee3..1099414cb1ea 100644 --- a/api/envoy/config/filter/http/squash/v2/squash.proto +++ b/api/envoy/config/filter/http/squash/v2/squash.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.squash.v2"; option java_outer_classname = "SquashProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/squash/v2;squashv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.squash.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/tap/v2alpha/tap.proto b/api/envoy/config/filter/http/tap/v2alpha/tap.proto index 3f984cec0d6c..b8381c540655 100644 --- a/api/envoy/config/filter/http/tap/v2alpha/tap.proto +++ b/api/envoy/config/filter/http/tap/v2alpha/tap.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.tap.v2alpha"; option java_outer_classname = "TapProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/tap/v2alpha"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.tap.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/http/transcoder/v2/transcoder.proto b/api/envoy/config/filter/http/transcoder/v2/transcoder.proto index ac6d7eefa78a..9f9cb4d87a2c 100644 --- a/api/envoy/config/filter/http/transcoder/v2/transcoder.proto +++ b/api/envoy/config/filter/http/transcoder/v2/transcoder.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.transcoder.v2"; option java_outer_classname = "TranscoderProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/http/transcoder/v2;transcoderv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.grpc_json_transcoder.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/listener/http_inspector/v2/http_inspector.proto b/api/envoy/config/filter/listener/http_inspector/v2/http_inspector.proto index 0496207e09bc..1b492ca3ff8d 100644 --- a/api/envoy/config/filter/listener/http_inspector/v2/http_inspector.proto +++ b/api/envoy/config/filter/listener/http_inspector/v2/http_inspector.proto @@ -8,6 +8,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.filter.listener.http_inspector.v2"; option java_outer_classname = "HttpInspectorProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/listener/http_inspector/v2;http_inspectorv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.listener.http_inspector.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/listener/original_dst/v2/original_dst.proto b/api/envoy/config/filter/listener/original_dst/v2/original_dst.proto index fa4acee45fc1..67f0fa53b4ee 100644 --- a/api/envoy/config/filter/listener/original_dst/v2/original_dst.proto +++ b/api/envoy/config/filter/listener/original_dst/v2/original_dst.proto @@ -8,6 +8,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.filter.listener.original_dst.v2"; option java_outer_classname = "OriginalDstProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/listener/original_dst/v2;original_dstv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.listener.original_dst.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/listener/original_src/v2alpha1/original_src.proto b/api/envoy/config/filter/listener/original_src/v2alpha1/original_src.proto index f9ddb98e745c..9b5219937f79 100644 --- a/api/envoy/config/filter/listener/original_src/v2alpha1/original_src.proto +++ b/api/envoy/config/filter/listener/original_src/v2alpha1/original_src.proto @@ -8,6 +8,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.filter.listener.original_src.v2alpha1"; option java_outer_classname = "OriginalSrcProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/listener/original_src/v2alpha1"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.listener.original_src.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/listener/proxy_protocol/v2/proxy_protocol.proto b/api/envoy/config/filter/listener/proxy_protocol/v2/proxy_protocol.proto index cabffb9fc0c0..9fbcd641cdd3 100644 --- a/api/envoy/config/filter/listener/proxy_protocol/v2/proxy_protocol.proto +++ b/api/envoy/config/filter/listener/proxy_protocol/v2/proxy_protocol.proto @@ -8,6 +8,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.filter.listener.proxy_protocol.v2"; option java_outer_classname = "ProxyProtocolProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/listener/proxy_protocol/v2;proxy_protocolv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.listener.proxy_protocol.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/listener/tls_inspector/v2/tls_inspector.proto b/api/envoy/config/filter/listener/tls_inspector/v2/tls_inspector.proto index 7ab679c47dc5..764627f74aa8 100644 --- a/api/envoy/config/filter/listener/tls_inspector/v2/tls_inspector.proto +++ b/api/envoy/config/filter/listener/tls_inspector/v2/tls_inspector.proto @@ -8,6 +8,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.filter.listener.tls_inspector.v2"; option java_outer_classname = "TlsInspectorProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/listener/tls_inspector/v2;tls_inspectorv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.listener.tls_inspector.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/network/client_ssl_auth/v2/client_ssl_auth.proto b/api/envoy/config/filter/network/client_ssl_auth/v2/client_ssl_auth.proto index 4da6d97ca299..5f4c05697035 100644 --- a/api/envoy/config/filter/network/client_ssl_auth/v2/client_ssl_auth.proto +++ b/api/envoy/config/filter/network/client_ssl_auth/v2/client_ssl_auth.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.network.client_ssl_auth.v2"; option java_outer_classname = "ClientSslAuthProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/client_ssl_auth/v2;client_ssl_authv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.client_ssl_auth.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/network/direct_response/v2/config.proto b/api/envoy/config/filter/network/direct_response/v2/config.proto index 15de7e3b5537..e0c018e2836c 100644 --- a/api/envoy/config/filter/network/direct_response/v2/config.proto +++ b/api/envoy/config/filter/network/direct_response/v2/config.proto @@ -10,6 +10,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.filter.network.direct_response.v2"; option java_outer_classname = "ConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/direct_response/v2;direct_responsev2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.direct_response.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/dubbo_proxy.proto b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/dubbo_proxy.proto index 47248932f94c..0a6c835973d6 100644 --- a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/dubbo_proxy.proto +++ b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/dubbo_proxy.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.network.dubbo_proxy.v2alpha1"; option java_outer_classname = "DubboProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/dubbo_proxy/v2alpha1"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.dubbo_proxy.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto index 9af461e3577c..cd287d06e79e 100644 --- a/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto +++ b/api/envoy/config/filter/network/dubbo_proxy/v2alpha1/route.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.network.dubbo_proxy.v2alpha1"; option java_outer_classname = "RouteProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/dubbo_proxy/v2alpha1"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.dubbo_proxy.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/network/echo/v2/echo.proto b/api/envoy/config/filter/network/echo/v2/echo.proto index 2b51ce4e18c3..5c8b31d1c19a 100644 --- a/api/envoy/config/filter/network/echo/v2/echo.proto +++ b/api/envoy/config/filter/network/echo/v2/echo.proto @@ -8,6 +8,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.filter.network.echo.v2"; option java_outer_classname = "EchoProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/echo/v2;echov2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.echo.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/network/ext_authz/v2/ext_authz.proto b/api/envoy/config/filter/network/ext_authz/v2/ext_authz.proto index 40cea7061868..2cda6d9ec1a1 100644 --- a/api/envoy/config/filter/network/ext_authz/v2/ext_authz.proto +++ b/api/envoy/config/filter/network/ext_authz/v2/ext_authz.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.network.ext_authz.v2"; option java_outer_classname = "ExtAuthzProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/ext_authz/v2;ext_authzv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.ext_authz.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/network/http_connection_manager/v2/http_connection_manager.proto b/api/envoy/config/filter/network/http_connection_manager/v2/http_connection_manager.proto index 3e7a4dc17769..6286e979a1f1 100644 --- a/api/envoy/config/filter/network/http_connection_manager/v2/http_connection_manager.proto +++ b/api/envoy/config/filter/network/http_connection_manager/v2/http_connection_manager.proto @@ -24,6 +24,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.network.http_connection_manager.v2"; option java_outer_classname = "HttpConnectionManagerProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2;http_connection_managerv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.http_connection_manager.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/network/kafka_broker/v2alpha1/kafka_broker.proto b/api/envoy/config/filter/network/kafka_broker/v2alpha1/kafka_broker.proto index ea2f60e71eed..3611c1d6759f 100644 --- a/api/envoy/config/filter/network/kafka_broker/v2alpha1/kafka_broker.proto +++ b/api/envoy/config/filter/network/kafka_broker/v2alpha1/kafka_broker.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.network.kafka_broker.v2alpha1"; option java_outer_classname = "KafkaBrokerProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/kafka_broker/v2alpha1"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.kafka_broker.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/network/local_rate_limit/v2alpha/local_rate_limit.proto b/api/envoy/config/filter/network/local_rate_limit/v2alpha/local_rate_limit.proto index 791b767f3e6a..9c21ab293b09 100644 --- a/api/envoy/config/filter/network/local_rate_limit/v2alpha/local_rate_limit.proto +++ b/api/envoy/config/filter/network/local_rate_limit/v2alpha/local_rate_limit.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.network.local_rate_limit.v2alpha"; option java_outer_classname = "LocalRateLimitProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/local_rate_limit/v2alpha"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.local_ratelimit.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/network/mongo_proxy/v2/mongo_proxy.proto b/api/envoy/config/filter/network/mongo_proxy/v2/mongo_proxy.proto index b261897858e2..88e914f752b3 100644 --- a/api/envoy/config/filter/network/mongo_proxy/v2/mongo_proxy.proto +++ b/api/envoy/config/filter/network/mongo_proxy/v2/mongo_proxy.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.network.mongo_proxy.v2"; option java_outer_classname = "MongoProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/mongo_proxy/v2;mongo_proxyv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.mongo_proxy.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/network/mysql_proxy/v1alpha1/mysql_proxy.proto b/api/envoy/config/filter/network/mysql_proxy/v1alpha1/mysql_proxy.proto index 78c6b7e971df..8c6066c65404 100644 --- a/api/envoy/config/filter/network/mysql_proxy/v1alpha1/mysql_proxy.proto +++ b/api/envoy/config/filter/network/mysql_proxy/v1alpha1/mysql_proxy.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.network.mysql_proxy.v1alpha1"; option java_outer_classname = "MysqlProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/mysql_proxy/v1alpha1"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.mysql_proxy.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/network/rate_limit/v2/rate_limit.proto b/api/envoy/config/filter/network/rate_limit/v2/rate_limit.proto index aed56c9af629..b9c02b2cc49a 100644 --- a/api/envoy/config/filter/network/rate_limit/v2/rate_limit.proto +++ b/api/envoy/config/filter/network/rate_limit/v2/rate_limit.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.network.rate_limit.v2"; option java_outer_classname = "RateLimitProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/rate_limit/v2;rate_limitv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.ratelimit.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/network/rbac/v2/rbac.proto b/api/envoy/config/filter/network/rbac/v2/rbac.proto index ce86794c71cc..780cdc85e31d 100644 --- a/api/envoy/config/filter/network/rbac/v2/rbac.proto +++ b/api/envoy/config/filter/network/rbac/v2/rbac.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.network.rbac.v2"; option java_outer_classname = "RbacProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/rbac/v2;rbacv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.rbac.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/network/redis_proxy/v2/redis_proxy.proto b/api/envoy/config/filter/network/redis_proxy/v2/redis_proxy.proto index 948d7c349ff0..c8b6807c3852 100644 --- a/api/envoy/config/filter/network/redis_proxy/v2/redis_proxy.proto +++ b/api/envoy/config/filter/network/redis_proxy/v2/redis_proxy.proto @@ -16,6 +16,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.network.redis_proxy.v2"; option java_outer_classname = "RedisProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/redis_proxy/v2;redis_proxyv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.redis_proxy.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/network/sni_cluster/v2/sni_cluster.proto b/api/envoy/config/filter/network/sni_cluster/v2/sni_cluster.proto index 71c161fc48f6..f5ef8de087f4 100644 --- a/api/envoy/config/filter/network/sni_cluster/v2/sni_cluster.proto +++ b/api/envoy/config/filter/network/sni_cluster/v2/sni_cluster.proto @@ -8,6 +8,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.filter.network.sni_cluster.v2"; option java_outer_classname = "SniClusterProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/sni_cluster/v2;sni_clusterv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.sni_cluster.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/network/tcp_proxy/v2/tcp_proxy.proto b/api/envoy/config/filter/network/tcp_proxy/v2/tcp_proxy.proto index 4ec68f320eed..25a020a670fb 100644 --- a/api/envoy/config/filter/network/tcp_proxy/v2/tcp_proxy.proto +++ b/api/envoy/config/filter/network/tcp_proxy/v2/tcp_proxy.proto @@ -17,6 +17,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.network.tcp_proxy.v2"; option java_outer_classname = "TcpProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/tcp_proxy/v2;tcp_proxyv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.tcp_proxy.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/network/thrift_proxy/v2alpha1/route.proto b/api/envoy/config/filter/network/thrift_proxy/v2alpha1/route.proto index 8230a52e341e..3144f3bf2304 100644 --- a/api/envoy/config/filter/network/thrift_proxy/v2alpha1/route.proto +++ b/api/envoy/config/filter/network/thrift_proxy/v2alpha1/route.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.network.thrift_proxy.v2alpha1"; option java_outer_classname = "RouteProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/thrift_proxy/v2alpha1"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.thrift_proxy.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/network/thrift_proxy/v2alpha1/thrift_proxy.proto b/api/envoy/config/filter/network/thrift_proxy/v2alpha1/thrift_proxy.proto index 96e750ef310d..56639dbf3e3b 100644 --- a/api/envoy/config/filter/network/thrift_proxy/v2alpha1/thrift_proxy.proto +++ b/api/envoy/config/filter/network/thrift_proxy/v2alpha1/thrift_proxy.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.network.thrift_proxy.v2alpha1"; option java_outer_classname = "ThriftProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/thrift_proxy/v2alpha1"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.thrift_proxy.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/network/zookeeper_proxy/v1alpha1/zookeeper_proxy.proto b/api/envoy/config/filter/network/zookeeper_proxy/v1alpha1/zookeeper_proxy.proto index cae622cecc34..68704f2b93a7 100644 --- a/api/envoy/config/filter/network/zookeeper_proxy/v1alpha1/zookeeper_proxy.proto +++ b/api/envoy/config/filter/network/zookeeper_proxy/v1alpha1/zookeeper_proxy.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.network.zookeeper_proxy.v1alpha1"; option java_outer_classname = "ZookeeperProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/zookeeper_proxy/v1alpha1"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.zookeeper_proxy.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/thrift/rate_limit/v2alpha1/rate_limit.proto b/api/envoy/config/filter/thrift/rate_limit/v2alpha1/rate_limit.proto index 389ddf35990e..0619db1082c0 100644 --- a/api/envoy/config/filter/thrift/rate_limit/v2alpha1/rate_limit.proto +++ b/api/envoy/config/filter/thrift/rate_limit/v2alpha1/rate_limit.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.thrift.rate_limit.v2alpha1"; option java_outer_classname = "RateLimitProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/thrift/rate_limit/v2alpha1"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.network.thrift_proxy.filters.ratelimit.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/filter/thrift/router/v2alpha1/router.proto b/api/envoy/config/filter/thrift/router/v2alpha1/router.proto index 5463ab6513be..5a7b988fe976 100644 --- a/api/envoy/config/filter/thrift/router/v2alpha1/router.proto +++ b/api/envoy/config/filter/thrift/router/v2alpha1/router.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.filter.thrift.router.v2alpha1"; option java_outer_classname = "RouterProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/thrift/router/v2alpha1"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Router] diff --git a/api/envoy/config/filter/udp/udp_proxy/v2alpha/udp_proxy.proto b/api/envoy/config/filter/udp/udp_proxy/v2alpha/udp_proxy.proto index 06dc150d5c70..e762caa971da 100644 --- a/api/envoy/config/filter/udp/udp_proxy/v2alpha/udp_proxy.proto +++ b/api/envoy/config/filter/udp/udp_proxy/v2alpha/udp_proxy.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.udp.udp_proxy.v2alpha"; option java_outer_classname = "UdpProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/filter/udp/udp_proxy/v2alpha"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.udp.udp_proxy.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/grpc_credential/v2alpha/aws_iam.proto b/api/envoy/config/grpc_credential/v2alpha/aws_iam.proto index b63d35af4018..d67900bd1441 100644 --- a/api/envoy/config/grpc_credential/v2alpha/aws_iam.proto +++ b/api/envoy/config/grpc_credential/v2alpha/aws_iam.proto @@ -8,6 +8,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.grpc_credential.v2alpha"; option java_outer_classname = "AwsIamProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/grpc_credential/v2alpha"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Grpc Credentials AWS IAM] diff --git a/api/envoy/config/grpc_credential/v2alpha/file_based_metadata.proto b/api/envoy/config/grpc_credential/v2alpha/file_based_metadata.proto index 41e67f0bf24b..07290a76db04 100644 --- a/api/envoy/config/grpc_credential/v2alpha/file_based_metadata.proto +++ b/api/envoy/config/grpc_credential/v2alpha/file_based_metadata.proto @@ -10,6 +10,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.grpc_credential.v2alpha"; option java_outer_classname = "FileBasedMetadataProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/grpc_credential/v2alpha"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Grpc Credentials File Based Metadata] diff --git a/api/envoy/config/grpc_credential/v3/aws_iam.proto b/api/envoy/config/grpc_credential/v3/aws_iam.proto index e2e9c7da4833..923d880fbd23 100644 --- a/api/envoy/config/grpc_credential/v3/aws_iam.proto +++ b/api/envoy/config/grpc_credential/v3/aws_iam.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.grpc_credential.v3"; option java_outer_classname = "AwsIamProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/grpc_credential/v3;grpc_credentialv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Grpc Credentials AWS IAM] diff --git a/api/envoy/config/grpc_credential/v3/file_based_metadata.proto b/api/envoy/config/grpc_credential/v3/file_based_metadata.proto index b364d2917099..a9e6c94a2cc7 100644 --- a/api/envoy/config/grpc_credential/v3/file_based_metadata.proto +++ b/api/envoy/config/grpc_credential/v3/file_based_metadata.proto @@ -11,6 +11,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.config.grpc_credential.v3"; option java_outer_classname = "FileBasedMetadataProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/grpc_credential/v3;grpc_credentialv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Grpc Credentials File Based Metadata] diff --git a/api/envoy/config/health_checker/redis/v2/redis.proto b/api/envoy/config/health_checker/redis/v2/redis.proto index 0c569f5c75e8..22d404c5368d 100644 --- a/api/envoy/config/health_checker/redis/v2/redis.proto +++ b/api/envoy/config/health_checker/redis/v2/redis.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.health_checker.redis.v2"; option java_outer_classname = "RedisProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/health_checker/redis/v2;redisv2"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Redis] diff --git a/api/envoy/config/listener/v2/api_listener.proto b/api/envoy/config/listener/v2/api_listener.proto index 6709d5fe0b52..ae47c7d338a9 100644 --- a/api/envoy/config/listener/v2/api_listener.proto +++ b/api/envoy/config/listener/v2/api_listener.proto @@ -10,6 +10,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.listener.v2"; option java_outer_classname = "ApiListenerProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/listener/v2;listenerv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.listener.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/listener/v3/BUILD b/api/envoy/config/listener/v3/BUILD index bea12662c0d7..c2d6c133a73a 100644 --- a/api/envoy/config/listener/v3/BUILD +++ b/api/envoy/config/listener/v3/BUILD @@ -7,11 +7,8 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ "//envoy/annotations:pkg", - "//envoy/api/v2:pkg", - "//envoy/api/v2/listener:pkg", "//envoy/config/accesslog/v3:pkg", "//envoy/config/core/v3:pkg", - "//envoy/config/listener/v2:pkg", "//envoy/type/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", "@com_github_cncf_udpa//xds/core/v3:pkg", diff --git a/api/envoy/config/listener/v3/api_listener.proto b/api/envoy/config/listener/v3/api_listener.proto index 77db7caaff5c..a3610e656883 100644 --- a/api/envoy/config/listener/v3/api_listener.proto +++ b/api/envoy/config/listener/v3/api_listener.proto @@ -10,6 +10,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.config.listener.v3"; option java_outer_classname = "ApiListenerProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3;listenerv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: API listener] diff --git a/api/envoy/config/listener/v3/listener.proto b/api/envoy/config/listener/v3/listener.proto index 92fb7e168a75..df64bb8e5002 100644 --- a/api/envoy/config/listener/v3/listener.proto +++ b/api/envoy/config/listener/v3/listener.proto @@ -24,6 +24,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.listener.v3"; option java_outer_classname = "ListenerProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3;listenerv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Listener configuration] @@ -35,7 +36,7 @@ message ListenerCollection { repeated xds.core.v3.CollectionEntry entries = 1; } -// [#next-free-field: 30] +// [#next-free-field: 32] message Listener { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.Listener"; @@ -314,4 +315,12 @@ message Listener { // [#not-implemented-hide:] InternalListenerConfig internal_listener = 27; } + + // Enable MPTCP (multi-path TCP) on this listener. Clients will be allowed to establish + // MPTCP connections. Non-MPTCP clients will fall back to regular TCP. + bool enable_mptcp = 30; + + // Whether the listener should limit connections based upon the value of + // :ref:`global_downstream_max_connections `. + bool ignore_global_conn_limit = 31; } diff --git a/api/envoy/config/listener/v3/listener_components.proto b/api/envoy/config/listener/v3/listener_components.proto index e737b14b1745..710ac7ab7595 100644 --- a/api/envoy/config/listener/v3/listener_components.proto +++ b/api/envoy/config/listener/v3/listener_components.proto @@ -19,6 +19,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.listener.v3"; option java_outer_classname = "ListenerComponentsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3;listenerv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Listener components] diff --git a/api/envoy/config/listener/v3/quic_config.proto b/api/envoy/config/listener/v3/quic_config.proto index 1432e1911b5d..9eadc05f718b 100644 --- a/api/envoy/config/listener/v3/quic_config.proto +++ b/api/envoy/config/listener/v3/quic_config.proto @@ -16,6 +16,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.listener.v3"; option java_outer_classname = "QuicConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3;listenerv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: QUIC listener config] diff --git a/api/envoy/config/listener/v3/udp_listener_config.proto b/api/envoy/config/listener/v3/udp_listener_config.proto index 57088ac5fe1f..16afc96799d2 100644 --- a/api/envoy/config/listener/v3/udp_listener_config.proto +++ b/api/envoy/config/listener/v3/udp_listener_config.proto @@ -11,6 +11,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.config.listener.v3"; option java_outer_classname = "UdpListenerConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3;listenerv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: UDP listener config] diff --git a/api/envoy/config/metrics/v2/metrics_service.proto b/api/envoy/config/metrics/v2/metrics_service.proto index f1f8662f0750..bb063770dd12 100644 --- a/api/envoy/config/metrics/v2/metrics_service.proto +++ b/api/envoy/config/metrics/v2/metrics_service.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.metrics.v2"; option java_outer_classname = "MetricsServiceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/metrics/v2;metricsv2"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Metrics service] diff --git a/api/envoy/config/metrics/v2/stats.proto b/api/envoy/config/metrics/v2/stats.proto index 62afcf56e4e7..592558a1cbed 100644 --- a/api/envoy/config/metrics/v2/stats.proto +++ b/api/envoy/config/metrics/v2/stats.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.metrics.v2"; option java_outer_classname = "StatsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/metrics/v2;metricsv2"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Stats] diff --git a/api/envoy/config/metrics/v3/BUILD b/api/envoy/config/metrics/v3/BUILD index 6de4af36aa4b..3f3a5395d2aa 100644 --- a/api/envoy/config/metrics/v3/BUILD +++ b/api/envoy/config/metrics/v3/BUILD @@ -7,7 +7,6 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ "//envoy/config/core/v3:pkg", - "//envoy/config/metrics/v2:pkg", "//envoy/type/matcher/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], diff --git a/api/envoy/config/metrics/v3/metrics_service.proto b/api/envoy/config/metrics/v3/metrics_service.proto index df3c71e6a630..effab34f9d47 100644 --- a/api/envoy/config/metrics/v3/metrics_service.proto +++ b/api/envoy/config/metrics/v3/metrics_service.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.metrics.v3"; option java_outer_classname = "MetricsServiceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/metrics/v3;metricsv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Metrics service] diff --git a/api/envoy/config/metrics/v3/stats.proto b/api/envoy/config/metrics/v3/stats.proto index d442cffe36ac..17ae761ea3c2 100644 --- a/api/envoy/config/metrics/v3/stats.proto +++ b/api/envoy/config/metrics/v3/stats.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.metrics.v3"; option java_outer_classname = "StatsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/metrics/v3;metricsv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Stats] diff --git a/api/envoy/config/overload/v2alpha/overload.proto b/api/envoy/config/overload/v2alpha/overload.proto index 03886cdee6d6..5c7639b01ea9 100644 --- a/api/envoy/config/overload/v2alpha/overload.proto +++ b/api/envoy/config/overload/v2alpha/overload.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.overload.v2alpha"; option java_outer_classname = "OverloadProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/overload/v2alpha"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Overload Manager] diff --git a/api/envoy/config/overload/v3/BUILD b/api/envoy/config/overload/v3/BUILD index 9fdea99c648d..9a76b7e148e0 100644 --- a/api/envoy/config/overload/v3/BUILD +++ b/api/envoy/config/overload/v3/BUILD @@ -6,7 +6,6 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ - "//envoy/config/overload/v2alpha:pkg", "//envoy/type/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], diff --git a/api/envoy/config/overload/v3/overload.proto b/api/envoy/config/overload/v3/overload.proto index 85fa761dbdd8..3868df234829 100644 --- a/api/envoy/config/overload/v3/overload.proto +++ b/api/envoy/config/overload/v3/overload.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.overload.v3"; option java_outer_classname = "OverloadProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/overload/v3;overloadv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Overload Manager] diff --git a/api/envoy/config/ratelimit/v2/rls.proto b/api/envoy/config/ratelimit/v2/rls.proto index 92801ea7b968..4fd3671de76e 100644 --- a/api/envoy/config/ratelimit/v2/rls.proto +++ b/api/envoy/config/ratelimit/v2/rls.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.ratelimit.v2"; option java_outer_classname = "RlsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/ratelimit/v2;ratelimitv2"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Rate limit service] diff --git a/api/envoy/config/ratelimit/v3/rls.proto b/api/envoy/config/ratelimit/v3/rls.proto index 98889b1e2882..6fc47ef501c0 100644 --- a/api/envoy/config/ratelimit/v3/rls.proto +++ b/api/envoy/config/ratelimit/v3/rls.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.ratelimit.v3"; option java_outer_classname = "RlsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/ratelimit/v3;ratelimitv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Rate limit service] diff --git a/api/envoy/config/rbac/v2/rbac.proto b/api/envoy/config/rbac/v2/rbac.proto index 943ac33e0859..941d62177208 100644 --- a/api/envoy/config/rbac/v2/rbac.proto +++ b/api/envoy/config/rbac/v2/rbac.proto @@ -16,6 +16,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.rbac.v2"; option java_outer_classname = "RbacProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v2;rbacv2"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Role Based Access Control (RBAC)] diff --git a/api/envoy/config/rbac/v3/rbac.proto b/api/envoy/config/rbac/v3/rbac.proto index 474f30a28563..8abde899d7ed 100644 --- a/api/envoy/config/rbac/v3/rbac.proto +++ b/api/envoy/config/rbac/v3/rbac.proto @@ -22,6 +22,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.rbac.v3"; option java_outer_classname = "RbacProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3;rbacv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Role Based Access Control (RBAC)] diff --git a/api/envoy/config/resource_monitor/fixed_heap/v2alpha/fixed_heap.proto b/api/envoy/config/resource_monitor/fixed_heap/v2alpha/fixed_heap.proto index 529622a071e7..3a5bec3901a9 100644 --- a/api/envoy/config/resource_monitor/fixed_heap/v2alpha/fixed_heap.proto +++ b/api/envoy/config/resource_monitor/fixed_heap/v2alpha/fixed_heap.proto @@ -8,6 +8,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.resource_monitor.fixed_heap.v2alpha"; option java_outer_classname = "FixedHeapProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/resource_monitor/fixed_heap/v2alpha"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Fixed heap] diff --git a/api/envoy/config/resource_monitor/injected_resource/v2alpha/injected_resource.proto b/api/envoy/config/resource_monitor/injected_resource/v2alpha/injected_resource.proto index a9f056d2d29a..3a804b439c99 100644 --- a/api/envoy/config/resource_monitor/injected_resource/v2alpha/injected_resource.proto +++ b/api/envoy/config/resource_monitor/injected_resource/v2alpha/injected_resource.proto @@ -8,6 +8,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.resource_monitor.injected_resource.v2alpha"; option java_outer_classname = "InjectedResourceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/resource_monitor/injected_resource/v2alpha"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Injected resource] diff --git a/api/envoy/config/retry/omit_canary_hosts/v2/omit_canary_hosts.proto b/api/envoy/config/retry/omit_canary_hosts/v2/omit_canary_hosts.proto index c2b2e58a1823..a1180fbcb4d0 100644 --- a/api/envoy/config/retry/omit_canary_hosts/v2/omit_canary_hosts.proto +++ b/api/envoy/config/retry/omit_canary_hosts/v2/omit_canary_hosts.proto @@ -8,6 +8,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.retry.omit_canary_hosts.v2"; option java_outer_classname = "OmitCanaryHostsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/retry/omit_canary_hosts/v2;omit_canary_hostsv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.retry.host.omit_canary_hosts.v3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/config/retry/omit_host_metadata/v2/omit_host_metadata_config.proto b/api/envoy/config/retry/omit_host_metadata/v2/omit_host_metadata_config.proto index d229cffef8ca..373a6c94e3e2 100644 --- a/api/envoy/config/retry/omit_host_metadata/v2/omit_host_metadata_config.proto +++ b/api/envoy/config/retry/omit_host_metadata/v2/omit_host_metadata_config.proto @@ -10,6 +10,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.retry.omit_host_metadata.v2"; option java_outer_classname = "OmitHostMetadataConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/retry/omit_host_metadata/v2;omit_host_metadatav2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.retry.host.omit_host_metadata.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/retry/previous_hosts/v2/previous_hosts.proto b/api/envoy/config/retry/previous_hosts/v2/previous_hosts.proto index f69c5054f9c9..6e5abf7496ab 100644 --- a/api/envoy/config/retry/previous_hosts/v2/previous_hosts.proto +++ b/api/envoy/config/retry/previous_hosts/v2/previous_hosts.proto @@ -8,6 +8,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.retry.previous_hosts.v2"; option java_outer_classname = "PreviousHostsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/retry/previous_hosts/v2;previous_hostsv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.retry.host.previous_hosts.v3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/config/retry/previous_priorities/previous_priorities_config.proto b/api/envoy/config/retry/previous_priorities/previous_priorities_config.proto index 3fc400c053a7..b10e4b6eabdb 100644 --- a/api/envoy/config/retry/previous_priorities/previous_priorities_config.proto +++ b/api/envoy/config/retry/previous_priorities/previous_priorities_config.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.retry.previous_priorities"; option java_outer_classname = "PreviousPrioritiesConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/retry/previous_priorities"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.retry.priority.previous_priorities.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/route/v3/BUILD b/api/envoy/config/route/v3/BUILD index b82843eee7dd..385c2c8c40e0 100644 --- a/api/envoy/config/route/v3/BUILD +++ b/api/envoy/config/route/v3/BUILD @@ -7,8 +7,6 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ "//envoy/annotations:pkg", - "//envoy/api/v2:pkg", - "//envoy/api/v2/route:pkg", "//envoy/config/core/v3:pkg", "//envoy/type/matcher/v3:pkg", "//envoy/type/metadata/v3:pkg", diff --git a/api/envoy/config/route/v3/route.proto b/api/envoy/config/route/v3/route.proto index e2bf52165be9..0e19f61c7e33 100644 --- a/api/envoy/config/route/v3/route.proto +++ b/api/envoy/config/route/v3/route.proto @@ -16,6 +16,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.route.v3"; option java_outer_classname = "RouteProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/route/v3;routev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: HTTP route configuration] diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index 5a915eee87ca..61325035c4c0 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -29,6 +29,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.route.v3"; option java_outer_classname = "RouteComponentsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/route/v3;routev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: HTTP route components] @@ -617,7 +618,7 @@ message CorsPolicy { core.v3.RuntimeFractionalPercent shadow_enabled = 10; } -// [#next-free-field: 38] +// [#next-free-field: 39] message RouteAction { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.route.RouteAction"; @@ -752,8 +753,8 @@ message RouteAction { "envoy.api.v2.route.RouteAction.HashPolicy.FilterState"; // The name of the Object in the per-request filterState, which is an - // Envoy::Http::Hashable object. If there is no data associated with the key, - // or the stored object is not Envoy::Http::Hashable, no hash will be produced. + // Envoy::Hashable object. If there is no data associated with the key, + // or the stored object is not Envoy::Hashable, no hash will be produced. string key = 1 [(validate.rules).string = {min_len: 1}]; } @@ -981,20 +982,29 @@ message RouteAction { oneof host_rewrite_specifier { // Indicates that during forwarding, the host header will be swapped with - // this value. + // this value. Using this option will append the + // :ref:`config_http_conn_man_headers_x-forwarded-host` header if + // :ref:`append_x_forwarded_host ` + // is set. string host_rewrite_literal = 6 [(validate.rules).string = {well_known_regex: HTTP_HEADER_VALUE strict: false}]; // Indicates that during forwarding, the host header will be swapped with // the hostname of the upstream host chosen by the cluster manager. This // option is applicable only when the destination cluster for a route is of - // type *strict_dns* or *logical_dns*. Setting this to true with other cluster - // types has no effect. + // type *strict_dns* or *logical_dns*. Setting this to true with other cluster types + // has no effect. Using this option will append the + // :ref:`config_http_conn_man_headers_x-forwarded-host` header if + // :ref:`append_x_forwarded_host ` + // is set. google.protobuf.BoolValue auto_host_rewrite = 7; // Indicates that during forwarding, the host header will be swapped with the content of given // downstream or :ref:`custom ` header. - // If header value is empty, host header is left intact. + // If header value is empty, host header is left intact. Using this option will append the + // :ref:`config_http_conn_man_headers_x-forwarded-host` header if + // :ref:`append_x_forwarded_host ` + // is set. // // .. attention:: // @@ -1010,6 +1020,10 @@ message RouteAction { // Indicates that during forwarding, the host header will be swapped with // the result of the regex substitution executed on path value with query and fragment removed. // This is useful for transitioning variable content between path segment and subdomain. + // Using this option will append the + // :ref:`config_http_conn_man_headers_x-forwarded-host` header if + // :ref:`append_x_forwarded_host ` + // is set. // // For example with the following config: // @@ -1025,6 +1039,15 @@ message RouteAction { type.matcher.v3.RegexMatchAndSubstitute host_rewrite_path_regex = 35; } + // If set, then a host rewrite action (one of + // :ref:`host_rewrite_literal `, + // :ref:`auto_host_rewrite `, + // :ref:`host_rewrite_header `, or + // :ref:`host_rewrite_path_regex `) + // causes the original value of the host header, if any, to be appended to the + // :ref:`config_http_conn_man_headers_x-forwarded-host` HTTP header. + bool append_x_forwarded_host = 38; + // Specifies the upstream timeout for the route. If not specified, the default is 15s. This // spans between the point at which the entire downstream request (i.e. end-of-stream) has been // processed and when the upstream response has been completely processed. A value of 0 will diff --git a/api/envoy/config/route/v3/scoped_route.proto b/api/envoy/config/route/v3/scoped_route.proto index 1b0904ec5723..4ac0ca7c23d5 100644 --- a/api/envoy/config/route/v3/scoped_route.proto +++ b/api/envoy/config/route/v3/scoped_route.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.route.v3"; option java_outer_classname = "ScopedRouteProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/route/v3;routev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: HTTP scoped routing configuration] diff --git a/api/envoy/config/tap/v3/BUILD b/api/envoy/config/tap/v3/BUILD index 416ccc0f9403..a457820fce67 100644 --- a/api/envoy/config/tap/v3/BUILD +++ b/api/envoy/config/tap/v3/BUILD @@ -10,7 +10,6 @@ api_proto_package( "//envoy/config/common/matcher/v3:pkg", "//envoy/config/core/v3:pkg", "//envoy/config/route/v3:pkg", - "//envoy/service/tap/v2alpha:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/api/envoy/config/tap/v3/common.proto b/api/envoy/config/tap/v3/common.proto index c25a2af5a3b5..e7d4659bf333 100644 --- a/api/envoy/config/tap/v3/common.proto +++ b/api/envoy/config/tap/v3/common.proto @@ -17,6 +17,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.tap.v3"; option java_outer_classname = "CommonProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/tap/v3;tapv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Common tap configuration] diff --git a/api/envoy/config/trace/v2/datadog.proto b/api/envoy/config/trace/v2/datadog.proto index 0992601a8acc..3034eecaf551 100644 --- a/api/envoy/config/trace/v2/datadog.proto +++ b/api/envoy/config/trace/v2/datadog.proto @@ -8,6 +8,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.trace.v2"; option java_outer_classname = "DatadogProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/trace/v2;tracev2"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Datadog tracer] diff --git a/api/envoy/config/trace/v2/dynamic_ot.proto b/api/envoy/config/trace/v2/dynamic_ot.proto index 55c6d401b335..928b096bb0f1 100644 --- a/api/envoy/config/trace/v2/dynamic_ot.proto +++ b/api/envoy/config/trace/v2/dynamic_ot.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.trace.v2"; option java_outer_classname = "DynamicOtProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/trace/v2;tracev2"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Dynamically loadable OpenTracing tracer] diff --git a/api/envoy/config/trace/v2/http_tracer.proto b/api/envoy/config/trace/v2/http_tracer.proto index fba830b987b6..778b9e718a70 100644 --- a/api/envoy/config/trace/v2/http_tracer.proto +++ b/api/envoy/config/trace/v2/http_tracer.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.trace.v2"; option java_outer_classname = "HttpTracerProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/trace/v2;tracev2"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Tracing] diff --git a/api/envoy/config/trace/v2/lightstep.proto b/api/envoy/config/trace/v2/lightstep.proto index 849749baaa0d..db866c825570 100644 --- a/api/envoy/config/trace/v2/lightstep.proto +++ b/api/envoy/config/trace/v2/lightstep.proto @@ -8,6 +8,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.trace.v2"; option java_outer_classname = "LightstepProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/trace/v2;tracev2"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: LightStep tracer] diff --git a/api/envoy/config/trace/v2/opencensus.proto b/api/envoy/config/trace/v2/opencensus.proto index 1a9a879b21e4..595f4fe27832 100644 --- a/api/envoy/config/trace/v2/opencensus.proto +++ b/api/envoy/config/trace/v2/opencensus.proto @@ -11,6 +11,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.trace.v2"; option java_outer_classname = "OpencensusProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/trace/v2;tracev2"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: OpenCensus tracer] diff --git a/api/envoy/config/trace/v2/service.proto b/api/envoy/config/trace/v2/service.proto index d102499b6261..85477cccbf23 100644 --- a/api/envoy/config/trace/v2/service.proto +++ b/api/envoy/config/trace/v2/service.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.trace.v2"; option java_outer_classname = "ServiceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/trace/v2;tracev2"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Trace Service] diff --git a/api/envoy/config/trace/v2/trace.proto b/api/envoy/config/trace/v2/trace.proto index 6ed394147db1..02d6fa28bd94 100644 --- a/api/envoy/config/trace/v2/trace.proto +++ b/api/envoy/config/trace/v2/trace.proto @@ -13,3 +13,4 @@ import public "envoy/config/trace/v2/zipkin.proto"; option java_package = "io.envoyproxy.envoy.config.trace.v2"; option java_outer_classname = "TraceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/trace/v2;tracev2"; diff --git a/api/envoy/config/trace/v2/zipkin.proto b/api/envoy/config/trace/v2/zipkin.proto index a825d85bb7f9..d052c7176b3d 100644 --- a/api/envoy/config/trace/v2/zipkin.proto +++ b/api/envoy/config/trace/v2/zipkin.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.trace.v2"; option java_outer_classname = "ZipkinProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/trace/v2;tracev2"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Zipkin tracer] diff --git a/api/envoy/config/trace/v2alpha/xray.proto b/api/envoy/config/trace/v2alpha/xray.proto index 27db3ba40b72..2ce5ed154430 100644 --- a/api/envoy/config/trace/v2alpha/xray.proto +++ b/api/envoy/config/trace/v2alpha/xray.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.trace.v2alpha"; option java_outer_classname = "XrayProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/trace/v2alpha"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: AWS X-Ray Tracer Configuration] diff --git a/api/envoy/config/trace/v3/BUILD b/api/envoy/config/trace/v3/BUILD index ec0d9dd6a65b..94596540dfc4 100644 --- a/api/envoy/config/trace/v3/BUILD +++ b/api/envoy/config/trace/v3/BUILD @@ -8,8 +8,6 @@ api_proto_package( deps = [ "//envoy/annotations:pkg", "//envoy/config/core/v3:pkg", - "//envoy/config/trace/v2:pkg", - "//envoy/config/trace/v2alpha:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", "@opencensus_proto//opencensus/proto/trace/v1:trace_config_proto", ], diff --git a/api/envoy/config/trace/v3/datadog.proto b/api/envoy/config/trace/v3/datadog.proto index c101ab2f03c9..1a01f6a33c8b 100644 --- a/api/envoy/config/trace/v3/datadog.proto +++ b/api/envoy/config/trace/v3/datadog.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.trace.v3"; option java_outer_classname = "DatadogProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3;tracev3"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.tracers.datadog.v4alpha"; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/config/trace/v3/dynamic_ot.proto b/api/envoy/config/trace/v3/dynamic_ot.proto index c28106871542..954c4a422abf 100644 --- a/api/envoy/config/trace/v3/dynamic_ot.proto +++ b/api/envoy/config/trace/v3/dynamic_ot.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.trace.v3"; option java_outer_classname = "DynamicOtProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3;tracev3"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.tracers.dynamic_ot.v4alpha"; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/config/trace/v3/http_tracer.proto b/api/envoy/config/trace/v3/http_tracer.proto index d3c59a8cbb00..8bd5151f4b10 100644 --- a/api/envoy/config/trace/v3/http_tracer.proto +++ b/api/envoy/config/trace/v3/http_tracer.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.trace.v3"; option java_outer_classname = "HttpTracerProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3;tracev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Tracing] diff --git a/api/envoy/config/trace/v3/lightstep.proto b/api/envoy/config/trace/v3/lightstep.proto index b5cff53fea96..0e2680832f0e 100644 --- a/api/envoy/config/trace/v3/lightstep.proto +++ b/api/envoy/config/trace/v3/lightstep.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.trace.v3"; option java_outer_classname = "LightstepProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3;tracev3"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.tracers.lightstep.v4alpha"; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/config/trace/v3/opencensus.proto b/api/envoy/config/trace/v3/opencensus.proto index ee2241e729a8..9b2d2361a49d 100644 --- a/api/envoy/config/trace/v3/opencensus.proto +++ b/api/envoy/config/trace/v3/opencensus.proto @@ -14,6 +14,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.config.trace.v3"; option java_outer_classname = "OpencensusProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3;tracev3"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.tracers.opencensus.v4alpha"; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/config/trace/v3/service.proto b/api/envoy/config/trace/v3/service.proto index 1e01ff61847f..4cb8c44c424d 100644 --- a/api/envoy/config/trace/v3/service.proto +++ b/api/envoy/config/trace/v3/service.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.trace.v3"; option java_outer_classname = "ServiceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3;tracev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Trace Service] diff --git a/api/envoy/config/trace/v3/skywalking.proto b/api/envoy/config/trace/v3/skywalking.proto index 3961a9e4db86..327defe9ba69 100644 --- a/api/envoy/config/trace/v3/skywalking.proto +++ b/api/envoy/config/trace/v3/skywalking.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.trace.v3"; option java_outer_classname = "SkywalkingProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3;tracev3"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.tracers.skywalking.v4alpha"; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/config/trace/v3/trace.proto b/api/envoy/config/trace/v3/trace.proto index 472e38b5abb8..5e5895e26bba 100644 --- a/api/envoy/config/trace/v3/trace.proto +++ b/api/envoy/config/trace/v3/trace.proto @@ -13,3 +13,4 @@ import public "envoy/config/trace/v3/zipkin.proto"; option java_package = "io.envoyproxy.envoy.config.trace.v3"; option java_outer_classname = "TraceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3;tracev3"; diff --git a/api/envoy/config/trace/v3/xray.proto b/api/envoy/config/trace/v3/xray.proto index 208170b60c3f..223aaaaf442e 100644 --- a/api/envoy/config/trace/v3/xray.proto +++ b/api/envoy/config/trace/v3/xray.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.trace.v3"; option java_outer_classname = "XrayProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3;tracev3"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.tracers.xray.v4alpha"; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/config/trace/v3/zipkin.proto b/api/envoy/config/trace/v3/zipkin.proto index 0638d89315fa..1d76b8137680 100644 --- a/api/envoy/config/trace/v3/zipkin.proto +++ b/api/envoy/config/trace/v3/zipkin.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.trace.v3"; option java_outer_classname = "ZipkinProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3;tracev3"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.tracers.zipkin.v4alpha"; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/config/transport_socket/alts/v2alpha/alts.proto b/api/envoy/config/transport_socket/alts/v2alpha/alts.proto index 92d5fb83a49c..6ad5428ba2d1 100644 --- a/api/envoy/config/transport_socket/alts/v2alpha/alts.proto +++ b/api/envoy/config/transport_socket/alts/v2alpha/alts.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.transport_socket.alts.v2alpha"; option java_outer_classname = "AltsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/transport_socket/alts/v2alpha"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.transport_sockets.alts.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/transport_socket/raw_buffer/v2/raw_buffer.proto b/api/envoy/config/transport_socket/raw_buffer/v2/raw_buffer.proto index 1b3fd395d572..40efb3ac80c5 100644 --- a/api/envoy/config/transport_socket/raw_buffer/v2/raw_buffer.proto +++ b/api/envoy/config/transport_socket/raw_buffer/v2/raw_buffer.proto @@ -8,6 +8,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.config.transport_socket.raw_buffer.v2"; option java_outer_classname = "RawBufferProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/transport_socket/raw_buffer/v2;raw_bufferv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.transport_sockets.raw_buffer.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/config/transport_socket/tap/v2alpha/tap.proto b/api/envoy/config/transport_socket/tap/v2alpha/tap.proto index 0802c7558ad3..42b1107b37f8 100644 --- a/api/envoy/config/transport_socket/tap/v2alpha/tap.proto +++ b/api/envoy/config/transport_socket/tap/v2alpha/tap.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.transport_socket.tap.v2alpha"; option java_outer_classname = "TapProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/transport_socket/tap/v2alpha"; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.transport_sockets.tap.v3"; option (udpa.annotations.file_status).work_in_progress = true; diff --git a/api/envoy/data/accesslog/v2/accesslog.proto b/api/envoy/data/accesslog/v2/accesslog.proto index af19197f62a6..272445d94d58 100644 --- a/api/envoy/data/accesslog/v2/accesslog.proto +++ b/api/envoy/data/accesslog/v2/accesslog.proto @@ -16,6 +16,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.data.accesslog.v2"; option java_outer_classname = "AccesslogProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/data/accesslog/v2;accesslogv2"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: gRPC access logs] diff --git a/api/envoy/data/accesslog/v3/BUILD b/api/envoy/data/accesslog/v3/BUILD index 9065b1b5c331..1c1a6f6b4423 100644 --- a/api/envoy/data/accesslog/v3/BUILD +++ b/api/envoy/data/accesslog/v3/BUILD @@ -7,7 +7,6 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ "//envoy/config/core/v3:pkg", - "//envoy/data/accesslog/v2:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/api/envoy/data/accesslog/v3/accesslog.proto b/api/envoy/data/accesslog/v3/accesslog.proto index c53ae0d6ab85..ab4a55900505 100644 --- a/api/envoy/data/accesslog/v3/accesslog.proto +++ b/api/envoy/data/accesslog/v3/accesslog.proto @@ -17,6 +17,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.data.accesslog.v3"; option java_outer_classname = "AccesslogProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/data/accesslog/v3;accesslogv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: gRPC access logs] diff --git a/api/envoy/data/cluster/v2alpha/outlier_detection_event.proto b/api/envoy/data/cluster/v2alpha/outlier_detection_event.proto index 3ea8bc2597fd..2ede4c4d9de3 100644 --- a/api/envoy/data/cluster/v2alpha/outlier_detection_event.proto +++ b/api/envoy/data/cluster/v2alpha/outlier_detection_event.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.data.cluster.v2alpha"; option java_outer_classname = "OutlierDetectionEventProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/data/cluster/v2alpha"; option (udpa.annotations.file_migrate).move_to_package = "envoy.data.cluster.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/data/cluster/v3/outlier_detection_event.proto b/api/envoy/data/cluster/v3/outlier_detection_event.proto index 2ba29d89954b..f996f0655205 100644 --- a/api/envoy/data/cluster/v3/outlier_detection_event.proto +++ b/api/envoy/data/cluster/v3/outlier_detection_event.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.data.cluster.v3"; option java_outer_classname = "OutlierDetectionEventProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/data/cluster/v3;clusterv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Outlier detection logging events] diff --git a/api/envoy/data/core/v2alpha/health_check_event.proto b/api/envoy/data/core/v2alpha/health_check_event.proto index 00fd69fd42d3..ac6155f52609 100644 --- a/api/envoy/data/core/v2alpha/health_check_event.proto +++ b/api/envoy/data/core/v2alpha/health_check_event.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.data.core.v2alpha"; option java_outer_classname = "HealthCheckEventProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/data/core/v2alpha"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Health check logging events] diff --git a/api/envoy/data/core/v3/health_check_event.proto b/api/envoy/data/core/v3/health_check_event.proto index 92e2d68d255d..63f7beb0c44b 100644 --- a/api/envoy/data/core/v3/health_check_event.proto +++ b/api/envoy/data/core/v3/health_check_event.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.data.core.v3"; option java_outer_classname = "HealthCheckEventProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/data/core/v3;corev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Health check logging events] diff --git a/api/envoy/data/dns/v2alpha/dns_table.proto b/api/envoy/data/dns/v2alpha/dns_table.proto index 7a9e535c4f3a..0b8c43d6249d 100644 --- a/api/envoy/data/dns/v2alpha/dns_table.proto +++ b/api/envoy/data/dns/v2alpha/dns_table.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.data.dns.v2alpha"; option java_outer_classname = "DnsTableProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/data/dns/v2alpha"; option (udpa.annotations.file_status).work_in_progress = true; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/data/dns/v3/dns_table.proto b/api/envoy/data/dns/v3/dns_table.proto index 5cc04440f700..1816a304405e 100644 --- a/api/envoy/data/dns/v3/dns_table.proto +++ b/api/envoy/data/dns/v3/dns_table.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.data.dns.v3"; option java_outer_classname = "DnsTableProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/data/dns/v3;dnsv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: DNS Filter Table Data] diff --git a/api/envoy/data/tap/v2alpha/common.proto b/api/envoy/data/tap/v2alpha/common.proto index 7c02aa771954..fb07bce115fd 100644 --- a/api/envoy/data/tap/v2alpha/common.proto +++ b/api/envoy/data/tap/v2alpha/common.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.data.tap.v2alpha"; option java_outer_classname = "CommonProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/data/tap/v2alpha"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Tap common data] diff --git a/api/envoy/data/tap/v2alpha/http.proto b/api/envoy/data/tap/v2alpha/http.proto index 60ea68b66d4a..7cb10ff3503c 100644 --- a/api/envoy/data/tap/v2alpha/http.proto +++ b/api/envoy/data/tap/v2alpha/http.proto @@ -10,6 +10,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.data.tap.v2alpha"; option java_outer_classname = "HttpProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/data/tap/v2alpha"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: HTTP tap data] diff --git a/api/envoy/data/tap/v2alpha/transport.proto b/api/envoy/data/tap/v2alpha/transport.proto index 82c2845ee338..0be758ded9da 100644 --- a/api/envoy/data/tap/v2alpha/transport.proto +++ b/api/envoy/data/tap/v2alpha/transport.proto @@ -12,6 +12,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.data.tap.v2alpha"; option java_outer_classname = "TransportProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/data/tap/v2alpha"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Transport tap data] diff --git a/api/envoy/data/tap/v2alpha/wrapper.proto b/api/envoy/data/tap/v2alpha/wrapper.proto index 769b95c6160a..5cc3e07320d1 100644 --- a/api/envoy/data/tap/v2alpha/wrapper.proto +++ b/api/envoy/data/tap/v2alpha/wrapper.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.data.tap.v2alpha"; option java_outer_classname = "WrapperProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/data/tap/v2alpha"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Tap data wrappers] diff --git a/api/envoy/data/tap/v3/BUILD b/api/envoy/data/tap/v3/BUILD index 7cdbc28e7cd4..1c1a6f6b4423 100644 --- a/api/envoy/data/tap/v3/BUILD +++ b/api/envoy/data/tap/v3/BUILD @@ -7,7 +7,6 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ "//envoy/config/core/v3:pkg", - "//envoy/data/tap/v2alpha:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/api/envoy/data/tap/v3/common.proto b/api/envoy/data/tap/v3/common.proto index 2c4fb9c61a55..8e0a28361e9d 100644 --- a/api/envoy/data/tap/v3/common.proto +++ b/api/envoy/data/tap/v3/common.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.data.tap.v3"; option java_outer_classname = "CommonProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/data/tap/v3;tapv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Tap common data] diff --git a/api/envoy/data/tap/v3/http.proto b/api/envoy/data/tap/v3/http.proto index d4f05fa09522..2bfa9206f8ee 100644 --- a/api/envoy/data/tap/v3/http.proto +++ b/api/envoy/data/tap/v3/http.proto @@ -11,6 +11,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.data.tap.v3"; option java_outer_classname = "HttpProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/data/tap/v3;tapv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: HTTP tap data] diff --git a/api/envoy/data/tap/v3/transport.proto b/api/envoy/data/tap/v3/transport.proto index 0ff4b7da0604..efd2d4168e73 100644 --- a/api/envoy/data/tap/v3/transport.proto +++ b/api/envoy/data/tap/v3/transport.proto @@ -13,6 +13,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.data.tap.v3"; option java_outer_classname = "TransportProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/data/tap/v3;tapv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Transport tap data] diff --git a/api/envoy/data/tap/v3/wrapper.proto b/api/envoy/data/tap/v3/wrapper.proto index 636547614c26..983b350b081c 100644 --- a/api/envoy/data/tap/v3/wrapper.proto +++ b/api/envoy/data/tap/v3/wrapper.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.data.tap.v3"; option java_outer_classname = "WrapperProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/data/tap/v3;tapv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Tap data wrappers] diff --git a/api/envoy/extensions/access_loggers/file/v3/file.proto b/api/envoy/extensions/access_loggers/file/v3/file.proto index bca7c913a65b..7badb8f180af 100644 --- a/api/envoy/extensions/access_loggers/file/v3/file.proto +++ b/api/envoy/extensions/access_loggers/file/v3/file.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.access_loggers.file.v3"; option java_outer_classname = "FileProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v3;filev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: File access log] diff --git a/api/envoy/extensions/access_loggers/filters/cel/v3/BUILD b/api/envoy/extensions/access_loggers/filters/cel/v3/BUILD new file mode 100644 index 000000000000..ee92fb652582 --- /dev/null +++ b/api/envoy/extensions/access_loggers/filters/cel/v3/BUILD @@ -0,0 +1,9 @@ +# 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 = ["@com_github_cncf_udpa//udpa/annotations:pkg"], +) diff --git a/api/envoy/extensions/access_loggers/filters/cel/v3/cel.proto b/api/envoy/extensions/access_loggers/filters/cel/v3/cel.proto new file mode 100644 index 000000000000..fc0b8e2e480a --- /dev/null +++ b/api/envoy/extensions/access_loggers/filters/cel/v3/cel.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +package envoy.extensions.access_loggers.filters.cel.v3; + +import "udpa/annotations/status.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.access_loggers.filters.cel.v3"; +option java_outer_classname = "CelProto"; +option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/filters/cel/v3;celv3"; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: ExpressionFilter] +// [#extension: envoy.access_loggers.extension_filters.cel] + +// ExpressionFilter is an access logging filter that evaluates configured +// symbolic Common Expression Language expressions to inform the decision +// to generate an access log. +message ExpressionFilter { + // Expression that, when evaluated, will be used to filter access logs. + // Expressions are based on the set of Envoy :ref:`attributes `. + // The provided expression must evaluate to true for logging (expression errors are considered false). + // Examples: + // - `response.code >= 400` + // - `(connection.mtls && request.headers['x-log-mtls'] == 'true') || request.url_path.contains('v1beta3')` + string expression = 1; +} diff --git a/api/envoy/extensions/access_loggers/grpc/v3/als.proto b/api/envoy/extensions/access_loggers/grpc/v3/als.proto index fa0a9f0f820d..c4f306e0bb07 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"; @@ -15,6 +16,7 @@ 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 go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/grpc/v3;grpcv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: gRPC Access Log Service (ALS)] @@ -54,7 +56,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 +88,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/api/envoy/extensions/access_loggers/open_telemetry/v3/logs_service.proto b/api/envoy/extensions/access_loggers/open_telemetry/v3/logs_service.proto index cd4a63181290..5dd9d1e1a3a1 100644 --- a/api/envoy/extensions/access_loggers/open_telemetry/v3/logs_service.proto +++ b/api/envoy/extensions/access_loggers/open_telemetry/v3/logs_service.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.access_loggers.open_telemetry.v3"; option java_outer_classname = "LogsServiceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/open_telemetry/v3;open_telemetryv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: OpenTelemetry (gRPC) Access Log] diff --git a/api/envoy/extensions/access_loggers/stream/v3/stream.proto b/api/envoy/extensions/access_loggers/stream/v3/stream.proto index bd704ccdb676..2dc319d06ed0 100644 --- a/api/envoy/extensions/access_loggers/stream/v3/stream.proto +++ b/api/envoy/extensions/access_loggers/stream/v3/stream.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.access_loggers.stream.v3"; option java_outer_classname = "StreamProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/stream/v3;streamv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Standard Streams Access loggers] diff --git a/api/envoy/extensions/access_loggers/wasm/v3/wasm.proto b/api/envoy/extensions/access_loggers/wasm/v3/wasm.proto index 44e96345dfee..a0ed0ee28f8b 100644 --- a/api/envoy/extensions/access_loggers/wasm/v3/wasm.proto +++ b/api/envoy/extensions/access_loggers/wasm/v3/wasm.proto @@ -9,6 +9,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.access_loggers.wasm.v3"; option java_outer_classname = "WasmProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/wasm/v3;wasmv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Wasm access log] diff --git a/api/envoy/extensions/cache/simple_http_cache/v3/config.proto b/api/envoy/extensions/cache/simple_http_cache/v3/config.proto index e7bd7cdbdf91..f698e522e86f 100644 --- a/api/envoy/extensions/cache/simple_http_cache/v3/config.proto +++ b/api/envoy/extensions/cache/simple_http_cache/v3/config.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.cache.simple_http_cache.v3"; option java_outer_classname = "ConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/cache/simple_http_cache/v3;simple_http_cachev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: SimpleHttpCache CacheFilter storage plugin] diff --git a/api/envoy/extensions/clusters/aggregate/v3/cluster.proto b/api/envoy/extensions/clusters/aggregate/v3/cluster.proto index aead1c451739..4f44ac9cd5c9 100644 --- a/api/envoy/extensions/clusters/aggregate/v3/cluster.proto +++ b/api/envoy/extensions/clusters/aggregate/v3/cluster.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.clusters.aggregate.v3"; option java_outer_classname = "ClusterProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/aggregate/v3;aggregatev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Aggregate cluster configuration] diff --git a/api/envoy/extensions/clusters/dynamic_forward_proxy/v3/cluster.proto b/api/envoy/extensions/clusters/dynamic_forward_proxy/v3/cluster.proto index 449ffee87c15..a572c0c46296 100644 --- a/api/envoy/extensions/clusters/dynamic_forward_proxy/v3/cluster.proto +++ b/api/envoy/extensions/clusters/dynamic_forward_proxy/v3/cluster.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.clusters.dynamic_forward_proxy.v3"; option java_outer_classname = "ClusterProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/dynamic_forward_proxy/v3;dynamic_forward_proxyv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Dynamic forward proxy cluster configuration] diff --git a/api/envoy/extensions/clusters/redis/v3/redis_cluster.proto b/api/envoy/extensions/clusters/redis/v3/redis_cluster.proto index 73598eafbe9d..88e4e89c4ef5 100644 --- a/api/envoy/extensions/clusters/redis/v3/redis_cluster.proto +++ b/api/envoy/extensions/clusters/redis/v3/redis_cluster.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.clusters.redis.v3"; option java_outer_classname = "RedisClusterProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/redis/v3;redisv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Redis Cluster Configuration] diff --git a/api/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto b/api/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto index f2f6db7f1332..36fabd59f24c 100644 --- a/api/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto +++ b/api/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto @@ -19,6 +19,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.common.dynamic_forward_proxy.v3"; option java_outer_classname = "DnsCacheProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/common/dynamic_forward_proxy/v3;dynamic_forward_proxyv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Dynamic forward proxy common configuration] diff --git a/api/envoy/extensions/common/matching/v3/extension_matcher.proto b/api/envoy/extensions/common/matching/v3/extension_matcher.proto index 10bd3b7389a6..010adc52846f 100644 --- a/api/envoy/extensions/common/matching/v3/extension_matcher.proto +++ b/api/envoy/extensions/common/matching/v3/extension_matcher.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.common.matching.v3"; option java_outer_classname = "ExtensionMatcherProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/common/matching/v3;matchingv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Extension Matcher] diff --git a/api/envoy/extensions/common/ratelimit/v3/BUILD b/api/envoy/extensions/common/ratelimit/v3/BUILD index 660d629ab7b0..9a76b7e148e0 100644 --- a/api/envoy/extensions/common/ratelimit/v3/BUILD +++ b/api/envoy/extensions/common/ratelimit/v3/BUILD @@ -6,7 +6,6 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ - "//envoy/api/v2/ratelimit:pkg", "//envoy/type/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], diff --git a/api/envoy/extensions/common/ratelimit/v3/ratelimit.proto b/api/envoy/extensions/common/ratelimit/v3/ratelimit.proto index 6bb771d25af9..9f8666b6a4ad 100644 --- a/api/envoy/extensions/common/ratelimit/v3/ratelimit.proto +++ b/api/envoy/extensions/common/ratelimit/v3/ratelimit.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.common.ratelimit.v3"; option java_outer_classname = "RatelimitProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/common/ratelimit/v3;ratelimitv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Common rate limit components] diff --git a/api/envoy/extensions/common/tap/v3/common.proto b/api/envoy/extensions/common/tap/v3/common.proto index 4c67af7d3008..d9f9e98cad06 100644 --- a/api/envoy/extensions/common/tap/v3/common.proto +++ b/api/envoy/extensions/common/tap/v3/common.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.common.tap.v3"; option java_outer_classname = "CommonProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/common/tap/v3;tapv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Common tap extension configuration] diff --git a/api/envoy/extensions/compression/brotli/compressor/v3/brotli.proto b/api/envoy/extensions/compression/brotli/compressor/v3/brotli.proto index cb2933dd5d38..4f9644ac66f0 100644 --- a/api/envoy/extensions/compression/brotli/compressor/v3/brotli.proto +++ b/api/envoy/extensions/compression/brotli/compressor/v3/brotli.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.compression.brotli.compressor.v3"; option java_outer_classname = "BrotliProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/brotli/compressor/v3;compressorv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Brotli Compressor] diff --git a/api/envoy/extensions/compression/brotli/decompressor/v3/brotli.proto b/api/envoy/extensions/compression/brotli/decompressor/v3/brotli.proto index 24511861cf93..401c7802ab32 100644 --- a/api/envoy/extensions/compression/brotli/decompressor/v3/brotli.proto +++ b/api/envoy/extensions/compression/brotli/decompressor/v3/brotli.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.compression.brotli.decompressor.v3"; option java_outer_classname = "BrotliProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/brotli/decompressor/v3;decompressorv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Brotli Decompressor] diff --git a/api/envoy/extensions/compression/gzip/compressor/v3/gzip.proto b/api/envoy/extensions/compression/gzip/compressor/v3/gzip.proto index 2f37315be355..33816b8cd25c 100644 --- a/api/envoy/extensions/compression/gzip/compressor/v3/gzip.proto +++ b/api/envoy/extensions/compression/gzip/compressor/v3/gzip.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.compression.gzip.compressor.v3"; option java_outer_classname = "GzipProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/gzip/compressor/v3;compressorv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Gzip Compressor] diff --git a/api/envoy/extensions/compression/gzip/decompressor/v3/gzip.proto b/api/envoy/extensions/compression/gzip/decompressor/v3/gzip.proto index 8fb694e88361..82e50c2337c8 100644 --- a/api/envoy/extensions/compression/gzip/decompressor/v3/gzip.proto +++ b/api/envoy/extensions/compression/gzip/decompressor/v3/gzip.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.compression.gzip.decompressor.v3"; option java_outer_classname = "GzipProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/gzip/decompressor/v3;decompressorv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Gzip Decompressor] diff --git a/api/envoy/extensions/filters/common/dependency/v3/dependency.proto b/api/envoy/extensions/filters/common/dependency/v3/dependency.proto index 9dce610afeef..e1cda9885a3a 100644 --- a/api/envoy/extensions/filters/common/dependency/v3/dependency.proto +++ b/api/envoy/extensions/filters/common/dependency/v3/dependency.proto @@ -8,6 +8,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.common.dependency.v3"; option java_outer_classname = "DependencyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/common/dependency/v3;dependencyv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Filter dependency specification] diff --git a/api/envoy/extensions/filters/common/fault/v3/BUILD b/api/envoy/extensions/filters/common/fault/v3/BUILD index f1a783082847..9a76b7e148e0 100644 --- a/api/envoy/extensions/filters/common/fault/v3/BUILD +++ b/api/envoy/extensions/filters/common/fault/v3/BUILD @@ -6,7 +6,6 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ - "//envoy/config/filter/fault/v2:pkg", "//envoy/type/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], diff --git a/api/envoy/extensions/filters/common/fault/v3/fault.proto b/api/envoy/extensions/filters/common/fault/v3/fault.proto index 62da059e2648..ab24f5d2374d 100644 --- a/api/envoy/extensions/filters/common/fault/v3/fault.proto +++ b/api/envoy/extensions/filters/common/fault/v3/fault.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.common.fault.v3"; option java_outer_classname = "FaultProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/common/fault/v3;faultv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Common fault injection types] diff --git a/api/envoy/extensions/filters/common/matcher/action/v3/skip_action.proto b/api/envoy/extensions/filters/common/matcher/action/v3/skip_action.proto index 2835c9f6d75a..151c01791c88 100644 --- a/api/envoy/extensions/filters/common/matcher/action/v3/skip_action.proto +++ b/api/envoy/extensions/filters/common/matcher/action/v3/skip_action.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.common.matcher.action.v3"; option java_outer_classname = "SkipActionProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/common/matcher/action/v3;actionv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Common Match Actions] diff --git a/api/envoy/extensions/filters/http/adaptive_concurrency/v3/adaptive_concurrency.proto b/api/envoy/extensions/filters/http/adaptive_concurrency/v3/adaptive_concurrency.proto index c524e022e859..f24278789973 100644 --- a/api/envoy/extensions/filters/http/adaptive_concurrency/v3/adaptive_concurrency.proto +++ b/api/envoy/extensions/filters/http/adaptive_concurrency/v3/adaptive_concurrency.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.adaptive_concurrency.v3"; option java_outer_classname = "AdaptiveConcurrencyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/adaptive_concurrency/v3;adaptive_concurrencyv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Adaptive Concurrency] diff --git a/api/envoy/extensions/filters/http/admission_control/v3/admission_control.proto b/api/envoy/extensions/filters/http/admission_control/v3/admission_control.proto index 702f03019b1c..83ccab791c15 100644 --- a/api/envoy/extensions/filters/http/admission_control/v3/admission_control.proto +++ b/api/envoy/extensions/filters/http/admission_control/v3/admission_control.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.admission_control.v3"; option java_outer_classname = "AdmissionControlProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/admission_control/v3;admission_controlv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Admission Control] diff --git a/api/envoy/extensions/filters/http/alternate_protocols_cache/v3/alternate_protocols_cache.proto b/api/envoy/extensions/filters/http/alternate_protocols_cache/v3/alternate_protocols_cache.proto index 0f0609b6e55e..241c4587bb2a 100644 --- a/api/envoy/extensions/filters/http/alternate_protocols_cache/v3/alternate_protocols_cache.proto +++ b/api/envoy/extensions/filters/http/alternate_protocols_cache/v3/alternate_protocols_cache.proto @@ -9,6 +9,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.alternate_protocols_cache.v3"; option java_outer_classname = "AlternateProtocolsCacheProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/alternate_protocols_cache/v3;alternate_protocols_cachev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Alternate Protocols Cache] diff --git a/api/envoy/extensions/filters/http/aws_lambda/v3/aws_lambda.proto b/api/envoy/extensions/filters/http/aws_lambda/v3/aws_lambda.proto index b4b9cc398f2e..5268550c0a1b 100644 --- a/api/envoy/extensions/filters/http/aws_lambda/v3/aws_lambda.proto +++ b/api/envoy/extensions/filters/http/aws_lambda/v3/aws_lambda.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.aws_lambda.v3"; option java_outer_classname = "AwsLambdaProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/aws_lambda/v3;aws_lambdav3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: AWS Lambda] 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 ee92fb652582..693f0b92ff34 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 ae46400130d5..ba26a3882407 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"; @@ -9,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.aws_request_signing.v3"; option java_outer_classname = "AwsRequestSigningProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/aws_request_signing/v3;aws_request_signingv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: AwsRequestSigning] @@ -16,6 +19,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 +52,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/api/envoy/extensions/filters/http/bandwidth_limit/v3/bandwidth_limit.proto b/api/envoy/extensions/filters/http/bandwidth_limit/v3/bandwidth_limit.proto index c512d541aaef..2264da41bdeb 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 @@ -13,13 +13,14 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.bandwidth_limit.v3"; option java_outer_classname = "BandwidthLimitProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/bandwidth_limit/v3;bandwidth_limitv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Bandwidth limit] // 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 +67,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/api/envoy/extensions/filters/http/buffer/v3/buffer.proto b/api/envoy/extensions/filters/http/buffer/v3/buffer.proto index 6f73244032c4..1733abb40968 100644 --- a/api/envoy/extensions/filters/http/buffer/v3/buffer.proto +++ b/api/envoy/extensions/filters/http/buffer/v3/buffer.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.buffer.v3"; option java_outer_classname = "BufferProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/buffer/v3;bufferv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Buffer] diff --git a/api/envoy/extensions/filters/http/cache/v3/cache.proto b/api/envoy/extensions/filters/http/cache/v3/cache.proto index 71f4a5bb73f9..dd3278f7945b 100644 --- a/api/envoy/extensions/filters/http/cache/v3/cache.proto +++ b/api/envoy/extensions/filters/http/cache/v3/cache.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.cache.v3"; option java_outer_classname = "CacheProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cache/v3;cachev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: HTTP Cache Filter] diff --git a/api/envoy/extensions/filters/http/cdn_loop/v3/cdn_loop.proto b/api/envoy/extensions/filters/http/cdn_loop/v3/cdn_loop.proto index 77a19511c3d4..06e1b111f4bf 100644 --- a/api/envoy/extensions/filters/http/cdn_loop/v3/cdn_loop.proto +++ b/api/envoy/extensions/filters/http/cdn_loop/v3/cdn_loop.proto @@ -8,6 +8,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.cdn_loop.v3"; option java_outer_classname = "CdnLoopProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cdn_loop/v3;cdn_loopv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: HTTP CDN-Loop Filter] diff --git a/api/envoy/extensions/filters/http/composite/v3/composite.proto b/api/envoy/extensions/filters/http/composite/v3/composite.proto index a53364e8adfa..064f58b7e9ac 100644 --- a/api/envoy/extensions/filters/http/composite/v3/composite.proto +++ b/api/envoy/extensions/filters/http/composite/v3/composite.proto @@ -11,6 +11,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.composite.v3"; option java_outer_classname = "CompositeProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/composite/v3;compositev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Composite] diff --git a/api/envoy/extensions/filters/http/compressor/v3/compressor.proto b/api/envoy/extensions/filters/http/compressor/v3/compressor.proto index 72b435c93dda..197ab8ab4230 100644 --- a/api/envoy/extensions/filters/http/compressor/v3/compressor.proto +++ b/api/envoy/extensions/filters/http/compressor/v3/compressor.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.compressor.v3"; option java_outer_classname = "CompressorProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/compressor/v3;compressorv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Compressor] diff --git a/api/envoy/extensions/filters/http/cors/v3/cors.proto b/api/envoy/extensions/filters/http/cors/v3/cors.proto index 0269e1bdfd8c..29e8196edf7a 100644 --- a/api/envoy/extensions/filters/http/cors/v3/cors.proto +++ b/api/envoy/extensions/filters/http/cors/v3/cors.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.cors.v3"; option java_outer_classname = "CorsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cors/v3;corsv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Cors] diff --git a/api/envoy/extensions/filters/http/csrf/v3/csrf.proto b/api/envoy/extensions/filters/http/csrf/v3/csrf.proto index 39b0455bd798..384e00955970 100644 --- a/api/envoy/extensions/filters/http/csrf/v3/csrf.proto +++ b/api/envoy/extensions/filters/http/csrf/v3/csrf.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.csrf.v3"; option java_outer_classname = "CsrfProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/csrf/v3;csrfv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: CSRF] diff --git a/api/envoy/extensions/filters/http/decompressor/v3/decompressor.proto b/api/envoy/extensions/filters/http/decompressor/v3/decompressor.proto index c4cca44020f6..82e1bc02d1bb 100644 --- a/api/envoy/extensions/filters/http/decompressor/v3/decompressor.proto +++ b/api/envoy/extensions/filters/http/decompressor/v3/decompressor.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.decompressor.v3"; option java_outer_classname = "DecompressorProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/decompressor/v3;decompressorv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Decompressor] diff --git a/api/envoy/extensions/filters/http/dynamic_forward_proxy/v3/dynamic_forward_proxy.proto b/api/envoy/extensions/filters/http/dynamic_forward_proxy/v3/dynamic_forward_proxy.proto index ecf2d271f952..838125f77aec 100644 --- a/api/envoy/extensions/filters/http/dynamic_forward_proxy/v3/dynamic_forward_proxy.proto +++ b/api/envoy/extensions/filters/http/dynamic_forward_proxy/v3/dynamic_forward_proxy.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.dynamic_forward_proxy.v3"; option java_outer_classname = "DynamicForwardProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamic_forward_proxy/v3;dynamic_forward_proxyv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Dynamic forward proxy] diff --git a/api/envoy/extensions/filters/http/dynamo/v3/dynamo.proto b/api/envoy/extensions/filters/http/dynamo/v3/dynamo.proto index 13a4f1c6ceee..9d6feb4ec9b4 100644 --- a/api/envoy/extensions/filters/http/dynamo/v3/dynamo.proto +++ b/api/envoy/extensions/filters/http/dynamo/v3/dynamo.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.dynamo.v3"; option java_outer_classname = "DynamoProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamo/v3;dynamov3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Dynamo] diff --git a/api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto b/api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto index b05420fa93cf..66daea8c5022 100644 --- a/api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto +++ b/api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto @@ -18,6 +18,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.ext_authz.v3"; option java_outer_classname = "ExtAuthzProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3;ext_authzv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: External Authorization] diff --git a/api/envoy/extensions/filters/http/ext_proc/v3/ext_proc.proto b/api/envoy/extensions/filters/http/ext_proc/v3/ext_proc.proto index e688657830a0..ceba3d061457 100644 --- a/api/envoy/extensions/filters/http/ext_proc/v3/ext_proc.proto +++ b/api/envoy/extensions/filters/http/ext_proc/v3/ext_proc.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.ext_proc.v3"; option java_outer_classname = "ExtProcProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_proc/v3;ext_procv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; option (xds.annotations.v3.file_status).work_in_progress = true; diff --git a/api/envoy/extensions/filters/http/ext_proc/v3/processing_mode.proto b/api/envoy/extensions/filters/http/ext_proc/v3/processing_mode.proto index c15a5569a12c..9c692a17d581 100644 --- a/api/envoy/extensions/filters/http/ext_proc/v3/processing_mode.proto +++ b/api/envoy/extensions/filters/http/ext_proc/v3/processing_mode.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.ext_proc.v3"; option java_outer_classname = "ProcessingModeProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_proc/v3;ext_procv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; option (xds.annotations.v3.file_status).work_in_progress = true; diff --git a/api/envoy/extensions/filters/http/fault/v3/fault.proto b/api/envoy/extensions/filters/http/fault/v3/fault.proto index 0c7fbb4480cf..64dbf89e435a 100644 --- a/api/envoy/extensions/filters/http/fault/v3/fault.proto +++ b/api/envoy/extensions/filters/http/fault/v3/fault.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.fault.v3"; option java_outer_classname = "FaultProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/fault/v3;faultv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Fault Injection] diff --git a/api/envoy/extensions/filters/http/grpc_http1_bridge/v3/config.proto b/api/envoy/extensions/filters/http/grpc_http1_bridge/v3/config.proto index 7e31da49e92b..146853161cdd 100644 --- a/api/envoy/extensions/filters/http/grpc_http1_bridge/v3/config.proto +++ b/api/envoy/extensions/filters/http/grpc_http1_bridge/v3/config.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.grpc_http1_bridge.v3"; option java_outer_classname = "ConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_http1_bridge/v3;grpc_http1_bridgev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: gRPC HTTP/1.1 Bridge] diff --git a/api/envoy/extensions/filters/http/grpc_http1_reverse_bridge/v3/config.proto b/api/envoy/extensions/filters/http/grpc_http1_reverse_bridge/v3/config.proto index 615fea923a8e..8fb93cf81729 100644 --- a/api/envoy/extensions/filters/http/grpc_http1_reverse_bridge/v3/config.proto +++ b/api/envoy/extensions/filters/http/grpc_http1_reverse_bridge/v3/config.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.grpc_http1_reverse_bridge.v3"; option java_outer_classname = "ConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_http1_reverse_bridge/v3;grpc_http1_reverse_bridgev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: gRPC HTTP/1.1 Reverse Bridge] diff --git a/api/envoy/extensions/filters/http/grpc_json_transcoder/v3/transcoder.proto b/api/envoy/extensions/filters/http/grpc_json_transcoder/v3/transcoder.proto index 7311abe8df6f..f72d7d74d12f 100644 --- a/api/envoy/extensions/filters/http/grpc_json_transcoder/v3/transcoder.proto +++ b/api/envoy/extensions/filters/http/grpc_json_transcoder/v3/transcoder.proto @@ -9,13 +9,14 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.grpc_json_transcoder.v3"; option java_outer_classname = "TranscoderProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_json_transcoder/v3;grpc_json_transcoderv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: gRPC-JSON transcoder] // gRPC-JSON transcoder :ref:`configuration overview `. // [#extension: envoy.filters.http.grpc_json_transcoder] -// [#next-free-field: 13] +// [#next-free-field: 14] // GrpcJsonTranscoder filter configuration. // The filter itself can be used per route / per virtual host or on the general level. The most // specific one is being used for a given route. If the list of services is empty - filter @@ -221,6 +222,17 @@ message GrpcJsonTranscoder { // This is to support `HTML 2.0 `_ bool query_param_unescape_plus = 12; + // If true, try to match the custom verb even if it is unregistered. By + // default, only match when it is registered. + // + // According to the http template `syntax `_, + // the custom verb is **":" LITERAL** at the end of http template. + // + // For a request with */foo/bar:baz* and *:baz* is not registered in any url_template, here is the behavior change + // - if the field is not set, *:baz* will not be treated as custom verb, so it will match **/foo/{x=*}**. + // - if the field is set, *:baz* is treated as custom verb, so it will NOT match **/foo/{x=*}** since the template doesn't use any custom verb. + bool match_unregistered_custom_verb = 13; + // Configure the behavior when handling requests that cannot be transcoded. // // By default, the transcoder will silently pass through HTTP requests that are malformed. diff --git a/api/envoy/extensions/filters/http/grpc_stats/v3/config.proto b/api/envoy/extensions/filters/http/grpc_stats/v3/config.proto index 79ecb7a92b70..403e480e0216 100644 --- a/api/envoy/extensions/filters/http/grpc_stats/v3/config.proto +++ b/api/envoy/extensions/filters/http/grpc_stats/v3/config.proto @@ -12,6 +12,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.grpc_stats.v3"; option java_outer_classname = "ConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_stats/v3;grpc_statsv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: gRPC statistics] gRPC statistics filter diff --git a/api/envoy/extensions/filters/http/grpc_web/v3/grpc_web.proto b/api/envoy/extensions/filters/http/grpc_web/v3/grpc_web.proto index 8161139f547b..7475c69b0109 100644 --- a/api/envoy/extensions/filters/http/grpc_web/v3/grpc_web.proto +++ b/api/envoy/extensions/filters/http/grpc_web/v3/grpc_web.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.grpc_web.v3"; option java_outer_classname = "GrpcWebProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_web/v3;grpc_webv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: gRPC Web] diff --git a/api/envoy/extensions/filters/http/gzip/v3/gzip.proto b/api/envoy/extensions/filters/http/gzip/v3/gzip.proto index a931ab78689f..f29e7eb415e9 100644 --- a/api/envoy/extensions/filters/http/gzip/v3/gzip.proto +++ b/api/envoy/extensions/filters/http/gzip/v3/gzip.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.gzip.v3"; option java_outer_classname = "GzipProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/gzip/v3;gzipv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Gzip] diff --git a/api/envoy/extensions/filters/http/header_to_metadata/v3/header_to_metadata.proto b/api/envoy/extensions/filters/http/header_to_metadata/v3/header_to_metadata.proto index 5e399790a7ec..eb97e3672698 100644 --- a/api/envoy/extensions/filters/http/header_to_metadata/v3/header_to_metadata.proto +++ b/api/envoy/extensions/filters/http/header_to_metadata/v3/header_to_metadata.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.header_to_metadata.v3"; option java_outer_classname = "HeaderToMetadataProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/header_to_metadata/v3;header_to_metadatav3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Header-To-Metadata Filter] diff --git a/api/envoy/extensions/filters/http/health_check/v3/health_check.proto b/api/envoy/extensions/filters/http/health_check/v3/health_check.proto index f3a0c42c388c..146ea3cf376a 100644 --- a/api/envoy/extensions/filters/http/health_check/v3/health_check.proto +++ b/api/envoy/extensions/filters/http/health_check/v3/health_check.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.health_check.v3"; option java_outer_classname = "HealthCheckProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/health_check/v3;health_checkv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Health check] diff --git a/api/envoy/extensions/filters/http/ip_tagging/v3/ip_tagging.proto b/api/envoy/extensions/filters/http/ip_tagging/v3/ip_tagging.proto index a23ad9dea0a9..edc9ef12ef9b 100644 --- a/api/envoy/extensions/filters/http/ip_tagging/v3/ip_tagging.proto +++ b/api/envoy/extensions/filters/http/ip_tagging/v3/ip_tagging.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.ip_tagging.v3"; option java_outer_classname = "IpTaggingProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ip_tagging/v3;ip_taggingv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: IP tagging] diff --git a/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto b/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto index 6d15956e1479..65ddd88e9df8 100644 --- a/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto +++ b/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto @@ -16,6 +16,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.jwt_authn.v3"; option java_outer_classname = "ConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/jwt_authn/v3;jwt_authnv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: JWT Authentication] diff --git a/api/envoy/extensions/filters/http/kill_request/v3/kill_request.proto b/api/envoy/extensions/filters/http/kill_request/v3/kill_request.proto index a0a23b0de3a3..b4de117a3a33 100644 --- a/api/envoy/extensions/filters/http/kill_request/v3/kill_request.proto +++ b/api/envoy/extensions/filters/http/kill_request/v3/kill_request.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.kill_request.v3"; option java_outer_classname = "KillRequestProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/kill_request/v3;kill_requestv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Kill Request] diff --git a/api/envoy/extensions/filters/http/local_ratelimit/v3/local_rate_limit.proto b/api/envoy/extensions/filters/http/local_ratelimit/v3/local_rate_limit.proto index 1cf6c5f2fa52..9d8252d9bc98 100644 --- a/api/envoy/extensions/filters/http/local_ratelimit/v3/local_rate_limit.proto +++ b/api/envoy/extensions/filters/http/local_ratelimit/v3/local_rate_limit.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.local_ratelimit.v3"; option java_outer_classname = "LocalRateLimitProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/local_ratelimit/v3;local_ratelimitv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Local Rate limit] diff --git a/api/envoy/extensions/filters/http/lua/v3/lua.proto b/api/envoy/extensions/filters/http/lua/v3/lua.proto index 1636c01ab1c7..e406cd4a7e1b 100644 --- a/api/envoy/extensions/filters/http/lua/v3/lua.proto +++ b/api/envoy/extensions/filters/http/lua/v3/lua.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.lua.v3"; option java_outer_classname = "LuaProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/lua/v3;luav3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Lua] diff --git a/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto b/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto index 15c71f3550fe..9b9ca8554198 100644 --- a/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto +++ b/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.oauth2.v3"; option java_outer_classname = "OauthProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/oauth2/v3;oauth2v3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: OAuth] diff --git a/api/envoy/extensions/filters/http/on_demand/v3/on_demand.proto b/api/envoy/extensions/filters/http/on_demand/v3/on_demand.proto index 27e709f7a8d6..6f04ada1e95d 100644 --- a/api/envoy/extensions/filters/http/on_demand/v3/on_demand.proto +++ b/api/envoy/extensions/filters/http/on_demand/v3/on_demand.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.on_demand.v3"; option java_outer_classname = "OnDemandProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/on_demand/v3;on_demandv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: OnDemand] diff --git a/api/envoy/extensions/filters/http/original_src/v3/original_src.proto b/api/envoy/extensions/filters/http/original_src/v3/original_src.proto index ca752b4c75ce..3f67a2f6e319 100644 --- a/api/envoy/extensions/filters/http/original_src/v3/original_src.proto +++ b/api/envoy/extensions/filters/http/original_src/v3/original_src.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.original_src.v3"; option java_outer_classname = "OriginalSrcProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/original_src/v3;original_srcv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Original Src Filter] diff --git a/api/envoy/extensions/filters/http/ratelimit/v3/rate_limit.proto b/api/envoy/extensions/filters/http/ratelimit/v3/rate_limit.proto index 53fb849361c1..b76cbf4a4a9e 100644 --- a/api/envoy/extensions/filters/http/ratelimit/v3/rate_limit.proto +++ b/api/envoy/extensions/filters/http/ratelimit/v3/rate_limit.proto @@ -16,6 +16,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.ratelimit.v3"; option java_outer_classname = "RateLimitProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ratelimit/v3;ratelimitv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Rate limit] diff --git a/api/envoy/extensions/filters/http/rbac/v3/rbac.proto b/api/envoy/extensions/filters/http/rbac/v3/rbac.proto index 7ad7ac5e6aa2..008818456e2f 100644 --- a/api/envoy/extensions/filters/http/rbac/v3/rbac.proto +++ b/api/envoy/extensions/filters/http/rbac/v3/rbac.proto @@ -10,6 +10,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.rbac.v3"; option java_outer_classname = "RbacProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/rbac/v3;rbacv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: RBAC] diff --git a/api/envoy/extensions/filters/http/router/v3/router.proto b/api/envoy/extensions/filters/http/router/v3/router.proto index ce595c057c01..7ce8b37dbb77 100644 --- a/api/envoy/extensions/filters/http/router/v3/router.proto +++ b/api/envoy/extensions/filters/http/router/v3/router.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.router.v3"; option java_outer_classname = "RouterProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3;routerv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Router] diff --git a/api/envoy/extensions/filters/http/set_metadata/v3/set_metadata.proto b/api/envoy/extensions/filters/http/set_metadata/v3/set_metadata.proto index f7ff348e2025..a50a1d4fad95 100644 --- a/api/envoy/extensions/filters/http/set_metadata/v3/set_metadata.proto +++ b/api/envoy/extensions/filters/http/set_metadata/v3/set_metadata.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.set_metadata.v3"; option java_outer_classname = "SetMetadataProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/set_metadata/v3;set_metadatav3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Set-Metadata Filter] diff --git a/api/envoy/extensions/filters/http/tap/v3/tap.proto b/api/envoy/extensions/filters/http/tap/v3/tap.proto index 81779443e4a5..edb2da6f4ca8 100644 --- a/api/envoy/extensions/filters/http/tap/v3/tap.proto +++ b/api/envoy/extensions/filters/http/tap/v3/tap.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.tap.v3"; option java_outer_classname = "TapProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/tap/v3;tapv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Tap] diff --git a/api/envoy/extensions/filters/http/wasm/v3/wasm.proto b/api/envoy/extensions/filters/http/wasm/v3/wasm.proto index a0cfcae1afb5..e2751404f8a4 100644 --- a/api/envoy/extensions/filters/http/wasm/v3/wasm.proto +++ b/api/envoy/extensions/filters/http/wasm/v3/wasm.proto @@ -9,6 +9,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.http.wasm.v3"; option java_outer_classname = "WasmProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/wasm/v3;wasmv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Wasm] diff --git a/api/envoy/extensions/filters/listener/http_inspector/v3/http_inspector.proto b/api/envoy/extensions/filters/listener/http_inspector/v3/http_inspector.proto index cb439b0973ba..c8ee26d27951 100644 --- a/api/envoy/extensions/filters/listener/http_inspector/v3/http_inspector.proto +++ b/api/envoy/extensions/filters/listener/http_inspector/v3/http_inspector.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.listener.http_inspector.v3"; option java_outer_classname = "HttpInspectorProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/http_inspector/v3;http_inspectorv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: HTTP Inspector Filter] diff --git a/api/envoy/extensions/filters/listener/original_dst/v3/original_dst.proto b/api/envoy/extensions/filters/listener/original_dst/v3/original_dst.proto index 8239c5c42c52..be6cf41e18b4 100644 --- a/api/envoy/extensions/filters/listener/original_dst/v3/original_dst.proto +++ b/api/envoy/extensions/filters/listener/original_dst/v3/original_dst.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.listener.original_dst.v3"; option java_outer_classname = "OriginalDstProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/original_dst/v3;original_dstv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Original Dst Filter] diff --git a/api/envoy/extensions/filters/listener/original_src/v3/original_src.proto b/api/envoy/extensions/filters/listener/original_src/v3/original_src.proto index aa0603cdff47..8cafe7d09c27 100644 --- a/api/envoy/extensions/filters/listener/original_src/v3/original_src.proto +++ b/api/envoy/extensions/filters/listener/original_src/v3/original_src.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.listener.original_src.v3"; option java_outer_classname = "OriginalSrcProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/original_src/v3;original_srcv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Original Src Filter] diff --git a/api/envoy/extensions/filters/listener/proxy_protocol/v3/proxy_protocol.proto b/api/envoy/extensions/filters/listener/proxy_protocol/v3/proxy_protocol.proto index fb8047d391e9..1dd001b93777 100644 --- a/api/envoy/extensions/filters/listener/proxy_protocol/v3/proxy_protocol.proto +++ b/api/envoy/extensions/filters/listener/proxy_protocol/v3/proxy_protocol.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.listener.proxy_protocol.v3"; option java_outer_classname = "ProxyProtocolProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/proxy_protocol/v3;proxy_protocolv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Proxy Protocol Filter] 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 eff9774844f4..40854d2ad338 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,12 +2,15 @@ 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"; option java_package = "io.envoyproxy.envoy.extensions.filters.listener.tls_inspector.v3"; option java_outer_classname = "TlsInspectorProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/tls_inspector/v3;tls_inspectorv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: TLS Inspector Filter] @@ -17,4 +20,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/api/envoy/extensions/filters/network/client_ssl_auth/v3/client_ssl_auth.proto b/api/envoy/extensions/filters/network/client_ssl_auth/v3/client_ssl_auth.proto index 2ed14c7f0e23..6b45a59e530e 100644 --- a/api/envoy/extensions/filters/network/client_ssl_auth/v3/client_ssl_auth.proto +++ b/api/envoy/extensions/filters/network/client_ssl_auth/v3/client_ssl_auth.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.client_ssl_auth.v3"; option java_outer_classname = "ClientSslAuthProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/client_ssl_auth/v3;client_ssl_authv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Client TLS authentication] diff --git a/api/envoy/extensions/filters/network/connection_limit/v3/connection_limit.proto b/api/envoy/extensions/filters/network/connection_limit/v3/connection_limit.proto index ccd30aaba692..61bf64b644d4 100644 --- a/api/envoy/extensions/filters/network/connection_limit/v3/connection_limit.proto +++ b/api/envoy/extensions/filters/network/connection_limit/v3/connection_limit.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.connection_limit.v3"; option java_outer_classname = "ConnectionLimitProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/connection_limit/v3;connection_limitv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Connection limit] diff --git a/api/envoy/extensions/filters/network/direct_response/v3/config.proto b/api/envoy/extensions/filters/network/direct_response/v3/config.proto index 2742372b2f91..e5a8347d6af7 100644 --- a/api/envoy/extensions/filters/network/direct_response/v3/config.proto +++ b/api/envoy/extensions/filters/network/direct_response/v3/config.proto @@ -10,6 +10,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.direct_response.v3"; option java_outer_classname = "ConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/direct_response/v3;direct_responsev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Direct response] diff --git a/api/envoy/extensions/filters/network/dubbo_proxy/router/v3/router.proto b/api/envoy/extensions/filters/network/dubbo_proxy/router/v3/router.proto index fa1959a425c8..6242596bd7e1 100644 --- a/api/envoy/extensions/filters/network/dubbo_proxy/router/v3/router.proto +++ b/api/envoy/extensions/filters/network/dubbo_proxy/router/v3/router.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.dubbo_proxy.router.v3"; option java_outer_classname = "RouterProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/dubbo_proxy/router/v3;routerv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Router] diff --git a/api/envoy/extensions/filters/network/dubbo_proxy/v3/dubbo_proxy.proto b/api/envoy/extensions/filters/network/dubbo_proxy/v3/dubbo_proxy.proto index 646f053ca9b6..2a9f9feaf03c 100644 --- a/api/envoy/extensions/filters/network/dubbo_proxy/v3/dubbo_proxy.proto +++ b/api/envoy/extensions/filters/network/dubbo_proxy/v3/dubbo_proxy.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.dubbo_proxy.v3"; option java_outer_classname = "DubboProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/dubbo_proxy/v3;dubbo_proxyv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Dubbo Proxy] diff --git a/api/envoy/extensions/filters/network/dubbo_proxy/v3/route.proto b/api/envoy/extensions/filters/network/dubbo_proxy/v3/route.proto index e255985ed8e4..f9446b91eb85 100644 --- a/api/envoy/extensions/filters/network/dubbo_proxy/v3/route.proto +++ b/api/envoy/extensions/filters/network/dubbo_proxy/v3/route.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.dubbo_proxy.v3"; option java_outer_classname = "RouteProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/dubbo_proxy/v3;dubbo_proxyv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Dubbo Proxy Route Configuration] diff --git a/api/envoy/extensions/filters/network/echo/v3/echo.proto b/api/envoy/extensions/filters/network/echo/v3/echo.proto index 077d87259b6b..495a55c05510 100644 --- a/api/envoy/extensions/filters/network/echo/v3/echo.proto +++ b/api/envoy/extensions/filters/network/echo/v3/echo.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.echo.v3"; option java_outer_classname = "EchoProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/echo/v3;echov3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Echo] diff --git a/api/envoy/extensions/filters/network/ext_authz/v3/ext_authz.proto b/api/envoy/extensions/filters/network/ext_authz/v3/ext_authz.proto index c40adb5f26bd..19d4283796e0 100644 --- a/api/envoy/extensions/filters/network/ext_authz/v3/ext_authz.proto +++ b/api/envoy/extensions/filters/network/ext_authz/v3/ext_authz.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.ext_authz.v3"; option java_outer_classname = "ExtAuthzProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/ext_authz/v3;ext_authzv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Network External Authorization ] diff --git a/api/envoy/extensions/filters/network/http_connection_manager/v3/BUILD b/api/envoy/extensions/filters/network/http_connection_manager/v3/BUILD index 55b63248136c..b244acf916a0 100644 --- a/api/envoy/extensions/filters/network/http_connection_manager/v3/BUILD +++ b/api/envoy/extensions/filters/network/http_connection_manager/v3/BUILD @@ -8,7 +8,6 @@ api_proto_package( deps = [ "//envoy/config/accesslog/v3:pkg", "//envoy/config/core/v3:pkg", - "//envoy/config/filter/network/http_connection_manager/v2:pkg", "//envoy/config/route/v3:pkg", "//envoy/config/trace/v3:pkg", "//envoy/type/http/v3:pkg", diff --git a/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto b/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto index 93d255904fb8..f4229eff0a32 100644 --- a/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto +++ b/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto @@ -28,6 +28,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3"; option java_outer_classname = "HttpConnectionManagerProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3;http_connection_managerv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: HTTP connection manager] diff --git a/api/envoy/extensions/filters/network/local_ratelimit/v3/local_rate_limit.proto b/api/envoy/extensions/filters/network/local_ratelimit/v3/local_rate_limit.proto index 3ee3655b7c3c..00e431d6fc74 100644 --- a/api/envoy/extensions/filters/network/local_ratelimit/v3/local_rate_limit.proto +++ b/api/envoy/extensions/filters/network/local_ratelimit/v3/local_rate_limit.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.local_ratelimit.v3"; option java_outer_classname = "LocalRateLimitProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/local_ratelimit/v3;local_ratelimitv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Local rate limit] diff --git a/api/envoy/extensions/filters/network/meta_protocol_proxy/matcher/action/v3/BUILD b/api/envoy/extensions/filters/network/meta_protocol_proxy/matcher/action/v3/BUILD new file mode 100644 index 000000000000..8b8b717770ec --- /dev/null +++ b/api/envoy/extensions/filters/network/meta_protocol_proxy/matcher/action/v3/BUILD @@ -0,0 +1,13 @@ +# 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/config/route/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + "@com_github_cncf_udpa//xds/annotations/v3:pkg", + ], +) diff --git a/api/envoy/extensions/filters/network/meta_protocol_proxy/matcher/action/v3/action.proto b/api/envoy/extensions/filters/network/meta_protocol_proxy/matcher/action/v3/action.proto new file mode 100644 index 000000000000..1d831c9f349e --- /dev/null +++ b/api/envoy/extensions/filters/network/meta_protocol_proxy/matcher/action/v3/action.proto @@ -0,0 +1,35 @@ +syntax = "proto3"; + +package envoy.extensions.filters.network.meta_protocol_proxy.matcher.action.v3; + +import "envoy/config/route/v3/route_components.proto"; + +import "xds/annotations/v3/status.proto"; + +import "udpa/annotations/status.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.network.meta_protocol_proxy.matcher.action.v3"; +option java_outer_classname = "ActionProto"; +option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/meta_protocol_proxy/matcher/action/v3;actionv3"; +option (udpa.annotations.file_status).package_version_status = ACTIVE; +option (xds.annotations.v3.file_status).work_in_progress = true; + +// [#protodoc-title: Meta Protocol Proxy Route Action Configuration] + +// Configuration for the route match action. +// [#not-implemented-hide:] +message RouteAction { + oneof cluster_specifier { + option (validate.required) = true; + + // Indicates the upstream cluster to which the request should be routed. + string cluster = 1; + + // Multiple upstream clusters can be specified for a given route. The request is routed to one + // of the upstream clusters based on weights assigned to each cluster. + // Currently ClusterWeight only supports the name and weight fields. + config.route.v3.WeightedCluster weighted_clusters = 2; + } +} diff --git a/api/envoy/extensions/filters/network/meta_protocol_proxy/matcher/v3/BUILD b/api/envoy/extensions/filters/network/meta_protocol_proxy/matcher/v3/BUILD new file mode 100644 index 000000000000..ec1e778e06e5 --- /dev/null +++ b/api/envoy/extensions/filters/network/meta_protocol_proxy/matcher/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 = [ + "@com_github_cncf_udpa//udpa/annotations:pkg", + "@com_github_cncf_udpa//xds/annotations/v3:pkg", + ], +) diff --git a/api/envoy/extensions/filters/network/meta_protocol_proxy/matcher/v3/matcher.proto b/api/envoy/extensions/filters/network/meta_protocol_proxy/matcher/v3/matcher.proto new file mode 100644 index 000000000000..60679639874f --- /dev/null +++ b/api/envoy/extensions/filters/network/meta_protocol_proxy/matcher/v3/matcher.proto @@ -0,0 +1,37 @@ +syntax = "proto3"; + +package envoy.extensions.filters.network.meta_protocol_proxy.matcher.v3; + +import "xds/annotations/v3/status.proto"; + +import "udpa/annotations/status.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.network.meta_protocol_proxy.matcher.v3"; +option java_outer_classname = "MatcherProto"; +option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/meta_protocol_proxy/matcher/v3;matcherv3"; +option (udpa.annotations.file_status).package_version_status = ACTIVE; +option (xds.annotations.v3.file_status).work_in_progress = true; + +// [#protodoc-title: Meta Protocol Proxy Route Matcher Configuration] + +// Used to match request service of the downstream request. Only applicable if a service provided +// by the application protocol. +// [#not-implemented-hide:] +message ServiceMatchInput { +} + +// Used to match request method of the downstream request. Only applicable if a method provided +// by the application protocol. +// [#not-implemented-hide:] +message MethodMatchInput { +} + +// Used to match an arbitrary property of the downstream request. +// These properties are populated by the codecs of application protocols. +// [#not-implemented-hide:] +message PropertyMatchInput { + // The property name to match on. + string property_name = 1 [(validate.rules).string = {min_len: 1}]; +} diff --git a/api/envoy/extensions/filters/network/meta_protocol_proxy/v3/BUILD b/api/envoy/extensions/filters/network/meta_protocol_proxy/v3/BUILD new file mode 100644 index 000000000000..6efc2efe01e8 --- /dev/null +++ b/api/envoy/extensions/filters/network/meta_protocol_proxy/v3/BUILD @@ -0,0 +1,14 @@ +# 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/config/core/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + "@com_github_cncf_udpa//xds/annotations/v3:pkg", + "@com_github_cncf_udpa//xds/type/matcher/v3:pkg", + ], +) diff --git a/api/envoy/extensions/filters/network/meta_protocol_proxy/v3/meta_protocol_proxy.proto b/api/envoy/extensions/filters/network/meta_protocol_proxy/v3/meta_protocol_proxy.proto new file mode 100644 index 000000000000..7ab09b16b8f6 --- /dev/null +++ b/api/envoy/extensions/filters/network/meta_protocol_proxy/v3/meta_protocol_proxy.proto @@ -0,0 +1,66 @@ +syntax = "proto3"; + +package envoy.extensions.filters.network.meta_protocol_proxy.v3; + +import "envoy/config/core/v3/config_source.proto"; +import "envoy/config/core/v3/extension.proto"; +import "envoy/extensions/filters/network/meta_protocol_proxy/v3/route.proto"; + +import "xds/annotations/v3/status.proto"; + +import "udpa/annotations/status.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.network.meta_protocol_proxy.v3"; +option java_outer_classname = "MetaProtocolProxyProto"; +option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/meta_protocol_proxy/v3;meta_protocol_proxyv3"; +option (udpa.annotations.file_status).package_version_status = ACTIVE; +option (xds.annotations.v3.file_status).work_in_progress = true; + +// [#protodoc-title: Meta Protocol Proxy] +// Meta Protocol proxy. + +// [#not-implemented-hide:] +// [#next-free-field: 6] +message MetaProtocolProxy { + // The human readable prefix to use when emitting statistics. + string stat_prefix = 1 [(validate.rules).string = {min_len: 1}]; + + // The application protocol built on top of the meta protocol proxy. + ApplicationProtocol application_protocol = 2 [(validate.rules).message = {required: true}]; + + oneof route_specifier { + option (validate.required) = true; + + // The meta protocol proxies route table will be dynamically loaded via the meta RDS API. + MetaRds rds = 3; + + // The route table for the meta protocol proxy is static and is specified in this property. + RouteConfiguration route_config = 4; + } + + // A list of individual Layer-7 filters that make up the filter chain for requests made to the + // meta protocol proxy. Order matters as the filters are processed sequentially as request events + // happen. + repeated config.core.v3.TypedExtensionConfig meta_protocol_filters = 5; +} + +// [#not-implemented-hide:] +message ApplicationProtocol { + // The name of the application protocol. + string name = 1 [(validate.rules).string = {min_len: 1}]; + + // The codec which encodes and decodes the application protocol. + config.core.v3.TypedExtensionConfig codec = 2 [(validate.rules).message = {required: true}]; +} + +// [#not-implemented-hide:] +message MetaRds { + // Configuration source specifier for RDS. + config.core.v3.ConfigSource config_source = 1 [(validate.rules).message = {required: true}]; + + // The name of the route configuration. This name will be passed to the RDS API. This allows an + // Envoy configuration with multiple meta protocol proxies to use different route configurations. + string route_config_name = 2 [(validate.rules).string = {min_len: 1}]; +} diff --git a/api/envoy/extensions/filters/network/meta_protocol_proxy/v3/route.proto b/api/envoy/extensions/filters/network/meta_protocol_proxy/v3/route.proto new file mode 100644 index 000000000000..f254886ea9f0 --- /dev/null +++ b/api/envoy/extensions/filters/network/meta_protocol_proxy/v3/route.proto @@ -0,0 +1,70 @@ +syntax = "proto3"; + +package envoy.extensions.filters.network.meta_protocol_proxy.v3; + +import "xds/annotations/v3/status.proto"; +import "xds/type/matcher/v3/matcher.proto"; + +import "udpa/annotations/status.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.network.meta_protocol_proxy.v3"; +option java_outer_classname = "RouteProto"; +option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/meta_protocol_proxy/v3;meta_protocol_proxyv3"; +option (udpa.annotations.file_status).package_version_status = ACTIVE; +option (xds.annotations.v3.file_status).work_in_progress = true; + +// [#protodoc-title: Meta Protocol Proxy Route Configuration] +// The meta protocol proxy makes use of the `xds matching API` for routing configurations. +// +// In the below example, we combine a top level tree matcher with a linear matcher to match +// the incoming requests, and send the matching requests to v1 of the upstream service. +// +// name: demo-v1 +// route: +// matcher_tree: +// input: +// name: request-service +// typed_config: +// "@type": type.googleapis.com/envoy.extensions.filters.network.meta_protocol_proxy.matcher.v3.ServiceMatchInput +// exact_match_map: +// map: +// org.apache.dubbo.samples.basic.api.DemoService: +// matcher: +// matcher_list: +// matchers: +// - predicate: +// and_matcher: +// predicate: +// - single_predicate: +// input: +// name: request-properties +// typed_config: +// "@type": type.googleapis.com/envoy.extensions.filters.network.meta_protocol_proxy.matcher.v3.PropertyMatchInput +// property_name: version +// value_match: +// exact: v1 +// - single_predicate: +// input: +// name: request-properties +// typed_config: +// "@type": type.googleapis.com/envoy.extensions.filters.network.meta_protocol_proxy.matcher.v3.PropertyMatchInput +// property_name: user +// value_match: +// exact: john +// on_match: +// action: +// name: route +// typed_config: +// "@type": type.googleapis.com/envoy.extensions.filters.network.meta_protocol_proxy.matcher.action.v3.routeAction +// cluster: outbound|20880|v1|org.apache.dubbo.samples.basic.api.demoservice +// [#not-implemented-hide:] +message RouteConfiguration { + // The name of the route configuration. For example, it might match route_config_name in + // envoy.extensions.filters.network.meta_protocol_proxy.v3.Rds. + string name = 1 [(validate.rules).string = {min_len: 1}]; + + // The match tree to use when resolving route actions for incoming requests. + xds.type.matcher.v3.Matcher route = 2 [(validate.rules).message = {required: true}]; +} diff --git a/api/envoy/extensions/filters/network/mongo_proxy/v3/mongo_proxy.proto b/api/envoy/extensions/filters/network/mongo_proxy/v3/mongo_proxy.proto index ebdfb6f2fcc0..721f722ef6d1 100644 --- a/api/envoy/extensions/filters/network/mongo_proxy/v3/mongo_proxy.proto +++ b/api/envoy/extensions/filters/network/mongo_proxy/v3/mongo_proxy.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.mongo_proxy.v3"; option java_outer_classname = "MongoProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/mongo_proxy/v3;mongo_proxyv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Mongo proxy] diff --git a/api/envoy/extensions/filters/network/ratelimit/v3/rate_limit.proto b/api/envoy/extensions/filters/network/ratelimit/v3/rate_limit.proto index 2fcdda846b6a..9e8ab8b63d09 100644 --- a/api/envoy/extensions/filters/network/ratelimit/v3/rate_limit.proto +++ b/api/envoy/extensions/filters/network/ratelimit/v3/rate_limit.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.ratelimit.v3"; option java_outer_classname = "RateLimitProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/ratelimit/v3;ratelimitv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Rate limit] diff --git a/api/envoy/extensions/filters/network/rbac/v3/rbac.proto b/api/envoy/extensions/filters/network/rbac/v3/rbac.proto index 4d1ff296fa4a..44141f167068 100644 --- a/api/envoy/extensions/filters/network/rbac/v3/rbac.proto +++ b/api/envoy/extensions/filters/network/rbac/v3/rbac.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.rbac.v3"; option java_outer_classname = "RbacProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/rbac/v3;rbacv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: RBAC] diff --git a/api/envoy/extensions/filters/network/redis_proxy/v3/BUILD b/api/envoy/extensions/filters/network/redis_proxy/v3/BUILD index bbf960251284..1c1a6f6b4423 100644 --- a/api/envoy/extensions/filters/network/redis_proxy/v3/BUILD +++ b/api/envoy/extensions/filters/network/redis_proxy/v3/BUILD @@ -7,7 +7,6 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ "//envoy/config/core/v3:pkg", - "//envoy/config/filter/network/redis_proxy/v2:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/api/envoy/extensions/filters/network/redis_proxy/v3/redis_proxy.proto b/api/envoy/extensions/filters/network/redis_proxy/v3/redis_proxy.proto index df67ccf009f4..fb2eac53cdb2 100644 --- a/api/envoy/extensions/filters/network/redis_proxy/v3/redis_proxy.proto +++ b/api/envoy/extensions/filters/network/redis_proxy/v3/redis_proxy.proto @@ -16,6 +16,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.redis_proxy.v3"; option java_outer_classname = "RedisProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/redis_proxy/v3;redis_proxyv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Redis Proxy] diff --git a/api/envoy/extensions/filters/network/sni_cluster/v3/sni_cluster.proto b/api/envoy/extensions/filters/network/sni_cluster/v3/sni_cluster.proto index 3d6f0ee234ab..15eae78f89f3 100644 --- a/api/envoy/extensions/filters/network/sni_cluster/v3/sni_cluster.proto +++ b/api/envoy/extensions/filters/network/sni_cluster/v3/sni_cluster.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.sni_cluster.v3"; option java_outer_classname = "SniClusterProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/sni_cluster/v3;sni_clusterv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: SNI Cluster Filter] diff --git a/api/envoy/extensions/filters/network/sni_dynamic_forward_proxy/v3/sni_dynamic_forward_proxy.proto b/api/envoy/extensions/filters/network/sni_dynamic_forward_proxy/v3/sni_dynamic_forward_proxy.proto index a084b0682b67..1ab471e0d772 100644 --- a/api/envoy/extensions/filters/network/sni_dynamic_forward_proxy/v3/sni_dynamic_forward_proxy.proto +++ b/api/envoy/extensions/filters/network/sni_dynamic_forward_proxy/v3/sni_dynamic_forward_proxy.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.sni_dynamic_forward_proxy.v3"; option java_outer_classname = "SniDynamicForwardProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/sni_dynamic_forward_proxy/v3;sni_dynamic_forward_proxyv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: SNI dynamic forward proxy] diff --git a/api/envoy/extensions/filters/network/tcp_proxy/v3/BUILD b/api/envoy/extensions/filters/network/tcp_proxy/v3/BUILD index 67dc2ebcf9f5..16c00029d091 100644 --- a/api/envoy/extensions/filters/network/tcp_proxy/v3/BUILD +++ b/api/envoy/extensions/filters/network/tcp_proxy/v3/BUILD @@ -8,7 +8,6 @@ api_proto_package( deps = [ "//envoy/config/accesslog/v3:pkg", "//envoy/config/core/v3:pkg", - "//envoy/config/filter/network/tcp_proxy/v2:pkg", "//envoy/type/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], diff --git a/api/envoy/extensions/filters/network/tcp_proxy/v3/tcp_proxy.proto b/api/envoy/extensions/filters/network/tcp_proxy/v3/tcp_proxy.proto index ac2cc7ef0256..ff37308b0c8d 100644 --- a/api/envoy/extensions/filters/network/tcp_proxy/v3/tcp_proxy.proto +++ b/api/envoy/extensions/filters/network/tcp_proxy/v3/tcp_proxy.proto @@ -16,6 +16,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.tcp_proxy.v3"; option java_outer_classname = "TcpProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3;tcp_proxyv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: TCP Proxy] 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 000000000000..693f0b92ff34 --- /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 000000000000..aa3bc02ef252 --- /dev/null +++ b/api/envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3/header_to_metadata.proto @@ -0,0 +1,110 @@ +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 go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3;header_to_metadatav3"; +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/envoy/extensions/filters/network/thrift_proxy/filters/ratelimit/v3/rate_limit.proto b/api/envoy/extensions/filters/network/thrift_proxy/filters/ratelimit/v3/rate_limit.proto index 8583bbe4b468..b3e70a4583b7 100644 --- a/api/envoy/extensions/filters/network/thrift_proxy/filters/ratelimit/v3/rate_limit.proto +++ b/api/envoy/extensions/filters/network/thrift_proxy/filters/ratelimit/v3/rate_limit.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.thrift_proxy.filters.ratelimit.v3"; option java_outer_classname = "RateLimitProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/thrift_proxy/filters/ratelimit/v3;ratelimitv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Rate limit] diff --git a/api/envoy/extensions/filters/network/thrift_proxy/router/v3/BUILD b/api/envoy/extensions/filters/network/thrift_proxy/router/v3/BUILD index c24f669b9bbd..ee92fb652582 100644 --- a/api/envoy/extensions/filters/network/thrift_proxy/router/v3/BUILD +++ b/api/envoy/extensions/filters/network/thrift_proxy/router/v3/BUILD @@ -5,8 +5,5 @@ load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") licenses(["notice"]) # Apache 2 api_proto_package( - deps = [ - "//envoy/config/filter/thrift/router/v2alpha1:pkg", - "@com_github_cncf_udpa//udpa/annotations:pkg", - ], + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], ) diff --git a/api/envoy/extensions/filters/network/thrift_proxy/router/v3/router.proto b/api/envoy/extensions/filters/network/thrift_proxy/router/v3/router.proto index 860622cb61e4..15ba7884daa0 100644 --- a/api/envoy/extensions/filters/network/thrift_proxy/router/v3/router.proto +++ b/api/envoy/extensions/filters/network/thrift_proxy/router/v3/router.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.thrift_proxy.router.v3"; option java_outer_classname = "RouterProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/thrift_proxy/router/v3;routerv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Router] diff --git a/api/envoy/extensions/filters/network/thrift_proxy/v3/BUILD b/api/envoy/extensions/filters/network/thrift_proxy/v3/BUILD index ccacc55735af..6eb33fe8151a 100644 --- a/api/envoy/extensions/filters/network/thrift_proxy/v3/BUILD +++ b/api/envoy/extensions/filters/network/thrift_proxy/v3/BUILD @@ -7,7 +7,6 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ "//envoy/config/core/v3:pkg", - "//envoy/config/filter/network/thrift_proxy/v2alpha1:pkg", "//envoy/config/route/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], diff --git a/api/envoy/extensions/filters/network/thrift_proxy/v3/route.proto b/api/envoy/extensions/filters/network/thrift_proxy/v3/route.proto index b79c9bc9619e..a1582a30e18a 100644 --- a/api/envoy/extensions/filters/network/thrift_proxy/v3/route.proto +++ b/api/envoy/extensions/filters/network/thrift_proxy/v3/route.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.thrift_proxy.v3"; option java_outer_classname = "RouteProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/thrift_proxy/v3;thrift_proxyv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Thrift Proxy Route Configuration] diff --git a/api/envoy/extensions/filters/network/thrift_proxy/v3/thrift_proxy.proto b/api/envoy/extensions/filters/network/thrift_proxy/v3/thrift_proxy.proto index 01c41c77bb2b..ba43179fe625 100644 --- a/api/envoy/extensions/filters/network/thrift_proxy/v3/thrift_proxy.proto +++ b/api/envoy/extensions/filters/network/thrift_proxy/v3/thrift_proxy.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.thrift_proxy.v3"; option java_outer_classname = "ThriftProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/thrift_proxy/v3;thrift_proxyv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Thrift Proxy] diff --git a/api/envoy/extensions/filters/network/wasm/v3/wasm.proto b/api/envoy/extensions/filters/network/wasm/v3/wasm.proto index 1b27e18e3c31..4fe67122f570 100644 --- a/api/envoy/extensions/filters/network/wasm/v3/wasm.proto +++ b/api/envoy/extensions/filters/network/wasm/v3/wasm.proto @@ -9,6 +9,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.wasm.v3"; option java_outer_classname = "WasmProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/wasm/v3;wasmv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Wasm] diff --git a/api/envoy/extensions/filters/network/zookeeper_proxy/v3/zookeeper_proxy.proto b/api/envoy/extensions/filters/network/zookeeper_proxy/v3/zookeeper_proxy.proto index eb2c202c58f1..4b894051acb2 100644 --- a/api/envoy/extensions/filters/network/zookeeper_proxy/v3/zookeeper_proxy.proto +++ b/api/envoy/extensions/filters/network/zookeeper_proxy/v3/zookeeper_proxy.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.network.zookeeper_proxy.v3"; option java_outer_classname = "ZookeeperProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/zookeeper_proxy/v3;zookeeper_proxyv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: ZooKeeper proxy] diff --git a/api/envoy/extensions/filters/udp/dns_filter/v3/dns_filter.proto b/api/envoy/extensions/filters/udp/dns_filter/v3/dns_filter.proto index 39c92d5c8864..0ea19f24e052 100644 --- a/api/envoy/extensions/filters/udp/dns_filter/v3/dns_filter.proto +++ b/api/envoy/extensions/filters/udp/dns_filter/v3/dns_filter.proto @@ -17,6 +17,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.udp.dns_filter.v3"; option java_outer_classname = "DnsFilterProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/udp/dns_filter/v3;dns_filterv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: DNS Filter] @@ -68,9 +69,10 @@ message DnsFilterConfig { [deprecated = true, (envoy.annotations.deprecated_at_minor_version) = "3.0"]; // DNS resolution configuration which includes the underlying dns resolver addresses and options. - // This field will be deprecated in favor of + // This field is deprecated in favor of // :ref:`typed_dns_resolver_config `. - config.core.v3.DnsResolutionConfig dns_resolution_config = 5; + config.core.v3.DnsResolutionConfig dns_resolution_config = 5 + [deprecated = true, (envoy.annotations.deprecated_at_minor_version) = "3.0"]; // DNS resolver type configuration extension. This extension can be used to configure c-ares, apple, // or any other DNS resolver types and the related parameters. diff --git a/api/envoy/extensions/filters/udp/udp_proxy/v3/udp_proxy.proto b/api/envoy/extensions/filters/udp/udp_proxy/v3/udp_proxy.proto index 9d410e28afe3..1be4417e67d9 100644 --- a/api/envoy/extensions/filters/udp/udp_proxy/v3/udp_proxy.proto +++ b/api/envoy/extensions/filters/udp/udp_proxy/v3/udp_proxy.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.filters.udp.udp_proxy.v3"; option java_outer_classname = "UdpProxyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/udp/udp_proxy/v3;udp_proxyv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: UDP proxy] @@ -20,7 +21,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#extension: envoy.filters.udp_listener.udp_proxy] // Configuration for the UDP proxy filter. -// [#next-free-field: 7] +// [#next-free-field: 8] message UdpProxyConfig { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.udp.udp_proxy.v2alpha.UdpProxyConfig"; @@ -82,4 +83,9 @@ message UdpProxyConfig { // :ref:`prefer_gro ` is true for upstream // sockets as the assumption is datagrams will be received from a single source. config.core.v3.UdpSocketConfig upstream_socket_config = 6; + + // Perform per packet load balancing (upstream host selection) on each received data chunk. + // The default if not specified is false, that means each data chunk is forwarded + // to upstream host selected on first chunk receival for that "session" (identified by source IP/port and local IP/port). + bool use_per_packet_load_balancing = 7; } diff --git a/api/envoy/extensions/formatter/metadata/v3/metadata.proto b/api/envoy/extensions/formatter/metadata/v3/metadata.proto index 9b110a489381..b91e291e3e2d 100644 --- a/api/envoy/extensions/formatter/metadata/v3/metadata.proto +++ b/api/envoy/extensions/formatter/metadata/v3/metadata.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.formatter.metadata.v3"; option java_outer_classname = "MetadataProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/formatter/metadata/v3;metadatav3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Formatter extension for printing various types of metadata] @@ -26,7 +27,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/api/envoy/extensions/formatter/req_without_query/v3/req_without_query.proto b/api/envoy/extensions/formatter/req_without_query/v3/req_without_query.proto index e1b6c32a97e6..de8bf982cc48 100644 --- a/api/envoy/extensions/formatter/req_without_query/v3/req_without_query.proto +++ b/api/envoy/extensions/formatter/req_without_query/v3/req_without_query.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.formatter.req_without_query.v3"; option java_outer_classname = "ReqWithoutQueryProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/formatter/req_without_query/v3;req_without_queryv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Formatter extension for printing request without query string] diff --git a/api/envoy/extensions/health_checkers/redis/v3/BUILD b/api/envoy/extensions/health_checkers/redis/v3/BUILD index 1cb4c6154f26..ee92fb652582 100644 --- a/api/envoy/extensions/health_checkers/redis/v3/BUILD +++ b/api/envoy/extensions/health_checkers/redis/v3/BUILD @@ -5,8 +5,5 @@ load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") licenses(["notice"]) # Apache 2 api_proto_package( - deps = [ - "//envoy/config/health_checker/redis/v2:pkg", - "@com_github_cncf_udpa//udpa/annotations:pkg", - ], + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], ) diff --git a/api/envoy/extensions/health_checkers/redis/v3/redis.proto b/api/envoy/extensions/health_checkers/redis/v3/redis.proto index 10f5c2b30b03..caa385996b52 100644 --- a/api/envoy/extensions/health_checkers/redis/v3/redis.proto +++ b/api/envoy/extensions/health_checkers/redis/v3/redis.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.health_checkers.redis.v3"; option java_outer_classname = "RedisProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/health_checkers/redis/v3;redisv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Redis] diff --git a/api/envoy/extensions/http/header_formatters/preserve_case/v3/preserve_case.proto b/api/envoy/extensions/http/header_formatters/preserve_case/v3/preserve_case.proto index 64bdd497ecab..7604972ebab2 100644 --- a/api/envoy/extensions/http/header_formatters/preserve_case/v3/preserve_case.proto +++ b/api/envoy/extensions/http/header_formatters/preserve_case/v3/preserve_case.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.http.header_formatters.preserve_case.v3"; option java_outer_classname = "PreserveCaseProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/http/header_formatters/preserve_case/v3;preserve_casev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Preserve case header formatter] @@ -16,4 +17,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // See the :ref:`header casing ` configuration guide for more // information. message PreserveCaseFormatterConfig { + // Allows forwarding reason phrase text. + // This is off by default, and a standard reason phrase is used for a corresponding HTTP response code. + bool forward_reason_phrase = 1; } diff --git a/api/envoy/extensions/http/original_ip_detection/custom_header/v3/custom_header.proto b/api/envoy/extensions/http/original_ip_detection/custom_header/v3/custom_header.proto index 5ea93d754843..b2d3d7d26f80 100644 --- a/api/envoy/extensions/http/original_ip_detection/custom_header/v3/custom_header.proto +++ b/api/envoy/extensions/http/original_ip_detection/custom_header/v3/custom_header.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.http.original_ip_detection.custom_header.v3"; option java_outer_classname = "CustomHeaderProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/http/original_ip_detection/custom_header/v3;custom_headerv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Custom header original IP detection extension] diff --git a/api/envoy/extensions/http/original_ip_detection/xff/v3/xff.proto b/api/envoy/extensions/http/original_ip_detection/xff/v3/xff.proto index 6864788f9f18..b09b6f311d84 100644 --- a/api/envoy/extensions/http/original_ip_detection/xff/v3/xff.proto +++ b/api/envoy/extensions/http/original_ip_detection/xff/v3/xff.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.http.original_ip_detection.xff.v3"; option java_outer_classname = "XffProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/http/original_ip_detection/xff/v3;xffv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: XFF original IP detection extension] diff --git a/api/envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.proto b/api/envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.proto index 90da16095fa9..e556072b6cee 100644 --- a/api/envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.proto +++ b/api/envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.proto @@ -8,6 +8,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.allow_listed_routes.v3"; option java_outer_classname = "AllowListedRoutesConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/internal_redirect/allow_listed_routes/v3;allow_listed_routesv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Allow listed routes internal redirect predicate] diff --git a/api/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto b/api/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto index c8b03e07b4b6..e5ed68cb7f41 100644 --- a/api/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto +++ b/api/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.previous_routes.v3"; option java_outer_classname = "PreviousRoutesConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/internal_redirect/previous_routes/v3;previous_routesv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Previous routes internal redirect predicate] diff --git a/api/envoy/extensions/internal_redirect/safe_cross_scheme/v3/safe_cross_scheme_config.proto b/api/envoy/extensions/internal_redirect/safe_cross_scheme/v3/safe_cross_scheme_config.proto index e3638adb9fdb..d4055766be50 100644 --- a/api/envoy/extensions/internal_redirect/safe_cross_scheme/v3/safe_cross_scheme_config.proto +++ b/api/envoy/extensions/internal_redirect/safe_cross_scheme/v3/safe_cross_scheme_config.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.safe_cross_scheme.v3"; option java_outer_classname = "SafeCrossSchemeConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/internal_redirect/safe_cross_scheme/v3;safe_cross_schemev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: SafeCrossScheme internal redirect predicate] diff --git a/api/envoy/extensions/key_value/file_based/v3/config.proto b/api/envoy/extensions/key_value/file_based/v3/config.proto index 82aa94f8cb64..49873f73b6bd 100644 --- a/api/envoy/extensions/key_value/file_based/v3/config.proto +++ b/api/envoy/extensions/key_value/file_based/v3/config.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.key_value.file_based.v3"; option java_outer_classname = "ConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/key_value/file_based/v3;file_basedv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: File Based Key Value Store storage plugin] diff --git a/api/envoy/extensions/matching/common_inputs/environment_variable/v3/input.proto b/api/envoy/extensions/matching/common_inputs/environment_variable/v3/input.proto index 6bbe86e68864..9bb8e5e7bdc4 100644 --- a/api/envoy/extensions/matching/common_inputs/environment_variable/v3/input.proto +++ b/api/envoy/extensions/matching/common_inputs/environment_variable/v3/input.proto @@ -8,6 +8,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.matching.common_inputs.environment_variable.v3"; option java_outer_classname = "InputProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/matching/common_inputs/environment_variable/v3;environment_variablev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Environment Variable Input] diff --git a/api/envoy/extensions/matching/input_matchers/consistent_hashing/v3/consistent_hashing.proto b/api/envoy/extensions/matching/input_matchers/consistent_hashing/v3/consistent_hashing.proto index c44b0b89d57b..de3c0d62a172 100644 --- a/api/envoy/extensions/matching/input_matchers/consistent_hashing/v3/consistent_hashing.proto +++ b/api/envoy/extensions/matching/input_matchers/consistent_hashing/v3/consistent_hashing.proto @@ -8,6 +8,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.matching.input_matchers.consistent_hashing.v3"; option java_outer_classname = "ConsistentHashingProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/matching/input_matchers/consistent_hashing/v3;consistent_hashingv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Consistent Hashing Matcher] diff --git a/api/envoy/extensions/matching/input_matchers/ip/v3/ip.proto b/api/envoy/extensions/matching/input_matchers/ip/v3/ip.proto index 3c7cb4eb5f19..34bb4723bb5f 100644 --- a/api/envoy/extensions/matching/input_matchers/ip/v3/ip.proto +++ b/api/envoy/extensions/matching/input_matchers/ip/v3/ip.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.matching.input_matchers.ip.v3"; option java_outer_classname = "IpProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/matching/input_matchers/ip/v3;ipv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: IP matcher] diff --git a/api/envoy/extensions/network/dns_resolver/apple/v3/apple_dns_resolver.proto b/api/envoy/extensions/network/dns_resolver/apple/v3/apple_dns_resolver.proto index cc47e69dedc0..c27d1c78276a 100644 --- a/api/envoy/extensions/network/dns_resolver/apple/v3/apple_dns_resolver.proto +++ b/api/envoy/extensions/network/dns_resolver/apple/v3/apple_dns_resolver.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.network.dns_resolver.apple.v3"; option java_outer_classname = "AppleDnsResolverProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/network/dns_resolver/apple/v3;applev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: apple DNS resolver] diff --git a/api/envoy/extensions/network/dns_resolver/cares/v3/cares_dns_resolver.proto b/api/envoy/extensions/network/dns_resolver/cares/v3/cares_dns_resolver.proto index 05464231cbb2..eb6c0ea7b2bf 100644 --- a/api/envoy/extensions/network/dns_resolver/cares/v3/cares_dns_resolver.proto +++ b/api/envoy/extensions/network/dns_resolver/cares/v3/cares_dns_resolver.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.network.dns_resolver.cares.v3"; option java_outer_classname = "CaresDnsResolverProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/network/dns_resolver/cares/v3;caresv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: c-ares DNS resolver] diff --git a/api/envoy/extensions/network/socket_interface/v3/default_socket_interface.proto b/api/envoy/extensions/network/socket_interface/v3/default_socket_interface.proto index d2c747ec49fb..f5a3294456a1 100644 --- a/api/envoy/extensions/network/socket_interface/v3/default_socket_interface.proto +++ b/api/envoy/extensions/network/socket_interface/v3/default_socket_interface.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.network.socket_interface.v3"; option java_outer_classname = "DefaultSocketInterfaceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/network/socket_interface/v3;socket_interfacev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Default Socket Interface configuration] diff --git a/api/envoy/extensions/quic/crypto_stream/v3/crypto_stream.proto b/api/envoy/extensions/quic/crypto_stream/v3/crypto_stream.proto index 6313f79861e8..d1065f7c2160 100644 --- a/api/envoy/extensions/quic/crypto_stream/v3/crypto_stream.proto +++ b/api/envoy/extensions/quic/crypto_stream/v3/crypto_stream.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.quic.crypto_stream.v3"; option java_outer_classname = "CryptoStreamProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/quic/crypto_stream/v3;crypto_streamv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: QUIC server crypto stream config] diff --git a/api/envoy/extensions/quic/proof_source/v3/proof_source.proto b/api/envoy/extensions/quic/proof_source/v3/proof_source.proto index 1459142d4091..30a9634adc75 100644 --- a/api/envoy/extensions/quic/proof_source/v3/proof_source.proto +++ b/api/envoy/extensions/quic/proof_source/v3/proof_source.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.quic.proof_source.v3"; option java_outer_classname = "ProofSourceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/quic/proof_source/v3;proof_sourcev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: QUIC proof source config] diff --git a/api/envoy/extensions/rate_limit_descriptors/expr/v3/expr.proto b/api/envoy/extensions/rate_limit_descriptors/expr/v3/expr.proto index 76d3505cba04..7c3dc8916bd3 100644 --- a/api/envoy/extensions/rate_limit_descriptors/expr/v3/expr.proto +++ b/api/envoy/extensions/rate_limit_descriptors/expr/v3/expr.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.rate_limit_descriptors.expr.v3"; option java_outer_classname = "ExprProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/rate_limit_descriptors/expr/v3;exprv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Rate limit descriptor expression] diff --git a/api/envoy/extensions/rbac/matchers/upstream_ip_port/v3/upstream_ip_port_matcher.proto b/api/envoy/extensions/rbac/matchers/upstream_ip_port/v3/upstream_ip_port_matcher.proto index a4bdc73fa81a..11efb390231c 100644 --- a/api/envoy/extensions/rbac/matchers/upstream_ip_port/v3/upstream_ip_port_matcher.proto +++ b/api/envoy/extensions/rbac/matchers/upstream_ip_port/v3/upstream_ip_port_matcher.proto @@ -10,6 +10,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.rbac.matchers.upstream_ip_port.v3"; option java_outer_classname = "UpstreamIpPortMatcherProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/rbac/matchers/upstream_ip_port/v3;upstream_ip_portv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: RBAC upstream IP and port matcher plugin] diff --git a/api/envoy/extensions/request_id/uuid/v3/uuid.proto b/api/envoy/extensions/request_id/uuid/v3/uuid.proto index 5c3f00da28d7..eb682e771cd1 100644 --- a/api/envoy/extensions/request_id/uuid/v3/uuid.proto +++ b/api/envoy/extensions/request_id/uuid/v3/uuid.proto @@ -9,6 +9,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.request_id.uuid.v3"; option java_outer_classname = "UuidProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/request_id/uuid/v3;uuidv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: UUID] diff --git a/api/envoy/extensions/resource_monitors/fixed_heap/v3/BUILD b/api/envoy/extensions/resource_monitors/fixed_heap/v3/BUILD index 3fb51ff1ccaa..ee92fb652582 100644 --- a/api/envoy/extensions/resource_monitors/fixed_heap/v3/BUILD +++ b/api/envoy/extensions/resource_monitors/fixed_heap/v3/BUILD @@ -5,8 +5,5 @@ load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") licenses(["notice"]) # Apache 2 api_proto_package( - deps = [ - "//envoy/config/resource_monitor/fixed_heap/v2alpha:pkg", - "@com_github_cncf_udpa//udpa/annotations:pkg", - ], + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], ) diff --git a/api/envoy/extensions/resource_monitors/fixed_heap/v3/fixed_heap.proto b/api/envoy/extensions/resource_monitors/fixed_heap/v3/fixed_heap.proto index 48aaa0a0268e..4a9f07d1001c 100644 --- a/api/envoy/extensions/resource_monitors/fixed_heap/v3/fixed_heap.proto +++ b/api/envoy/extensions/resource_monitors/fixed_heap/v3/fixed_heap.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.resource_monitors.fixed_heap.v3"; option java_outer_classname = "FixedHeapProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/resource_monitors/fixed_heap/v3;fixed_heapv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Fixed heap] diff --git a/api/envoy/extensions/resource_monitors/injected_resource/v3/BUILD b/api/envoy/extensions/resource_monitors/injected_resource/v3/BUILD index 975b8fcbd5a3..ee92fb652582 100644 --- a/api/envoy/extensions/resource_monitors/injected_resource/v3/BUILD +++ b/api/envoy/extensions/resource_monitors/injected_resource/v3/BUILD @@ -5,8 +5,5 @@ load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") licenses(["notice"]) # Apache 2 api_proto_package( - deps = [ - "//envoy/config/resource_monitor/injected_resource/v2alpha:pkg", - "@com_github_cncf_udpa//udpa/annotations:pkg", - ], + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], ) diff --git a/api/envoy/extensions/resource_monitors/injected_resource/v3/injected_resource.proto b/api/envoy/extensions/resource_monitors/injected_resource/v3/injected_resource.proto index 643ea68651c7..edbce845a75b 100644 --- a/api/envoy/extensions/resource_monitors/injected_resource/v3/injected_resource.proto +++ b/api/envoy/extensions/resource_monitors/injected_resource/v3/injected_resource.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.resource_monitors.injected_resource.v3"; option java_outer_classname = "InjectedResourceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/resource_monitors/injected_resource/v3;injected_resourcev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Injected resource] diff --git a/api/envoy/extensions/retry/host/omit_canary_hosts/v3/BUILD b/api/envoy/extensions/retry/host/omit_canary_hosts/v3/BUILD index 0eab79b89fda..ee92fb652582 100644 --- a/api/envoy/extensions/retry/host/omit_canary_hosts/v3/BUILD +++ b/api/envoy/extensions/retry/host/omit_canary_hosts/v3/BUILD @@ -5,8 +5,5 @@ load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") licenses(["notice"]) # Apache 2 api_proto_package( - deps = [ - "//envoy/config/retry/omit_canary_hosts/v2:pkg", - "@com_github_cncf_udpa//udpa/annotations:pkg", - ], + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], ) diff --git a/api/envoy/extensions/retry/host/omit_canary_hosts/v3/omit_canary_hosts.proto b/api/envoy/extensions/retry/host/omit_canary_hosts/v3/omit_canary_hosts.proto index 930cced83703..07de41855324 100644 --- a/api/envoy/extensions/retry/host/omit_canary_hosts/v3/omit_canary_hosts.proto +++ b/api/envoy/extensions/retry/host/omit_canary_hosts/v3/omit_canary_hosts.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.retry.host.omit_canary_hosts.v3"; option java_outer_classname = "OmitCanaryHostsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/retry/host/omit_canary_hosts/v3;omit_canary_hostsv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Omit Canary Hosts Predicate] diff --git a/api/envoy/extensions/retry/host/omit_host_metadata/v3/omit_host_metadata_config.proto b/api/envoy/extensions/retry/host/omit_host_metadata/v3/omit_host_metadata_config.proto index fb7adf440288..00c6bfa8f636 100644 --- a/api/envoy/extensions/retry/host/omit_host_metadata/v3/omit_host_metadata_config.proto +++ b/api/envoy/extensions/retry/host/omit_host_metadata/v3/omit_host_metadata_config.proto @@ -10,6 +10,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.retry.host.omit_host_metadata.v3"; option java_outer_classname = "OmitHostMetadataConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/retry/host/omit_host_metadata/v3;omit_host_metadatav3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Omit host metadata retry predicate] diff --git a/api/envoy/extensions/retry/host/previous_hosts/v3/BUILD b/api/envoy/extensions/retry/host/previous_hosts/v3/BUILD index 88d9a6e255a3..ee92fb652582 100644 --- a/api/envoy/extensions/retry/host/previous_hosts/v3/BUILD +++ b/api/envoy/extensions/retry/host/previous_hosts/v3/BUILD @@ -5,8 +5,5 @@ load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") licenses(["notice"]) # Apache 2 api_proto_package( - deps = [ - "//envoy/config/retry/previous_hosts/v2:pkg", - "@com_github_cncf_udpa//udpa/annotations:pkg", - ], + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], ) diff --git a/api/envoy/extensions/retry/host/previous_hosts/v3/previous_hosts.proto b/api/envoy/extensions/retry/host/previous_hosts/v3/previous_hosts.proto index addce657fefe..20961f8f3e68 100644 --- a/api/envoy/extensions/retry/host/previous_hosts/v3/previous_hosts.proto +++ b/api/envoy/extensions/retry/host/previous_hosts/v3/previous_hosts.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.retry.host.previous_hosts.v3"; option java_outer_classname = "PreviousHostsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/retry/host/previous_hosts/v3;previous_hostsv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Previous Hosts Predicate] diff --git a/api/envoy/extensions/retry/priority/previous_priorities/v3/previous_priorities_config.proto b/api/envoy/extensions/retry/priority/previous_priorities/v3/previous_priorities_config.proto index b6a4bbecbae8..eaed4a79b3fc 100644 --- a/api/envoy/extensions/retry/priority/previous_priorities/v3/previous_priorities_config.proto +++ b/api/envoy/extensions/retry/priority/previous_priorities/v3/previous_priorities_config.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.retry.priority.previous_priorities.v3"; option java_outer_classname = "PreviousPrioritiesConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/retry/priority/previous_priorities/v3;previous_prioritiesv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Previous priorities retry selector] diff --git a/api/envoy/extensions/stat_sinks/graphite_statsd/v3/graphite_statsd.proto b/api/envoy/extensions/stat_sinks/graphite_statsd/v3/graphite_statsd.proto index 72306389bfec..7d538cf354c3 100644 --- a/api/envoy/extensions/stat_sinks/graphite_statsd/v3/graphite_statsd.proto +++ b/api/envoy/extensions/stat_sinks/graphite_statsd/v3/graphite_statsd.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.stat_sinks.graphite_statsd.v3"; option java_outer_classname = "GraphiteStatsdProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/stat_sinks/graphite_statsd/v3;graphite_statsdv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Graphite+Statsd] diff --git a/api/envoy/extensions/stat_sinks/wasm/v3/wasm.proto b/api/envoy/extensions/stat_sinks/wasm/v3/wasm.proto index 9d61eda713c7..a0a2c6fabe3d 100644 --- a/api/envoy/extensions/stat_sinks/wasm/v3/wasm.proto +++ b/api/envoy/extensions/stat_sinks/wasm/v3/wasm.proto @@ -9,6 +9,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.stat_sinks.wasm.v3"; option java_outer_classname = "WasmProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/stat_sinks/wasm/v3;wasmv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Wasm] diff --git a/api/envoy/extensions/transport_sockets/alts/v3/BUILD b/api/envoy/extensions/transport_sockets/alts/v3/BUILD index 8a8435d89897..ee92fb652582 100644 --- a/api/envoy/extensions/transport_sockets/alts/v3/BUILD +++ b/api/envoy/extensions/transport_sockets/alts/v3/BUILD @@ -5,8 +5,5 @@ load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") licenses(["notice"]) # Apache 2 api_proto_package( - deps = [ - "//envoy/config/transport_socket/alts/v2alpha:pkg", - "@com_github_cncf_udpa//udpa/annotations:pkg", - ], + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], ) diff --git a/api/envoy/extensions/transport_sockets/alts/v3/alts.proto b/api/envoy/extensions/transport_sockets/alts/v3/alts.proto index 93c6f9b834ef..e5d7a8b24495 100644 --- a/api/envoy/extensions/transport_sockets/alts/v3/alts.proto +++ b/api/envoy/extensions/transport_sockets/alts/v3/alts.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.alts.v3"; option java_outer_classname = "AltsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/alts/v3;altsv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: ALTS] diff --git a/api/envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.proto b/api/envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.proto index 687226574d29..87effb72c3b4 100644 --- a/api/envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.proto +++ b/api/envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.proxy_protocol.v3"; option java_outer_classname = "UpstreamProxyProtocolProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/proxy_protocol/v3;proxy_protocolv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Upstream Proxy Protocol] diff --git a/api/envoy/extensions/transport_sockets/quic/v3/quic_transport.proto b/api/envoy/extensions/transport_sockets/quic/v3/quic_transport.proto index 25122b09c597..2f403b966293 100644 --- a/api/envoy/extensions/transport_sockets/quic/v3/quic_transport.proto +++ b/api/envoy/extensions/transport_sockets/quic/v3/quic_transport.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.quic.v3"; option java_outer_classname = "QuicTransportProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/quic/v3;quicv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: quic transport] diff --git a/api/envoy/extensions/transport_sockets/raw_buffer/v3/raw_buffer.proto b/api/envoy/extensions/transport_sockets/raw_buffer/v3/raw_buffer.proto index 85406c1f7713..4d6436f2a784 100644 --- a/api/envoy/extensions/transport_sockets/raw_buffer/v3/raw_buffer.proto +++ b/api/envoy/extensions/transport_sockets/raw_buffer/v3/raw_buffer.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.raw_buffer.v3"; option java_outer_classname = "RawBufferProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/raw_buffer/v3;raw_bufferv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Raw Buffer] diff --git a/api/envoy/extensions/transport_sockets/s2a/v3/s2a.proto b/api/envoy/extensions/transport_sockets/s2a/v3/s2a.proto index 7c77222f59d6..a8042c0417f0 100644 --- a/api/envoy/extensions/transport_sockets/s2a/v3/s2a.proto +++ b/api/envoy/extensions/transport_sockets/s2a/v3/s2a.proto @@ -8,6 +8,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.s2a.v3"; option java_outer_classname = "S2aProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/s2a/v3;s2av3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#not-implemented-hide:] diff --git a/api/envoy/extensions/transport_sockets/starttls/v3/starttls.proto b/api/envoy/extensions/transport_sockets/starttls/v3/starttls.proto index 69254819baf7..3e6d4cfffe37 100644 --- a/api/envoy/extensions/transport_sockets/starttls/v3/starttls.proto +++ b/api/envoy/extensions/transport_sockets/starttls/v3/starttls.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.starttls.v3"; option java_outer_classname = "StarttlsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/starttls/v3;starttlsv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: StartTls] diff --git a/api/envoy/extensions/transport_sockets/tap/v3/tap.proto b/api/envoy/extensions/transport_sockets/tap/v3/tap.proto index ef61575f67f7..281b657e88f7 100644 --- a/api/envoy/extensions/transport_sockets/tap/v3/tap.proto +++ b/api/envoy/extensions/transport_sockets/tap/v3/tap.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.tap.v3"; option java_outer_classname = "TapProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tap/v3;tapv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Tap] diff --git a/api/envoy/extensions/transport_sockets/tcp_stats/v3/BUILD b/api/envoy/extensions/transport_sockets/tcp_stats/v3/BUILD new file mode 100644 index 000000000000..1c1a6f6b4423 --- /dev/null +++ b/api/envoy/extensions/transport_sockets/tcp_stats/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/config/core/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/api/envoy/extensions/transport_sockets/tcp_stats/v3/tcp_stats.proto b/api/envoy/extensions/transport_sockets/tcp_stats/v3/tcp_stats.proto new file mode 100644 index 000000000000..7428dacae8fe --- /dev/null +++ b/api/envoy/extensions/transport_sockets/tcp_stats/v3/tcp_stats.proto @@ -0,0 +1,35 @@ +syntax = "proto3"; + +package envoy.extensions.transport_sockets.tcp_stats.v3; + +import "envoy/config/core/v3/base.proto"; + +import "google/protobuf/duration.proto"; + +import "udpa/annotations/status.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.tcp_stats.v3"; +option java_outer_classname = "TcpStatsProto"; +option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tcp_stats/v3;tcp_statsv3"; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: TCP Stats Transport Socket wrapper] +// [#extension: envoy.transport_sockets.tcp_stats] + +// Configuration for the TCP Stats transport socket wrapper, which wraps another transport socket for +// all communication, but emits stats about the underlying TCP connection. +// +// The stats are documented :ref:`here ` for listeners and +// :ref:`here ` for clusters. +// +// This transport socket is currently only supported on Linux. +message Config { + // The underlying transport socket being wrapped. + config.core.v3.TransportSocket transport_socket = 1 [(validate.rules).message = {required: true}]; + + // Period to update stats while the connection is open. If unset, updates only happen when the + // connection is closed. Stats are always updated one final time when the connection is closed. + google.protobuf.Duration update_period = 2 [(validate.rules).duration = {gte {nanos: 1000000}}]; +} diff --git a/api/envoy/extensions/transport_sockets/tls/v3/BUILD b/api/envoy/extensions/transport_sockets/tls/v3/BUILD index 47b9b9ae57e9..75026a89c29b 100644 --- a/api/envoy/extensions/transport_sockets/tls/v3/BUILD +++ b/api/envoy/extensions/transport_sockets/tls/v3/BUILD @@ -7,7 +7,6 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ "//envoy/annotations:pkg", - "//envoy/api/v2/auth:pkg", "//envoy/config/core/v3:pkg", "//envoy/type/matcher/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", diff --git a/api/envoy/extensions/transport_sockets/tls/v3/cert.proto b/api/envoy/extensions/transport_sockets/tls/v3/cert.proto index b451d45381ca..8a5f8962bd1a 100644 --- a/api/envoy/extensions/transport_sockets/tls/v3/cert.proto +++ b/api/envoy/extensions/transport_sockets/tls/v3/cert.proto @@ -9,3 +9,4 @@ import public "envoy/extensions/transport_sockets/tls/v3/tls.proto"; option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.tls.v3"; option java_outer_classname = "CertProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3;tlsv3"; diff --git a/api/envoy/extensions/transport_sockets/tls/v3/common.proto b/api/envoy/extensions/transport_sockets/tls/v3/common.proto index 82dcb37cd7ca..4ed80caa4190 100644 --- a/api/envoy/extensions/transport_sockets/tls/v3/common.proto +++ b/api/envoy/extensions/transport_sockets/tls/v3/common.proto @@ -9,6 +9,7 @@ import "envoy/type/matcher/v3/string.proto"; import "google/protobuf/any.proto"; import "google/protobuf/wrappers.proto"; +import "envoy/annotations/deprecation.proto"; import "udpa/annotations/migrate.proto"; import "udpa/annotations/sensitive.proto"; import "udpa/annotations/status.proto"; @@ -18,6 +19,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.tls.v3"; option java_outer_classname = "CommonProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3;tlsv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Common TLS configuration] @@ -149,7 +151,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 +170,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 @@ -253,7 +270,26 @@ message CertificateProviderPluginInstance { string certificate_name = 2; } -// [#next-free-field: 14] +// Matcher for subject alternative names, to match both type and value of the SAN. +message SubjectAltNameMatcher { + // Indicates the choice of GeneralName as defined in section 4.2.1.5 of RFC 5280 to match + // against. + enum SanType { + SAN_TYPE_UNSPECIFIED = 0; + EMAIL = 1; + DNS = 2; + URI = 3; + IP_ADDRESS = 4; + } + + // Specification of type of SAN. Note that the default enum value is an invalid choice. + SanType san_type = 1 [(validate.rules).enum = {defined_only: true not_in: 0}]; + + // Matcher for SAN value. + type.matcher.v3.StringMatcher matcher = 2 [(validate.rules).message = {required: true}]; +} + +// [#next-free-field: 16] message CertificateValidationContext { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.auth.CertificateValidationContext"; @@ -283,8 +319,8 @@ message CertificateValidationContext { // `, // :ref:`verify_certificate_hash // `, or - // :ref:`match_subject_alt_names - // `) is also + // :ref:`match_typed_subject_alt_names + // `) is also // specified. // // It can optionally contain certificate revocation lists, in which case Envoy will verify @@ -292,6 +328,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. @@ -388,6 +427,8 @@ message CertificateValidationContext { // An optional list of Subject Alternative name matchers. If specified, Envoy will verify that the // Subject Alternative Name of the presented certificate matches one of the specified matchers. + // The matching uses "any" semantics, that is to say, the SAN is verified if at least one matcher is + // matched. // // When a certificate has wildcard DNS SAN entries, to match a specific client, it should be // configured with exact match type in the :ref:`string matcher `. @@ -396,15 +437,22 @@ message CertificateValidationContext { // // .. code-block:: yaml // - // match_subject_alt_names: - // exact: "api.example.com" + // match_typed_subject_alt_names: + // - san_type: DNS + // matcher: + // exact: "api.example.com" // // .. attention:: // // Subject Alternative Names are easily spoofable and verifying only them is insecure, // therefore this option must be used together with :ref:`trusted_ca // `. - repeated type.matcher.v3.StringMatcher match_subject_alt_names = 9; + repeated SubjectAltNameMatcher match_typed_subject_alt_names = 15; + + // This field is deprecated in favor of ref:`match_typed_subject_alt_names + // ` + repeated type.matcher.v3.StringMatcher match_subject_alt_names = 9 + [deprecated = true, (envoy.annotations.deprecated_at_minor_version) = "3.0"]; // [#not-implemented-hide:] Must present signed certificate time-stamp. google.protobuf.BoolValue require_signed_certificate_timestamp = 6; @@ -417,7 +465,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 +483,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/api/envoy/extensions/transport_sockets/tls/v3/secret.proto b/api/envoy/extensions/transport_sockets/tls/v3/secret.proto index f7c849c0334e..83ad364c4bf5 100644 --- a/api/envoy/extensions/transport_sockets/tls/v3/secret.proto +++ b/api/envoy/extensions/transport_sockets/tls/v3/secret.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.tls.v3"; option java_outer_classname = "SecretProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3;tlsv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Secrets configuration] diff --git a/api/envoy/extensions/transport_sockets/tls/v3/tls.proto b/api/envoy/extensions/transport_sockets/tls/v3/tls.proto index f680207955a8..99b762b0e44f 100644 --- a/api/envoy/extensions/transport_sockets/tls/v3/tls.proto +++ b/api/envoy/extensions/transport_sockets/tls/v3/tls.proto @@ -17,6 +17,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.tls.v3"; option java_outer_classname = "TlsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3;tlsv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: TLS transport socket] @@ -109,10 +110,9 @@ message DownstreamTlsContext { bool disable_stateless_session_resumption = 7; } - // If specified, session_timeout will change maximum lifetime (in seconds) of TLS session - // Currently this value is used as a hint to `TLS session ticket lifetime (for TLSv1.2) - // ` - // only seconds could be specified (fractional seconds are going to be ignored). + // If specified, ``session_timeout`` will change the maximum lifetime (in seconds) of the TLS session. + // Currently this value is used as a hint for the `TLS session ticket lifetime (for TLSv1.2) `_. + // Only seconds can be specified (fractional seconds are ignored). google.protobuf.Duration session_timeout = 6 [(validate.rules).duration = { lt {seconds: 4294967296} gte {} diff --git a/api/envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.proto b/api/envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.proto index cfb5e5c07e90..f51bd6591c99 100644 --- a/api/envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.proto +++ b/api/envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.tls.v3"; option java_outer_classname = "TlsSpiffeValidatorConfigProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3;tlsv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: SPIFFE Certificate Validator] @@ -42,7 +43,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // Note that SPIFFE validator inherits and uses the following options from :ref:`CertificateValidationContext `. // // - :ref:`allow_expired_certificate ` to allow expired certificates. -// - :ref:`match_subject_alt_names ` to match **URI** SAN of certificates. Unlike the default validator, SPIFFE validator only matches **URI** SAN (which equals to SVID in SPIFFE terminology) and ignore other SAN types. +// - :ref:`match_typed_subject_alt_names ` to match **URI** SAN of certificates. Unlike the default validator, SPIFFE validator only matches **URI** SAN (which equals to SVID in SPIFFE terminology) and ignore other SAN types. // message SPIFFECertValidatorConfig { message TrustDomain { diff --git a/api/envoy/extensions/upstreams/http/generic/v3/generic_connection_pool.proto b/api/envoy/extensions/upstreams/http/generic/v3/generic_connection_pool.proto index 44e207172c9b..e76dde8573e9 100644 --- a/api/envoy/extensions/upstreams/http/generic/v3/generic_connection_pool.proto +++ b/api/envoy/extensions/upstreams/http/generic/v3/generic_connection_pool.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.upstreams.http.generic.v3"; option java_outer_classname = "GenericConnectionPoolProtoOuterClass"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/generic/v3;genericv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Generic Connection Pool] diff --git a/api/envoy/extensions/upstreams/http/http/v3/http_connection_pool.proto b/api/envoy/extensions/upstreams/http/http/v3/http_connection_pool.proto index 8318f3c666d9..45f3a164aa08 100644 --- a/api/envoy/extensions/upstreams/http/http/v3/http_connection_pool.proto +++ b/api/envoy/extensions/upstreams/http/http/v3/http_connection_pool.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.upstreams.http.http.v3"; option java_outer_classname = "HttpConnectionPoolProtoOuterClass"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/http/v3;httpv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Http Connection Pool] diff --git a/api/envoy/extensions/upstreams/http/tcp/v3/tcp_connection_pool.proto b/api/envoy/extensions/upstreams/http/tcp/v3/tcp_connection_pool.proto index 7c1d633432e9..1f0f288925f0 100644 --- a/api/envoy/extensions/upstreams/http/tcp/v3/tcp_connection_pool.proto +++ b/api/envoy/extensions/upstreams/http/tcp/v3/tcp_connection_pool.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.upstreams.http.tcp.v3"; option java_outer_classname = "TcpConnectionPoolProtoOuterClass"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/tcp/v3;tcpv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Tcp Connection Pool] diff --git a/api/envoy/extensions/upstreams/http/v3/http_protocol_options.proto b/api/envoy/extensions/upstreams/http/v3/http_protocol_options.proto index 1267488d98c6..95f4573672e2 100644 --- a/api/envoy/extensions/upstreams/http/v3/http_protocol_options.proto +++ b/api/envoy/extensions/upstreams/http/v3/http_protocol_options.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.upstreams.http.v3"; option java_outer_classname = "HttpProtocolOptionsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3;httpv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: HTTP Protocol Options] diff --git a/api/envoy/extensions/upstreams/tcp/generic/v3/generic_connection_pool.proto b/api/envoy/extensions/upstreams/tcp/generic/v3/generic_connection_pool.proto index 5754491b91d1..d9bf11925680 100644 --- a/api/envoy/extensions/upstreams/tcp/generic/v3/generic_connection_pool.proto +++ b/api/envoy/extensions/upstreams/tcp/generic/v3/generic_connection_pool.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.upstreams.tcp.generic.v3"; option java_outer_classname = "GenericConnectionPoolProtoOuterClass"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/tcp/generic/v3;genericv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Generic Connection Pool] diff --git a/api/envoy/extensions/wasm/v3/wasm.proto b/api/envoy/extensions/wasm/v3/wasm.proto index b4566c826ed0..9193aa3562cb 100644 --- a/api/envoy/extensions/wasm/v3/wasm.proto +++ b/api/envoy/extensions/wasm/v3/wasm.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.wasm.v3"; option java_outer_classname = "WasmProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/wasm/v3;wasmv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Wasm] diff --git a/api/envoy/extensions/watchdog/profile_action/v3/profile_action.proto b/api/envoy/extensions/watchdog/profile_action/v3/profile_action.proto index 07c3907fbd61..dec616c12f83 100644 --- a/api/envoy/extensions/watchdog/profile_action/v3/profile_action.proto +++ b/api/envoy/extensions/watchdog/profile_action/v3/profile_action.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.watchdog.profile_action.v3"; option java_outer_classname = "ProfileActionProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/watchdog/profile_action/v3;profile_actionv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Watchdog Action that does CPU profiling.] diff --git a/api/envoy/service/accesslog/v2/als.proto b/api/envoy/service/accesslog/v2/als.proto index bbd871ff83a4..2e9631da9018 100644 --- a/api/envoy/service/accesslog/v2/als.proto +++ b/api/envoy/service/accesslog/v2/als.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.service.accesslog.v2"; option java_outer_classname = "AlsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/accesslog/v2;accesslogv2"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/service/accesslog/v3/BUILD b/api/envoy/service/accesslog/v3/BUILD index d44839fbe095..c913d31f62fe 100644 --- a/api/envoy/service/accesslog/v3/BUILD +++ b/api/envoy/service/accesslog/v3/BUILD @@ -9,7 +9,6 @@ api_proto_package( deps = [ "//envoy/config/core/v3:pkg", "//envoy/data/accesslog/v3:pkg", - "//envoy/service/accesslog/v2:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/api/envoy/service/accesslog/v3/als.proto b/api/envoy/service/accesslog/v3/als.proto index 94a290ad4a32..1497eb35388a 100644 --- a/api/envoy/service/accesslog/v3/als.proto +++ b/api/envoy/service/accesslog/v3/als.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.service.accesslog.v3"; option java_outer_classname = "AlsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/accesslog/v3;accesslogv3"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/service/auth/v2/attribute_context.proto b/api/envoy/service/auth/v2/attribute_context.proto index 8e0170067d24..cf5cda3b1462 100644 --- a/api/envoy/service/auth/v2/attribute_context.proto +++ b/api/envoy/service/auth/v2/attribute_context.proto @@ -12,6 +12,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.service.auth.v2"; option java_outer_classname = "AttributeContextProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/auth/v2;authv2"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Attribute Context ] diff --git a/api/envoy/service/auth/v2/external_auth.proto b/api/envoy/service/auth/v2/external_auth.proto index 7dbfd3556968..e3be9de49379 100644 --- a/api/envoy/service/auth/v2/external_auth.proto +++ b/api/envoy/service/auth/v2/external_auth.proto @@ -14,6 +14,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.service.auth.v2"; option java_outer_classname = "ExternalAuthProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/auth/v2;authv2"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/service/auth/v2alpha/external_auth.proto b/api/envoy/service/auth/v2alpha/external_auth.proto index 85e9c12c6afb..4d6b68a8c176 100644 --- a/api/envoy/service/auth/v2alpha/external_auth.proto +++ b/api/envoy/service/auth/v2alpha/external_auth.proto @@ -6,6 +6,7 @@ option java_multiple_files = true; option java_generic_services = true; option java_outer_classname = "CertsProto"; option java_package = "io.envoyproxy.envoy.service.auth.v2alpha"; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/auth/v2alpha"; import "envoy/service/auth/v2/external_auth.proto"; diff --git a/api/envoy/service/auth/v3/BUILD b/api/envoy/service/auth/v3/BUILD index 0774dda23e42..f39e4f85d88f 100644 --- a/api/envoy/service/auth/v3/BUILD +++ b/api/envoy/service/auth/v3/BUILD @@ -9,7 +9,6 @@ api_proto_package( deps = [ "//envoy/annotations:pkg", "//envoy/config/core/v3:pkg", - "//envoy/service/auth/v2:pkg", "//envoy/type/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], diff --git a/api/envoy/service/auth/v3/attribute_context.proto b/api/envoy/service/auth/v3/attribute_context.proto index 452a1e1ad9a5..ec9bba49c08e 100644 --- a/api/envoy/service/auth/v3/attribute_context.proto +++ b/api/envoy/service/auth/v3/attribute_context.proto @@ -13,6 +13,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.service.auth.v3"; option java_outer_classname = "AttributeContextProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3;authv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Attribute Context ] diff --git a/api/envoy/service/auth/v3/external_auth.proto b/api/envoy/service/auth/v3/external_auth.proto index 11fc057da888..66d8daf228d8 100644 --- a/api/envoy/service/auth/v3/external_auth.proto +++ b/api/envoy/service/auth/v3/external_auth.proto @@ -16,6 +16,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.service.auth.v3"; option java_outer_classname = "ExternalAuthProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3;authv3"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/service/cluster/v3/BUILD b/api/envoy/service/cluster/v3/BUILD index d3be4fae57fa..9f2ae1e747c5 100644 --- a/api/envoy/service/cluster/v3/BUILD +++ b/api/envoy/service/cluster/v3/BUILD @@ -8,7 +8,6 @@ api_proto_package( has_services = True, deps = [ "//envoy/annotations:pkg", - "//envoy/api/v2:pkg", "//envoy/service/discovery/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], diff --git a/api/envoy/service/cluster/v3/cds.proto b/api/envoy/service/cluster/v3/cds.proto index 100ecad39a96..dbace7a7e1c3 100644 --- a/api/envoy/service/cluster/v3/cds.proto +++ b/api/envoy/service/cluster/v3/cds.proto @@ -13,6 +13,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.service.cluster.v3"; option java_outer_classname = "CdsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/cluster/v3;clusterv3"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/service/discovery/v2/ads.proto b/api/envoy/service/discovery/v2/ads.proto index d70e0cdc8e14..1da1606bf64b 100644 --- a/api/envoy/service/discovery/v2/ads.proto +++ b/api/envoy/service/discovery/v2/ads.proto @@ -9,17 +9,18 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.service.discovery.v2"; option java_outer_classname = "AdsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v2;discoveryv2"; option java_generic_services = true; 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`. -// See https://github.com/lyft/envoy-api#apis for a description of the role of +// See https://github.com/envoyproxy/envoy-api#apis for a description of the role of // ADS and how it is intended to be used by a management server. ADS requests // have the same structure as their singleton xDS counterparts, but can // multiplex many resource types on a single stream. The type_url in the diff --git a/api/envoy/service/discovery/v2/hds.proto b/api/envoy/service/discovery/v2/hds.proto index 76f91c5a456d..dcc3708ac37e 100644 --- a/api/envoy/service/discovery/v2/hds.proto +++ b/api/envoy/service/discovery/v2/hds.proto @@ -15,6 +15,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.service.discovery.v2"; option java_outer_classname = "HdsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v2;discoveryv2"; option java_generic_services = true; option (udpa.annotations.file_migrate).move_to_package = "envoy.service.health.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/service/discovery/v2/rtds.proto b/api/envoy/service/discovery/v2/rtds.proto index 713ac277072b..844ea771a95c 100644 --- a/api/envoy/service/discovery/v2/rtds.proto +++ b/api/envoy/service/discovery/v2/rtds.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.service.discovery.v2"; option java_outer_classname = "RtdsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v2;discoveryv2"; option java_generic_services = true; option (udpa.annotations.file_migrate).move_to_package = "envoy.service.runtime.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/service/discovery/v2/sds.proto b/api/envoy/service/discovery/v2/sds.proto index 4d01d475c59b..d7a30dad40f4 100644 --- a/api/envoy/service/discovery/v2/sds.proto +++ b/api/envoy/service/discovery/v2/sds.proto @@ -13,6 +13,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.service.discovery.v2"; option java_outer_classname = "SdsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v2;discoveryv2"; option java_generic_services = true; option (udpa.annotations.file_migrate).move_to_package = "envoy.service.secret.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/service/discovery/v3/BUILD b/api/envoy/service/discovery/v3/BUILD index 074bab85eb71..7753cfeb3d6e 100644 --- a/api/envoy/service/discovery/v3/BUILD +++ b/api/envoy/service/discovery/v3/BUILD @@ -7,9 +7,7 @@ licenses(["notice"]) # Apache 2 api_proto_package( has_services = True, deps = [ - "//envoy/api/v2:pkg", "//envoy/config/core/v3:pkg", - "//envoy/service/discovery/v2:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/api/envoy/service/discovery/v3/ads.proto b/api/envoy/service/discovery/v3/ads.proto index 03021559ab66..2a07622714c7 100644 --- a/api/envoy/service/discovery/v3/ads.proto +++ b/api/envoy/service/discovery/v3/ads.proto @@ -10,17 +10,18 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.service.discovery.v3"; option java_outer_classname = "AdsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3;discoveryv3"; option java_generic_services = true; 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`. -// See https://github.com/lyft/envoy-api#apis for a description of the role of +// See https://github.com/envoyproxy/envoy-api#apis for a description of the role of // ADS and how it is intended to be used by a management server. ADS requests // have the same structure as their singleton xDS counterparts, but can // multiplex many resource types on a single stream. The type_url in the diff --git a/api/envoy/service/discovery/v3/discovery.proto b/api/envoy/service/discovery/v3/discovery.proto index 4a474d0fe260..7d937f932023 100644 --- a/api/envoy/service/discovery/v3/discovery.proto +++ b/api/envoy/service/discovery/v3/discovery.proto @@ -14,6 +14,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.service.discovery.v3"; option java_outer_classname = "DiscoveryProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3;discoveryv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Common discovery API components] diff --git a/api/envoy/service/endpoint/v3/BUILD b/api/envoy/service/endpoint/v3/BUILD index d3be4fae57fa..9f2ae1e747c5 100644 --- a/api/envoy/service/endpoint/v3/BUILD +++ b/api/envoy/service/endpoint/v3/BUILD @@ -8,7 +8,6 @@ api_proto_package( has_services = True, deps = [ "//envoy/annotations:pkg", - "//envoy/api/v2:pkg", "//envoy/service/discovery/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], diff --git a/api/envoy/service/endpoint/v3/eds.proto b/api/envoy/service/endpoint/v3/eds.proto index 7f560b87b79e..1b3868446e81 100644 --- a/api/envoy/service/endpoint/v3/eds.proto +++ b/api/envoy/service/endpoint/v3/eds.proto @@ -13,6 +13,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.service.endpoint.v3"; option java_outer_classname = "EdsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/endpoint/v3;endpointv3"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/service/endpoint/v3/leds.proto b/api/envoy/service/endpoint/v3/leds.proto index 89172f487eba..e712ae58c335 100644 --- a/api/envoy/service/endpoint/v3/leds.proto +++ b/api/envoy/service/endpoint/v3/leds.proto @@ -10,6 +10,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.service.endpoint.v3"; option java_outer_classname = "LedsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/endpoint/v3;endpointv3"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/service/event_reporting/v2alpha/event_reporting_service.proto b/api/envoy/service/event_reporting/v2alpha/event_reporting_service.proto index 8d07f04640ca..6862cec0b387 100644 --- a/api/envoy/service/event_reporting/v2alpha/event_reporting_service.proto +++ b/api/envoy/service/event_reporting/v2alpha/event_reporting_service.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.service.event_reporting.v2alpha"; option java_outer_classname = "EventReportingServiceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/event_reporting/v2alpha"; option java_generic_services = true; option (udpa.annotations.file_migrate).move_to_package = "envoy.service.event_reporting.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/service/event_reporting/v3/event_reporting_service.proto b/api/envoy/service/event_reporting/v3/event_reporting_service.proto index 30c161a1c530..372eb6399bb1 100644 --- a/api/envoy/service/event_reporting/v3/event_reporting_service.proto +++ b/api/envoy/service/event_reporting/v3/event_reporting_service.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.service.event_reporting.v3"; option java_outer_classname = "EventReportingServiceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/event_reporting/v3;event_reportingv3"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/service/ext_proc/v3/external_processor.proto b/api/envoy/service/ext_proc/v3/external_processor.proto index dc6b527d5bcc..6d202733b84a 100644 --- a/api/envoy/service/ext_proc/v3/external_processor.proto +++ b/api/envoy/service/ext_proc/v3/external_processor.proto @@ -16,6 +16,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.service.ext_proc.v3"; option java_outer_classname = "ExternalProcessorProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3;ext_procv3"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; option (xds.annotations.v3.file_status).work_in_progress = true; diff --git a/api/envoy/service/extension/v3/config_discovery.proto b/api/envoy/service/extension/v3/config_discovery.proto index cf83adbd2644..3a0337a3140e 100644 --- a/api/envoy/service/extension/v3/config_discovery.proto +++ b/api/envoy/service/extension/v3/config_discovery.proto @@ -12,6 +12,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.service.extension.v3"; option java_outer_classname = "ConfigDiscoveryProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/extension/v3;extensionv3"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/service/health/v3/BUILD b/api/envoy/service/health/v3/BUILD index 30ba155208b5..b28383997467 100644 --- a/api/envoy/service/health/v3/BUILD +++ b/api/envoy/service/health/v3/BUILD @@ -11,7 +11,6 @@ api_proto_package( "//envoy/config/cluster/v3:pkg", "//envoy/config/core/v3:pkg", "//envoy/config/endpoint/v3:pkg", - "//envoy/service/discovery/v2:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/api/envoy/service/health/v3/hds.proto b/api/envoy/service/health/v3/hds.proto index 51266a64fa95..025e822ebf1b 100644 --- a/api/envoy/service/health/v3/hds.proto +++ b/api/envoy/service/health/v3/hds.proto @@ -17,6 +17,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.service.health.v3"; option java_outer_classname = "HdsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/health/v3;healthv3"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/service/listener/v3/BUILD b/api/envoy/service/listener/v3/BUILD index d3be4fae57fa..9f2ae1e747c5 100644 --- a/api/envoy/service/listener/v3/BUILD +++ b/api/envoy/service/listener/v3/BUILD @@ -8,7 +8,6 @@ api_proto_package( has_services = True, deps = [ "//envoy/annotations:pkg", - "//envoy/api/v2:pkg", "//envoy/service/discovery/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], diff --git a/api/envoy/service/listener/v3/lds.proto b/api/envoy/service/listener/v3/lds.proto index 5b8c0d520725..b340a098013a 100644 --- a/api/envoy/service/listener/v3/lds.proto +++ b/api/envoy/service/listener/v3/lds.proto @@ -13,6 +13,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.service.listener.v3"; option java_outer_classname = "LdsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/listener/v3;listenerv3"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/service/load_stats/v2/lrs.proto b/api/envoy/service/load_stats/v2/lrs.proto index 7ab87c2dfb04..c39d74aacf6f 100644 --- a/api/envoy/service/load_stats/v2/lrs.proto +++ b/api/envoy/service/load_stats/v2/lrs.proto @@ -12,6 +12,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.service.load_stats.v2"; option java_outer_classname = "LrsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/load_stats/v2;load_statsv2"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/service/load_stats/v3/BUILD b/api/envoy/service/load_stats/v3/BUILD index d69e005bae22..1ee733dc7d82 100644 --- a/api/envoy/service/load_stats/v3/BUILD +++ b/api/envoy/service/load_stats/v3/BUILD @@ -9,7 +9,6 @@ api_proto_package( deps = [ "//envoy/config/core/v3:pkg", "//envoy/config/endpoint/v3:pkg", - "//envoy/service/load_stats/v2:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/api/envoy/service/load_stats/v3/lrs.proto b/api/envoy/service/load_stats/v3/lrs.proto index 0b565ebe7236..6f7545376da2 100644 --- a/api/envoy/service/load_stats/v3/lrs.proto +++ b/api/envoy/service/load_stats/v3/lrs.proto @@ -13,6 +13,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.service.load_stats.v3"; option java_outer_classname = "LrsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/load_stats/v3;load_statsv3"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/service/metrics/v2/metrics_service.proto b/api/envoy/service/metrics/v2/metrics_service.proto index 78d6e47e20ab..ab990b07a8d0 100644 --- a/api/envoy/service/metrics/v2/metrics_service.proto +++ b/api/envoy/service/metrics/v2/metrics_service.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.service.metrics.v2"; option java_outer_classname = "MetricsServiceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/metrics/v2;metricsv2"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/service/metrics/v3/BUILD b/api/envoy/service/metrics/v3/BUILD index b266dfc5558d..b9a1679e2cb8 100644 --- a/api/envoy/service/metrics/v3/BUILD +++ b/api/envoy/service/metrics/v3/BUILD @@ -8,7 +8,6 @@ api_proto_package( has_services = True, deps = [ "//envoy/config/core/v3:pkg", - "//envoy/service/metrics/v2:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", "@prometheus_metrics_model//:client_model", ], diff --git a/api/envoy/service/metrics/v3/metrics_service.proto b/api/envoy/service/metrics/v3/metrics_service.proto index e86bda356f7d..9971774aac92 100644 --- a/api/envoy/service/metrics/v3/metrics_service.proto +++ b/api/envoy/service/metrics/v3/metrics_service.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.service.metrics.v3"; option java_outer_classname = "MetricsServiceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/metrics/v3;metricsv3"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/service/ratelimit/v2/rls.proto b/api/envoy/service/ratelimit/v2/rls.proto index cee8cd7bc3d5..2084573f66a8 100644 --- a/api/envoy/service/ratelimit/v2/rls.proto +++ b/api/envoy/service/ratelimit/v2/rls.proto @@ -11,6 +11,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.service.ratelimit.v2"; option java_outer_classname = "RlsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/ratelimit/v2;ratelimitv2"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/service/ratelimit/v3/BUILD b/api/envoy/service/ratelimit/v3/BUILD index 222b9ac52292..1cec1e02cde9 100644 --- a/api/envoy/service/ratelimit/v3/BUILD +++ b/api/envoy/service/ratelimit/v3/BUILD @@ -9,7 +9,6 @@ api_proto_package( deps = [ "//envoy/config/core/v3:pkg", "//envoy/extensions/common/ratelimit/v3:pkg", - "//envoy/service/ratelimit/v2:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/api/envoy/service/ratelimit/v3/rls.proto b/api/envoy/service/ratelimit/v3/rls.proto index 113998c4082d..5f840f44f9d2 100644 --- a/api/envoy/service/ratelimit/v3/rls.proto +++ b/api/envoy/service/ratelimit/v3/rls.proto @@ -16,6 +16,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.service.ratelimit.v3"; option java_outer_classname = "RlsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/ratelimit/v3;ratelimitv3"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/service/route/v3/BUILD b/api/envoy/service/route/v3/BUILD index d3be4fae57fa..9f2ae1e747c5 100644 --- a/api/envoy/service/route/v3/BUILD +++ b/api/envoy/service/route/v3/BUILD @@ -8,7 +8,6 @@ api_proto_package( has_services = True, deps = [ "//envoy/annotations:pkg", - "//envoy/api/v2:pkg", "//envoy/service/discovery/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], diff --git a/api/envoy/service/route/v3/rds.proto b/api/envoy/service/route/v3/rds.proto index 62a7da409493..d980afc7502a 100644 --- a/api/envoy/service/route/v3/rds.proto +++ b/api/envoy/service/route/v3/rds.proto @@ -13,6 +13,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.service.route.v3"; option java_outer_classname = "RdsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/route/v3;routev3"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/service/route/v3/srds.proto b/api/envoy/service/route/v3/srds.proto index 64fe45fee1fa..579df387e817 100644 --- a/api/envoy/service/route/v3/srds.proto +++ b/api/envoy/service/route/v3/srds.proto @@ -13,6 +13,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.service.route.v3"; option java_outer_classname = "SrdsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/route/v3;routev3"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/service/runtime/v3/BUILD b/api/envoy/service/runtime/v3/BUILD index fb6a1656ca9b..9f2ae1e747c5 100644 --- a/api/envoy/service/runtime/v3/BUILD +++ b/api/envoy/service/runtime/v3/BUILD @@ -8,7 +8,6 @@ api_proto_package( has_services = True, deps = [ "//envoy/annotations:pkg", - "//envoy/service/discovery/v2:pkg", "//envoy/service/discovery/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], diff --git a/api/envoy/service/runtime/v3/rtds.proto b/api/envoy/service/runtime/v3/rtds.proto index 796b6fac24e6..8cffbe583dd1 100644 --- a/api/envoy/service/runtime/v3/rtds.proto +++ b/api/envoy/service/runtime/v3/rtds.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.service.runtime.v3"; option java_outer_classname = "RtdsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/runtime/v3;runtimev3"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/service/secret/v3/BUILD b/api/envoy/service/secret/v3/BUILD index fb6a1656ca9b..9f2ae1e747c5 100644 --- a/api/envoy/service/secret/v3/BUILD +++ b/api/envoy/service/secret/v3/BUILD @@ -8,7 +8,6 @@ api_proto_package( has_services = True, deps = [ "//envoy/annotations:pkg", - "//envoy/service/discovery/v2:pkg", "//envoy/service/discovery/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], diff --git a/api/envoy/service/secret/v3/sds.proto b/api/envoy/service/secret/v3/sds.proto index 3c9441d7c760..178813851ff5 100644 --- a/api/envoy/service/secret/v3/sds.proto +++ b/api/envoy/service/secret/v3/sds.proto @@ -13,6 +13,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.service.secret.v3"; option java_outer_classname = "SdsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/secret/v3;secretv3"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/service/status/v2/csds.proto b/api/envoy/service/status/v2/csds.proto index 10f603cedb15..0dc667ce82f5 100644 --- a/api/envoy/service/status/v2/csds.proto +++ b/api/envoy/service/status/v2/csds.proto @@ -13,6 +13,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.service.status.v2"; option java_outer_classname = "CsdsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/status/v2;statusv2"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/service/status/v3/BUILD b/api/envoy/service/status/v3/BUILD index a73963967ef7..45ec162b9093 100644 --- a/api/envoy/service/status/v3/BUILD +++ b/api/envoy/service/status/v3/BUILD @@ -10,7 +10,6 @@ api_proto_package( "//envoy/admin/v3:pkg", "//envoy/annotations:pkg", "//envoy/config/core/v3:pkg", - "//envoy/service/status/v2:pkg", "//envoy/type/matcher/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], diff --git a/api/envoy/service/status/v3/csds.proto b/api/envoy/service/status/v3/csds.proto index 1d940d6a2dfe..89d92efd2d04 100644 --- a/api/envoy/service/status/v3/csds.proto +++ b/api/envoy/service/status/v3/csds.proto @@ -17,6 +17,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.service.status.v3"; option java_outer_classname = "CsdsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/status/v3;statusv3"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/service/tap/v2alpha/common.proto b/api/envoy/service/tap/v2alpha/common.proto index 990a3826481b..b06039f6c647 100644 --- a/api/envoy/service/tap/v2alpha/common.proto +++ b/api/envoy/service/tap/v2alpha/common.proto @@ -15,6 +15,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.service.tap.v2alpha"; option java_outer_classname = "CommonProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/tap/v2alpha"; option (udpa.annotations.file_migrate).move_to_package = "envoy.config.tap.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/service/tap/v2alpha/tap.proto b/api/envoy/service/tap/v2alpha/tap.proto index 9fd18eae5d36..98f5f05a1f13 100644 --- a/api/envoy/service/tap/v2alpha/tap.proto +++ b/api/envoy/service/tap/v2alpha/tap.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.service.tap.v2alpha"; option java_outer_classname = "TapProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/tap/v2alpha"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/service/tap/v3/BUILD b/api/envoy/service/tap/v3/BUILD index 5ee1ce553f48..8948f580a51e 100644 --- a/api/envoy/service/tap/v3/BUILD +++ b/api/envoy/service/tap/v3/BUILD @@ -9,7 +9,6 @@ api_proto_package( deps = [ "//envoy/config/core/v3:pkg", "//envoy/data/tap/v3:pkg", - "//envoy/service/tap/v2alpha:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/api/envoy/service/tap/v3/tap.proto b/api/envoy/service/tap/v3/tap.proto index 5d9866e57074..f97fe451218a 100644 --- a/api/envoy/service/tap/v3/tap.proto +++ b/api/envoy/service/tap/v3/tap.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.service.tap.v3"; option java_outer_classname = "TapProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/tap/v3;tapv3"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/service/trace/v2/trace_service.proto b/api/envoy/service/trace/v2/trace_service.proto index 4e07f9e1f609..6a0314905c40 100644 --- a/api/envoy/service/trace/v2/trace_service.proto +++ b/api/envoy/service/trace/v2/trace_service.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.service.trace.v2"; option java_outer_classname = "TraceServiceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/trace/v2;tracev2"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/service/trace/v3/trace_service.proto b/api/envoy/service/trace/v3/trace_service.proto index 65970593d786..69772a88d99d 100644 --- a/api/envoy/service/trace/v3/trace_service.proto +++ b/api/envoy/service/trace/v3/trace_service.proto @@ -13,6 +13,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.service.trace.v3"; option java_outer_classname = "TraceServiceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/trace/v3;tracev3"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/api/envoy/type/hash_policy.proto b/api/envoy/type/hash_policy.proto index b6aeb31fcbfd..f022f09e7db8 100644 --- a/api/envoy/type/hash_policy.proto +++ b/api/envoy/type/hash_policy.proto @@ -8,6 +8,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type"; option java_outer_classname = "HashPolicyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Hash Policy] diff --git a/api/envoy/type/http.proto b/api/envoy/type/http.proto index c1c787411fad..51768f17368f 100644 --- a/api/envoy/type/http.proto +++ b/api/envoy/type/http.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.type"; option java_outer_classname = "HttpProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: HTTP] diff --git a/api/envoy/type/http/v3/path_transformation.proto b/api/envoy/type/http/v3/path_transformation.proto index 0b3d72009f5f..50350c48f9dd 100644 --- a/api/envoy/type/http/v3/path_transformation.proto +++ b/api/envoy/type/http/v3/path_transformation.proto @@ -8,6 +8,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.http.v3"; option java_outer_classname = "PathTransformationProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/http/v3;httpv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Path Transformations API] diff --git a/api/envoy/type/http_status.proto b/api/envoy/type/http_status.proto index 99b44a98c251..0d22234e466f 100644 --- a/api/envoy/type/http_status.proto +++ b/api/envoy/type/http_status.proto @@ -8,6 +8,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type"; option java_outer_classname = "HttpStatusProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: HTTP status codes] diff --git a/api/envoy/type/matcher/metadata.proto b/api/envoy/type/matcher/metadata.proto index ed58d04adb02..20da230b4fdb 100644 --- a/api/envoy/type/matcher/metadata.proto +++ b/api/envoy/type/matcher/metadata.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.matcher"; option java_outer_classname = "MetadataProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/matcher"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Metadata matcher] diff --git a/api/envoy/type/matcher/node.proto b/api/envoy/type/matcher/node.proto index c9e84a46279a..5dcf1e69bcd9 100644 --- a/api/envoy/type/matcher/node.proto +++ b/api/envoy/type/matcher/node.proto @@ -10,6 +10,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.type.matcher"; option java_outer_classname = "NodeProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/matcher"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Node matcher] diff --git a/api/envoy/type/matcher/number.proto b/api/envoy/type/matcher/number.proto index e488f16a4a0c..4c5b4db38d0b 100644 --- a/api/envoy/type/matcher/number.proto +++ b/api/envoy/type/matcher/number.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.matcher"; option java_outer_classname = "NumberProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/matcher"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Number matcher] diff --git a/api/envoy/type/matcher/path.proto b/api/envoy/type/matcher/path.proto index 860a1c69f18a..1a97bbc154ae 100644 --- a/api/envoy/type/matcher/path.proto +++ b/api/envoy/type/matcher/path.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.matcher"; option java_outer_classname = "PathProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/matcher"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Path matcher] diff --git a/api/envoy/type/matcher/regex.proto b/api/envoy/type/matcher/regex.proto index 6c499235bbe2..6daa16e478fa 100644 --- a/api/envoy/type/matcher/regex.proto +++ b/api/envoy/type/matcher/regex.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.matcher"; option java_outer_classname = "RegexProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/matcher"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Regex matcher] diff --git a/api/envoy/type/matcher/string.proto b/api/envoy/type/matcher/string.proto index 499eaf21775f..b4571ce727a0 100644 --- a/api/envoy/type/matcher/string.proto +++ b/api/envoy/type/matcher/string.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.matcher"; option java_outer_classname = "StringProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/matcher"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: String matcher] diff --git a/api/envoy/type/matcher/struct.proto b/api/envoy/type/matcher/struct.proto index 10d4672e0622..f08bf793d6d0 100644 --- a/api/envoy/type/matcher/struct.proto +++ b/api/envoy/type/matcher/struct.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.matcher"; option java_outer_classname = "StructProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/matcher"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Struct matcher] diff --git a/api/envoy/type/matcher/v3/BUILD b/api/envoy/type/matcher/v3/BUILD index a117fd27e4ff..fb28aa0e973d 100644 --- a/api/envoy/type/matcher/v3/BUILD +++ b/api/envoy/type/matcher/v3/BUILD @@ -7,7 +7,6 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ "//envoy/annotations:pkg", - "//envoy/type/matcher:pkg", "//envoy/type/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], diff --git a/api/envoy/type/matcher/v3/http_inputs.proto b/api/envoy/type/matcher/v3/http_inputs.proto index 403e4676f7b9..36e12a81fdc7 100644 --- a/api/envoy/type/matcher/v3/http_inputs.proto +++ b/api/envoy/type/matcher/v3/http_inputs.proto @@ -8,6 +8,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.matcher.v3"; option java_outer_classname = "HttpInputsProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3;matcherv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Common HTTP Inputs] diff --git a/api/envoy/type/matcher/v3/metadata.proto b/api/envoy/type/matcher/v3/metadata.proto index de19a2f34dbd..d3316e88a882 100644 --- a/api/envoy/type/matcher/v3/metadata.proto +++ b/api/envoy/type/matcher/v3/metadata.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.matcher.v3"; option java_outer_classname = "MetadataProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3;matcherv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Metadata matcher] diff --git a/api/envoy/type/matcher/v3/node.proto b/api/envoy/type/matcher/v3/node.proto index fe507312135f..baa92fb60351 100644 --- a/api/envoy/type/matcher/v3/node.proto +++ b/api/envoy/type/matcher/v3/node.proto @@ -11,6 +11,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.type.matcher.v3"; option java_outer_classname = "NodeProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3;matcherv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Node matcher] diff --git a/api/envoy/type/matcher/v3/number.proto b/api/envoy/type/matcher/v3/number.proto index 2379efdcbd23..99681c989cac 100644 --- a/api/envoy/type/matcher/v3/number.proto +++ b/api/envoy/type/matcher/v3/number.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.matcher.v3"; option java_outer_classname = "NumberProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3;matcherv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Number matcher] diff --git a/api/envoy/type/matcher/v3/path.proto b/api/envoy/type/matcher/v3/path.proto index 0ce89871c9d9..d332a17d6b7d 100644 --- a/api/envoy/type/matcher/v3/path.proto +++ b/api/envoy/type/matcher/v3/path.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.matcher.v3"; option java_outer_classname = "PathProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3;matcherv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Path matcher] diff --git a/api/envoy/type/matcher/v3/regex.proto b/api/envoy/type/matcher/v3/regex.proto index 3e7bb477ecbf..32565995f379 100644 --- a/api/envoy/type/matcher/v3/regex.proto +++ b/api/envoy/type/matcher/v3/regex.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.matcher.v3"; option java_outer_classname = "RegexProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3;matcherv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Regex matcher] @@ -44,6 +45,12 @@ message RegexMatcher { // // This field is deprecated; regexp validation should be performed on the management server // instead of being done by each individual client. + // + // .. note:: + // + // Although this field is deprecated, the program size will still be checked against the + // global ``re2.max_program_size.error_level`` runtime value. + // google.protobuf.UInt32Value max_program_size = 1 [deprecated = true, (envoy.annotations.deprecated_at_minor_version) = "3.0"]; } diff --git a/api/envoy/type/matcher/v3/string.proto b/api/envoy/type/matcher/v3/string.proto index c64edde142ff..efea6c0ab4ba 100644 --- a/api/envoy/type/matcher/v3/string.proto +++ b/api/envoy/type/matcher/v3/string.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.matcher.v3"; option java_outer_classname = "StringProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3;matcherv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: String matcher] diff --git a/api/envoy/type/matcher/v3/struct.proto b/api/envoy/type/matcher/v3/struct.proto index c753d07a5c0a..ad72e2cc783d 100644 --- a/api/envoy/type/matcher/v3/struct.proto +++ b/api/envoy/type/matcher/v3/struct.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.matcher.v3"; option java_outer_classname = "StructProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3;matcherv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Struct matcher] diff --git a/api/envoy/type/matcher/v3/value.proto b/api/envoy/type/matcher/v3/value.proto index 040332273ba3..bd46acc0713c 100644 --- a/api/envoy/type/matcher/v3/value.proto +++ b/api/envoy/type/matcher/v3/value.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.matcher.v3"; option java_outer_classname = "ValueProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3;matcherv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Value matcher] diff --git a/api/envoy/type/matcher/value.proto b/api/envoy/type/matcher/value.proto index aaecd14e8ecd..89d341bbbaa4 100644 --- a/api/envoy/type/matcher/value.proto +++ b/api/envoy/type/matcher/value.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.matcher"; option java_outer_classname = "ValueProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/matcher"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Value matcher] diff --git a/api/envoy/type/metadata/v2/metadata.proto b/api/envoy/type/metadata/v2/metadata.proto index 43a1a7ca9275..75f025009dad 100644 --- a/api/envoy/type/metadata/v2/metadata.proto +++ b/api/envoy/type/metadata/v2/metadata.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.metadata.v2"; option java_outer_classname = "MetadataProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/metadata/v2;metadatav2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.type.metadata.v3"; option (udpa.annotations.file_status).package_version_status = FROZEN; diff --git a/api/envoy/type/metadata/v3/BUILD b/api/envoy/type/metadata/v3/BUILD index aa64935f43d1..ee92fb652582 100644 --- a/api/envoy/type/metadata/v3/BUILD +++ b/api/envoy/type/metadata/v3/BUILD @@ -5,8 +5,5 @@ load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") licenses(["notice"]) # Apache 2 api_proto_package( - deps = [ - "//envoy/type/metadata/v2:pkg", - "@com_github_cncf_udpa//udpa/annotations:pkg", - ], + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], ) diff --git a/api/envoy/type/metadata/v3/metadata.proto b/api/envoy/type/metadata/v3/metadata.proto index 5dd58b23c623..0d535374b810 100644 --- a/api/envoy/type/metadata/v3/metadata.proto +++ b/api/envoy/type/metadata/v3/metadata.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.metadata.v3"; option java_outer_classname = "MetadataProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/metadata/v3;metadatav3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Metadata] diff --git a/api/envoy/type/percent.proto b/api/envoy/type/percent.proto index fc41a26662fe..6457e2a035fe 100644 --- a/api/envoy/type/percent.proto +++ b/api/envoy/type/percent.proto @@ -8,6 +8,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type"; option java_outer_classname = "PercentProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Percent] diff --git a/api/envoy/type/range.proto b/api/envoy/type/range.proto index 79aaa81975c3..9e66e6f22586 100644 --- a/api/envoy/type/range.proto +++ b/api/envoy/type/range.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.type"; option java_outer_classname = "RangeProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Range] diff --git a/api/envoy/type/semantic_version.proto b/api/envoy/type/semantic_version.proto index 80fe016bfa16..f6a508cc9588 100644 --- a/api/envoy/type/semantic_version.proto +++ b/api/envoy/type/semantic_version.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.type"; option java_outer_classname = "SemanticVersionProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Semantic Version] diff --git a/api/envoy/type/token_bucket.proto b/api/envoy/type/token_bucket.proto index 41b6d268d5f6..7419ebc003c7 100644 --- a/api/envoy/type/token_bucket.proto +++ b/api/envoy/type/token_bucket.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type"; option java_outer_classname = "TokenBucketProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Token bucket] diff --git a/api/envoy/type/tracing/v2/custom_tag.proto b/api/envoy/type/tracing/v2/custom_tag.proto index 7506ae886125..c37b662e51d6 100644 --- a/api/envoy/type/tracing/v2/custom_tag.proto +++ b/api/envoy/type/tracing/v2/custom_tag.proto @@ -10,6 +10,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.tracing.v2"; option java_outer_classname = "CustomTagProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/tracing/v2;tracingv2"; option (udpa.annotations.file_status).package_version_status = FROZEN; // [#protodoc-title: Custom Tag] diff --git a/api/envoy/type/tracing/v3/BUILD b/api/envoy/type/tracing/v3/BUILD index 38eb160d482b..c797ae66c28a 100644 --- a/api/envoy/type/tracing/v3/BUILD +++ b/api/envoy/type/tracing/v3/BUILD @@ -7,7 +7,6 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ "//envoy/type/metadata/v3:pkg", - "//envoy/type/tracing/v2:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/api/envoy/type/tracing/v3/custom_tag.proto b/api/envoy/type/tracing/v3/custom_tag.proto index ad99cafb22bf..feb57e8eb66e 100644 --- a/api/envoy/type/tracing/v3/custom_tag.proto +++ b/api/envoy/type/tracing/v3/custom_tag.proto @@ -11,6 +11,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.tracing.v3"; option java_outer_classname = "CustomTagProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/tracing/v3;tracingv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Custom Tag] diff --git a/api/envoy/type/v3/BUILD b/api/envoy/type/v3/BUILD index da3a8659d2a8..ee92fb652582 100644 --- a/api/envoy/type/v3/BUILD +++ b/api/envoy/type/v3/BUILD @@ -5,8 +5,5 @@ load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") licenses(["notice"]) # Apache 2 api_proto_package( - deps = [ - "//envoy/type:pkg", - "@com_github_cncf_udpa//udpa/annotations:pkg", - ], + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], ) diff --git a/api/envoy/type/v3/hash_policy.proto b/api/envoy/type/v3/hash_policy.proto index 96c39299698f..69452ca71dbb 100644 --- a/api/envoy/type/v3/hash_policy.proto +++ b/api/envoy/type/v3/hash_policy.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.v3"; option java_outer_classname = "HashPolicyProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/v3;typev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Hash Policy] @@ -23,9 +24,20 @@ message HashPolicy { option (udpa.annotations.versioning).previous_message_type = "envoy.type.HashPolicy.SourceIp"; } + // An Object in the :ref:`filterState ` will be used + // to compute the hash used by hash-based load balancing algorithms. + message FilterState { + // The name of the Object in the filterState, which is an Envoy::Hashable object. If there is no + // data associated with the key, or the stored object is not Envoy::Hashable, no hash will be + // produced. + string key = 1 [(validate.rules).string = {min_len: 1}]; + } + oneof policy_specifier { option (validate.required) = true; SourceIp source_ip = 1; + + FilterState filter_state = 2; } } diff --git a/api/envoy/type/v3/http.proto b/api/envoy/type/v3/http.proto index fec15d11f871..a1a5a04fc87f 100644 --- a/api/envoy/type/v3/http.proto +++ b/api/envoy/type/v3/http.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.type.v3"; option java_outer_classname = "HttpProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/v3;typev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: HTTP] diff --git a/api/envoy/type/v3/http_status.proto b/api/envoy/type/v3/http_status.proto index 8914b7a0264a..ab03e1b2b727 100644 --- a/api/envoy/type/v3/http_status.proto +++ b/api/envoy/type/v3/http_status.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.v3"; option java_outer_classname = "HttpStatusProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/v3;typev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: HTTP status codes] diff --git a/api/envoy/type/v3/percent.proto b/api/envoy/type/v3/percent.proto index 3a89a3f44fd5..e041ecddc78d 100644 --- a/api/envoy/type/v3/percent.proto +++ b/api/envoy/type/v3/percent.proto @@ -9,6 +9,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.v3"; option java_outer_classname = "PercentProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/v3;typev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Percent] diff --git a/api/envoy/type/v3/range.proto b/api/envoy/type/v3/range.proto index de1d55b09a21..3b1af814858b 100644 --- a/api/envoy/type/v3/range.proto +++ b/api/envoy/type/v3/range.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.type.v3"; option java_outer_classname = "RangeProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/v3;typev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Range] diff --git a/api/envoy/type/v3/ratelimit_unit.proto b/api/envoy/type/v3/ratelimit_unit.proto index a3fb27ff47ba..b976e6842ada 100644 --- a/api/envoy/type/v3/ratelimit_unit.proto +++ b/api/envoy/type/v3/ratelimit_unit.proto @@ -7,6 +7,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.type.v3"; option java_outer_classname = "RatelimitUnitProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/v3;typev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Ratelimit Time Unit] diff --git a/api/envoy/type/v3/semantic_version.proto b/api/envoy/type/v3/semantic_version.proto index a4126336f03a..e1567612ab7d 100644 --- a/api/envoy/type/v3/semantic_version.proto +++ b/api/envoy/type/v3/semantic_version.proto @@ -8,6 +8,7 @@ import "udpa/annotations/versioning.proto"; option java_package = "io.envoyproxy.envoy.type.v3"; option java_outer_classname = "SemanticVersionProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/v3;typev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Semantic Version] diff --git a/api/envoy/type/v3/token_bucket.proto b/api/envoy/type/v3/token_bucket.proto index a96d50fbd0ab..87686d57b0aa 100644 --- a/api/envoy/type/v3/token_bucket.proto +++ b/api/envoy/type/v3/token_bucket.proto @@ -12,6 +12,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.type.v3"; option java_outer_classname = "TokenBucketProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/v3;typev3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Token bucket] diff --git a/api/envoy/watchdog/v3/abort_action.proto b/api/envoy/watchdog/v3/abort_action.proto index 325c3d3dc7a8..9f3e3b03a4f6 100644 --- a/api/envoy/watchdog/v3/abort_action.proto +++ b/api/envoy/watchdog/v3/abort_action.proto @@ -9,6 +9,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.watchdog.v3"; option java_outer_classname = "AbortActionProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/watchdog/v3;watchdogv3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Watchdog Action that kills a stuck thread to kill the process.] diff --git a/api/versioning/BUILD b/api/versioning/BUILD index c0bbd3bc987f..d1b865f254a9 100644 --- a/api/versioning/BUILD +++ b/api/versioning/BUILD @@ -49,6 +49,7 @@ proto_library( "//envoy/data/dns/v3:pkg", "//envoy/data/tap/v3:pkg", "//envoy/extensions/access_loggers/file/v3:pkg", + "//envoy/extensions/access_loggers/filters/cel/v3:pkg", "//envoy/extensions/access_loggers/grpc/v3:pkg", "//envoy/extensions/access_loggers/open_telemetry/v3:pkg", "//envoy/extensions/access_loggers/stream/v3:pkg", @@ -123,6 +124,9 @@ proto_library( "//envoy/extensions/filters/network/ext_authz/v3:pkg", "//envoy/extensions/filters/network/http_connection_manager/v3:pkg", "//envoy/extensions/filters/network/local_ratelimit/v3:pkg", + "//envoy/extensions/filters/network/meta_protocol_proxy/matcher/action/v3:pkg", + "//envoy/extensions/filters/network/meta_protocol_proxy/matcher/v3:pkg", + "//envoy/extensions/filters/network/meta_protocol_proxy/v3:pkg", "//envoy/extensions/filters/network/mongo_proxy/v3:pkg", "//envoy/extensions/filters/network/ratelimit/v3:pkg", "//envoy/extensions/filters/network/rbac/v3:pkg", @@ -130,6 +134,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", @@ -173,6 +178,7 @@ proto_library( "//envoy/extensions/transport_sockets/s2a/v3:pkg", "//envoy/extensions/transport_sockets/starttls/v3:pkg", "//envoy/extensions/transport_sockets/tap/v3:pkg", + "//envoy/extensions/transport_sockets/tcp_stats/v3:pkg", "//envoy/extensions/transport_sockets/tls/v3:pkg", "//envoy/extensions/upstreams/http/generic/v3:pkg", "//envoy/extensions/upstreams/http/http/v3:pkg", diff --git a/bazel/BUILD b/bazel/BUILD index 3b22ffc8ff87..b4f803b23703 100644 --- a/bazel/BUILD +++ b/bazel/BUILD @@ -370,6 +370,11 @@ config_setting( values = {"define": "perf_annotation=enabled"}, ) +config_setting( + name = "enable_perf_tracing", + values = {"define": "perf_tracing=enabled"}, +) + config_setting( name = "force_libcpp", values = {"define": "force_libcpp=enabled"}, diff --git a/bazel/EXTERNAL_DEPS.md b/bazel/EXTERNAL_DEPS.md index 9820ff4cf993..0e1cdb0e2744 100644 --- a/bazel/EXTERNAL_DEPS.md +++ b/bazel/EXTERNAL_DEPS.md @@ -25,7 +25,7 @@ This is the preferred style of adding dependencies that use CMake for their buil 1. Define a the source Bazel repository in [`bazel/repositories.bzl`](repositories.bzl), in the `envoy_dependencies()` function. -2. Add a `cmake_external` rule to [`bazel/foreign_cc/BUILD`](foreign_cc/BUILD). This will reference +2. Add an `envoy_cmake` rule to [`bazel/foreign_cc/BUILD`](foreign_cc/BUILD). This will reference the source repository in step 1. 3. Reference your new external dependency in some `envoy_cc_library` via the name bound in step 1 `external_deps` attribute. diff --git a/bazel/PPROF.md b/bazel/PPROF.md index c3ad65aca5d8..ba0ea555b89c 100644 --- a/bazel/PPROF.md +++ b/bazel/PPROF.md @@ -17,6 +17,7 @@ * [On-CPU analysis](#on-cpu-analysis) * [Memory analysis](#memory-analysis) * [Performance annotations](#performance-annotations) + * [Performance analysis with Perfetto](#performance-analysis-with-perfetto) # CPU or memory consumption testing with `gperftools` and `pprof` @@ -327,3 +328,78 @@ private: PERF_OWNER(perf_operation_); }; ``` + +# Performance analysis with Perfetto + +Similar results can be achieved with [Perfetto tracing macros](https://github.com/envoyproxy/envoy/blob/main/source/common/common/perf_tracing.h) enabled with + +``` +bazel --define=perf_tracing=enabled ... +``` + +[Pefetto](https://perfetto.dev/) is a production-grade open-source stack for +performance instrumentation and trace analysis. It offers services and libraries +for recording system-level and app-level traces, a library for analyzing traces +using SQL and a web-based UI to visualize and explore multi-GB traces. + +Currently when the Perfetto support is enabled the tracing data in binary Protobuf +format is dumped into `envoy.pftrace` upon process termination. The file +can be analyzed online at https://ui.perfetto.dev/ or with a custom tool. + +To generate a scoped trace event which uses C++ RAII under the hood add the +`TRACE_EVENT` macro to the block of your interest: + +```c++ +#include "source/common/common/perf_tracing.h" + +RequestDecoder& ConnectionManagerImpl::newStream(ResponseEncoder& response_encoder, + bool is_internally_created) { + TRACE_EVENT("core", "ConnectionManagerImpl::newStream"); // Begin "ConnectionManagerImpl::newStream" slice. + ... + + // End "ConnectionManagerImpl::newStream" slice. +} +``` + +For events that don't follow function scoping, use `TRACE_EVENT_BEGIN` and +`TRACE_EVENT_END`. Please be careful with these events as all events on a given +thread share the same stack. This means that it's not recommended to have a matching +pair of `TRACE_EVENT_BEGIN` and `TRACE_EVENT_END` markers in separate functions, +since an unrelated event might terminate the original event unexpectedly; for events +that cross function boundaries it's usually best to emit them on a separate track. +Below is an example for a trace event covering an object's life span: + +```c++ +#include "source/common/common/perf_tracing.h" + +Http::Request::Request(int request_id) + : request_id_(request_id) { + TRACE_EVENT_BEGIN("core", "Http::Request", + perfetto::Track(request_id_, perfetto::ThreadTrack::Current())); + ... +} + +Http::Request::~Request() { + ... + + TRACE_EVENT_END("core", perfetto::Track(request_id_, perfetto::ThreadTrack::Current())); +} + +``` + +Unfortunately this may lead to excessive number of tracks if they are unique for every +pair of emitted events. The existing visualization tools may not work well if the number +of tracks is too big. In this case the resulting trace data needs to be processed +differently. Alternatively, if you are interested in benchmarking only and don't need +any tracing capabilities, then you can resort to the Performance Annotation system mentioned +above which supports cross-scoped events too, but doesn't require any post-processing to get +a benchmark's final report. + +Time-varying numeric data can be recorded with the `TRACE_COUNTER` macro: + +```c++ +TRACE_COUNTER("extensions", "MemoryAllocated", + tcmalloc::MallocExtension::GetNumericProperty("generic.current_allocated_bytes")); +``` + +For more details please refer https://perfetto.dev/docs/instrumentation/track-events. diff --git a/bazel/README.md b/bazel/README.md index 9337efb33ca8..1f0463e22617 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/bazel/dependency_imports.bzl b/bazel/dependency_imports.bzl index b382e3fd02cd..6a12830e3b2f 100644 --- a/bazel/dependency_imports.bzl +++ b/bazel/dependency_imports.bzl @@ -11,7 +11,7 @@ load("@proxy_wasm_rust_sdk//bazel:dependencies.bzl", "proxy_wasm_rust_sdk_depend load("@rules_cc//cc:repositories.bzl", "rules_cc_dependencies", "rules_cc_toolchains") # go version for rules_go -GO_VERSION = "1.15.5" +GO_VERSION = "1.17.5" def envoy_dependency_imports(go_version = GO_VERSION): # TODO: allow building of tools for easier onboarding diff --git a/bazel/envoy_internal.bzl b/bazel/envoy_internal.bzl index 9c5130f15e4b..b942be424c1f 100644 --- a/bazel/envoy_internal.bzl +++ b/bazel/envoy_internal.bzl @@ -121,6 +121,7 @@ def envoy_copts(repository, test = False): }) + envoy_select_hot_restart(["-DENVOY_HOT_RESTART"], repository) + \ envoy_select_enable_http3(["-DENVOY_ENABLE_QUIC"], repository) + \ _envoy_select_perf_annotation(["-DENVOY_PERF_ANNOTATION"]) + \ + _envoy_select_perfetto(["-DENVOY_PERFETTO"]) + \ envoy_select_google_grpc(["-DENVOY_GOOGLE_GRPC"], repository) + \ _envoy_select_path_normalization_by_default(["-DENVOY_NORMALIZE_PATH_BY_DEFAULT"], repository) @@ -179,3 +180,9 @@ def _envoy_select_perf_annotation(xs): "@envoy//bazel:enable_perf_annotation": xs, "//conditions:default": [], }) + +def _envoy_select_perfetto(xs): + return select({ + "@envoy//bazel:enable_perf_tracing": xs, + "//conditions:default": [], + }) diff --git a/bazel/envoy_test.bzl b/bazel/envoy_test.bzl index 0cd48ba28620..4fa57c80843f 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"], diff --git a/bazel/external/quiche.BUILD b/bazel/external/quiche.BUILD index 4ad3433fb646..8c6a2ec45976 100644 --- a/bazel/external/quiche.BUILD +++ b/bazel/external/quiche.BUILD @@ -83,6 +83,75 @@ 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 = [ + "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.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", + ":http2_event_forwarder", + ":spdy_core_framer_lib", + ":spdy_core_header_block_lib", + ":spdy_core_http2_deframer_lib", + ":spdy_core_protocol_lib", + ":spdy_header_byte_listener_interface_lib", + ":spdy_no_op_headers_handler_lib", + ], +) + envoy_cc_library( name = "http2_core_http2_priority_write_scheduler_lib", hdrs = ["quiche/http2/core/http2_priority_write_scheduler.h"], @@ -108,6 +177,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"], @@ -144,7 +228,10 @@ envoy_cc_library( hdrs = ["quiche/http2/http2_constants.h"], copts = quiche_copts, repository = "@envoy", - deps = [":http2_platform"], + deps = [ + ":http2_platform", + ":quiche_common_text_utils_lib", + ], ) envoy_cc_library( @@ -780,6 +867,26 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "spdy_no_op_headers_handler_lib", + hdrs = ["quiche/spdy/core/no_op_headers_handler.h"], + repository = "@envoy", + visibility = ["//visibility:public"], + deps = [ + ":quiche_common_platform", + ], +) + +envoy_cc_library( + name = "spdy_header_byte_listener_interface_lib", + hdrs = ["quiche/spdy/core/header_byte_listener_interface.h"], + repository = "@envoy", + visibility = ["//visibility:public"], + deps = [ + ":quiche_common_platform", + ], +) + envoy_cc_library( name = "spdy_core_alt_svc_wire_format_lib", srcs = ["quiche/spdy/core/spdy_alt_svc_wire_format.cc"], @@ -837,6 +944,7 @@ envoy_cc_library( deps = [ ":quiche_common_lib", ":quiche_common_platform", + ":quiche_common_text_utils_lib", ":spdy_core_header_storage_lib", ], ) @@ -956,6 +1064,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"], @@ -1884,10 +2003,10 @@ envoy_cc_library( "quiche/quic/core/crypto/curve25519_key_exchange.cc", "quiche/quic/core/crypto/key_exchange.cc", "quiche/quic/core/crypto/p256_key_exchange.cc", + "quiche/quic/core/crypto/quic_client_session_cache.cc", "quiche/quic/core/crypto/quic_compressed_certs_cache.cc", "quiche/quic/core/crypto/quic_crypto_client_config.cc", "quiche/quic/core/crypto/quic_crypto_server_config.cc", - "quiche/quic/core/crypto/server_proof_verifier.h", "quiche/quic/core/crypto/transport_parameters.cc", ], hdrs = [ @@ -1904,6 +2023,7 @@ envoy_cc_library( "quiche/quic/core/crypto/key_exchange.h", "quiche/quic/core/crypto/p256_key_exchange.h", "quiche/quic/core/crypto/proof_verifier.h", + "quiche/quic/core/crypto/quic_client_session_cache.h", "quiche/quic/core/crypto/quic_compressed_certs_cache.h", "quiche/quic/core/crypto/quic_crypto_client_config.h", "quiche/quic/core/crypto/quic_crypto_server_config.h", @@ -1931,6 +2051,7 @@ envoy_cc_library( deps = [ ":quic_core_clock_lib", ":quic_core_crypto_certificate_view_lib", + ":quic_core_crypto_client_proof_source_lib", ":quic_core_crypto_encryption_lib", ":quic_core_crypto_hkdf_lib", ":quic_core_crypto_proof_source_lib", @@ -2077,6 +2198,7 @@ envoy_cc_library( tags = ["nofips"], visibility = ["//visibility:public"], deps = [ + ":quic_core_crypto_certificate_view_lib", ":quic_core_packets_lib", ":quic_core_versions_lib", ":quic_platform_base", @@ -2084,6 +2206,24 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "quic_core_crypto_client_proof_source_lib", + srcs = [ + "quiche/quic/core/crypto/client_proof_source.cc", + ], + hdrs = [ + "quiche/quic/core/crypto/client_proof_source.h", + ], + copts = quiche_copts, + repository = "@envoy", + tags = ["nofips"], + visibility = ["//visibility:public"], + deps = [ + ":quic_core_crypto_proof_source_lib", + ":quic_platform_base", + ], +) + envoy_cc_library( name = "quic_core_crypto_random_lib", srcs = ["quiche/quic/core/crypto/quic_random.cc"], @@ -3469,6 +3609,7 @@ envoy_cc_library( ":quic_core_time_lib", ":quic_platform_base", ":quiche_common_endian_lib", + ":quiche_common_print_elements_lib", ], ) diff --git a/bazel/external/quiche.genrule_cmd b/bazel/external/quiche.genrule_cmd index ed451e6d9e33..c80eaa882b5d 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 000000000000..e69de29bb2d1 diff --git a/bazel/external/wee8.genrule_cmd b/bazel/external/wee8.genrule_cmd index 8c92818102fe..ae858cc54487 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++. @@ -100,6 +101,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\"" @@ -131,7 +137,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 diff --git a/bazel/external/wee8.patch b/bazel/external/wee8.patch index c15f5d867a3b..c7566fe53e2b 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/foreign_cc/BUILD b/bazel/foreign_cc/BUILD index e55355fe8f53..4d3a2816691b 100644 --- a/bazel/foreign_cc/BUILD +++ b/bazel/foreign_cc/BUILD @@ -211,7 +211,7 @@ envoy_cmake( # options, and they cannot be nested. # If https://github.com/bazelbuild/rules_foreign_cc/issues/289 is fixed # this can be removed. - # More details https://github.com/lyft/envoy-mobile/issues/116 + # More details https://github.com/envoyproxy/envoy-mobile/issues/116 "_GNU_SOURCE": "on", }, lib_source = "@com_github_libevent_libevent//:all", @@ -219,7 +219,7 @@ envoy_cmake( # macOS organization of libevent is different from Windows/Linux. # Including libevent_core is a requirement on those platforms, but # results in duplicate symbols when built on macOS. - # See https://github.com/lyft/envoy-mobile/issues/677 for details. + # See https://github.com/envoyproxy/envoy-mobile/issues/677 for details. "//bazel:apple": [ "libevent.a", "libevent_pthreads.a", @@ -277,45 +277,73 @@ envoy_cmake( lib_source = "@org_llvm_llvm//:all", out_static_libs = select({ "//conditions:default": [ - # Order from llvm-config --libnames asmparser core debuginfodwarf - # engine lto mcparser mirparser orcjit passes runtimedyld - # support x86asmparser x86desc + # This list must be updated when the bazel llvm version is updated + # (in `bazel/repository_locations.bzl`) + # + # The list can be regenerated by compiling the correct/updated llvm version + # from sources and running: + # + # `llvm-config --libnames` + # + "libLLVMWindowsManifest.a", + "libLLVMXRay.a", + "libLLVMLibDriver.a", + "libLLVMDlltoolDriver.a", + "libLLVMCoverage.a", + "libLLVMLineEditor.a", + "libLLVMX86Disassembler.a", + "libLLVMX86AsmParser.a", + "libLLVMX86CodeGen.a", + "libLLVMX86Desc.a", + "libLLVMX86Info.a", "libLLVMOrcJIT.a", - "libLLVMOrcError.a", + "libLLVMMCJIT.a", "libLLVMJITLink.a", - "libLLVMMIRParser.a", + "libLLVMOrcTargetProcess.a", + "libLLVMOrcShared.a", + "libLLVMInterpreter.a", + "libLLVMExecutionEngine.a", + "libLLVMRuntimeDyld.a", + "libLLVMSymbolize.a", + "libLLVMDebugInfoPDB.a", + "libLLVMDebugInfoGSYM.a", + "libLLVMOption.a", + "libLLVMObjectYAML.a", + "libLLVMMCA.a", + "libLLVMMCDisassembler.a", "libLLVMLTO.a", "libLLVMPasses.a", + "libLLVMCFGuard.a", + "libLLVMCoroutines.a", "libLLVMObjCARCOpts.a", + "libLLVMHelloNew.a", "libLLVMipo.a", - "libLLVMInstrumentation.a", "libLLVMVectorize.a", "libLLVMLinker.a", - "libLLVMIRReader.a", - "libLLVMX86Disassembler.a", - "libLLVMX86AsmParser.a", - "libLLVMX86CodeGen.a", - "libLLVMCFGuard.a", + "libLLVMInstrumentation.a", + "libLLVMFrontendOpenMP.a", + "libLLVMFrontendOpenACC.a", + "libLLVMExtensions.a", + "libLLVMDWARFLinker.a", "libLLVMGlobalISel.a", - "libLLVMSelectionDAG.a", + "libLLVMMIRParser.a", "libLLVMAsmPrinter.a", + "libLLVMDebugInfoDWARF.a", + "libLLVMSelectionDAG.a", "libLLVMCodeGen.a", + "libLLVMIRReader.a", + "libLLVMAsmParser.a", + "libLLVMInterfaceStub.a", + "libLLVMFileCheck.a", + "libLLVMFuzzMutate.a", + "libLLVMTarget.a", "libLLVMScalarOpts.a", "libLLVMInstCombine.a", "libLLVMAggressiveInstCombine.a", "libLLVMTransformUtils.a", "libLLVMBitWriter.a", - "libLLVMX86Desc.a", - "libLLVMMCDisassembler.a", - "libLLVMX86Utils.a", - "libLLVMX86Info.a", - "libLLVMMCJIT.a", - "libLLVMExecutionEngine.a", - "libLLVMTarget.a", "libLLVMAnalysis.a", "libLLVMProfileData.a", - "libLLVMRuntimeDyld.a", - "libLLVMDebugInfoDWARF.a", "libLLVMObject.a", "libLLVMTextAPI.a", "libLLVMMCParser.a", @@ -323,7 +351,6 @@ envoy_cmake( "libLLVMDebugInfoCodeView.a", "libLLVMDebugInfoMSF.a", "libLLVMBitReader.a", - "libLLVMAsmParser.a", "libLLVMCore.a", "libLLVMRemarks.a", "libLLVMBitstreamReader.a", diff --git a/bazel/foreign_cc/vpp_vcl.patch b/bazel/foreign_cc/vpp_vcl.patch index 0d440fb02945..56635f3bb338 100644 --- a/bazel/foreign_cc/vpp_vcl.patch +++ b/bazel/foreign_cc/vpp_vcl.patch @@ -1,7 +1,7 @@ # Not a git repo so embed version --- src/CMakeLists.txt +++ src/CMakeLists.txt -@@ -42,12 +42,8 @@ include(cmake/ccache.cmake) +@@ -40,12 +40,8 @@ include(cmake/ccache.cmake) ############################################################################## # VPP Version ############################################################################## @@ -12,19 +12,19 @@ - OUTPUT_STRIP_TRAILING_WHITESPACE -) + -+set(VPP_VERSION 21.10-rc0~334-g596c45b22) ++set(VPP_VERSION 22.02-rc0~385-g7c3275e84) string(REPLACE "-" ";" VPP_LIB_VERSION ${VPP_VERSION}) list(GET VPP_LIB_VERSION 0 VPP_LIB_VERSION) - -@@ -179,7 +179,7 @@ if(VPP_HOST_TOOLS_ONLY) - elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + +@@ -188,7 +184,7 @@ elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") find_package(OpenSSL) set(SUBDIRS -- vppinfra svm vlib vlibmemory vlibapi vnet vpp vat vat2 vcl plugins -+ vppinfra svm vlib vlibmemory vlibapi vnet vpp vat vat2 vcl - vpp-api tools/vppapigen tools/g2 tools/perftool cmake pkg + vppinfra svm vlib vlibmemory vlibapi vnet vpp vat vat2 vcl vpp-api +- plugins tools/vppapigen tools/g2 tools/perftool cmake pkg ++ tools/vppapigen tools/g2 tools/perftool cmake pkg tools/appimage ) + elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") --- src/cmake/ccache.cmake +++ src/cmake/ccache.cmake @@ -43,9 +43,9 @@ @@ -24,7 +24,7 @@ macro(add_vpp_library lib) set_target_properties(${lo} PROPERTIES POSITION_INDEPENDENT_CODE ON) target_compile_options(${lo} PUBLIC ${VPP_DEFAULT_MARCH_FLAGS}) - + - add_library(${lib} SHARED) + add_library(${lib} STATIC) target_sources(${lib} PRIVATE $) - + if(VPP_LIB_VERSION) diff --git a/bazel/genrule_repository.bzl b/bazel/genrule_repository.bzl index 7eb6c2bfb60f..e263c43d4689 100644 --- a/bazel/genrule_repository.bzl +++ b/bazel/genrule_repository.bzl @@ -11,18 +11,9 @@ 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)) - - # https://github.com/bazelbuild/bazel/issues/3766 - genrule_cmd_file = Label("@envoy//bazel").relative(str(ctx.attr.genrule_cmd_file)) - ctx.symlink(genrule_cmd_file, "_envoy_genrule_cmd.genrule_cmd") - cat_genrule_cmd = ctx.execute(["cat", "_envoy_genrule_cmd.genrule_cmd"]) - if cat_genrule_cmd.return_code != 0: - fail("Failed to read genrule command %r: %s" % ( - genrule_cmd_file, - cat_genrule_cmd.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,)) ctx.delete("BUILD.bazel") ctx.symlink(ctx.attr.build_file, "BUILD.bazel") @@ -33,8 +24,8 @@ def _genrule_repository(ctx): ctx.file("genrule_cmd.bzl", """ _GENRULE_CMD = {%r: %r} def genrule_cmd(label): - return _GENRULE_CMD[label] -""" % (str(genrule_cmd_file), cat_genrule_cmd.stdout)) + return _GENRULE_CMD[Label(label)] +""" % (ctx.attr.genrule_cmd_file, genrule_cmd)) genrule_repository = repository_rule( attrs = { diff --git a/bazel/protobuf.patch b/bazel/protobuf.patch index a6318ce8e49f..6c68b2d8aedc 100644 --- a/bazel/protobuf.patch +++ b/bazel/protobuf.patch @@ -1,32 +1,24 @@ -# https://github.com/protocolbuffers/protobuf/pull/6720 -diff --git a/third_party/BUILD b/third_party/BUILD -new file mode 100644 -index 0000000000..b66101a39a ---- /dev/null -+++ b/third_party/BUILD -@@ -0,0 +1,1 @@ -+exports_files(["six.BUILD", "zlib.BUILD"]) diff --git a/BUILD b/BUILD -index 7de87f884..3f0fd5362 100644 +index 1690d4219..8a7f1bf14 100644 --- a/BUILD +++ b/BUILD @@ -19,7 +19,7 @@ exports_files(["LICENSE"]) # ZLIB configuration ################################################################################ - + -ZLIB_DEPS = ["@zlib//:zlib"] +ZLIB_DEPS = ["//external:zlib"] - + ################################################################################ # Protobuf Runtime Library diff --git a/python/google/protobuf/__init__.py b/python/google/protobuf/__init__.py -index cb4740412..91fe69ce5 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.18.0' + + __version__ = '3.19.1' + +if __name__ != '__main__': + try: diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index 0fd7212d032b..cf336fc50259 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -115,10 +115,6 @@ def _python_deps(): name = "com_github_twitter_common_finagle_thrift", build_file = "@envoy//bazel/external:twitter_common_finagle_thrift.BUILD", ) - external_http_archive( - name = "six", - build_file = "@com_google_protobuf//third_party:six.BUILD", - ) # Bazel native C++ dependencies. For the dependencies that doesn't provide autoconf/automake builds. def _cc_deps(): @@ -224,6 +220,7 @@ def envoy_dependencies(skip_targets = []): external_http_archive("proxy_wasm_rust_sdk") external_http_archive("com_googlesource_code_re2") _com_google_cel_cpp() + _com_github_google_perfetto() external_http_archive("com_github_google_flatbuffers") external_http_archive("bazel_toolchains") external_http_archive("bazel_compdb") @@ -473,6 +470,19 @@ cc_library( patches = ["@envoy//bazel:antlr.patch"], ) +def _com_github_google_perfetto(): + external_http_archive( + name = "com_github_google_perfetto", + build_file_content = """ +package(default_visibility = ["//visibility:public"]) +cc_library( + name = "perfetto", + srcs = ["perfetto.cc"], + hdrs = ["perfetto.h"], +) +""", + ) + def _com_github_nghttp2_nghttp2(): external_http_archive( name = "com_github_nghttp2_nghttp2", @@ -813,11 +823,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", @@ -1057,6 +1072,9 @@ def _rules_fuzzing(): repo_mapping = { "@fuzzing_py_deps": "@fuzzing_pip3", }, + # TODO(asraa): Try this fix for OSS-Fuzz build failure on tar command. + patch_args = ["-p1"], + patches = ["@envoy//bazel:rules_fuzzing.patch"], ) def _kafka_deps(): diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 00d7e857b72b..f39c13d23bdc 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -4,21 +4,21 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "bazel-compilation-database", project_desc = "Clang JSON compilation database support for Bazel", project_url = "https://github.com/grailbio/bazel-compilation-database", - version = "0.4.5", - sha256 = "bcecfd622c4ef272fd4ba42726a52e140b961c4eac23025f18b346c968a8cfb4", + version = "0.5.2", + sha256 = "d32835b26dd35aad8fd0ba0d712265df6565a3ad860d39e4c01ad41059ea7eda", strip_prefix = "bazel-compilation-database-{version}", urls = ["https://github.com/grailbio/bazel-compilation-database/archive/{version}.tar.gz"], - release_date = "2020-08-01", + release_date = "2021-09-10", use_category = ["build"], ), bazel_gazelle = dict( project_name = "Gazelle", project_desc = "Bazel BUILD file generator for Go projects", project_url = "https://github.com/bazelbuild/bazel-gazelle", - version = "0.22.2", - sha256 = "b85f48fa105c4403326e9525ad2b2cc437babaa6e15a3fc0b1dbab0ab064bc7c", + version = "0.24.0", + sha256 = "de69a09dc70417580aabf20a28619bb3ef60d038470c7cf8442fafcf627c21cb", urls = ["https://github.com/bazelbuild/bazel-gazelle/releases/download/v{version}/bazel-gazelle-v{version}.tar.gz"], - release_date = "2020-10-02", + release_date = "2021-10-11", use_category = ["build"], ), bazel_toolchains = dict( @@ -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( @@ -79,15 +79,15 @@ REPOSITORY_LOCATIONS_SPEC = dict( # To update BoringSSL, which tracks Chromium releases: # 1. Open https://omahaproxy.appspot.com/ and note of linux/dev release. # 2. Open https://chromium.googlesource.com/chromium/src/+/refs/tags//DEPS and note . - # 3. Find a commit in BoringSSL's "master-with-bazel" branch that merges . + # 3. Find a commit in BoringSSL's "main-with-bazel" branch that merges . # - # chromium-92.0.4511.0 (linux/dev) - version = "75edea1922aefe415e0e60ac576116634b0a94f8", - sha256 = "70e9d8737e35d67f94b9e742ca59c02c36f30f1d822d5a3706511a23798d8049", + # chromium-94.0.4606.81 (linux/dev) + version = "648cbaf033401b7fe7acdce02f275b06a88aab5c", + sha256 = "579cb415458e9f3642da0a39a72f79fdfe6dc9c1713b3a823f1e276681b9703e", strip_prefix = "boringssl-{version}", urls = ["https://github.com/google/boringssl/archive/{version}.tar.gz"], use_category = ["controlplane", "dataplane_core"], - release_date = "2021-05-13", + release_date = "2021-07-15", cpe = "cpe:2.3:a:google:boringssl:*", ), boringssl_fips = dict( @@ -113,6 +113,20 @@ REPOSITORY_LOCATIONS_SPEC = dict( release_date = "2021-06-03", cpe = "N/A", ), + # This dependency is built only when performance tracing is enabled with the + # option --define=perf_tracing=enabled. It's never built for releases. + com_github_google_perfetto = dict( + project_name = "Perfetto", + project_desc = "Perfetto Tracing SDK", + project_url = "https://perfetto.dev/", + version = "22.1", + sha256 = "013ba743019a1ca04627f7ce8bf424b60ed7f0f57e232eff719ae879be4c90fd", + strip_prefix = "perfetto-{version}/sdk", + urls = ["https://github.com/google/perfetto/archive/v{version}.tar.gz"], + use_category = ["dataplane_core", "controlplane"], + release_date = "2021-12-07", + cpe = "N/A", + ), com_github_c_ares_c_ares = dict( project_name = "c-ares", project_desc = "C library for asynchronous DNS requests", @@ -141,12 +155,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "xxHash", project_desc = "Extremely fast hash algorithm", project_url = "https://github.com/Cyan4973/xxHash", - version = "0.8.0", - sha256 = "7054c3ebd169c97b64a92d7b994ab63c70dd53a06974f1f630ab782c28db0f4f", + version = "0.8.1", + sha256 = "3bb6b7d6f30c591dd65aaaff1c8b7a5b94d81687998ca9400082c739a690436c", strip_prefix = "xxHash-{version}", urls = ["https://github.com/Cyan4973/xxHash/archive/v{version}.tar.gz"], use_category = ["dataplane_core", "controlplane"], - release_date = "2020-07-27", + release_date = "2021-11-29", cpe = "N/A", ), com_github_envoyproxy_sqlparser = dict( @@ -306,12 +320,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "Nghttp2", project_desc = "Implementation of HTTP/2 and its header compression algorithm HPACK in Cimplementation of HTTP/2 and its header compression algorithm HPACK in C", project_url = "https://nghttp2.org", - version = "1.45.1", - sha256 = "2379ebeff7b02e14b9a414551d73540ddce5442bbecda2748417e8505916f3e7", + version = "1.46.0", + sha256 = "4b6d11c85f2638531d1327fe1ed28c1e386144e8841176c04153ed32a4878208", strip_prefix = "nghttp2-{version}", urls = ["https://github.com/nghttp2/nghttp2/releases/download/v{version}/nghttp2-{version}.tar.gz"], use_category = ["controlplane", "dataplane_core"], - release_date = "2021-09-21", + release_date = "2021-10-19", cpe = "cpe:2.3:a:nghttp2:nghttp2:*", ), io_opentracing_cpp = dict( @@ -403,7 +417,7 @@ REPOSITORY_LOCATIONS_SPEC = dict( # stats (see https://github.com/libevent/libevent/pull/793) and the fix for a race condition # in the watchers (see https://github.com/libevent/libevent/pull/802). # This also includes the fixes for https://github.com/libevent/libevent/issues/806 - # and https://github.com/lyft/envoy-mobile/issues/215. + # and https://github.com/envoyproxy/envoy-mobile/issues/215. # This also includes the fixes for Phantom events with EV_ET (see # https://github.com/libevent/libevent/issues/984). # This also includes the wepoll backend for Windows (see @@ -548,14 +562,14 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "nlohmann JSON", project_desc = "Fast JSON parser/generator for C++", project_url = "https://nlohmann.github.io/json", - version = "3.10.2", - sha256 = "081ed0f9f89805c2d96335c3acfa993b39a0a5b4b4cef7edb68dd2210a13458c", + version = "3.10.4", + sha256 = "1155fd1a83049767360e9a120c43c578145db3204d2b309eba49fbbedd0f4ed3", strip_prefix = "json-{version}", urls = ["https://github.com/nlohmann/json/archive/v{version}.tar.gz"], # This will be a replacement for rapidJSON used in extensions and may also be a fast # replacement for protobuf JSON. use_category = ["controlplane", "dataplane_core"], - release_date = "2021-08-26", + release_date = "2021-10-16", cpe = "cpe:2.3:a:json-for-modern-cpp_project:json-for-modern-cpp:*", ), # This is an external dependency needed while running the @@ -626,36 +640,36 @@ 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.18.0", - sha256 = "52b6160ae9266630adb5e96a9fc645215336371a740e87d411bfb63ea2f268a0", + 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-09-15", + release_date = "2021-10-29", cpe = "cpe:2.3:a:google:protobuf:*", ), grpc_httpjson_transcoding = 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 = "bdd203e981d5ec25166aa5f5df6b443986eea556", + sha256 = "2ce3a6306b245cf46834a3538bcac327359fc4b1f8b0e2d4881c9ff0acfe5ba5", 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-12-03", cpe = "N/A", ), io_bazel_rules_go = dict( project_name = "Go rules for Bazel", project_desc = "Bazel rules for the Go language", project_url = "https://github.com/bazelbuild/rules_go", - version = "0.27.0", - sha256 = "69de5c704a05ff37862f7e0f5534d4f479418afc21806c887db544a316f3cb6b", - urls = ["https://github.com/bazelbuild/rules_go/releases/download/v{version}/rules_go-v{version}.tar.gz"], + version = "0.29.0", + sha256 = "2b1641428dff9018f9e85c0384f03ec6c10660d935b750e3fa1492a281a53b0f", + urls = ["https://github.com/bazelbuild/rules_go/releases/download/v{version}/rules_go-v{version}.zip"], use_category = ["build", "api"], - release_date = "2021-03-18", + release_date = "2021-10-06", implied_untracked_deps = [ "com_github_golang_protobuf", "io_bazel_rules_nogo", @@ -688,9 +702,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"], ), @@ -698,31 +712,24 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "Packaging rules for Bazel", project_desc = "Bazel rules for the packaging distributions", project_url = "https://github.com/bazelbuild/rules_pkg", - version = "0.5.1", - sha256 = "a89e203d3cf264e564fcb96b6e06dd70bc0557356eb48400ce4b5d97c2c3720d", - urls = ["https://github.com/bazelbuild/rules_pkg/releases/download/{version}/rules_pkg-{version}.tar.gz"], + version = "ad57589abb069baa48f982778de408ea02d714fd", + sha256 = "ec14799a45f1d3b6c3e61c4d04513001bddac9208f09077b1f8c91ab47d234d2", + strip_prefix = "rules_pkg-{version}/pkg", + urls = ["https://github.com/bazelbuild/rules_pkg/archive/{version}.tar.gz"], use_category = ["build"], - release_date = "2021-08-18", - ), - six = dict( - project_name = "Six", - project_desc = "Python 2 and 3 compatibility library", - project_url = "https://github.com/benjaminp/six", - version = "1.12.0", - sha256 = "0ce7aef70d066b8dda6425c670d00c25579c3daad8108b3e3d41bef26003c852", - urls = ["https://github.com/benjaminp/six/archive/{version}.tar.gz"], - release_date = "2018-12-10", - use_category = ["other"], + release_date = "2021-10-22", ), org_llvm_llvm = dict( + # When changing this, you must re-generate the list of llvm libs + # see `bazel/foreign_cc/BUILD` for further information. project_name = "LLVM", project_desc = "LLVM Compiler Infrastructure", project_url = "https://llvm.org", - version = "10.0.0", - sha256 = "df83a44b3a9a71029049ec101fb0077ecbbdf5fe41e395215025779099a98fdf", + version = "12.0.1", + sha256 = "7d9a8405f557cefc5a21bf5672af73903b64749d9bc3a50322239f56f34ffddf", strip_prefix = "llvm-{version}.src", urls = ["https://github.com/llvm/llvm-project/releases/download/llvmorg-{version}/llvm-{version}.src.tar.xz"], - release_date = "2020-03-24", + release_date = "2021-07-09", use_category = ["dataplane_ext"], extensions = [ "envoy.wasm.runtime.wamr", @@ -747,11 +754,11 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "WAVM", project_desc = "WebAssembly Virtual Machine", project_url = "https://wavm.github.io", - version = "79c3aa29366615d9b1593cd527e5b4b94cc6072a", - sha256 = "ce899269516313b400005a8cc9bc3bcd8329663f43f7b4baae211ea0cd456a39", + version = "9ffd3e2f8dcbbe4e965825c32195bd70d6ebc95d", + sha256 = "e4d2d1f53deda4313209b6edceddfc59eb93f367cf3ca41b590ac2e54bb7daf3", strip_prefix = "WAVM-{version}", urls = ["https://github.com/WAVM/WAVM/archive/{version}.tar.gz"], - release_date = "2021-03-31", + release_date = "2021-10-16", use_category = ["dataplane_ext"], extensions = ["envoy.wasm.runtime.wavm"], cpe = "cpe:2.3:a:webassembly_virtual_machine_project:webassembly_virtual_machine:*", @@ -760,11 +767,11 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "wasmtime", project_desc = "A standalone runtime for WebAssembly", project_url = "https://github.com/bytecodealliance/wasmtime", - version = "0.26.0", - sha256 = "e95d274822ac72bf06355bdfbeddcacae60d7e98fec8ee4b2e21740636fb5c2c", + version = "0.31.0", + sha256 = "4f9fc62453f2d8faf2374699a40e95d9265829e675b5a28e45e2af4b642e7219", strip_prefix = "wasmtime-{version}", urls = ["https://github.com/bytecodealliance/wasmtime/archive/v{version}.tar.gz"], - release_date = "2021-04-05", + release_date = "2021-10-29", use_category = ["dataplane_ext"], extensions = ["envoy.wasm.runtime.wasmtime"], cpe = "cpe:2.3:a:bytecodealliance:wasmtime:*", @@ -802,8 +809,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"], @@ -813,34 +820,34 @@ 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( 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( 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 = "b6e4ee8a1617031b04c8d5f4773469c742d3aaa1", - sha256 = "3947598748ab0034fe44f18a1fdda89d4418f9a64bcf583acbb5c25252a391a5", + version = "e87010ff958c9397ee861e00d7747de6f3938e19", + sha256 = "8b3b6ac35947335a362cc77a43e5a6c3ef699ef5d1f0c73fa068059b4b3fc5b6", urls = ["https://github.com/google/quiche/archive/{version}.tar.gz"], strip_prefix = "quiche-{version}", use_category = ["dataplane_core"], - release_date = "2021-10-18", + release_date = "2021-12-07", cpe = "N/A", ), com_googlesource_googleurl = dict( @@ -860,12 +867,13 @@ 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"], extensions = [ + "envoy.access_loggers.extension_filters.cel", "envoy.access_loggers.wasm", "envoy.bootstrap.wasm", "envoy.rate_limit_descriptors.expr", @@ -876,7 +884,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( @@ -889,6 +897,7 @@ REPOSITORY_LOCATIONS_SPEC = dict( urls = ["https://github.com/google/flatbuffers/archive/v{version}.tar.gz"], use_category = ["dataplane_ext"], extensions = [ + "envoy.access_loggers.extension_filters.cel", "envoy.access_loggers.wasm", "envoy.bootstrap.wasm", "envoy.rate_limit_descriptors.expr", @@ -906,12 +915,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 @@ -946,45 +955,45 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "Kafka (source)", project_desc = "Open-source distributed event streaming platform", project_url = "https://kafka.apache.org", - version = "2.8.1", - sha256 = "c3fd89257e056e11b5e1b09d4bbd8332ce5abfdfa7c7a5bb6a5cfe9860fcc688", + version = "3.0.0", + sha256 = "862526ee07c372d7b2f7e672c096fe84bb1e115ef536e0ad7497e6fb50e08e02", strip_prefix = "kafka-{version}/clients/src/main/resources/common/message", urls = ["https://github.com/apache/kafka/archive/{version}.zip"], use_category = ["dataplane_ext"], extensions = ["envoy.filters.network.kafka_broker", "envoy.filters.network.kafka_mesh"], - release_date = "2021-09-14", + release_date = "2021-09-08", cpe = "cpe:2.3:a:apache:kafka:*", ), edenhill_librdkafka = dict( project_name = "Kafka (C/C++ client)", project_desc = "C/C++ client for Apache Kafka (open-source distributed event streaming platform)", project_url = "https://github.com/edenhill/librdkafka", - version = "1.7.0", - sha256 = "c71b8c5ff419da80c31bb8d3036a408c87ad523e0c7588e7660ee5f3c8973057", + version = "1.8.2", + sha256 = "6a747d293a7a4613bd2897e28e8791476fbe1ae7361f2530a876e0fd483482a6", strip_prefix = "librdkafka-{version}", urls = ["https://github.com/edenhill/librdkafka/archive/v{version}.tar.gz"], use_category = ["dataplane_ext"], extensions = ["envoy.filters.network.kafka_mesh"], - release_date = "2021-05-10", + release_date = "2021-10-18", cpe = "N/A", ), kafka_server_binary = dict( project_name = "Kafka (server binary)", project_desc = "Open-source distributed event streaming platform", project_url = "https://kafka.apache.org", - version = "2.8.1", - sha256 = "4888b03e3b27dd94f2d830ce3bae9d7d98b0ccee3a5d30c919ccb60e0fa1f139", + version = "3.0.0", + sha256 = "a82728166bbccf406009747a25e1fe52dbcb4d575e4a7a8616429b5818cd02d1", strip_prefix = "kafka_2.13-{version}", urls = ["https://archive.apache.org/dist/kafka/{version}/kafka_2.13-{version}.tgz"], - release_date = "2021-09-14", + release_date = "2021-09-20", use_category = ["test_only"], ), kafka_python_client = dict( project_name = "Kafka (Python client)", project_desc = "Open-source distributed event streaming platform", project_url = "https://kafka.apache.org", - version = "2.0.1", - sha256 = "05f7c6eecb402f11fcb7e524c903f1ba1c38d3bdc9bf42bc8ec3cf7567b9f979", + version = "2.0.2", + sha256 = "5dcf87c559e7aee4f18d621a02e247db3e3552ee4589ca611d51eef87b37efed", strip_prefix = "kafka-python-{version}", urls = ["https://github.com/dpkp/kafka-python/archive/{version}.tar.gz"], release_date = "2020-09-30", @@ -1018,8 +1027,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 = "03185974ef574233a5f6383311eb74a380146fe2", - sha256 = "34948e3ba239cc721af8d0a0a5b678325f363cbd542bddecf2267d24780d5b4d", + version = "f38347360feaaf5b2a733f219c4d8c9660d626f0", + sha256 = "bf10de946eb5785813895c2bf16504afc0cd590b9655d9ee52fb1074d0825ea3", 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"], @@ -1035,7 +1044,7 @@ REPOSITORY_LOCATIONS_SPEC = dict( "envoy.wasm.runtime.wavm", "envoy.wasm.runtime.wasmtime", ], - release_date = "2021-08-12", + release_date = "2021-11-18", cpe = "N/A", ), proxy_wasm_rust_sdk = dict( @@ -1065,13 +1074,13 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "Bazel rust rules", project_desc = "Bazel rust rules (used by Wasm)", project_url = "https://github.com/bazelbuild/rules_rust", - version = "7e7246f6c48a5d4e69744cd79b9ccb8886966ee2", - sha256 = "d54b379559f3fe6ff0cd251be216a5e35acf241451eec8144455482e8f4748f8", + version = "82b650d5d0709ae4c0ee8584f4ed92112ba11d67", + sha256 = "d087851b76204935f7f23c172eb0d136c09720b8484d8151019523652ce77004", strip_prefix = "rules_rust-{version}", urls = ["https://github.com/bazelbuild/rules_rust/archive/{version}.tar.gz"], use_category = ["dataplane_ext"], extensions = ["envoy.wasm.runtime.wasmtime"], - release_date = "2021-06-29", + release_date = "2021-10-19", cpe = "N/A", ), rules_antlr = dict( @@ -1085,6 +1094,7 @@ REPOSITORY_LOCATIONS_SPEC = dict( # ANTLR has a runtime component, so is not purely build. use_category = ["dataplane_ext"], extensions = [ + "envoy.access_loggers.extension_filters.cel", "envoy.access_loggers.wasm", "envoy.bootstrap.wasm", "envoy.rate_limit_descriptors.expr", @@ -1105,6 +1115,7 @@ REPOSITORY_LOCATIONS_SPEC = dict( urls = ["https://github.com/antlr/antlr4/archive/{version}.tar.gz"], use_category = ["dataplane_ext"], extensions = [ + "envoy.access_loggers.extension_filters.cel", "envoy.access_loggers.wasm", "envoy.bootstrap.wasm", "envoy.rate_limit_descriptors.expr", @@ -1119,13 +1130,13 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "VPP Comms Library", project_desc = "FD.io Vector Packet Processor (VPP) Comms Library", project_url = "https://fd.io/", - version = "596c45b22211c9af243b624dc037f58c0aa1c302", - sha256 = "e4c3fad7e1a6952e5c081cfe25f1f091d97fae8e75c5f03205def37d34c27741", + version = "7c3275e84b64ade4e20d00e4457bd4e437b1894f", + sha256 = "d456d37bbb7f90ec1ef166c1387e788b4c3078d38303f12ab41f1d0ac1a1cfc0", strip_prefix = "vpp-{version}", urls = ["https://github.com/FDio/vpp/archive/{version}.tar.gz"], use_category = ["other"], extensions = ["envoy.bootstrap.vcl"], - release_date = "2021-09-13", + release_date = "2021-12-10", cpe = "N/A", ), ) diff --git a/bazel/rules_fuzzing.patch b/bazel/rules_fuzzing.patch new file mode 100644 index 000000000000..eca1b56e4d52 --- /dev/null +++ b/bazel/rules_fuzzing.patch @@ -0,0 +1,13 @@ +diff --git a/fuzzing/private/oss_fuzz/package.bzl b/fuzzing/private/oss_fuzz/package.bzl +index e5e9dc4..a3bb1b8 100644 +--- a/fuzzing/private/oss_fuzz/package.bzl ++++ b/fuzzing/private/oss_fuzz/package.bzl +@@ -71,7 +71,7 @@ def _oss_fuzz_package_impl(ctx): + if [[ -n "{options_path}" ]]; then + ln -s "$(pwd)/{options_path}" "$STAGING_DIR/{base_name}.options" + fi +- tar -chf "{output}" -C "$STAGING_DIR" . ++ tar -czhf "{output}" -C "$STAGING_DIR" . + """.format( + base_name = ctx.attr.base_name, + binary_path = binary_info.binary_file.path, \ No newline at end of file diff --git a/ci/README.md b/ci/README.md index 0561facedf73..fd4a3b2d6353 100644 --- a/ci/README.md +++ b/ci/README.md @@ -135,7 +135,7 @@ The `./ci/run_envoy_docker.sh './ci/do_ci.sh '` targets are: * `bazel.clang_tidy ` — build and run clang-tidy specified source files, if no files specified, runs against the diff with the last GitHub commit. * `check_format`— run `clang-format` and `buildifier` on entire source tree. * `fix_format`— run and enforce `clang-format` and `buildifier` on entire source tree. -* `check_spelling_pedantic`— run `aspell` on C++ and proto comments. +* `format_pre`— run validation and linting tools. * `docs`— build documentation tree in `generated/docs`. ## On Windows diff --git a/ci/do_ci.sh b/ci/do_ci.sh index 4334c2304b34..d7a0c90e7934 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -462,7 +462,6 @@ elif [[ "$CI_TARGET" == "deps" ]]; then exit 0 elif [[ "$CI_TARGET" == "cve_scan" ]]; then echo "scanning for CVEs in dependencies..." - bazel run "${BAZEL_BUILD_OPTIONS[@]}" //tools/dependency:cve_scan_test bazel run "${BAZEL_BUILD_OPTIONS[@]}" //tools/dependency:cve_scan exit 0 elif [[ "$CI_TARGET" == "tooling" ]]; then @@ -482,11 +481,6 @@ elif [[ "$CI_TARGET" == "tooling" ]]; then echo "dependency validate_test..." "${ENVOY_SRCDIR}"/tools/dependency/validate_test.py - # Validate the CVE scanner works. We do it here as well as in cve_scan, since this blocks - # presubmits, but cve_scan only runs async. - echo "cve_scan_test..." - bazel run "${BAZEL_BUILD_OPTIONS[@]}" //tools/dependency:cve_scan_test - exit 0 elif [[ "$CI_TARGET" == "verify_examples" ]]; then run_ci_verify "*" "wasm-cc|win32-front-proxy" diff --git a/ci/filter_example_setup.sh b/ci/filter_example_setup.sh index 5ef74fa49119..2447a79e41d4 100644 --- a/ci/filter_example_setup.sh +++ b/ci/filter_example_setup.sh @@ -25,6 +25,8 @@ sed -e "s|{ENVOY_SRCDIR}|${ENVOY_SRCDIR}|" "${ENVOY_SRCDIR}"/ci/WORKSPACE.filter mkdir -p "${ENVOY_FILTER_EXAMPLE_SRCDIR}"/bazel ln -sf "${ENVOY_SRCDIR}"/bazel/get_workspace_status "${ENVOY_FILTER_EXAMPLE_SRCDIR}"/bazel/ cp -f "${ENVOY_SRCDIR}"/.bazelrc "${ENVOY_FILTER_EXAMPLE_SRCDIR}"/ +rm -f "${ENVOY_FILTER_EXAMPLE_SRCDIR}"/.bazelversion +cp -f "${ENVOY_SRCDIR}"/.bazelversion "${ENVOY_FILTER_EXAMPLE_SRCDIR}"/ cp -f "$(bazel info workspace)"/*.bazelrc "${ENVOY_FILTER_EXAMPLE_SRCDIR}"/ export FILTER_WORKSPACE_SET=1 diff --git a/ci/run_clang_tidy.sh b/ci/run_clang_tidy.sh index cdf2270a625f..1665334447f4 100755 --- a/ci/run_clang_tidy.sh +++ b/ci/run_clang_tidy.sh @@ -89,7 +89,13 @@ function run_clang_tidy() { } function run_clang_tidy_diff() { - git diff "$1" | filter_excludes | \ + local diff + diff="$(git diff "${1}")" + if [[ -z "$diff" ]]; then + echo "No changes detected, skipping clang_tidy_diff" + return 0 + fi + echo "$diff" | filter_excludes | \ python3 "${LLVM_PREFIX}/share/clang/clang-tidy-diff.py" \ -clang-tidy-binary="${CLANG_TIDY}" \ -export-fixes="${FIX_YAML}" -j "${NUM_CPUS:-0}" -p 1 -quiet diff --git a/configs/encapsulate_in_http2_connect.yaml b/configs/encapsulate_in_http2_connect.yaml index 1f985457ab2d..55629ef0df2a 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/configs/envoy_double_proxy.template.yaml b/configs/envoy_double_proxy.template.yaml index b574d6a518c5..56f4d17ad201 100644 --- a/configs/envoy_double_proxy.template.yaml +++ b/configs/envoy_double_proxy.template.yaml @@ -149,8 +149,10 @@ static_resources: validation_context: trusted_ca: filename: certs/cacert.pem - match_subject_alt_names: - exact: "front-proxy.yourcompany.net" + match_typed_subject_alt_names: + - san_type: DNS + matcher: + exact: "front-proxy.yourcompany.net" typed_extension_protocol_options: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions @@ -188,8 +190,10 @@ static_resources: validation_context: trusted_ca: filename: certs/cacert.pem - match_subject_alt_names: - exact: "collector-grpc.lightstep.com" + match_typed_subject_alt_names: + - san_type: DNS + matcher: + exact: "collector-grpc.lightstep.com" flags_path: "/etc/envoy/flags" stats_sinks: - name: envoy.stat_sinks.statsd diff --git a/configs/envoy_service_to_service.template.yaml b/configs/envoy_service_to_service.template.yaml index 6ec2f0bde905..7bdd6a4d0fad 100644 --- a/configs/envoy_service_to_service.template.yaml +++ b/configs/envoy_service_to_service.template.yaml @@ -350,8 +350,10 @@ static_resources: trusted_ca: filename: certs/cacert.pem {% if host.get('verify_subject_alt_name', False) %} - match_subject_alt_names: - exact: "{{host['verify_subject_alt_name'] }}" + match_typed_subject_alt_names: + - san_type: DNS + matcher: + exact: "{{host['verify_subject_alt_name'] }}" {% endif %} {% if host.get('sni', False) %} sni: "{{ host['sni'] }}" @@ -520,8 +522,10 @@ static_resources: validation_context: trusted_ca: filename: certs/cacert.pem - match_subject_alt_names: - exact: "collector-grpc.lightstep.com" + match_typed_subject_alt_names: + - san_type: DNS + matcher: + exact: "collector-grpc.lightstep.com" - name: cds_cluster connect_timeout: 0.25s type: STRICT_DNS diff --git a/configs/requirements.txt b/configs/requirements.txt index 7e65450464ab..d0aaf38b7b60 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 \ diff --git a/contrib/kafka/filters/network/source/protocol/generator.py b/contrib/kafka/filters/network/source/protocol/generator.py index 2fd18ebc2d69..05ab8d059966 100755 --- a/contrib/kafka/filters/network/source/protocol/generator.py +++ b/contrib/kafka/filters/network/source/protocol/generator.py @@ -126,12 +126,7 @@ def parse_messages(self, input_files): r'^\s*$', '', without_comments, flags=re.MULTILINE) # Windows support: see PR 10542 for details. amended = re.sub(r'-2147483648', 'INT32_MIN', without_empty_newlines) - # Kafka JSON files are malformed. See KAFKA-12794. - if input_file == 'external/kafka_source/DescribeProducersRequest.json': - amended = amended[:-6] message_spec = json.loads(amended) - # Adopt publicly available messages only: - # https://kafka.apache.org/28/protocol.html#protocol_api_keys api_key = message_spec['apiKey'] if api_key <= 51 or api_key in [56, 57, 60, 61]: message = self.parse_top_level_element(message_spec) diff --git a/contrib/rocketmq_proxy/filters/network/source/conn_manager.cc b/contrib/rocketmq_proxy/filters/network/source/conn_manager.cc index 7dfb03973643..f19ae099f64d 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)); diff --git a/contrib/rocketmq_proxy/filters/network/source/protocol.h b/contrib/rocketmq_proxy/filters/network/source/protocol.h index 03082f3398c3..2de55a04130f 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_; diff --git a/contrib/vcl/source/config.cc b/contrib/vcl/source/config.cc index 89c918f8fd2f..365d06f32b3f 100644 --- a/contrib/vcl/source/config.cc +++ b/contrib/vcl/source/config.cc @@ -21,10 +21,9 @@ ProtobufTypes::MessagePtr VclSocketInterface::createEmptyConfigProto() { return std::make_unique(); } -Envoy::Network::IoHandlePtr VclSocketInterface::socket(Envoy::Network::Socket::Type socket_type, - Envoy::Network::Address::Type addr_type, - Envoy::Network::Address::IpVersion, - bool) const { +Envoy::Network::IoHandlePtr VclSocketInterface::socket( + Envoy::Network::Socket::Type socket_type, Envoy::Network::Address::Type addr_type, + Envoy::Network::Address::IpVersion, bool, const Envoy::Network::SocketCreationOptions&) const { if (vppcom_worker_index() == -1) { vclInterfaceWorkerRegister(); } @@ -42,8 +41,10 @@ Envoy::Network::IoHandlePtr VclSocketInterface::socket(Envoy::Network::Socket::T Envoy::Network::IoHandlePtr VclSocketInterface::socket(Envoy::Network::Socket::Type socket_type, - const Envoy::Network::Address::InstanceConstSharedPtr addr) const { - return socket(socket_type, addr->type(), Envoy::Network::Address::IpVersion::v4, false); + const Envoy::Network::Address::InstanceConstSharedPtr addr, + const Envoy::Network::SocketCreationOptions& creation_options) const { + return socket(socket_type, addr->type(), Envoy::Network::Address::IpVersion::v4, false, + creation_options); } bool VclSocketInterface::ipFamilySupported(int) { return true; }; diff --git a/contrib/vcl/source/config.h b/contrib/vcl/source/config.h index 65c9c53bfbcb..68770be877e6 100644 --- a/contrib/vcl/source/config.h +++ b/contrib/vcl/source/config.h @@ -18,11 +18,11 @@ class VclSocketInterface : public Envoy::Network::SocketInterfaceBase { // Network::SocketInterface Envoy::Network::IoHandlePtr socket(Envoy::Network::Socket::Type socket_type, Envoy::Network::Address::Type addr_type, - Envoy::Network::Address::IpVersion version, - bool socket_v6only) const override; - Envoy::Network::IoHandlePtr - socket(Envoy::Network::Socket::Type socket_type, - const Envoy::Network::Address::InstanceConstSharedPtr addr) const override; + Envoy::Network::Address::IpVersion version, bool socket_v6only, + const Envoy::Network::SocketCreationOptions&) const override; + Envoy::Network::IoHandlePtr socket(Envoy::Network::Socket::Type socket_type, + const Envoy::Network::Address::InstanceConstSharedPtr addr, + const Envoy::Network::SocketCreationOptions&) const override; bool ipFamilySupported(int domain) override; // Server::Configuration::BootstrapExtensionFactory diff --git a/contrib/vcl/source/vcl_event.cc b/contrib/vcl/source/vcl_event.cc index b37ecbb415b3..9e7fc87c49f5 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) { diff --git a/contrib/vcl/source/vcl_interface.cc b/contrib/vcl/source/vcl_interface.cc index 3b877fa00c4e..9d576581ba41 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 diff --git a/docs/root/_include/tcp_stats.rst b/docs/root/_include/tcp_stats.rst new file mode 100644 index 000000000000..9d38f4996daf --- /dev/null +++ b/docs/root/_include/tcp_stats.rst @@ -0,0 +1,19 @@ +.. note:: + These metrics are provided by the operating system. Due to differences in operating system metrics available and the methodology + used to take measurements, the values may not be consistent across different operating systems or versions of the same operating + system. + +.. csv-table:: + :header: Name, Type, Description + :widths: 1, 1, 2 + + cx_tx_segments, Counter, Total TCP segments transmitted + cx_rx_segments, Counter, Total TCP segments received + cx_tx_data_segments, Counter, Total TCP segments with a non-zero data length transmitted + cx_rx_data_segments, Counter, Total TCP segments with a non-zero data length received + cx_tx_retransmitted_segments, Counter, Total TCP segments retransmitted + cx_tx_unsent_bytes, Gauge, Bytes which Envoy has sent to the operating system which have not yet been sent + cx_tx_unacked_segments, Gauge, Segments which have been transmitted that have not yet been acknowledged + cx_tx_percent_retransmitted_segments, Histogram, Percent of segments on a connection which were retransmistted + cx_rtt_us, Histogram, Smoothed round trip time estimate in microseconds + cx_rtt_variance_us, Histogram, Estimated variance in microseconds of the round trip time. Higher values indicated more variability. diff --git a/docs/root/api-v3/config/accesslog/accesslog.rst b/docs/root/api-v3/config/accesslog/accesslog.rst index dae49773f788..6f265939d7ee 100644 --- a/docs/root/api-v3/config/accesslog/accesslog.rst +++ b/docs/root/api-v3/config/accesslog/accesslog.rst @@ -9,3 +9,4 @@ Access loggers v3/* ../../extensions/access_loggers/*/v3/* + ../../extensions/access_loggers/filters/*/v3/* diff --git a/docs/root/api-v3/config/accesslog/filters.rst b/docs/root/api-v3/config/accesslog/filters.rst new file mode 100644 index 000000000000..692d0a18586b --- /dev/null +++ b/docs/root/api-v3/config/accesslog/filters.rst @@ -0,0 +1,8 @@ +Extension Filters +================= + +.. toctree:: + :glob: + :maxdepth: 2 + + filters/filters diff --git a/docs/root/api-v3/config/accesslog/filters/filters.rst b/docs/root/api-v3/config/accesslog/filters/filters.rst new file mode 100644 index 000000000000..a27e04e37692 --- /dev/null +++ b/docs/root/api-v3/config/accesslog/filters/filters.rst @@ -0,0 +1,8 @@ +Extension Filters +================= + +.. toctree:: + :glob: + :maxdepth: 2 + + ../../../extensions/access_loggers/filters/*/v3/* diff --git a/docs/root/api-v3/config/config.rst b/docs/root/api-v3/config/config.rst index 6d4034ff5b8d..605afc346a32 100644 --- a/docs/root/api-v3/config/config.rst +++ b/docs/root/api-v3/config/config.rst @@ -9,6 +9,7 @@ Extensions filter/filter accesslog/accesslog + accesslog/filters rbac/rbac health_checker/health_checker transport_socket/transport_socket diff --git a/docs/root/configuration/http/http_conn_man/headers.rst b/docs/root/configuration/http/http_conn_man/headers.rst index de03c5e89ca2..33b844f86028 100644 --- a/docs/root/configuration/http/http_conn_man/headers.rst +++ b/docs/root/configuration/http/http_conn_man/headers.rst @@ -363,6 +363,25 @@ A few very important notes about XFF: XFF is parsed to determine if a request is internal. In this scenario, do not forward XFF and allow Envoy to generate a new one with a single internal origin IP. +.. _config_http_conn_man_headers_x-forwarded-host: + +x-forwarded-host +---------------- + +The *x-forwarded-host* header is a de-facto standard proxy header which indicates the original host +requested by the client in the *:authority* (*host* in HTTP1) header. A compliant proxy *appends* +the original value of the *:authority* header to *x-forwarded-host* only if the *:authority* header +is modified. + +Envoy updates the *:authority* header if a host rewrite option (one of +:ref:`host_rewrite_literal `, +:ref:`auto_host_rewrite `, +:ref:`host_rewrite_header `, or +:ref:`host_rewrite_path_regex `) +is used and appends its original value to *x-forwarded-host* if +:ref:`append_x_forwarded_host ` +is set. + .. _config_http_conn_man_headers_x-forwarded-proto: x-forwarded-proto @@ -682,6 +701,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, @@ -738,3 +763,6 @@ Supported variable names are: %RESPONSE_CODE_DETAILS% Response code details provides additional information about the HTTP response code, such as who set it (the upstream or envoy) and why. + +%VIRTUAL_CLUSTER_NAME% + Name of the Virtual Cluster which gets matched (if any). diff --git a/docs/root/configuration/http/http_conn_man/stats.rst b/docs/root/configuration/http/http_conn_man/stats.rst index a12c2adcc2af..efb77be6f17e 100644 --- a/docs/root/configuration/http/http_conn_man/stats.rst +++ b/docs/root/configuration/http/http_conn_man/stats.rst @@ -196,6 +196,7 @@ On the upstream side all http3 statistics are rooted at *cluster..http3.* rx_reset, Counter, Total number of reset stream frames received by Envoy tx_reset, Counter, Total number of reset stream frames transmitted by Envoy metadata_not_supported_error, Counter, Total number of metadata dropped during HTTP/3 encoding + quic_version_h3_29, Counter, Total number of quic connections that use transport version h3-29. QUIC h3-29 is unsupported by default and this counter will be removed when h3-29 support is completely removed. quic_version_rfc_v1, Counter, Total number of quic connections that use transport version rfc-v1. diff --git a/docs/root/configuration/http/http_filters/_include/composite.yaml b/docs/root/configuration/http/http_filters/_include/composite.yaml index f7c4c4f0dbae..42949658dfaa 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/configuration/http/http_filters/_include/grpc-transcoder-filter.yaml b/docs/root/configuration/http/http_filters/_include/grpc-transcoder-filter.yaml index 80a8c67e0f72..919d61296e0e 100644 --- a/docs/root/configuration/http/http_filters/_include/grpc-transcoder-filter.yaml +++ b/docs/root/configuration/http/http_filters/_include/grpc-transcoder-filter.yaml @@ -21,7 +21,7 @@ static_resources: domains: ["*"] routes: # NOTE: by default, matching happens based on the gRPC route, and not on the incoming request path. - # Reference: https://www.envoyproxy.io/docs/envoy/latest/configuration/http_filters/grpc_json_transcoder_filter#route-configs-for-transcoded-requests + # Reference: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/grpc_json_transcoder_filter#route-configs-for-transcoded-requests - match: {prefix: "/helloworld.Greeter"} route: {cluster: grpc, timeout: 60s} http_filters: 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 4c31387c503a..27d5f58a340d 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/configuration/http/http_filters/bandwidth_limit_filter.rst b/docs/root/configuration/http/http_filters/bandwidth_limit_filter.rst index fa0b6d27b96c..57072ac10a8d 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/configuration/http/http_filters/lua_filter.rst b/docs/root/configuration/http/http_filters/lua_filter.rst index 78777d4e9c7b..28dad9499db6 100644 --- a/docs/root/configuration/http/http_filters/lua_filter.rst +++ b/docs/root/configuration/http/http_filters/lua_filter.rst @@ -248,8 +248,7 @@ There are two ways of doing this, the first one is via the ``body()`` API. .. code-block:: lua function envoy_on_response(response_handle) - local content_length = response_handle:body():setBytes("Not Found") - response_handle:headers():replace("content-length", content_length) + response_handle:body():setBytes("Not Found") response_handle:headers():replace("content-type", "text/html") end @@ -260,8 +259,7 @@ Or, through ``bodyChunks()`` API, which let Envoy to skip buffering the upstream function envoy_on_response(response_handle) - -- Sets the content-length. - response_handle:headers():replace("content-length", 28) + -- Sets the content-type. response_handle:headers():replace("content-type", "text/html") local last @@ -769,8 +767,8 @@ its keys can only be *string* or *numeric*. function envoy_on_request(request_handle) local headers = request_handle:headers() request_handle:streamInfo():dynamicMetadata():set("envoy.filters.http.lua", "request.info", { - auth: headers:get("authorization"), - token: headers:get("x-request-token"), + auth = headers:get("authorization"), + token = headers:get("x-request-token"), }) end diff --git a/docs/root/configuration/http/http_filters/oauth2_filter.rst b/docs/root/configuration/http/http_filters/oauth2_filter.rst index d28789ca8889..c919c2af526a 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/configuration/listeners/network_filters/kafka_broker_filter.rst b/docs/root/configuration/listeners/network_filters/kafka_broker_filter.rst index ec7828db5c12..8608fd366916 100644 --- a/docs/root/configuration/listeners/network_filters/kafka_broker_filter.rst +++ b/docs/root/configuration/listeners/network_filters/kafka_broker_filter.rst @@ -5,8 +5,8 @@ Kafka Broker filter The Apache Kafka broker filter decodes the client protocol for `Apache Kafka `_, both the requests and responses in the payload. -The message versions in `Kafka 2.8.1 `_ -are supported. +The message versions in `Kafka 3.0.0 `_ +are supported (apart from API keys 65-67 which were introduced recently). The filter attempts not to influence the communication between client and brokers, so the messages that could not be decoded (due to Kafka client or broker running a newer version than supported by this filter) are forwarded as-is. diff --git a/docs/root/configuration/listeners/network_filters/kafka_mesh_filter.rst b/docs/root/configuration/listeners/network_filters/kafka_mesh_filter.rst index bebb7c31aa5b..2ad5bd2c2924 100644 --- a/docs/root/configuration/listeners/network_filters/kafka_mesh_filter.rst +++ b/docs/root/configuration/listeners/network_filters/kafka_mesh_filter.rst @@ -6,7 +6,7 @@ Kafka Mesh filter The Apache Kafka mesh filter provides a facade for `Apache Kafka `_ producers. Produce requests sent to this filter insance can be forwarded to one of multiple clusters, depending on configured forwarding rules. Corresponding message versions from -Kafka 2.8.1 are supported. +Kafka 3.0.0 are supported. * :ref:`v3 API reference ` * This filter should be configured with the name *envoy.filters.network.kafka_mesh*. diff --git a/docs/root/configuration/listeners/stats.rst b/docs/root/configuration/listeners/stats.rst index 4ad0a29a9c14..4b3ed840237f 100644 --- a/docs/root/configuration/listeners/stats.rst +++ b/docs/root/configuration/listeners/stats.rst @@ -36,6 +36,16 @@ The following TLS statistics are rooted at *listener.
.ssl.*: .. include:: ../../_include/ssl_stats.rst +.. _config_listener_stats_tcp: + +TCP statistics +-------------- + +The following TCP statistics, which are available when using the :ref:`TCP stats transport socket `, +are rooted at *listener.
.tcp_stats.*: + +.. include:: ../../_include/tcp_stats.rst + .. _config_listener_stats_udp: UDP statistics diff --git a/docs/root/configuration/listeners/udp_filters/udp_proxy.rst b/docs/root/configuration/listeners/udp_filters/udp_proxy.rst index 0c731c7e297c..44250b0e0bef 100644 --- a/docs/root/configuration/listeners/udp_filters/udp_proxy.rst +++ b/docs/root/configuration/listeners/udp_filters/udp_proxy.rst @@ -20,17 +20,26 @@ Each session is index by the 4-tuple consisting of source IP/port and local IP/p datagram is received on. Sessions last until the :ref:`idle timeout ` is reached. +Above *session stickness* could be disabled by setting :ref:`use_per_packet_load_balancing +` to true. +In that case, *per packet load balancing* is enabled. It means that upstream host is selected on every single data chunk +received by udp proxy using currently used load balancing policy. + The UDP proxy listener filter also can operate as a *transparent* proxy if the :ref:`use_original_src_ip ` -field is set. But please keep in mind that it does not forward the port to upstreams. It forwards only the IP address to upstreams. +field is set to true. But please keep in mind that it does not forward the port to upstreams. It forwards only the IP address to upstreams. Load balancing and unhealthy host handling ------------------------------------------ Envoy will fully utilize the configured load balancer for the configured upstream cluster when -load balancing UDP datagrams. When a new session is created, Envoy will associate the session +load balancing UDP datagrams. By default, when a new session is created, Envoy will associate the session with an upstream host selected using the configured load balancer. All future datagrams that -belong to the session will be routed to the same upstream host. +belong to the session will be routed to the same upstream host. However, if :ref:`use_per_packet_load_balancing +` +field is set to true, Envoy selects another upstream host on next datagram using the configured load balancer +and creates a new session if such does not exist. So in case of several upstream hosts available for the load balancer +each data chunk is forwarded to a different host. When an upstream host becomes unhealthy (due to :ref:`active health checking `), Envoy will attempt to create a new session to a healthy host diff --git a/docs/root/configuration/observability/access_log/usage.rst b/docs/root/configuration/observability/access_log/usage.rst index 383e6e12b1a8..8599f838f4f3 100644 --- a/docs/root/configuration/observability/access_log/usage.rst +++ b/docs/root/configuration/observability/access_log/usage.rst @@ -241,6 +241,17 @@ The following command operators are supported: TCP Downstream bytes sent on connection. +%UPSTREAM_REQUEST_ATTEMPT_COUNT% + HTTP + Number of times the request is attempted upstream. Note that an attempt count of '0' means that + the request was never attempted upstream. + + TCP + Number of times the connection request is attempted upstream. Note that an attempt count of '0' + means that the connection request was never attempted upstream. + + Renders a numeric value in typed JSON logs. + %UPSTREAM_WIRE_BYTES_SENT% HTTP Total number of bytes sent to the upstream by the http stream. @@ -378,12 +389,19 @@ 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% Name of the route. +%VIRTUAL_CLUSTER_NAME% + HTTP*/gRPC + Name of the matched Virtual Cluster (if any). + + TCP/UDP + Not implemented ("-") + %UPSTREAM_HOST% Upstream host URL (e.g., tcp://ip:port for TCP connections). @@ -506,7 +524,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 +559,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: diff --git a/docs/root/configuration/observability/application_logging.rst b/docs/root/configuration/observability/application_logging.rst index 7a18fb210e92..7434d2cd5791 100644 --- a/docs/root/configuration/observability/application_logging.rst +++ b/docs/root/configuration/observability/application_logging.rst @@ -14,7 +14,7 @@ Stackdriver Logging with GKE `Google Kubernetes Engine `_. Envoy should be configured with the following :ref:`command line options `: -* ``--log-format '%L%m%d %T.%e %t envoy] [%t][%n]%v'``: Logs are formatted in `glog `_ +* ``--log-format '%L%m%d %T.%e %t envoy/%@] [%t][%n]%v'``: Logs are formatted in `glog `_ format, allowing Stackdriver to parse the log severity and timestamp. * ``--log-format-escaped``: Each string that is logged will be printed in a single line. C-style escape sequences (such as ``\n``) will be escaped and prevent a single string diff --git a/docs/root/configuration/operations/overload_manager/overload_manager.rst b/docs/root/configuration/operations/overload_manager/overload_manager.rst index 7dacd28b323b..48f5aa9499da 100644 --- a/docs/root/configuration/operations/overload_manager/overload_manager.rst +++ b/docs/root/configuration/operations/overload_manager/overload_manager.rst @@ -143,6 +143,8 @@ Note in the example that the minimum idle time is specified as an absolute durat would be computed based on the maximum (specified elsewhere). So if ``idle_timeout`` is again 600 seconds, then the minimum timer value would be :math:`10\% \cdot 600s = 60s`. +.. _config_overload_manager_limiting_connections: + Limiting Active Connections --------------------------- @@ -155,6 +157,13 @@ If the value is unspecified, there is no global limit on the number of active do and Envoy will emit a warning indicating this at startup. To disable the warning without setting a limit on the number of active downstream connections, the runtime value may be set to a very large limit (~2e9). +Listeners can opt out of this global connection limit by setting +:ref:`Listener.ignore_global_conn_limit ` +to true. Similarly, you can opt out the admin listener by setting +:ref:`Admin.ignore_global_conn_limit `. +You may want to opt out a listener to be able to probe Envoy or collect stats while it is otherwise at its +connection limit. Note that connections to listeners that opt out are still tracked and count towards the +global limit. If it is desired to only limit the number of downstream connections for a particular listener, per-listener limits can be set via the :ref:`listener configuration `. 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 000000000000..611142166349 --- /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 5dadb3c5f8d4..3dd28c652332 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/configuration/upstream/cluster_manager/cluster_stats.rst b/docs/root/configuration/upstream/cluster_manager/cluster_stats.rst index 3ffb29a946b5..23775aaa8b52 100644 --- a/docs/root/configuration/upstream/cluster_manager/cluster_stats.rst +++ b/docs/root/configuration/upstream/cluster_manager/cluster_stats.rst @@ -245,6 +245,16 @@ If TLS is used by the cluster the following statistics are rooted at *cluster.`, +are rooted at *cluster..tcp_stats.*: + +.. include:: ../../../_include/tcp_stats.rst + .. _config_cluster_manager_cluster_stats_alt_tree: Alternate tree dynamic HTTP statistics diff --git a/docs/root/faq/extensions/contract.rst b/docs/root/faq/extensions/contract.rst index 755e15eb080b..90c40bab6612 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. - 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 6111adfd23ee..22df44b44a74 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 5fc3a5c3e8ec..195419353b63 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 1433fa75d108..11276dcf7d57 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/intro/arch_overview/other_protocols/redis.rst b/docs/root/intro/arch_overview/other_protocols/redis.rst index cfe2a45b77b4..0349a0d8b888 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. diff --git a/docs/root/intro/arch_overview/security/_include/ssl.yaml b/docs/root/intro/arch_overview/security/_include/ssl.yaml index 6f666e4d92a5..00bd6083239b 100644 --- a/docs/root/intro/arch_overview/security/_include/ssl.yaml +++ b/docs/root/intro/arch_overview/security/_include/ssl.yaml @@ -50,7 +50,9 @@ static_resources: private_key: {"filename": "certs/serverkey.pem"} ocsp_staple: {"filename": "certs/server_ocsp_resp.der"} validation_context: - match_subject_alt_names: - - exact: "foo" + match_typed_subject_alt_names: + - san_type: DNS + matcher: + exact: "foo" trusted_ca: filename: /etc/ssl/certs/ca-certificates.crt diff --git a/docs/root/intro/arch_overview/security/ssl.rst b/docs/root/intro/arch_overview/security/ssl.rst index e1192833232c..8878c1faf419 100644 --- a/docs/root/intro/arch_overview/security/ssl.rst +++ b/docs/root/intro/arch_overview/security/ssl.rst @@ -76,7 +76,7 @@ Example configuration */etc/ssl/certs/ca-certificates.crt* is the default path for the system CA bundle on Debian systems. :ref:`trusted_ca ` along with -:ref:`match_subject_alt_names ` +:ref:`match_typed_subject_alt_names ` makes Envoy verify the server identity of *127.0.0.1:1234* as "foo" in the same way as e.g. cURL does on standard Debian installations. Common paths for system CA bundles on Linux and BSD are: diff --git a/docs/root/operations/admin.rst b/docs/root/operations/admin.rst index 6c785184a3cd..0d6d57d750ab 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. diff --git a/docs/root/operations/cli.rst b/docs/root/operations/cli.rst index 11eb0480fff0..2e3acf908827 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/docs/root/start/quick-start/_include/envoy-demo-tls-client-auth.yaml b/docs/root/start/quick-start/_include/envoy-demo-tls-client-auth.yaml index 84367c637f68..dd48870fbda6 100644 --- a/docs/root/start/quick-start/_include/envoy-demo-tls-client-auth.yaml +++ b/docs/root/start/quick-start/_include/envoy-demo-tls-client-auth.yaml @@ -34,8 +34,10 @@ static_resources: validation_context: trusted_ca: filename: certs/cacert.pem - match_subject_alt_names: - - exact: proxy-postgres-frontend.example.com + match_typed_subject_alt_names: + - san_type: DNS + matcher: + exact: proxy-postgres-frontend.example.com tls_certificates: - certificate_chain: filename: certs/servercert.pem diff --git a/docs/root/start/quick-start/_include/envoy-demo-tls-validation.yaml b/docs/root/start/quick-start/_include/envoy-demo-tls-validation.yaml index b9ad6cc0635e..054f79e55f36 100644 --- a/docs/root/start/quick-start/_include/envoy-demo-tls-validation.yaml +++ b/docs/root/start/quick-start/_include/envoy-demo-tls-validation.yaml @@ -48,5 +48,7 @@ static_resources: validation_context: trusted_ca: filename: certs/cacert.pem - match_subject_alt_names: - - exact: proxy-postgres-backend.example.com + match_typed_subject_alt_names: + - san_type: DNS + matcher: + exact: proxy-postgres-backend.example.com diff --git a/docs/root/start/quick-start/securing.rst b/docs/root/start/quick-start/securing.rst index ccfd6bd0ce06..35ff830450a6 100644 --- a/docs/root/start/quick-start/securing.rst +++ b/docs/root/start/quick-start/securing.rst @@ -100,7 +100,7 @@ certificate is valid for. .. note:: If the "Subject Alternative Names" for a certificate are for a wildcard domain, eg ``*.example.com``, - this is what you should use when matching with ``match_subject_alt_names``. + this is what you should use when matching with ``match_typed_subject_alt_names``. .. note:: @@ -122,20 +122,20 @@ and specify a mutually trusted certificate authority: :language: yaml :linenos: :lineno-start: 27 - :lines: 27-39 + :lines: 27-41 :emphasize-lines: 6, 8-10 :caption: :download:`envoy-demo-tls-client-auth.yaml <_include/envoy-demo-tls-client-auth.yaml>` You can further restrict the authentication of connecting clients by specifying the allowed "Subject Alternative Names" in -:ref:`match_subject_alt_names `, +:ref:`match_typed_subject_alt_names `, similar to validating upstream certificates :ref:`described above `. .. literalinclude:: _include/envoy-demo-tls-client-auth.yaml :language: yaml :linenos: :lineno-start: 27 - :lines: 27-39 + :lines: 27-41 :emphasize-lines: 7, 11-12 :caption: :download:`envoy-demo-tls-client-auth.yaml <_include/envoy-demo-tls-client-auth.yaml>` @@ -154,8 +154,8 @@ When connecting to an upstream with client certificates you can set them as foll .. literalinclude:: _include/envoy-demo-tls-client-auth.yaml :language: yaml :linenos: - :lineno-start: 44 - :lines: 44-68 + :lineno-start: 46 + :lines: 46-70 :emphasize-lines: 20-25 :caption: :download:`envoy-demo-tls-client-auth.yaml <_include/envoy-demo-tls-client-auth.yaml>` diff --git a/docs/root/start/sandboxes/index.rst b/docs/root/start/sandboxes/index.rst index ffc5aab0610a..4ba33f443069 100644 --- a/docs/root/start/sandboxes/index.rst +++ b/docs/root/start/sandboxes/index.rst @@ -60,6 +60,7 @@ The following sandboxes are available: jaeger_native_tracing jaeger_tracing load_reporting_service + locality_load_balancing lua mysql postgres diff --git a/docs/root/start/sandboxes/locality_load_balancing.rst b/docs/root/start/sandboxes/locality_load_balancing.rst new file mode 100644 index 000000000000..a49e6cfab016 --- /dev/null +++ b/docs/root/start/sandboxes/locality_load_balancing.rst @@ -0,0 +1,157 @@ +.. _install_sandboxes_locality_load_balancing: + +Locality Weighted Load Balancing +================================ + +.. sidebar:: Requirements + + .. include:: _include/docker-env-setup-link.rst + + :ref:`curl ` + Used to make ``HTTP`` requests. + +This example demonstrates the :ref:`locality weighted load balancing ` feature in Envoy proxy. The demo simulates a scenario that a backend service resides in two local zones and one remote zone. + +The components used in this demo are as follows: + +- A client container: runs Envoy proxy +- Backend container in the same locality as the client, with priority set to 0, referred to as ``local-1``. +- Backend container in the same locality as the client, with priority set to 1, referred to as ``local-2``. +- Backend container in the the remote locality, with priority set to 1, referred to as ``remote-1``. +- Backend container in the the remote locality, with priority set to 2, referred to as ``remote-2``. + +The client Envoy proxy configures the 4 backend containers in the same Envoy cluster, so that Envoy handles load balancing to those backend servers. From here we can see, we have localities with 3 different priorities: + +- priority 0: ``local-1`` +- priority 1: ``local-2`` and ``remote-1`` +- priority 2: ``remote-2`` + +In Envoy, when the healthiness of a given locality drops below a threshold (71% by default), the next priority locality will start to share the request loads. The demo below will show this behavior. + +Step 1: Start all of our containers +*********************************** + +In terminal, move to the ``examples/locality_load_balancing`` directory. + +To build this sandbox example and start the example services, run the following commands: + +.. code-block:: console + + # Start demo + $ docker-compose up --build -d + +The locality configuration is set in the client container via static Envoy configuration file. Please refer to the ``cluster`` section of the :download:`proxy configuration <_include/locality-load-balancing/envoy-proxy.yaml>` file. + +Step 2: Scenario with one replica in the highest priority locality +****************************************************************** + +In this scenario, each locality has 1 healthy replica running and all the requests should be sent to the locality with the highest priority (i.e. lowest integer set for priority - ``0``), which is ``local-1``. + +.. code-block:: console + + # all requests to local-1 + $ docker-compose exec -T client-envoy python3 client.py http://localhost:3000/ 100 + Hello from backend-local-1!: 100, 100.0% + Failed: 0 + +If locality ``local-1`` becomes unhealthy (i.e. fails the Envoy health check), the requests should be load balanced among the subsequent priority localities, which are ``local-2`` and ``remote-1``. They both have priority 1. We then send 100 requests to the backend cluster, and check the responders. + +.. code-block:: console + + # bring down local-1 + $ docker-compose exec -T client-envoy curl -s locality-load-balancing_backend-local-1_1:8000/unhealthy + [backend-local-1] Set to unhealthy + + # local-2 and remote-1 localities split the traffic 50:50 + $ docker-compose exec -T client-envoy python3 client.py http://localhost:3000/ 100 + Hello from backend-remote-1!: 51, 51.0% + Hello from backend-local-2!: 49, 49.0% + Failed: 0 + +Now if ``local-2`` becomes unhealthy also, priority 1 locality is only 50% healthy. Thus priority 2 locality starts to share the request load. Requests will be sent to both ``remote-1`` and ``remote-2``. + +.. code-block:: console + + # bring down local-2 + $ docker-compose exec -T client-envoy curl -s locality-load-balancing_backend-local-2_1:8000/unhealthy + + # remote-1 locality receive 100% of the traffic + $ docker-compose exec -T client-envoy python3 client.py http://localhost:3000/ 100 + Hello from backend-remote-1!: actual weight 69.0% + Hello from backend-remote-2!: actual weight 31.0% + Failed: 0 + + +Step 3: Recover servers +*********************** + +Before moving on, we need to server local-1 and local-2 first. + +.. code-block:: console + + # recover local-1 and local-2 after the demo + $ docker-compose exec -T client-envoy curl -s locality-load-balancing_backend-local-1_1:8000/healthy + $ docker-compose exec -T client-envoy curl -s locality-load-balancing_backend-local-2_1:8000/healthy + + +Step 4: Scenario with multiple replicas in the highest priority locality +************************************************************************ + +To demonstrate how locality based load balancing works in multiple replicas setup, let's now scale up the ``local-1`` locality to 5 replicas. + +.. code-block:: console + + $ docker-compose up --scale backend-local-1=5 -d + +We are going to show the scenario that ``local-1`` is just partially healthy. So let's bring down 4 of the replicas in ``local-1``. + +.. code-block:: console + + # bring down local-1 replicas + $ docker-compose exec -T client-envoy curl -s locality-load-balancing_backend-local-1_2:8000/unhealthy + $ docker-compose exec -T client-envoy curl -s locality-load-balancing_backend-local-1_3:8000/unhealthy + $ docker-compose exec -T client-envoy curl -s locality-load-balancing_backend-local-1_4:8000/unhealthy + $ docker-compose exec -T client-envoy curl -s locality-load-balancing_backend-local-1_5:8000/unhealthy + +Then we check the endpoints again: + +.. code-block:: console + + # check healthiness + $ docker-compose exec -T client-envoy curl -s localhost:8001/clusters | grep health_flags + + backend::172.28.0.4:8000::health_flags::/failed_active_hc + backend::172.28.0.2:8000::health_flags::/failed_active_hc + backend::172.28.0.5:8000::health_flags::/failed_active_hc + backend::172.28.0.6:8000::health_flags::/failed_active_hc + backend::172.28.0.7:8000::health_flags::healthy + backend::172.28.0.8:8000::health_flags::healthy + backend::172.28.0.3:8000::health_flags::healthy + +We can confirm that 4 backend endpoints become unhealthy. + +Now we send the 100 requests again. + +.. code-block:: console + + # watch traffic change + $ docker-compose exec -T client-envoy python3 client.py http://localhost:3000/ 100 + + Hello from backend-remote-1!: actual weight 37.0% + Hello from backend-local-2!: actual weight 36.0% + Hello from backend-local-1!: actual weight 27.0% + Failed: 0 + +As ``local-1`` does not have enough healthy workloads, requests are partially shared by secondary localities. + +If we bring down all the servers in priority 1 locality, it will make priority 1 locality 0% healthy. The traffic should split between priority 0 and priority 2 localities. + +.. code-block:: console + + $ docker-compose exec -T client-envoy curl -s locality-load-balancing_backend-local-2_1:8000/unhealthy + $ docker-compose exec -T client-envoy curl -s locality-load-balancing_backend-remote-1_1:8000/unhealthy + $ docker-compose exec -T client-envoy python3 client.py http://localhost:3000/ 100 + + Hello from backend-remote-2!: actual weight 77.0% + Hello from backend-local-1!: actual weight 23.0% + Failed: 0 diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index c13492aaca47..30c930490f58 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -11,15 +11,26 @@ Minor Behavior Changes ---------------------- *Changes that may cause incompatibilities for some users, but should not for most* -* 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. +* 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*. * 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. +* http: when envoy run out of ``max_requests_per_connection``, it will send an HTTP/2 "shutdown nofitication" (GOAWAY frame with max stream ID) and go to a default grace period of 5000 milliseconds (5 seconds) if ``drain_timeout`` is not specified. During this grace period, envoy will continue to accept new streams. After the grace period, a final GOAWAY is sent and envoy will start refusing new streams. However before bugfix, during the grace period, every time a new stream is received, old envoy will always send a "shutdown notification" and restart drain again which actually causes the grace period to be extended and is no longer equal to ``drain_timeout``. +* 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. +* router: take elapsed time into account when setting the x-envoy-expected-rq-timeout-ms header for retries, and never send a value that's longer than the request timeout. This behavioral change can be temporarily reverted by setting runtime guard ``envoy.reloadable_features.update_expected_rq_timeout_on_retry`` to false. Bug Fixes --------- *Changes expected to improve the state of the world and are unlikely to have negative effects* -* listener: fixed the crash when updating listeners that do not bind to port. +* ext_authz: fix the ext_authz http filter to correctly set response flags to ``UAEX`` when a connection is denied. +* ext_authz: fix the ext_authz network filter to correctly set response flag and code details to ``UAEX`` when a connection is denied. +* hcm: stop processing the response if encoding it has caused downstream reset. The fix is guarded by ``envoy.reloadable_features.handle_stream_reset_during_hcm_encoding``. +* listener: fixed issue where more than one listener could listen on the same port if using reuse port, thus randomly accepting connections on different listeners. This configuration is now rejected. +* thrift_proxy: do not close downstream connections when an upstream connection overflow happens. * thrift_proxy: fix the thrift_proxy connection manager to correctly report success/error response metrics when performing :ref:`payload passthrough `. Removed Config or Runtime @@ -27,36 +38,68 @@ Removed Config or Runtime *Normally occurs at the end of the* :ref:`deprecation period ` * 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. * http: removed ``envoy.reloadable_features.dont_add_content_length_for_bodiless_requests deprecation`` and legacy code paths. +* http: removed ``envoy.reloadable_features.grpc_json_transcoder_adhere_to_buffer_limits`` and legacy code paths. * http: removed ``envoy.reloadable_features.http2_skip_encoding_empty_trailers`` and legacy code paths. Envoy will always encode empty trailers by sending empty data with ``end_stream`` true (instead of sending empty trailers) for HTTP/2. * http: removed ``envoy.reloadable_features.improved_stream_limit_handling`` and legacy code paths. * http: removed ``envoy.reloadable_features.remove_forked_chromium_url`` and legacy code paths. * 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 ------------ +* access log: added :ref:`grpc_stream_retry_policy ` to the gRPC logger to reconnect when a connection fails to be established. +* access_log: added :ref:`METADATA` token to handle all types of metadata (DYNAMIC, CLUSTER, ROUTE). +* access_log: added a CEL extension filter to enable filtering of access logs based on Envoy attribute expressions. +* access_log: added new access_log command operator ``%UPSTREAM_REQUEST_ATTEMPT_COUNT%`` to retrieve the number of times given request got attempted upstream. +* access_log: added new access_log command operator ``%VIRTUAL_CLUSTER_NAME%`` to retrieve the matched Virtual Cluster name. * 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 `. +* dns: added :ref:`ALL ` option to return both IPv4 and IPv6 addresses. * dns_cache: added :ref:`typed_dns_resolver_config ` in the dns_cache to support DNS resolver as an extension. * dns_filter: added :ref:`typed_dns_resolver_config ` in the dns_filter to support DNS resolver as an extension. * 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. +* grpc_json_transcoder: added support for matching unregistered custom verb :ref:`match_unregistered_custom_verb `. +* http: added support for %REQUESTED_SERVER_NAME% to extract SNI as a custom header. +* http: added support for %VIRTUAL_CLUSTER_NAME% to extract the matched Virtual Cluster name 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. +* http: added support for :ref:`forwarding HTTP1 reason phrase `. +* 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). +* listener: added support for opting out listeners from the globally set downstream connection limit via :ref:`ignore_global_conn_limit `. * 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. +* perf: added support for [Perfetto](https://perfetto.dev) performance tracing. +* router: added support for the :ref:`config_http_conn_man_headers_x-forwarded-host` header. +* 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. +* thrift_proxy: support header flags. * thrift_proxy: support subset lb when using request or route metadata. +* tls: added support for :ref:`match_typed_subject_alt_names ` for subject alternative names to enforce specifying the subject alternative name type for the matcher to prevent matching against an unintended type in the certificate. +* 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. +* udp_proxy: added :ref:`use_per_packet_load_balancing ` option to enable per packet load balancing (selection of upstream host on each data chunk). * upstream: added the ability to :ref:`configure max connection duration ` for upstream clusters. * vcl_socket_interface: added VCL socket interface extension for fd.io VPP integration to :ref:`contrib images `. This can be enabled via :ref:`VCL ` configuration. * xds: re-introduced unified delta and sotw xDS multiplexers that share most of the implementation. Added a new runtime config ``envoy.reloadable_features.unified_mux`` (disabled by default) that when enabled, switches xDS to use unified multiplexers. @@ -66,3 +109,5 @@ Deprecated * bootstrap: :ref:`dns_resolution_config ` is deprecated in favor of :ref:`typed_dns_resolver_config `. * cluster: :ref:`dns_resolution_config ` is deprecated in favor of :ref:`typed_dns_resolver_config `. * dns_cache: :ref:`dns_resolution_config ` is deprecated in favor of :ref:`typed_dns_resolver_config `. +* tls: :ref:`match_subject_alt_names ` has been deprecated in favor of the :ref:`match_typed_subject_alt_names `. +* dns_filter: :ref:`dns_resolution_config ` is deprecated in favor of :ref:`typed_dns_resolver_config `. diff --git a/docs/root/version_history/v1.20.1.rst b/docs/root/version_history/v1.20.1.rst new file mode 100644 index 000000000000..5ee3ba7bc0c2 --- /dev/null +++ b/docs/root/version_history/v1.20.1.rst @@ -0,0 +1,32 @@ +1.20.1 (November 30, 2021) +========================== + +Incompatible Behavior Changes +----------------------------- +*Changes that are expected to cause an incompatibility if applicable; deployment changes are likely required* + +Minor Behavior Changes +---------------------- +*Changes that may cause incompatibilities for some users, but should not for most* + +* 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. + +Bug Fixes +--------- +*Changes expected to improve the state of the world and are unlikely to have negative effects* + +* http: remove redundant Warn log in HTTP codec. +* listener: fix a crash when updating any listener that does not bind to port. +* listener: listener add can reuse the listener socket of a draining filter chain listener and fix the request lost. +* mac: fix crash on startup on macOS 12 by changing the default allocator. +* tcp: fixed a bug where upstream circuit breakers applied HTTP per-request bounds to TCP connections. + +Removed Config or Runtime +------------------------- +*Normally occurs at the end of the* :ref:`deprecation period ` + +New Features +------------ + +Deprecated +---------- diff --git a/docs/root/version_history/v1.8.0.rst b/docs/root/version_history/v1.8.0.rst index d6cf45d5703d..b14ca658bdcb 100644 --- a/docs/root/version_history/v1.8.0.rst +++ b/docs/root/version_history/v1.8.0.rst @@ -73,7 +73,7 @@ Changes upstream cluster specified by the SNI value presented by the client during a TLS handshake. * proxy_protocol: added support for HAProxy Proxy Protocol v2 (AF_INET/AF_INET6 only). * ratelimit: added support for :repo:`api/envoy/service/ratelimit/v2/rls.proto`. - Lyft's reference implementation of the `ratelimit `_ service also supports the data-plane-api proto as of v1.1.0. + Lyft's reference implementation of the `ratelimit `_ service also supports the data-plane-api proto as of v1.1.0. Envoy can use either proto to send client requests to a ratelimit server with the use of the ``use_data_plane_proto`` boolean flag in the ratelimit configuration. Support for the legacy proto ``source/common/ratelimit/ratelimit.proto`` is deprecated and will be removed at the start of the 1.9.0 release cycle. diff --git a/docs/root/version_history/version_history.rst b/docs/root/version_history/version_history.rst index 7cca61970b5b..006173c8ade0 100644 --- a/docs/root/version_history/version_history.rst +++ b/docs/root/version_history/version_history.rst @@ -7,6 +7,7 @@ Version history :titlesonly: current + v1.20.1 v1.20.0 v1.19.1 v1.19.0 diff --git a/envoy/api/BUILD b/envoy/api/BUILD index cbdc13440690..3ac0872aac61 100644 --- a/envoy/api/BUILD +++ b/envoy/api/BUILD @@ -36,4 +36,8 @@ envoy_cc_library( "os_sys_calls_hot_restart.h", "os_sys_calls_linux.h", ], + external_deps = ["abseil_optional"], + deps = [ + "//envoy/network:address_interface", + ], ) diff --git a/envoy/api/os_sys_calls.h b/envoy/api/os_sys_calls.h index 0c0071742c6b..3c9b6f9698ac 100644 --- a/envoy/api/os_sys_calls.h +++ b/envoy/api/os_sys_calls.h @@ -5,10 +5,14 @@ #include #include #include +#include #include "envoy/api/os_sys_calls_common.h" #include "envoy/common/platform.h" #include "envoy/common/pure.h" +#include "envoy/network/address.h" + +#include "absl/types/optional.h" namespace Envoy { namespace Api { @@ -17,6 +21,23 @@ struct EnvoyTcpInfo { std::chrono::microseconds tcpi_rtt; }; +// Small struct to avoid exposing ifaddrs -- which is not defined in all platforms -- to the +// codebase. +struct InterfaceAddress { + InterfaceAddress(absl::string_view interface_name, unsigned int interface_flags, + Envoy::Network::Address::InstanceConstSharedPtr interface_addr) + : interface_name_(interface_name), interface_flags_(interface_flags), + interface_addr_(interface_addr) {} + + std::string interface_name_; + unsigned int interface_flags_; + Envoy::Network::Address::InstanceConstSharedPtr interface_addr_; +}; + +using InterfaceAddressVector = std::vector; + +using AlternateGetifaddrs = std::function; + class OsSysCalls { public: virtual ~OsSysCalls() = default; @@ -87,6 +108,11 @@ class OsSysCalls { */ virtual bool supportsIpTransparent() const PURE; + /** + * return true if the OS supports multi-path TCP + */ + virtual bool supportsMptcp() const PURE; + /** * Release all resources allocated for fd. * @return zero on success, -1 returned otherwise. @@ -190,6 +216,29 @@ class OsSysCalls { * @see man TCP_INFO. Get the tcp info for the socket. */ virtual SysCallBoolResult socketTcpInfo(os_fd_t sockfd, EnvoyTcpInfo* tcp_info) PURE; + + /** + * return true if the OS supports getifaddrs. + */ + virtual bool supportsGetifaddrs() const PURE; + + /** + * @see man getifaddrs + */ + virtual SysCallIntResult getifaddrs(InterfaceAddressVector& interfaces) PURE; + + /** + * allows a platform to override getifaddrs or provide an implementation if one does not exist + * natively. + * + * @arg alternate_getifaddrs function pointer to implementation. + */ + virtual void setAlternateGetifaddrs(AlternateGetifaddrs alternate_getifaddrs) { + alternate_getifaddrs_ = alternate_getifaddrs; + } + +protected: + absl::optional alternate_getifaddrs_{}; }; using OsSysCallsPtr = std::unique_ptr; diff --git a/envoy/common/BUILD b/envoy/common/BUILD index dd12783097de..58aa7f91d6df 100644 --- a/envoy/common/BUILD +++ b/envoy/common/BUILD @@ -99,3 +99,8 @@ envoy_cc_library( name = "scope_tracker_interface", hdrs = ["scope_tracker.h"], ) + +envoy_cc_library( + name = "hashable_interface", + hdrs = ["hashable.h"], +) diff --git a/envoy/common/hashable.h b/envoy/common/hashable.h new file mode 100644 index 000000000000..af357911748e --- /dev/null +++ b/envoy/common/hashable.h @@ -0,0 +1,25 @@ +#pragma once + +#include "envoy/common/pure.h" + +#include "absl/types/optional.h" + +namespace Envoy { + +/** + * Interface for hashable types used in heterogeneous contexts (see, for example, usage in + * FilterStateHashMethod). + */ +class Hashable { +public: + virtual ~Hashable() = default; + + /** + * Request the 64-bit hash for this object. + * @return absl::optional the hash value, or absl::nullopt if a hash could not be + * produced for this instance. + */ + virtual absl::optional hash() const PURE; +}; + +} // namespace Envoy diff --git a/envoy/common/platform.h b/envoy/common/platform.h index 570713dca67a..a877e91083d1 100644 --- a/envoy/common/platform.h +++ b/envoy/common/platform.h @@ -178,7 +178,9 @@ constexpr bool win32SupportsOriginalDestination() { #include #include #include +#if !defined(DO_NOT_INCLUDE_NETINET_TCP_H) #include +#endif #include // for UDP_GRO #include #include // for mode_t @@ -203,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 @@ -230,6 +234,10 @@ constexpr bool win32SupportsOriginalDestination() { #define UDP_SEGMENT 103 #endif +#ifndef IPPROTO_MPTCP +#define IPPROTO_MPTCP 262 +#endif + typedef int os_fd_t; // NOLINT(modernize-use-using) typedef int filesystem_os_id_t; // NOLINT(modernize-use-using) typedef int signal_t; // NOLINT(modernize-use-using) @@ -276,7 +284,7 @@ constexpr absl::string_view null_device_path{"/dev/null"}; // Note: chromium disabled recvmmsg regardless of ndk version. However, the only Android target // currently actively using Envoy is Envoy Mobile, where recvmmsg is not actively disabled. In fact, // defining mmsghdr here caused a conflicting definition with the ndk's definition of the struct -// (https://github.com/lyft/envoy-mobile/pull/772/checks?check_run_id=534152886#step:4:64). +// (https://github.com/envoyproxy/envoy-mobile/pull/772/checks?check_run_id=534152886#step:4:64). // Therefore, we decided to remove the Android check introduced here in // https://github.com/envoyproxy/envoy/pull/10120. If someone out there encounters problems with // this please bring up in Envoy's slack channel #envoy-udp-quic-dev. @@ -293,18 +301,6 @@ struct mmsghdr { }; #endif -#define SUPPORTS_GETIFADDRS -#ifdef WIN32 -#undef SUPPORTS_GETIFADDRS -#endif - -// https://android.googlesource.com/platform/prebuilts/ndk/+/dev/platform/sysroot/usr/include/ifaddrs.h -#ifdef __ANDROID_API__ -#if __ANDROID_API__ < 24 -#undef SUPPORTS_GETIFADDRS -#endif // __ANDROID_API__ < 24 -#endif // ifdef __ANDROID_API__ - // TODO: Remove once bazel supports NDKs > 21 #define SUPPORTS_CPP_17_CONTIGUOUS_ITERATOR #ifdef __ANDROID_API__ diff --git a/envoy/config/extension_config_provider.h b/envoy/config/extension_config_provider.h index ce84225eb623..ba8761f045fa 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; @@ -35,17 +35,20 @@ template class ExtensionConfigProvider { virtual absl::optional config() PURE; }; -template class DynamicExtensionConfigProviderBase { +class DynamicExtensionConfigProviderBase { public: virtual ~DynamicExtensionConfigProviderBase() = default; /** - * Update the provider with a new configuration. - * @param config is an extension factory callback to replace the existing configuration. + * Update the provider with a new configuration. This interface accepts proto rather than a + * factory callback so that it can be generic over factory types. If instantiating the factory + * throws, it should only do so on the main thread, before any changes are applied to workers. + * @param config is the new configuration. It is expected that the configuration has already been + * validated. * @param version_info is the version of the new extension configuration. * @param cb the continuation callback for a completed configuration application on all threads. */ - virtual void onConfigUpdate(FactoryCallback config, const std::string& version_info, + virtual void onConfigUpdate(const Protobuf::Message& config, const std::string& version_info, ConfigAppliedCb applied_on_all_threads) PURE; /** @@ -60,9 +63,9 @@ template class DynamicExtensionConfigProviderBase { virtual void applyDefaultConfiguration() PURE; }; -template -class DynamicExtensionConfigProvider : public DynamicExtensionConfigProviderBase, - public ExtensionConfigProvider {}; +template +class DynamicExtensionConfigProvider : public DynamicExtensionConfigProviderBase, + public ExtensionConfigProvider {}; } // namespace Config } // namespace Envoy diff --git a/envoy/event/dispatcher.h b/envoy/event/dispatcher.h index 94cb73e5c4fb..86fd2855a151 100644 --- a/envoy/event/dispatcher.h +++ b/envoy/event/dispatcher.h @@ -228,11 +228,13 @@ class Dispatcher : public DispatcherBase, public ScopeTracker { * @param socket supplies the socket to listen on. * @param cb supplies the callbacks to invoke for listener events. * @param bind_to_port controls whether the listener binds to a transport port or not. + * @param ignore_global_conn_limit controls whether the listener is limited by the global + * connection limit. * @return Network::ListenerPtr a new listener that is owned by the caller. */ virtual Network::ListenerPtr createListener(Network::SocketSharedPtr&& socket, - Network::TcpListenerCallbacks& cb, - bool bind_to_port) PURE; + Network::TcpListenerCallbacks& cb, bool bind_to_port, + bool ignore_global_conn_limit) PURE; /** * Creates a logical udp listener on a specific port. diff --git a/envoy/filter/BUILD b/envoy/filter/BUILD index 02e267213684..9a6697e0ee63 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 824c36e73dea..cd1affc57997 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/envoy/http/codec.h b/envoy/http/codec.h index a1d5fc829ae0..7445fb20eec5 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 100-Continue, 102-Processing, and 103-Early-Data 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 100-Continue, 102-Processing, and 103-Early-Data 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. @@ -394,6 +396,14 @@ class ConnectionCallbacks { * @param ReceivedSettings the settings received from the peer. */ virtual void onSettings(ReceivedSettings& settings) { UNREFERENCED_PARAMETER(settings); } + + /** + * Fires when the MAX_STREAMS frame is received from the peer. + * This is an HTTP/3 frame, indicating the new maximum stream ID which can be opened. + * This may occur multiple times across the lifetime of an HTTP/3 connection. + * @param num_streams the number of streams now allowed to be opened. + */ + virtual void onMaxStreamsChanged(uint32_t num_streams) { UNREFERENCED_PARAMETER(num_streams); } }; /** diff --git a/envoy/http/filter.h b/envoy/http/filter.h index 9a6527ca6519..c8fadf77f983 100644 --- a/envoy/http/filter.h +++ b/envoy/http/filter.h @@ -448,22 +448,25 @@ 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; + virtual ResponseHeaderMapOptRef informationalHeaders() const PURE; /** * Called with headers to be encoded, optionally indicating end of stream. @@ -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/envoy/http/hash_policy.h b/envoy/http/hash_policy.h index 2a031247d93d..de04a8eecfa2 100644 --- a/envoy/http/hash_policy.h +++ b/envoy/http/hash_policy.h @@ -9,12 +9,6 @@ namespace Envoy { namespace Http { -class Hashable { -public: - virtual absl::optional hash() const PURE; - virtual ~Hashable() = default; -}; - /** * Request hash policy. I.e., if using a hashing load balancer, how a request should be hashed onto * an upstream host. diff --git a/envoy/http/header_formatter.h b/envoy/http/header_formatter.h index 8615cd7a7a5d..6a7c244e5e0a 100644 --- a/envoy/http/header_formatter.h +++ b/envoy/http/header_formatter.h @@ -33,6 +33,16 @@ class StatefulHeaderKeyFormatter : public HeaderKeyFormatter { * Called for each header key received by the codec. */ virtual void processKey(absl::string_view key) PURE; + + /** + * Called to save received reason phrase + */ + virtual void setReasonPhrase(absl::string_view reason_phrase) PURE; + + /** + * Called to get saved reason phrase + */ + virtual absl::string_view getReasonPhrase() const PURE; }; using StatefulHeaderKeyFormatterPtr = std::unique_ptr; diff --git a/envoy/http/header_map.h b/envoy/http/header_map.h index 3fb5506c84f0..dd004fbd799a 100644 --- a/envoy/http/header_map.h +++ b/envoy/http/header_map.h @@ -304,6 +304,7 @@ class HeaderEntry { HEADER_FUNC(Expect) \ HEADER_FUNC(ForwardedClientCert) \ HEADER_FUNC(ForwardedFor) \ + HEADER_FUNC(ForwardedHost) \ HEADER_FUNC(ForwardedProto) \ HEADER_FUNC(GrpcTimeout) \ HEADER_FUNC(Host) \ diff --git a/envoy/network/BUILD b/envoy/network/BUILD index 3caab27a2aaa..77c31f791aa8 100644 --- a/envoy/network/BUILD +++ b/envoy/network/BUILD @@ -11,9 +11,6 @@ envoy_package() envoy_cc_library( name = "address_interface", hdrs = ["address.h"], - deps = [ - "//envoy/api:os_sys_calls_interface", - ], ) envoy_cc_library( @@ -63,6 +60,15 @@ envoy_cc_library( deps = ["//envoy/network:address_interface"], ) +envoy_cc_library( + name = "dns_resolver_interface", + hdrs = ["dns_resolver.h"], + deps = [ + "//envoy/api:api_interface", + "//source/common/config:utility_lib", + ], +) + envoy_cc_library( name = "drain_decision_interface", hdrs = ["drain_decision.h"], @@ -93,7 +99,7 @@ envoy_cc_library( hdrs = ["hash_policy.h"], external_deps = ["abseil_optional"], deps = [ - ":address_interface", + ":connection_interface", ], ) @@ -177,6 +183,7 @@ envoy_cc_library( ":udp_packet_writer_handler_interface", "//envoy/access_log:access_log_interface", "//envoy/common:resource_interface", + "//envoy/config:typed_metadata_interface", "//envoy/init:manager_interface", "//envoy/stats:stats_interface", "//source/common/common:interval_value", diff --git a/envoy/network/address.h b/envoy/network/address.h index bd28205bd630..2b50b0728168 100644 --- a/envoy/network/address.h +++ b/envoy/network/address.h @@ -7,7 +7,6 @@ #include #include -#include "envoy/api/os_sys_calls.h" #include "envoy/common/platform.h" #include "envoy/common/pure.h" diff --git a/envoy/network/dns.h b/envoy/network/dns.h index 38dd39bf4576..226d39d238c0 100644 --- a/envoy/network/dns.h +++ b/envoy/network/dns.h @@ -9,6 +9,8 @@ #include "envoy/common/pure.h" #include "envoy/network/address.h" +#include "absl/types/variant.h" + namespace Envoy { namespace Network { @@ -36,17 +38,41 @@ class ActiveDnsQuery { }; /** - * DNS response. + * DNS A/AAAA record response. */ -struct DnsResponse { - DnsResponse(const Address::InstanceConstSharedPtr& address, const std::chrono::seconds ttl) - : address_(address), ttl_(ttl) {} - +struct AddrInfoResponse { const Address::InstanceConstSharedPtr address_; const std::chrono::seconds ttl_; }; -enum class DnsLookupFamily { V4Only, V6Only, Auto, V4Preferred }; +/** + * DNS SRV record response. + */ +struct SrvResponse { + const std::string host_; + const uint16_t port_; + const uint16_t priority_; + const uint16_t weight_; +}; + +enum class RecordType { A, AAAA, SRV }; + +enum class DnsLookupFamily { V4Only, V6Only, Auto, V4Preferred, All }; + +class DnsResponse { +public: + DnsResponse(const Address::InstanceConstSharedPtr& address, const std::chrono::seconds ttl) + : response_(AddrInfoResponse{address, ttl}) {} + DnsResponse(const std::string& host, uint16_t port, uint16_t priority, uint16_t weight) + : response_(SrvResponse{host, port, priority, weight}) {} + + const AddrInfoResponse& addrInfo() const { return absl::get(response_); } + + const SrvResponse& srv() const { return absl::get(response_); } + +private: + absl::variant response_; +}; /** * An asynchronous DNS resolver. diff --git a/envoy/network/dns_resolver.h b/envoy/network/dns_resolver.h new file mode 100644 index 000000000000..2a326f152fe8 --- /dev/null +++ b/envoy/network/dns_resolver.h @@ -0,0 +1,32 @@ +#pragma once + +#include "envoy/api/api.h" +#include "envoy/event/dispatcher.h" +#include "envoy/network/dns.h" + +#include "source/common/config/utility.h" + +namespace Envoy { +namespace Network { + +constexpr absl::string_view CaresDnsResolver = "envoy.network.dns_resolver.cares"; +constexpr absl::string_view AppleDnsResolver = "envoy.network.dns_resolver.apple"; +constexpr absl::string_view DnsResolverCategory = "envoy.network.dns_resolver"; + +class DnsResolverFactory : public Config::TypedFactory { +public: + /* + * @returns a DnsResolver object. + * @param dispatcher: the local dispatcher thread + * @param api: API interface to interact with system resources + * @param typed_dns_resolver_config: the typed DNS resolver config + */ + virtual DnsResolverSharedPtr createDnsResolver( + Event::Dispatcher& dispatcher, Api::Api& api, + const envoy::config::core::v3::TypedExtensionConfig& typed_dns_resolver_config) const PURE; + + std::string category() const override { return std::string(DnsResolverCategory); } +}; + +} // namespace Network +} // namespace Envoy diff --git a/envoy/network/hash_policy.h b/envoy/network/hash_policy.h index 49a38da8c14d..c7d62591c113 100644 --- a/envoy/network/hash_policy.h +++ b/envoy/network/hash_policy.h @@ -1,6 +1,6 @@ #pragma once -#include "envoy/network/address.h" +#include "envoy/network/connection.h" #include "absl/types/optional.h" @@ -14,14 +14,14 @@ class HashPolicy { virtual ~HashPolicy() = default; /** - * @param downstream_address is the address of the connected client. - * @param upstream_address is the address of the connected server. + * @param connection is the raw downstream connection. Different implementations of HashPolicy can + * compute hashes based on different data accessible from the connection (e.g. IP address, + * filter state, etc.). * @return absl::optional an optional hash value to route on. A hash value might not be - * returned if for example the downstream address is nullptr. + * returned if the hash policy implementation doesn't find the expected data in the connection + * (e.g. IP address is null, filter state is not populated, etc.). */ - virtual absl::optional - generateHash(const Network::Address::Instance* downstream_address, - const Network::Address::Instance* upstream_address) const PURE; + virtual absl::optional generateHash(const Network::Connection& connection) const PURE; }; } // namespace Network } // namespace Envoy diff --git a/envoy/network/io_handle.h b/envoy/network/io_handle.h index a960887a56a1..12a45cf0c0d3 100644 --- a/envoy/network/io_handle.h +++ b/envoy/network/io_handle.h @@ -4,6 +4,7 @@ #include #include "envoy/api/io_error.h" +#include "envoy/api/os_sys_calls_common.h" #include "envoy/common/platform.h" #include "envoy/common/pure.h" #include "envoy/event/file_event.h" diff --git a/envoy/network/listen_socket.h b/envoy/network/listen_socket.h index b7b89c050e63..a900ad7ee5e1 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/listener.h b/envoy/network/listener.h index ad2b69a6c750..fac995eb3ed6 100644 --- a/envoy/network/listener.h +++ b/envoy/network/listener.h @@ -10,6 +10,7 @@ #include "envoy/common/resource.h" #include "envoy/config/core/v3/base.pb.h" #include "envoy/config/listener/v3/udp_listener_config.pb.h" +#include "envoy/config/typed_metadata.h" #include "envoy/init/manager.h" #include "envoy/network/connection.h" #include "envoy/network/connection_balancer.h" @@ -106,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. */ @@ -184,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. */ @@ -214,6 +230,12 @@ class ListenerConfig { * @return init manager of the listener. */ virtual Init::Manager& initManager() PURE; + + /** + * @return bool whether the listener should avoid blocking connections based on the globally set + * limit. + */ + virtual bool ignoreGlobalConnLimit() const PURE; }; /** @@ -427,6 +449,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. */ @@ -454,5 +510,10 @@ class UdpListenerWorkerRouter { using UdpListenerWorkerRouterPtr = std::unique_ptr; +/** + * Base class for all listener typed metadata factories. + */ +class ListenerTypedMetadataFactory : public Envoy::Config::TypedMetadataFactory {}; + } // namespace Network } // namespace Envoy diff --git a/envoy/network/socket.h b/envoy/network/socket.h index 225b76297a06..4f3e84452c69 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)) {} @@ -97,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 { @@ -141,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; @@ -299,8 +310,18 @@ class Socket { virtual absl::optional
getOptionDetails(const Socket& socket, envoy::config::core::v3::SocketOption::SocketState state) const PURE; + + /** + * Whether the socket implementation is supported. Real implementations should typically return + * true. Placeholder implementations may indicate such by returning false. Note this does NOT + * inherently prevent an option from being applied if it's passed to socket/connection + * interfaces. + * @return Whether this is a supported socket option. + */ + virtual bool isSupported() const PURE; }; + using OptionConstPtr = std::unique_ptr; using OptionConstSharedPtr = std::shared_ptr; using Options = std::vector; using OptionsSharedPtr = std::shared_ptr; diff --git a/envoy/network/socket_interface.h b/envoy/network/socket_interface.h index 1bc922fa3303..b4878fc0c5fd 100644 --- a/envoy/network/socket_interface.h +++ b/envoy/network/socket_interface.h @@ -6,6 +6,20 @@ namespace Envoy { namespace Network { + +/** + * Options for creating a socket. + */ +struct SocketCreationOptions { + // Specifies whether MPTCP should be enabled on the socket. This is only valid for Stream sockets, + // and only valid on Linux. + bool mptcp_enabled_{false}; + + bool operator==(const SocketCreationOptions& rhs) const { + return mptcp_enabled_ == rhs.mptcp_enabled_; + } +}; + class SocketInterface { public: virtual ~SocketInterface() = default; @@ -17,20 +31,22 @@ class SocketInterface { * @param addr_type type of address used with the socket * @param version IP version if address type is IP * @param socket_v6only if the socket is ipv6 version only + * @param options additional options for how to create the socket * @return @ref Network::IoHandlePtr that wraps the underlying socket file descriptor */ virtual IoHandlePtr socket(Socket::Type type, Address::Type addr_type, Address::IpVersion version, - bool socket_v6only) const PURE; + bool socket_v6only, const SocketCreationOptions& options) const PURE; /** * Low level api to create a socket in the underlying host stack. Does not create an * @ref Network::SocketImpl * @param socket_type type of socket requested * @param addr address that is gleaned for address type and version if needed + * @param options additional options for how to create the socket * @return @ref Network::IoHandlePtr that wraps the underlying socket file descriptor */ - virtual IoHandlePtr socket(Socket::Type socket_type, - const Address::InstanceConstSharedPtr addr) const PURE; + virtual IoHandlePtr socket(Socket::Type socket_type, const Address::InstanceConstSharedPtr addr, + const SocketCreationOptions& options) const PURE; /** * Returns true if the given family is supported on this machine. @@ -48,8 +64,9 @@ using SocketInterfacePtr = std::unique_ptr; * @return @ref Network::IoHandlePtr that wraps the underlying socket file descriptor */ static inline IoHandlePtr ioHandleForAddr(Socket::Type type, - const Address::InstanceConstSharedPtr addr) { - return addr->socketInterface().socket(type, addr); + const Address::InstanceConstSharedPtr addr, + const SocketCreationOptions& options) { + return addr->socketInterface().socket(type, addr, options); } } // namespace Network diff --git a/envoy/router/BUILD b/envoy/router/BUILD index 86bc52bc99f0..4b3c20de69cf 100644 --- a/envoy/router/BUILD +++ b/envoy/router/BUILD @@ -11,6 +11,9 @@ envoy_package() envoy_cc_library( name = "context_interface", hdrs = ["context.h"], + deps = [ + ":router_interface", + ], ) envoy_cc_library( diff --git a/envoy/router/context.h b/envoy/router/context.h index 2d3dc4ad92b8..5ee2431417c7 100644 --- a/envoy/router/context.h +++ b/envoy/router/context.h @@ -1,6 +1,7 @@ #pragma once #include "envoy/common/pure.h" +#include "envoy/router/router.h" namespace Envoy { namespace Router { @@ -21,6 +22,11 @@ class Context { * @return a struct containing StatNames for virtual cluster stats. */ virtual const VirtualClusterStatNames& virtualClusterStatNames() const PURE; + + /** + * @return a reference to the default generic connection pool factory. + */ + virtual GenericConnPoolFactory& genericConnPoolFactory() PURE; }; } // namespace Router diff --git a/envoy/router/rds.h b/envoy/router/rds.h index 486a95050f9e..1b4d0ac0431c 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 13ab7c45cefb..a81f33a16f9d 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/envoy/router/router.h b/envoy/router/router.h index c829262d1403..09cc4040943a 100644 --- a/envoy/router/router.h +++ b/envoy/router/router.h @@ -501,6 +501,11 @@ class VirtualCluster { public: virtual ~VirtualCluster() = default; + /** + * @return the string name of the virtual cluster. + */ + virtual const absl::optional& name() const PURE; + /** * @return the stat-name of the virtual cluster. */ @@ -870,6 +875,11 @@ class RouteEntry : public ResponseEntry { */ virtual bool autoHostRewrite() const PURE; + /** + * @return bool true if the x-forwarded-host header should be updated. + */ + virtual bool appendXfh() const PURE; + /** * @return MetadataMatchCriteria* the metadata that a subset load balancer should match when * selecting an upstream host diff --git a/envoy/server/BUILD b/envoy/server/BUILD index ccd2478deea7..bd34c1e8e5fd 100644 --- a/envoy/server/BUILD +++ b/envoy/server/BUILD @@ -184,6 +184,7 @@ envoy_cc_library( "//envoy/access_log:access_log_interface", "//envoy/api:api_interface", "//envoy/config:typed_config_interface", + "//envoy/config:typed_metadata_interface", "//envoy/grpc:context_interface", "//envoy/http:codes_interface", "//envoy/http:context_interface", @@ -248,6 +249,7 @@ envoy_cc_library( ":guarddog_interface", "//envoy/network:filter_interface", "//envoy/network:listen_socket_interface", + "//envoy/network:socket_interface_interface", "//envoy/ssl:context_interface", "//source/common/protobuf", "@envoy_api//envoy/admin/v3:pkg_cc_proto", @@ -292,6 +294,14 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "proactive_resource_monitor_interface", + hdrs = ["proactive_resource_monitor.h"], + deps = [ + "//envoy/stats:stats_interface", + ], +) + envoy_cc_library( name = "request_id_extension_config_interface", hdrs = ["request_id_extension_config.h"], @@ -307,6 +317,7 @@ envoy_cc_library( hdrs = ["resource_monitor_config.h"], deps = [ ":options_interface", + ":proactive_resource_monitor_interface", ":resource_monitor_interface", "//envoy/api:api_interface", "//envoy/event:dispatcher_interface", diff --git a/envoy/server/configuration.h b/envoy/server/configuration.h index b43da186eabc..32c7e3f661b1 100644 --- a/envoy/server/configuration.h +++ b/envoy/server/configuration.h @@ -141,6 +141,12 @@ class Admin { * @return Network::Address::OptionsSharedPtr the list of listener socket options. */ virtual Network::Socket::OptionsSharedPtr socketOptions() PURE; + + /** + * @return bool whether the listener should avoid blocking connections based on the globally set + * limit. + */ + virtual bool ignoreGlobalConnLimit() const PURE; }; /** diff --git a/envoy/server/factory_context.h b/envoy/server/factory_context.h index 349f07ea8c38..6a02ca65dfca 100644 --- a/envoy/server/factory_context.h +++ b/envoy/server/factory_context.h @@ -8,6 +8,7 @@ #include "envoy/common/random_generator.h" #include "envoy/config/core/v3/base.pb.h" #include "envoy/config/typed_config.h" +#include "envoy/config/typed_metadata.h" #include "envoy/grpc/context.h" #include "envoy/http/codes.h" #include "envoy/http/context.h" @@ -230,6 +231,12 @@ class FactoryContext : public virtual CommonFactoryContext { */ virtual const envoy::config::core::v3::Metadata& listenerMetadata() const PURE; + /** + * @return const Envoy::Config::TypedMetadata& return the typed metadata provided in the config + * for this listener. + */ + virtual const Envoy::Config::TypedMetadata& listenerTypedMetadata() const PURE; + /** * @return OverloadManager& the overload manager for the server. */ diff --git a/envoy/server/instance.h b/envoy/server/instance.h index 880aa4889b37..f779abb42a17 100644 --- a/envoy/server/instance.h +++ b/envoy/server/instance.h @@ -2,6 +2,7 @@ #include #include +#include #include #include "envoy/access_log/access_log.h" @@ -31,6 +32,11 @@ #include "envoy/upstream/cluster_manager.h" namespace Envoy { + +namespace Stats { +class SinkPredicates; +} + namespace Server { /** @@ -269,6 +275,12 @@ class Instance { * TODO(mattklein123): This can be removed when version 1.20.0 is no longer supported. */ virtual bool enableReusePortDefault() PURE; + + /** + * Set predicates for filtering counters, gauges and text readouts to be flushed to sinks. + */ + virtual void + setSinkPredicates(std::unique_ptr&& sink_predicates) PURE; }; } // namespace Server diff --git a/envoy/server/listener_manager.h b/envoy/server/listener_manager.h index 329afe168e64..e9e902379203 100644 --- a/envoy/server/listener_manager.h +++ b/envoy/server/listener_manager.h @@ -9,6 +9,7 @@ #include "envoy/network/filter.h" #include "envoy/network/listen_socket.h" #include "envoy/network/listener.h" +#include "envoy/network/socket_interface.h" #include "envoy/server/api_listener.h" #include "envoy/server/drain_manager.h" #include "envoy/server/filter_config.h" @@ -64,14 +65,14 @@ class ListenerComponentFactory { * @param socket_type the type of socket (stream or datagram) to create. * @param options to be set on the created socket just before calling 'bind()'. * @param bind_type supplies the bind type of the listen socket. + * @param creation_options additional options for how to create the socket. * @param worker_index supplies the socket/worker index of the new socket. * @return Network::SocketSharedPtr an initialized and potentially bound socket. */ - virtual Network::SocketSharedPtr - createListenSocket(Network::Address::InstanceConstSharedPtr address, - Network::Socket::Type socket_type, - const Network::Socket::OptionsSharedPtr& options, BindType bind_type, - uint32_t worker_index) PURE; + virtual Network::SocketSharedPtr 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) PURE; /** * Creates a list of filter factories. diff --git a/envoy/server/options.h b/envoy/server/options.h index 3968b7f88ca3..f029a54d1335 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/envoy/server/overload/BUILD b/envoy/server/overload/BUILD index 6d937fb8d5e1..fd1bf3e0bc7b 100644 --- a/envoy/server/overload/BUILD +++ b/envoy/server/overload/BUILD @@ -25,6 +25,7 @@ envoy_cc_library( deps = [ "//envoy/event:scaled_range_timer_manager_interface", "//envoy/event:timer_interface", + "//envoy/server:proactive_resource_monitor_interface", "//envoy/thread_local:thread_local_object", "//source/common/common:interval_value", ], diff --git a/envoy/server/overload/thread_local_overload_state.h b/envoy/server/overload/thread_local_overload_state.h index 9a57400d8eb3..35f36050945e 100644 --- a/envoy/server/overload/thread_local_overload_state.h +++ b/envoy/server/overload/thread_local_overload_state.h @@ -5,13 +5,33 @@ #include "envoy/common/pure.h" #include "envoy/event/scaled_range_timer_manager.h" #include "envoy/event/timer.h" +#include "envoy/server/proactive_resource_monitor.h" #include "envoy/thread_local/thread_local_object.h" #include "source/common/common/interval_value.h" +#include "source/common/singleton/const_singleton.h" namespace Envoy { namespace Server { +enum class OverloadProactiveResourceName { + GlobalDownstreamMaxConnections, +}; + +class OverloadProactiveResourceNameValues { +public: + // Overload action to stop accepting new HTTP requests. + const std::string GlobalDownstreamMaxConnections = + "envoy.resource_monitors.global_downstream_max_connections"; + + absl::flat_hash_map + proactive_action_name_to_resource_ = { + {GlobalDownstreamMaxConnections, + OverloadProactiveResourceName::GlobalDownstreamMaxConnections}}; +}; + +using OverloadProactiveResources = ConstSingleton; + /** * Tracks the state of an overload action. The state is a number between 0 and 1 that represents the * level of saturation. The values are categorized in two groups: @@ -46,6 +66,32 @@ class ThreadLocalOverloadState : public ThreadLocal::ThreadLocalObject { public: // Get a thread-local reference to the value for the given action key. virtual const OverloadActionState& getState(const std::string& action) PURE; + /** + * Invokes the corresponding resource monitor to allocate resource for given resource monitor in + * a thread safe manner. Returns true if there is enough resource quota available and allocation + * has succeeded, false if allocation failed or resource is not registered. + * @param name of corresponding resource monitor. + * @param increment to add to current resource usage value within monitor. + */ + virtual bool tryAllocateResource(OverloadProactiveResourceName resource_name, + int64_t increment) PURE; + /** + * Invokes the corresponding resource monitor to deallocate resource for given resource monitor in + * a thread safe manner. Returns true if there is enough resource quota available and deallocation + * has succeeded, false if deallocation failed or resource is not registered. + * @param name of corresponding resource monitor. + * @param decrement to subtract from current resource usage value within monitor. + */ + virtual bool tryDeallocateResource(OverloadProactiveResourceName resource_name, + int64_t decrement) PURE; + + /** + * TODO(nezdolik) remove this method once downstream connection tracking is fully moved to + * overload manager. Checks if resource monitor is registered and resource usage tracking is + * enabled in overload manager. Returns true if resource monitor is registered, false otherwise. + * @param name of resource monitor to check. + */ + virtual bool isResourceMonitorEnabled(OverloadProactiveResourceName resource_name) PURE; }; } // namespace Server diff --git a/envoy/server/proactive_resource_monitor.h b/envoy/server/proactive_resource_monitor.h new file mode 100644 index 000000000000..ee37ccf094fd --- /dev/null +++ b/envoy/server/proactive_resource_monitor.h @@ -0,0 +1,86 @@ +#pragma once + +#include + +#include "envoy/common/pure.h" +#include "envoy/stats/scope.h" +#include "envoy/stats/stats.h" + +#include "source/common/common/assert.h" +#include "source/common/stats/symbol_table_impl.h" + +namespace Envoy { +namespace Server { + +class ProactiveResourceMonitor { +public: + ProactiveResourceMonitor() = default; + virtual ~ProactiveResourceMonitor() = default; + /** + * Tries to allocate resource for given resource monitor in thread safe manner. + * Returns true if there is enough resource quota available and allocation has succeeded, false + * otherwise. + * @param increment to add to current resource usage value and compare against configured max + * threshold. + */ + virtual bool tryAllocateResource(int64_t increment) PURE; + /** + * Tries to deallocate resource for given resource monitor in thread safe manner. + * Returns true if there is enough resource quota available and deallocation has succeeded, false + * otherwise. + * @param decrement to subtract from current resource usage value. + */ + virtual bool tryDeallocateResource(int64_t decrement) PURE; + /** + * Returns current resource usage (most recent read) tracked by monitor. + */ + virtual int64_t currentResourceUsage() const PURE; + /** + * Returns max resource usage configured in monitor. + */ + virtual int64_t maxResourceUsage() const PURE; +}; + +using ProactiveResourceMonitorPtr = std::unique_ptr; + +class ProactiveResource { +public: + ProactiveResource(const std::string& name, ProactiveResourceMonitorPtr monitor, + Stats::Scope& stats_scope) + : name_(name), monitor_(std::move(monitor)), + failed_updates_counter_(makeCounter(stats_scope, name, "failed_updates")) {} + + bool tryAllocateResource(int64_t increment) { + if (monitor_->tryAllocateResource(increment)) { + return true; + } else { + failed_updates_counter_.inc(); + return false; + } + } + + bool tryDeallocateResource(int64_t decrement) { + if (monitor_->tryDeallocateResource(decrement)) { + return true; + } else { + failed_updates_counter_.inc(); + return false; + } + } + + int64_t currentResourceUsage() { return monitor_->currentResourceUsage(); } + +private: + const std::string name_; + ProactiveResourceMonitorPtr monitor_; + Stats::Counter& failed_updates_counter_; + + Stats::Counter& makeCounter(Stats::Scope& scope, absl::string_view a, absl::string_view b) { + Stats::StatNameManagedStorage stat_name(absl::StrCat("overload.", a, ".", b), + scope.symbolTable()); + return scope.counterFromStatName(stat_name.statName()); + } +}; + +} // namespace Server +} // namespace Envoy diff --git a/envoy/server/resource_monitor.h b/envoy/server/resource_monitor.h index 4eb947527ade..e2b0d0647572 100644 --- a/envoy/server/resource_monitor.h +++ b/envoy/server/resource_monitor.h @@ -5,6 +5,8 @@ #include "envoy/common/exception.h" #include "envoy/common/pure.h" +#include "source/common/common/assert.h" + namespace Envoy { namespace Server { @@ -18,36 +20,36 @@ struct ResourceUsage { double resource_pressure_; }; -class ResourceMonitor { +/** + * Notifies caller of updated resource usage. + */ +class ResourceUpdateCallbacks { public: - virtual ~ResourceMonitor() = default; + virtual ~ResourceUpdateCallbacks() = default; + + /** + * Called when the request for updated resource usage succeeds. + * @param usage the updated resource usage + */ + virtual void onSuccess(const ResourceUsage& usage) PURE; /** - * Notifies caller of updated resource usage. + * Called when the request for updated resource usage fails. + * @param error the exception caught when trying to get updated resource usage */ - class Callbacks { - public: - virtual ~Callbacks() = default; - - /** - * Called when the request for updated resource usage succeeds. - * @param usage the updated resource usage - */ - virtual void onSuccess(const ResourceUsage& usage) PURE; - - /** - * Called when the request for updated resource usage fails. - * @param error the exception caught when trying to get updated resource usage - */ - virtual void onFailure(const EnvoyException& error) PURE; - }; + virtual void onFailure(const EnvoyException& error) PURE; +}; + +class ResourceMonitor { +public: + virtual ~ResourceMonitor() = default; /** * Recalculate resource usage. * This must be non-blocking so if RPCs need to be made they should be * done asynchronously and invoke the callback when finished. */ - virtual void updateResourceUsage(Callbacks& callbacks) PURE; + virtual void updateResourceUsage(ResourceUpdateCallbacks& callbacks) PURE; }; using ResourceMonitorPtr = std::unique_ptr; diff --git a/envoy/server/resource_monitor_config.h b/envoy/server/resource_monitor_config.h index 9f680f44f8f2..a7d9086c28f0 100644 --- a/envoy/server/resource_monitor_config.h +++ b/envoy/server/resource_monitor_config.h @@ -6,6 +6,7 @@ #include "envoy/event/dispatcher.h" #include "envoy/protobuf/message_validator.h" #include "envoy/server/options.h" +#include "envoy/server/proactive_resource_monitor.h" #include "envoy/server/resource_monitor.h" #include "source/common/protobuf/protobuf.h" @@ -64,6 +65,26 @@ class ResourceMonitorFactory : public Config::TypedFactory { std::string category() const override { return "envoy.resource_monitors"; } }; +class ProactiveResourceMonitorFactory : public Config::TypedFactory { +public: + ~ProactiveResourceMonitorFactory() override = default; + + /** + * Create a particular proactive resource monitor implementation. + * @param config const ProtoBuf::Message& supplies the config for the proactive resource monitor + * implementation. + * @param context ResourceMonitorFactoryContext& supplies the resource monitor's context. + * @return ProactiveResourceMonitorPtr the resource monitor instance. Should not be nullptr. + * @throw EnvoyException if the implementation is unable to produce an instance with + * the provided parameters. + */ + virtual ProactiveResourceMonitorPtr + createProactiveResourceMonitor(const Protobuf::Message& config, + ResourceMonitorFactoryContext& context) PURE; + + std::string category() const override { return "envoy.resource_monitors"; } +}; + } // namespace Configuration } // namespace Server } // namespace Envoy diff --git a/envoy/ssl/certificate_validation_context_config.h b/envoy/ssl/certificate_validation_context_config.h index 544215a73dae..d331c45ce254 100644 --- a/envoy/ssl/certificate_validation_context_config.h +++ b/envoy/ssl/certificate_validation_context_config.h @@ -7,6 +7,7 @@ #include "envoy/api/api.h" #include "envoy/common/pure.h" #include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h" +#include "envoy/extensions/transport_sockets/tls/v3/common.pb.h" #include "envoy/type/matcher/v3/string.pb.h" #include "absl/types/optional.h" @@ -43,7 +44,7 @@ class CertificateValidationContextConfig { /** * @return The subject alt name matchers to be verified, if enabled. */ - virtual const std::vector& + virtual const std::vector& subjectAltNameMatchers() const PURE; /** @@ -78,6 +79,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/envoy/ssl/connection.h b/envoy/ssl/connection.h index c0bca9c6ab32..501a99b80f28 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/envoy/ssl/tls_certificate_config.h b/envoy/ssl/tls_certificate_config.h index 06113a9f33df..634c4ed4635a 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/envoy/stats/allocator.h b/envoy/stats/allocator.h index 2924ebf0ab30..223b1cab4706 100644 --- a/envoy/stats/allocator.h +++ b/envoy/stats/allocator.h @@ -18,6 +18,9 @@ namespace Envoy { namespace Stats { +class Sink; +class SinkPredicates; + /** * Abstract interface for allocating statistics. Implementations can * be created utilizing a single fixed-size block suitable for @@ -70,19 +73,32 @@ class Allocator { virtual void markTextReadoutForDeletion(const TextReadoutSharedPtr& text_readout) PURE; /** - * Iterate over all stats that need to be added to a sink. Note, that implementations can + * Iterate over all stats. Note, that implementations can potentially hold on to a mutex that + * will deadlock if the passed in functors try to create or delete a stat. + * @param f_size functor that is provided the current number of all stats. Note that this is + * called only once, prior to any calls to f_stat. + * @param f_stat functor that is provided one stat at a time from the stats container. + */ + virtual void forEachCounter(SizeFn f_size, StatFn f_stat) const PURE; + virtual void forEachGauge(SizeFn f_size, StatFn f_stat) const PURE; + virtual void forEachTextReadout(SizeFn f_size, StatFn f_stat) const PURE; + + /** + * Iterate over all stats that need to be flushed to sinks. Note, that implementations can * potentially hold on to a mutex that will deadlock if the passed in functors try to create * or delete a stat. - * @param f_size functor that is provided the number of all stats in the sink. Note this is - * called only once, prior to any calls to f_stat. - * @param f_stat functor that is provided one stat in the sink at a time. + * @param f_size functor that is provided the number of all stats that will be flushed to sinks. + * Note that this is called only once, prior to any calls to f_stat. + * @param f_stat functor that is provided one stat that will be flushed to sinks, at a time. + */ + virtual void forEachSinkedCounter(SizeFn f_size, StatFn f_stat) const PURE; + virtual void forEachSinkedGauge(SizeFn f_size, StatFn f_stat) const PURE; + virtual void forEachSinkedTextReadout(SizeFn f_size, StatFn f_stat) const PURE; + + /** + * Set the predicates to filter counters, gauges and text readouts for sink. */ - virtual void forEachCounter(std::function f_size, - std::function f_stat) const PURE; - virtual void forEachGauge(std::function f_size, - std::function f_stat) const PURE; - virtual void forEachTextReadout(std::function f_size, - std::function f_stat) const PURE; + virtual void setSinkPredicates(std::unique_ptr&& sink_predicates) PURE; // TODO(jmarantz): create a parallel mechanism to instantiate histograms. At // the moment, histograms don't fit the same pattern of counters and gauges diff --git a/envoy/stats/sink.h b/envoy/stats/sink.h index ff0e607ffaa8..a0d914416bd9 100644 --- a/envoy/stats/sink.h +++ b/envoy/stats/sink.h @@ -48,6 +48,29 @@ class MetricSnapshot { virtual SystemTime snapshotTime() const PURE; }; +/** + * A class to define predicates to filter counters, gauges and text readouts for flushing to sinks. + */ +class SinkPredicates { +public: + virtual ~SinkPredicates() = default; + + /** + * @return true if @param counter needs to be flushed to sinks. + */ + virtual bool includeCounter(const Counter& counter) PURE; + + /** + * @return true if @param gague needs to be flushed to sinks. + */ + virtual bool includeGauge(const Gauge& gauge) PURE; + + /** + * @return true if @param text_readout needs to be flushed to sinks. + */ + virtual bool includeTextReadout(const TextReadout& text_readout) PURE; +}; + /** * A sink for stats. Each sink is responsible for writing stats to a backing store. */ diff --git a/envoy/stats/stats.h b/envoy/stats/stats.h index 4a9688b3fd42..9fba64c34ccf 100644 --- a/envoy/stats/stats.h +++ b/envoy/stats/stats.h @@ -190,5 +190,15 @@ class TextReadout : public virtual Metric { using TextReadoutSharedPtr = RefcountPtr; +/** + * Callback invoked to provide size of stats container. + */ +using SizeFn = std::function; + +/** + * Callback invoked for each stat during iteration. + */ +template using StatFn = std::function; + } // namespace Stats } // namespace Envoy diff --git a/envoy/stats/store.h b/envoy/stats/store.h index 3d456bbe7bec..a54a17036578 100644 --- a/envoy/stats/store.h +++ b/envoy/stats/store.h @@ -24,6 +24,7 @@ class Instance; namespace Stats { class Sink; +class SinkPredicates; /** * A store for all known counters, gauges, and timers. @@ -51,20 +52,27 @@ class Store : public Scope { virtual std::vector histograms() const PURE; /** - * Iterate over all stats that need to be added to a sink. Note, that implementations can - * potentially hold on to a mutex that will deadlock if the passed in functors try to create - * or delete a stat. - * @param f_size functor that is provided the number of all stats in the sink. - * @param f_stat functor that is provided one stat in the sink at a time. + * Iterate over all stats. Note, that implementations can potentially hold on to a mutex that + * will deadlock if the passed in functors try to create or delete a stat. + * @param f_size functor that is provided the current number of all stats. Note that this is + * called only once, prior to any calls to f_stat. + * @param f_stat functor that is provided one stat at a time from the stats container. */ - virtual void forEachCounter(std::function f_size, - std::function f_stat) const PURE; - - virtual void forEachGauge(std::function f_size, - std::function f_stat) const PURE; + virtual void forEachCounter(SizeFn f_size, StatFn f_stat) const PURE; + virtual void forEachGauge(SizeFn f_size, StatFn f_stat) const PURE; + virtual void forEachTextReadout(SizeFn f_size, StatFn f_stat) const PURE; - virtual void forEachTextReadout(std::function f_size, - std::function f_stat) const PURE; + /** + * Iterate over all stats that need to be flushed to sinks. Note, that implementations can + * potentially hold on to a mutex that will deadlock if the passed in functors try to create + * or delete a stat. + * @param f_size functor that is provided the number of all stats that will be flushed to sinks. + * Note that this is called only once, prior to any calls to f_stat. + * @param f_stat functor that is provided one stat that will be flushed to sinks, at a time. + */ + virtual void forEachSinkedCounter(SizeFn f_size, StatFn f_stat) const PURE; + virtual void forEachSinkedGauge(SizeFn f_size, StatFn f_stat) const PURE; + virtual void forEachSinkedTextReadout(SizeFn f_size, StatFn f_stat) const PURE; }; using StorePtr = std::unique_ptr; @@ -123,6 +131,14 @@ class StoreRoot : public Store { * method would be asserted. */ virtual void mergeHistograms(PostMergeCb merge_complete_cb) PURE; + + /** + * Set predicates for filtering counters, gauges and text readouts to be flushed to sinks. + * Note that if the sink predicates object is set, we do not send non-sink stats over to the + * child process during hot restart. This will result in the admin stats console being wrong + * during hot restart. + */ + virtual void setSinkPredicates(std::unique_ptr&& sink_predicates) PURE; }; using StoreRootPtr = std::unique_ptr; diff --git a/envoy/stream_info/stream_info.h b/envoy/stream_info/stream_info.h index 5c4c2b473b61..b591285a8093 100644 --- a/envoy/stream_info/stream_info.h +++ b/envoy/stream_info/stream_info.h @@ -192,7 +192,7 @@ struct ResponseCodeDetailValues { const std::string FilterRemovedRequiredResponseHeaders = "filter_removed_required_response_headers"; // The request was rejected because the original IP couldn't be detected. - const std::string OriginalIPDetectionFailed = "rejecting because detection failed"; + const std::string OriginalIPDetectionFailed = "rejecting_because_detection_failed"; // Changes or additions to details should be reflected in // docs/root/configuration/http/http_conn_man/response_code_details.rst }; @@ -232,10 +232,72 @@ 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 { +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_; + } + 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. @@ -258,6 +320,101 @@ struct BytesMeter { using BytesMeterSharedPtr = std::shared_ptr; +// TODO(alyssawilk) after landing this, remove all the duplicate getters and +// setters from StreamInfo. +class UpstreamInfo { +public: + virtual ~UpstreamInfo() = default; + + /** + * Dump the upstream info to the specified ostream. + * + * @param os the ostream to dump state to + * @param indent_level the depth, for pretty-printing. + * + * This function is called on Envoy fatal errors so should avoid memory allocation. + */ + virtual void dumpState(std::ostream& os, int indent_level = 0) const PURE; + + /** + * @param connection ID of the upstream connection. + */ + virtual void setUpstreamConnectionId(uint64_t id) PURE; + + /** + * @return the ID of the upstream connection, or absl::nullopt if not available. + */ + virtual absl::optional upstreamConnectionId() const PURE; + + /** + * @param connection_info sets the upstream ssl connection. + */ + virtual void + setUpstreamSslConnection(const Ssl::ConnectionInfoConstSharedPtr& ssl_connection_info) PURE; + + /** + * @return the upstream SSL connection. This will be nullptr if the upstream + * connection does not use SSL. + */ + virtual Ssl::ConnectionInfoConstSharedPtr upstreamSslConnection() const PURE; + + /* + * @return the upstream timing for this stream + * */ + virtual UpstreamTiming& upstreamTiming() PURE; + virtual const UpstreamTiming& upstreamTiming() const PURE; + + /** + * @param upstream_local_address sets the local address of the upstream connection. Note that it + * can be different than the local address of the downstream connection. + */ + virtual void setUpstreamLocalAddress( + const Network::Address::InstanceConstSharedPtr& upstream_local_address) PURE; + + /** + * @return the upstream local address. + */ + virtual const Network::Address::InstanceConstSharedPtr& upstreamLocalAddress() const PURE; + + /** + * @param failure_reason the upstream transport failure reason. + */ + virtual void setUpstreamTransportFailureReason(absl::string_view failure_reason) PURE; + + /** + * @return const std::string& the upstream transport failure reason, e.g. certificate validation + * failed. + */ + virtual const std::string& upstreamTransportFailureReason() const PURE; + + /** + * @param host the selected upstream host for the request. + */ + virtual void setUpstreamHost(Upstream::HostDescriptionConstSharedPtr host) PURE; + + /** + * @return upstream host description. + */ + virtual Upstream::HostDescriptionConstSharedPtr upstreamHost() const PURE; + + /** + * Filter State object to be shared between upstream and downstream filters. + * @param pointer to upstream connections filter state. + * @return pointer to filter state to be used by upstream connections. + */ + virtual const FilterStateSharedPtr& upstreamFilterState() const PURE; + virtual void setUpstreamFilterState(const FilterStateSharedPtr& filter_state) PURE; + + /** + * Getters and setters for the number of streams started on this connection. + * For upstream connections this is updated as streams are created. + * For downstream connections this is latched at the time the upstream stream + * is assigned. + */ + virtual void setUpstreamNumStreams(uint64_t num_streams) PURE; + virtual uint64_t upstreamNumStreams() const PURE; +}; + /** * Additional information about a completed request for logging. */ @@ -296,11 +453,6 @@ class StreamInfo { */ virtual bool intersectResponseFlags(uint64_t response_flags) const PURE; - /** - * @param host the selected upstream host for the request. - */ - virtual void onUpstreamHostSelected(Upstream::HostDescriptionConstSharedPtr host) PURE; - /** * @param std::string name denotes the name of the route. */ @@ -311,6 +463,16 @@ class StreamInfo { */ virtual const std::string& getRouteName() const PURE; + /** + * @param std::string name denotes the name of the virtual cluster. + */ + virtual void setVirtualClusterName(const absl::optional& name) PURE; + + /** + * @return std::string& the name of the virtual cluster which got matched. + */ + virtual const absl::optional& virtualClusterName() const PURE; + /** * @param bytes_received denotes number of bytes to add to total received bytes. */ @@ -358,69 +520,15 @@ class StreamInfo { virtual MonotonicTime startTimeMonotonic() const PURE; /** - * @return the duration between the last byte of the request was received and the start of the - * request. - */ - 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 - * information for the one that "wins". - */ - virtual void setUpstreamTiming(const UpstreamTiming& upstream_timing) 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 - * value due to filters. - */ - virtual absl::optional firstUpstreamTxByteSent() const PURE; - - /** - * @return the duration between the last byte of the request was sent upstream and the start of - * the request. - */ - virtual absl::optional lastUpstreamTxByteSent() const PURE; - - /** - * @return the duration between the first byte of the response is received from upstream and the - * start of the request. - */ - virtual absl::optional firstUpstreamRxByteReceived() const PURE; - - /** - * @return the duration between the last byte of the response is received from upstream and the - * start of the request. - */ - virtual absl::optional lastUpstreamRxByteReceived() const PURE; - /** - * @return the duration between the first byte of the response is sent downstream and the start of - * the request. There may be a considerable delta between lastUpstreamByteReceived and this value - * due to filters. - */ - 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. + * Sets the upstream information for this stream. */ - virtual absl::optional lastDownstreamTxByteSent() const PURE; + virtual void setUpstreamInfo(std::shared_ptr) PURE; /** - * Sets the time when the last byte of the response is sent downstream. + * Returns the upstream information for this stream. */ - virtual void onLastDownstreamTxByteSent() PURE; + virtual std::shared_ptr upstreamInfo() PURE; + virtual OptRef upstreamInfo() const PURE; /** * @return the total duration of the request (i.e., when the request's ActiveStream is destroyed) @@ -434,6 +542,12 @@ class StreamInfo { */ virtual void onRequestComplete() PURE; + /** + * @return the downstream timing information. + */ + virtual DownstreamTiming& downstreamTiming() PURE; + virtual OptRef downstreamTiming() const PURE; + /** * @param bytes_sent denotes the number of bytes to add to total sent bytes. */ @@ -459,23 +573,6 @@ class StreamInfo { */ virtual uint64_t responseFlags() const PURE; - /** - * @return upstream host description. - */ - virtual Upstream::HostDescriptionConstSharedPtr upstreamHost() const PURE; - - /** - * @param upstream_local_address sets the local address of the upstream connection. Note that it - * can be different than the local address of the downstream connection. - */ - virtual void setUpstreamLocalAddress( - const Network::Address::InstanceConstSharedPtr& upstream_local_address) PURE; - - /** - * @return the upstream local address. - */ - virtual const Network::Address::InstanceConstSharedPtr& upstreamLocalAddress() const PURE; - /** * @return whether the request is a health check request or not. */ @@ -491,18 +588,6 @@ class StreamInfo { */ virtual const Network::ConnectionInfoProvider& downstreamAddressProvider() const PURE; - /** - * @param connection_info sets the upstream ssl connection. - */ - virtual void - setUpstreamSslConnection(const Ssl::ConnectionInfoConstSharedPtr& ssl_connection_info) PURE; - - /** - * @return the upstream SSL connection. This will be nullptr if the upstream - * connection does not use SSL. - */ - virtual Ssl::ConnectionInfoConstSharedPtr upstreamSslConnection() const PURE; - /** * @return const Router::RouteConstSharedPtr Get the route selected for this request. */ @@ -532,25 +617,6 @@ class StreamInfo { virtual const FilterStateSharedPtr& filterState() PURE; virtual const FilterState& filterState() const PURE; - /** - * Filter State object to be shared between upstream and downstream filters. - * @param pointer to upstream connections filter state. - * @return pointer to filter state to be used by upstream connections. - */ - virtual const FilterStateSharedPtr& upstreamFilterState() const PURE; - virtual void setUpstreamFilterState(const FilterStateSharedPtr& filter_state) PURE; - - /** - * @param failure_reason the upstream transport failure reason. - */ - virtual void setUpstreamTransportFailureReason(absl::string_view failure_reason) PURE; - - /** - * @return const std::string& the upstream transport failure reason, e.g. certificate validation - * failed. - */ - virtual const std::string& upstreamTransportFailureReason() const PURE; - /** * @param headers request headers. */ @@ -605,16 +671,6 @@ class StreamInfo { */ virtual const std::string& filterChainName() const PURE; - /** - * @param connection ID of the upstream connection. - */ - virtual void setUpstreamConnectionId(uint64_t id) PURE; - - /** - * @return the ID of the upstream connection, or absl::nullopt if not available. - */ - virtual absl::optional upstreamConnectionId() const PURE; - /** * @param attempt_count, the number of times the request was attempted upstream. */ diff --git a/envoy/upstream/load_balancer.h b/envoy/upstream/load_balancer.h index 6d96dcdd294a..75252fae82d6 100644 --- a/envoy/upstream/load_balancer.h +++ b/envoy/upstream/load_balancer.h @@ -141,7 +141,7 @@ class LoadBalancer { virtual HostConstSharedPtr peekAnotherHost(LoadBalancerContext* context) PURE; /** - * Returns connnection lifetime callbacks that may be used to inform the load balancer of + * Returns connection lifetime callbacks that may be used to inform the load balancer of * connection events. Load balancers which do not intend to track connection lifetime events * will return nullopt. * @return optional lifetime callbacks for this load balancer. @@ -155,7 +155,8 @@ class LoadBalancer { * for example if no matching connection is found. */ virtual absl::optional - selectPool(LoadBalancerContext* context, const Host& host, std::vector& hash_key) PURE; + selectExistingConnection(LoadBalancerContext* context, const Host& host, + std::vector& hash_key) PURE; }; using LoadBalancerPtr = std::unique_ptr; diff --git a/examples/double-proxy/envoy-backend.yaml b/examples/double-proxy/envoy-backend.yaml index 07cc1a7905f1..0636354c3771 100644 --- a/examples/double-proxy/envoy-backend.yaml +++ b/examples/double-proxy/envoy-backend.yaml @@ -26,8 +26,10 @@ static_resources: private_key: filename: certs/serverkey.pem validation_context: - match_subject_alt_names: - - exact: proxy-postgres-frontend.example.com + match_typed_subject_alt_names: + - san_type: DNS + matcher: + exact: proxy-postgres-frontend.example.com trusted_ca: filename: certs/cacert.pem diff --git a/examples/double-proxy/envoy-frontend.yaml b/examples/double-proxy/envoy-frontend.yaml index 37acbf334124..21fa643e62ed 100644 --- a/examples/double-proxy/envoy-frontend.yaml +++ b/examples/double-proxy/envoy-frontend.yaml @@ -36,7 +36,9 @@ static_resources: private_key: filename: certs/clientkey.pem validation_context: - match_subject_alt_names: - - exact: proxy-postgres-backend.example.com + match_typed_subject_alt_names: + - san_type: DNS + matcher: + exact: proxy-postgres-backend.example.com trusted_ca: filename: certs/cacert.pem diff --git a/examples/grpc-bridge/client/requirements.txt b/examples/grpc-bridge/client/requirements.txt index e8e3d0721547..e1c408292b81 100644 --- a/examples/grpc-bridge/client/requirements.txt +++ b/examples/grpc-bridge/client/requirements.txt @@ -12,126 +12,129 @@ charset-normalizer==2.0.6 \ --hash=sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6 \ --hash=sha256:5ec46d183433dcbd0ab716f2d7f29d8dee50505b3fdb40c6b985c7c4f5a3591f # via requests -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 - # via -r requirements.in -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.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 +grpcio-tools==1.42.0 \ + --hash=sha256:0d6b1376fc517304cf86bf6a23bc692747cc25c8944b68c28960f64fd6e9d8f4 \ + --hash=sha256:1315326c12343d1fb85a1da715f4f7a54ab17ee4c951eb675113002bb1014edd \ + --hash=sha256:1d3af9eda66028ec9e5bd0af8922314ae5c710915e3b26b6ffae9b3318b89aa8 \ + --hash=sha256:28ad66858426895d0a0fce5b908b82356c51ce7131fd9df931809c39233915ca \ + --hash=sha256:2c162eaaa8514862fe8008cf5be8f61f15e083c3547ced888be0590e68919c35 \ + --hash=sha256:361e48504472ab741757c019271cf70444d15456c8f299ef9bd5f88211461221 \ + --hash=sha256:4e67fcc9aa46c8b6cf5e6ee72897c372fc2be8fc5654b868d0e17857fd8ede38 \ + --hash=sha256:5a0e46b37768a680a018b36805f4465fff09dd9b7be58b7d39c42e60ec7c0fa4 \ + --hash=sha256:649cd111965995a765978268f030cf91da2daa61547792d4bde9e737fbeeb88b \ + --hash=sha256:67447309196fdc0ea779959a99210ec5b8167274c528044c0781975741fbdf20 \ + --hash=sha256:6bc53d11783c69449702d6a0e90ec85e1bf91d6b9fafcafc9021c71a9b7e3165 \ + --hash=sha256:7002beacb0a1040dc544b3fe7cff65a4726f3e1bdbaa83e4714a2cbf79346c54 \ + --hash=sha256:75756eca7683c0fc6d011017f37d4039a9b7c3c3f68c7ae107dd88efd71aa421 \ + --hash=sha256:7b982222efa5c545d7dec57bd0ad832c510d2227a3ca6bd62f3d243d12a1d0fb \ + --hash=sha256:7c90310b6edd3d3ed07e9a90bdfc7948c7be5e973f399e5157050b0fc87282ac \ + --hash=sha256:7f22493b71b25966769154ca14574f4e8b7f30cb2512725bdb1d926fee53c37d \ + --hash=sha256:837d9efeaa940ec0a7e1105ed8b926711f7b41ce86e8d26fd63d2099de4b0df6 \ + --hash=sha256:840559ea51d97b8d27db6ea632183b800af7c04ae451bf59262a9204617ea981 \ + --hash=sha256:8555e9f09fdb3dd7ab3bbf50548d0a136e0c43a4438aeecb5646b44022a67c22 \ + --hash=sha256:8c67933f604a537b25a4c4747c3771cafc3b126420d44cb28d602d9bd52c304d \ + --hash=sha256:8df874b9f3d28343baf713a227ae801102cc0a14fc41696cdba89462835df6a1 \ + --hash=sha256:8f733945b84c86722906c15d6621cdbf6adb5c297cfcdb69427fe964547e701c \ + --hash=sha256:952cc3ab80297f8aa7617864de64f37f970fde76613e68c26bbaf2fa73e76168 \ + --hash=sha256:962206c4fba534dfe300aadf8b847aef7eaee39873c699d5eab4cb659b32dcf8 \ + --hash=sha256:a4cfc18b3f34bd045feef9b7c18dce391bfd1fc6e7877dce560fc2b9acda0d10 \ + --hash=sha256:a571db5065d6c3cf8a4eeaf19e6196fee6b6ffadb5d235d5f2e8e054c344cc81 \ + --hash=sha256:a665dea4b31484e75358e815650892675e8377c444cd7b9a1b80363b5968adbd \ + --hash=sha256:a73ede0610c3366ba7026803d52424ba30dea30e523c7f94b91f70eecd525f83 \ + --hash=sha256:acd1e6f3e939a062fbbd9d5a26e87e263844cd1d3565ae429d9662b3d1c6bbcd \ + --hash=sha256:b3e97344ae35cf57b70981fc1a3f48d8a3a9bedd3663c06dbff0ecc5419fd29c \ + --hash=sha256:bce8922ccf0dc64dcdac1dcc2be967f40c2cc9d16a8fc8f50bfbe670abc017ec \ + --hash=sha256:bd0cafd2017d45e48ac03b3831f36441334c31ba0bcbd7d9001b120d04268867 \ + --hash=sha256:bd35c55a68f76554348da5faec2863e666dcae87f111271013cf3d4ddb3f0413 \ + --hash=sha256:c49c435a6494e83e918cfacfa403d418996d9dbbaf516bbebbe3cea67c465c68 \ + --hash=sha256:d0a0daa82eb2c2fb8e12b82a458d1b7c5516fe1135551da92b1a02e2cba93422 \ + --hash=sha256:d934077076018ce3a2a7a6597ee50275cf0cbe0d3d59098e5ca5c3f239f6c5d8 \ + --hash=sha256:db4fc105c1c9ba3beb8423fa8cab62cd0aaa35fa9bd72f617b4bc1d0a1027b32 \ + --hash=sha256:dcc6c97264183f948dcf2025b80c399db729aa6cce97da7facf48a7683de3ded \ + --hash=sha256:dfe9d60c4b09d8137c4065970e4e75b6a5ca2a4eef0f6db1d1c763c4e50171c4 \ + --hash=sha256:e2146d5427b836e67d920a18d84e13ac26da7794efe73c5c484ad559b301db41 \ + --hash=sha256:e2f6caad3b0872fe197a84bd49d8b31f5006b0e6437056de2b9f22a8c85cabe0 \ + --hash=sha256:e4fe886a001c204273be2e33c68ec6ed2280b029ec871401239c60fe13c18309 \ + --hash=sha256:f76cff42ccbaa95f803d6ecd92067e925bd820042dc941ae8eed5f57b628a85e \ + --hash=sha256:fadd44afc67beae908fc8f8b40371fe08b5ca061a0c22c1b9c2978e2e09d3920 + # via -r requirements.in idna==3.2 \ --hash=sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a \ --hash=sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3 # via requests -protobuf==3.18.0 \ - --hash=sha256:0a59ea8da307118372750e2fdfe0961622e675b8dd35e05c42384d618189a938 \ - --hash=sha256:17181fc0814655812aac108e755bd5185d71aa8d81bd241cec6e232c84097918 \ - --hash=sha256:18b308946a592e245299391e53c01b5b8efc2794f49986e80f37d7b5e60a270f \ - --hash=sha256:1f3ecec3038c2fb4dad952d3d6cb9ca301999903a09e43794fb348da48f7577f \ - --hash=sha256:3b5b81bb665aac548b413480f4e0d8c38a74bc4dea57835f288a3ce74f63dfe9 \ - --hash=sha256:42c04e66ec5a38ad2171639dc9860c2f9594668f709ea3a4a192acf7346853a7 \ - --hash=sha256:5201333b7aa711965c5769b250f8565a9924e8e27f8b622bbc5e6847aeaab1b1 \ - --hash=sha256:568c049ff002a7523ed33fb612e6b97da002bf87ffb619a1fc3eadf2257a3b31 \ - --hash=sha256:5730de255c95b3403eedd1a568eb28203b913b6192ff5a3fdc3ff30f37107a38 \ - --hash=sha256:615099e52e9fbc9fde00177267a94ca820ecf4e80093e390753568b7d8cb3c1a \ - --hash=sha256:7646c20605fbee57e77fdbc4a90175538281b152f46ba17019916593f8062c2a \ - --hash=sha256:7e791a94db391ae22b3943fc88f6ba0e1f62b6ad58b33db7517df576c7834d23 \ - --hash=sha256:80b0a5157f3a53043daf8eb7cfa1220b27a5a63dd6655dbd8e1e6f7b5dcd6347 \ - --hash=sha256:877664b1b8d1e23553634f625e4e12aae4ff16cbbef473f8118c239d478f422a \ - --hash=sha256:9072cb18fca8998b77f969fb74d25a11d7f4a39a8b1ddc3cf76cd5abda8499cb \ - --hash=sha256:9147565f93e6699d7512747766598afe63205f226ac7b61f47954974c9aab852 \ - --hash=sha256:93c077fd83879cf48f327a2491c24da447a09da6a7ab3cc311a6f5a61fcb5de0 \ - --hash=sha256:d11465040cadcea8ecf5f0b131af5099a9696f9d0bef6f88148b372bacc1c52d \ - --hash=sha256:f589346b5b3f702c1d30e2343c9897e6c35e7bd495c10a0e17d11ecb5ee5bd06 \ - --hash=sha256:f6138462643adce0ed6e49007a63b7fd7dc4fda1ef4e15a70fcebe76c1407a71 \ - --hash=sha256:f7c8193ec805324ff6024242b00f64a24b94d56b895f62bf28a9d72a228d4fca +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/examples/locality-load-balancing/Dockerfile-client b/examples/locality-load-balancing/Dockerfile-client new file mode 100644 index 000000000000..92869c792d57 --- /dev/null +++ b/examples/locality-load-balancing/Dockerfile-client @@ -0,0 +1,11 @@ +FROM envoyproxy/envoy-dev:latest +RUN apt-get update && apt-get install -y bash curl python3 + +COPY ./envoy-proxy.yaml /etc/envoy.yaml +COPY ./client.py /client.py + +RUN chmod go+r /etc/envoy.yaml + +EXPOSE 8001 + +CMD ["/usr/local/bin/envoy", "-c", "/etc/envoy.yaml", "--service-node", "${HOSTNAME}", "--service-cluster", "client"] diff --git a/examples/locality-load-balancing/Dockerfile-server b/examples/locality-load-balancing/Dockerfile-server new file mode 100644 index 000000000000..03c01cf7238c --- /dev/null +++ b/examples/locality-load-balancing/Dockerfile-server @@ -0,0 +1,8 @@ +FROM alpine:latest + +RUN apk update && apk add py3-pip +RUN pip3 install -q Flask==0.11.1 +RUN mkdir /code +COPY ./service.py /code + +CMD ["python3", "/code/service.py"] diff --git a/examples/locality-load-balancing/client.py b/examples/locality-load-balancing/client.py new file mode 100644 index 000000000000..0eef5ff4dda4 --- /dev/null +++ b/examples/locality-load-balancing/client.py @@ -0,0 +1,20 @@ +import sys +import urllib.request +from collections import Counter + +url, n_requests = sys.argv[1], int(sys.argv[2]) + +count = Counter() +count_fail = 0 + +for i in range(n_requests): + try: + with urllib.request.urlopen(url) as resp: + content = resp.read().decode("utf-8").strip() + count[content] += 1 + except: + count_fail += 1 + +for k in count: + print(f"{k}: actual weight {count[k] / n_requests * 100}%") +print(f"Failed: {count_fail}") diff --git a/examples/locality-load-balancing/docker-compose.yaml b/examples/locality-load-balancing/docker-compose.yaml new file mode 100644 index 000000000000..de6777cfff43 --- /dev/null +++ b/examples/locality-load-balancing/docker-compose.yaml @@ -0,0 +1,50 @@ +version: "3.7" +services: + client-envoy: + build: + context: . + dockerfile: Dockerfile-client + ports: + - 8001:8001 + networks: + - envoymesh + depends_on: + - "backend-local-1" + - "backend-local-2" + - "backend-remote-1" + - "backend-remote-2" + backend-local-1: + build: + context: . + dockerfile: Dockerfile-server + environment: + - HOST=backend-local-1 + networks: + - envoymesh + backend-local-2: + build: + context: . + dockerfile: Dockerfile-server + environment: + - HOST=backend-local-2 + networks: + - envoymesh + backend-remote-1: + build: + context: . + dockerfile: Dockerfile-server + environment: + - HOST=backend-remote-1 + networks: + - envoymesh + backend-remote-2: + build: + context: . + dockerfile: Dockerfile-server + environment: + - HOST=backend-remote-2 + networks: + - envoymesh + +networks: + envoymesh: {} diff --git a/examples/locality-load-balancing/envoy-proxy.yaml b/examples/locality-load-balancing/envoy-proxy.yaml new file mode 100644 index 000000000000..bbc2b2699a4a --- /dev/null +++ b/examples/locality-load-balancing/envoy-proxy.yaml @@ -0,0 +1,107 @@ +node: + cluster: test-cluster + id: test-id +admin: + address: + socket_address: + address: 0.0.0.0 + port_value: 8001 +static_resources: + listeners: + - name: backend + address: + socket_address: + address: 0.0.0.0 + port_value: 3000 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + codec_type: AUTO + stat_prefix: ingress_http + route_config: + name: local_route + virtual_hosts: + - name: backend + domains: + - "*" + routes: + - match: + prefix: "/" + route: + cluster: backend + http_filters: + - name: envoy.filters.http.router + clusters: + - name: backend + type: STRICT_DNS + lb_policy: ROUND_ROBIN + health_checks: + - interval: 2s + timeout: 3s + no_traffic_interval: 4s + no_traffic_healthy_interval: 4s + unhealthy_threshold: 1 + healthy_threshold: 1 + http_health_check: + path: "/" + load_assignment: + cluster_name: backend + endpoints: + - locality: + region: local + zone: zone-1 + load_balancing_weight: 1 + priority: 0 # highest + lb_endpoints: + - endpoint: + address: + socket_address: + address: backend-local-1 + port_value: 8000 + health_check_config: + port_value: 8000 + hostname: backend-local-1 + - locality: + region: local + zone: zone-2 + load_balancing_weight: 1 + priority: 1 + lb_endpoints: + - endpoint: + address: + socket_address: + address: backend-local-2 + port_value: 8000 + health_check_config: + port_value: 8000 + hostname: backend-local-2 + - locality: + region: remote + zone: zone-1 + load_balancing_weight: 1 + priority: 1 + lb_endpoints: + - endpoint: + address: + socket_address: + address: backend-remote-1 + port_value: 8000 + health_check_config: + port_value: 8000 + hostname: backend-remote-1 + - locality: + region: remote + zone: zone-2 + load_balancing_weight: 1 + priority: 2 + lb_endpoints: + - endpoint: + address: + socket_address: + address: backend-remote-2 + port_value: 8000 + health_check_config: + port_value: 8000 + hostname: backend-remote-2 diff --git a/examples/locality-load-balancing/service.py b/examples/locality-load-balancing/service.py new file mode 100644 index 000000000000..a2d2273008aa --- /dev/null +++ b/examples/locality-load-balancing/service.py @@ -0,0 +1,32 @@ +from flask import Flask +import os + +app = Flask(__name__) +healthy = True + + +@app.route('/') +def hello(): + global healthy + if healthy: + return f"Hello from {os.environ['HOST']}!\n" + else: + return "Unhealthy", 503 + + +@app.route('/healthy') +def healthy(): + global healthy + healthy = True + return f"[{os.environ['HOST']}] Set to healthy\n", 201 + + +@app.route('/unhealthy') +def unhealthy(): + global healthy + healthy = False + return f"[{os.environ['HOST']}] Set to unhealthy\n", 201 + + +if __name__ == "__main__": + app.run(host='0.0.0.0', port=8000, debug=False) diff --git a/examples/locality-load-balancing/verify.sh b/examples/locality-load-balancing/verify.sh new file mode 100755 index 000000000000..7b25ae6c1903 --- /dev/null +++ b/examples/locality-load-balancing/verify.sh @@ -0,0 +1,98 @@ +#!/bin/bash -e + +export NAME=locality-load-balancing +export DELAY=5 + +# shellcheck source=examples/verify-common.sh +. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" + +check_health() { + docker-compose exec -T client-envoy curl -s localhost:8001/clusters | grep health_flags +} + +check_backend() { + output=$(docker-compose exec -T client-envoy python3 client.py http://localhost:3000/ 100) + echo "$output" + for expected in "$@" + do + count=$(echo "$output" | grep -c "$expected" | xargs) + if [ "$count" -eq 0 ]; then + echo "Test fail: locality $expected is expected to be routed to." + return 1 + fi + done +} + +bring_up_backend() { + local server + server="$1" + + docker-compose exec -T client-envoy curl -s "$server":8000/healthy +} + +bring_down_backend() { + local server + server="$1" + + docker-compose exec -T client-envoy curl -s "$server":8000/unhealthy +} + +run_log "=== Demo setup +client -> backend-local-1 [priority: 0, weight: 1] + -> backend-local-2 [priority: 1, weight: 1] + -> backend-remote-1 [priority: 1, weight: 1] + -> backend-remote-2 [priority: 2, weight: 1] +" + +run_log "=== Scenario 1: one replica in the highest priority locality" + +run_log "Send requests to backend." +check_health +check_backend backend-local-1 + +run_log "Bring down backend-local-1 then snooze for ${DELAY}s. Priority 0 locality is 0% healthy." +bring_down_backend "${NAME}"_backend-local-1_1 +sleep ${DELAY} + +run_log "Send requests to backend." +check_health +check_backend backend-local-2 backend-remote-1 + +run_log "Bring down backend-local-2 then snooze for ${DELAY}s. Priority 1 locality is 50% healthy." +bring_down_backend "${NAME}"_backend-local-2_1 +sleep ${DELAY} + +run_log "Traffic is load balanced goes to remote only." +check_health +check_backend backend-remote-1 backend-remote-2 + +run_log "=== Scenario 2: multiple replica in the highest priority locality" + +run_log "Recover local-1 and local-2 then snooze for ${DELAY}s" +bring_up_backend "${NAME}"_backend-local-1_1 +bring_up_backend "${NAME}"_backend-local-2_1 +sleep ${DELAY} + +run_log "Scale backend-local-1 to 5 replicas then snooze for ${DELAY}s" +docker-compose -p ${NAME} up --scale backend-local-1=5 -d --build +sleep ${DELAY} + +run_log "Bring down 4 replicas in backend-local-1 then snooze for ${DELAY}s. Priority 0 locality is 20% healthy." +bring_down_backend "${NAME}"_backend-local-1_2 +bring_down_backend "${NAME}"_backend-local-1_3 +bring_down_backend "${NAME}"_backend-local-1_4 +bring_down_backend "${NAME}"_backend-local-1_5 +sleep ${DELAY} + +run_log "Send requests to backend." +check_health +check_backend backend-local-1 backend-local-2 backend-remote-1 + +run_log "Bring down all endpoints of priority 1. Priority 1 locality is 0% healthy." +bring_down_backend "${NAME}"_backend-local-2_1 +bring_down_backend "${NAME}"_backend-remote-1_1 +sleep ${DELAY} + +run_log "Send requests to backend." +check_health +check_backend backend-local-1 backend-remote-2 diff --git a/repokitteh.star b/repokitteh.star index 57c90e3599eb..c5aa3ddf66f1 100644 --- a/repokitteh.star +++ b/repokitteh.star @@ -18,22 +18,22 @@ use( }, { "owner": "envoyproxy/api-shepherds!", - "path": "api/envoy/", + "path": "(api/envoy/|docs/root/api-docs/)", "label": "api", "github_status_label": "any API change", "auto_assign": True, }, { "owner": "envoyproxy/api-watchers", - "path": "api/envoy/", + "path": "(api/envoy/|docs/root/api-docs/)", }, { "owner": "envoyproxy/dependency-shepherds!", "path": "(bazel/.*repos.*\.bzl)|(bazel/dependency_imports\.bzl)|(api/bazel/.*\.bzl)|(.*/requirements\.txt)|(.*\.patch)", "label": "deps", - "allow_global_approval": False, "github_status_label": "any dependency change", + "auto_assign": True, }, ], ) diff --git a/source/common/api/BUILD b/source/common/api/BUILD index 07443785ab89..7ed8334ae882 100644 --- a/source/common/api/BUILD +++ b/source/common/api/BUILD @@ -48,6 +48,7 @@ envoy_cc_library( }), deps = [ "//envoy/api:os_sys_calls_interface", + "//source/common/network:address_lib", "//source/common/singleton:threadsafe_singleton", ], ) diff --git a/source/common/api/posix/os_sys_calls_impl.cc b/source/common/api/posix/os_sys_calls_impl.cc index ad2a36dd2af1..5411580a212b 100644 --- a/source/common/api/posix/os_sys_calls_impl.cc +++ b/source/common/api/posix/os_sys_calls_impl.cc @@ -6,6 +6,7 @@ #include #include "source/common/api/os_sys_calls_impl.h" +#include "source/common/network/address_impl.h" namespace Envoy { namespace Api { @@ -151,6 +152,20 @@ bool OsSysCallsImpl::supportsIpTransparent() const { #endif } +bool OsSysCallsImpl::supportsMptcp() const { +#if !defined(__linux__) + return false; +#else + int fd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP); + if (fd < 0) { + return false; + } + + ::close(fd); + return true; +#endif +} + SysCallIntResult OsSysCallsImpl::ftruncate(int fd, off_t length) { const int rc = ::ftruncate(fd, length); return {rc, rc != -1 ? 0 : errno}; @@ -282,5 +297,68 @@ SysCallBoolResult OsSysCallsImpl::socketTcpInfo([[maybe_unused]] os_fd_t sockfd, return {false, EOPNOTSUPP}; } +bool OsSysCallsImpl::supportsGetifaddrs() const { +// TODO: eliminate this branching by upstreaming an alternative Android implementation +// e.g.: https://github.com/envoyproxy/envoy-mobile/blob/main/third_party/android/ifaddrs-android.h +#if defined(__ANDROID_API__) && __ANDROID_API__ < 24 + if (alternate_getifaddrs_.has_value()) { + return true; + } + return false; +#else + // Note: posix defaults to true regardless of whether an alternate getifaddrs has been set or not. + // This is because as far as we are aware only Android<24 lacks an implementation and thus another + // posix based platform that lacks a native getifaddrs implementation should be a programming + // error. + // + // That being said, if an alternate getifaddrs impl is set, that will be used in calls to + // OsSysCallsImpl::getifaddrs as seen below. + return true; +#endif +} + +SysCallIntResult OsSysCallsImpl::getifaddrs([[maybe_unused]] InterfaceAddressVector& interfaces) { + if (alternate_getifaddrs_.has_value()) { + return alternate_getifaddrs_.value()(interfaces); + } + +// TODO: eliminate this branching by upstreaming an alternative Android implementation +// e.g.: https://github.com/envoyproxy/envoy-mobile/blob/main/third_party/android/ifaddrs-android.h +#if defined(__ANDROID_API__) && __ANDROID_API__ < 24 + NOT_IMPLEMENTED_GCOVR_EXCL_LINE; +#else + struct ifaddrs* ifaddr; + struct ifaddrs* ifa; + + const int rc = ::getifaddrs(&ifaddr); + if (rc == -1) { + return {rc, errno}; + } + + for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == nullptr) { + continue; + } + + if (ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6) { + const sockaddr_storage* ss = reinterpret_cast(ifa->ifa_addr); + size_t ss_len = + ifa->ifa_addr->sa_family == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6); + StatusOr address = + Network::Address::addressFromSockAddr(*ss, ss_len, ifa->ifa_addr->sa_family == AF_INET6); + if (address.ok()) { + interfaces.emplace_back(ifa->ifa_name, ifa->ifa_flags, *address); + } + } + } + + if (ifaddr) { + ::freeifaddrs(ifaddr); + } + + return {rc, 0}; +#endif +} + } // namespace Api } // namespace Envoy diff --git a/source/common/api/posix/os_sys_calls_impl.h b/source/common/api/posix/os_sys_calls_impl.h index 77ccb0da2b08..20b9adf56ef2 100644 --- a/source/common/api/posix/os_sys_calls_impl.h +++ b/source/common/api/posix/os_sys_calls_impl.h @@ -26,6 +26,7 @@ class OsSysCallsImpl : public OsSysCalls { bool supportsUdpGro() const override; bool supportsUdpGso() const override; bool supportsIpTransparent() const override; + bool supportsMptcp() const override; SysCallIntResult close(os_fd_t fd) override; SysCallIntResult ftruncate(int fd, off_t length) override; SysCallPtrResult mmap(void* addr, size_t length, int prot, int flags, int fd, @@ -49,6 +50,8 @@ class OsSysCallsImpl : public OsSysCalls { SysCallSocketResult duplicate(os_fd_t oldfd) override; SysCallSocketResult accept(os_fd_t socket, sockaddr* addr, socklen_t* addrlen) override; SysCallBoolResult socketTcpInfo(os_fd_t sockfd, EnvoyTcpInfo* tcp_info) override; + bool supportsGetifaddrs() const override; + SysCallIntResult getifaddrs(InterfaceAddressVector& interfaces) override; }; using OsSysCallsSingleton = ThreadSafeSingleton; diff --git a/source/common/api/win32/os_sys_calls_impl.cc b/source/common/api/win32/os_sys_calls_impl.cc index 3766c54a2100..5d172f88e6c0 100644 --- a/source/common/api/win32/os_sys_calls_impl.cc +++ b/source/common/api/win32/os_sys_calls_impl.cc @@ -196,6 +196,11 @@ bool OsSysCallsImpl::supportsIpTransparent() const { return false; } +bool OsSysCallsImpl::supportsMptcp() const { + // Windows doesn't support it. + return false; +} + SysCallIntResult OsSysCallsImpl::ftruncate(int fd, off_t length) { const int rc = ::_chsize_s(fd, length); return {rc, rc == 0 ? 0 : errno}; @@ -404,5 +409,19 @@ SysCallBoolResult OsSysCallsImpl::socketTcpInfo([[maybe_unused]] os_fd_t sockfd, return {false, WSAEOPNOTSUPP}; } +bool OsSysCallsImpl::supportsGetifaddrs() const { + if (alternate_getifaddrs_.has_value()) { + return true; + } + return false; +} + +SysCallIntResult OsSysCallsImpl::getifaddrs([[maybe_unused]] InterfaceAddressVector& interfaces) { + if (alternate_getifaddrs_.has_value()) { + return alternate_getifaddrs_.value()(interfaces); + } + NOT_IMPLEMENTED_GCOVR_EXCL_LINE; +} + } // namespace Api } // namespace Envoy diff --git a/source/common/api/win32/os_sys_calls_impl.h b/source/common/api/win32/os_sys_calls_impl.h index 4dc62e0770d2..5268643f47e2 100644 --- a/source/common/api/win32/os_sys_calls_impl.h +++ b/source/common/api/win32/os_sys_calls_impl.h @@ -27,6 +27,7 @@ class OsSysCallsImpl : public OsSysCalls { bool supportsUdpGro() const override; bool supportsUdpGso() const override; bool supportsIpTransparent() const override; + bool supportsMptcp() const override; SysCallIntResult close(os_fd_t fd) override; SysCallIntResult ftruncate(int fd, off_t length) override; SysCallPtrResult mmap(void* addr, size_t length, int prot, int flags, int fd, @@ -51,6 +52,8 @@ class OsSysCallsImpl : public OsSysCalls { SysCallSocketResult duplicate(os_fd_t oldfd) override; SysCallSocketResult accept(os_fd_t socket, sockaddr* addr, socklen_t* addrlen) override; SysCallBoolResult socketTcpInfo(os_fd_t sockfd, EnvoyTcpInfo* tcp_info) override; + bool supportsGetifaddrs() const override; + SysCallIntResult getifaddrs(InterfaceAddressVector&) override; }; using OsSysCallsSingleton = ThreadSafeSingleton; diff --git a/source/common/buffer/buffer_impl.h b/source/common/buffer/buffer_impl.h index 12152a927302..7368b4880e1b 100644 --- a/source/common/buffer/buffer_impl.h +++ b/source/common/buffer/buffer_impl.h @@ -397,7 +397,7 @@ class Slice { /** Length of the byte array that base_ points to. This is also the offset in bytes from the start * of the slice to the end of the Reservable section. */ - uint64_t capacity_; + uint64_t capacity_ = 0; /** Backing storage for mutable slices which own their own storage. This storage should never be * accessed directly; access base_ instead. */ @@ -407,11 +407,11 @@ class Slice { uint8_t* base_{nullptr}; /** Offset in bytes from the start of the slice to the start of the Data section. */ - uint64_t data_; + uint64_t data_ = 0; /** Offset in bytes from the start of the slice to the start of the Reservable section which is * also the end of the Data section. */ - uint64_t reservable_; + uint64_t reservable_ = 0; /** Hooks to execute when the slice is destroyed. */ std::list> drain_trackers_; diff --git a/source/common/buffer/watermark_buffer.h b/source/common/buffer/watermark_buffer.h index 95cd369c82c8..ab1590deaa0a 100644 --- a/source/common/buffer/watermark_buffer.h +++ b/source/common/buffer/watermark_buffer.h @@ -67,7 +67,7 @@ class WatermarkBuffer : public OwnedImpl { uint32_t low_watermark_{0}; uint32_t overflow_watermark_{0}; // Tracks the latest state of watermark callbacks. - // True between the time above_high_watermark_ has been called until above_high_watermark_ has + // True between the time above_high_watermark_ has been called until below_low_watermark_ has // been called. bool above_high_watermark_called_{false}; // Set to true when above_overflow_watermark_ is called (and isn't cleared). diff --git a/source/common/common/BUILD b/source/common/common/BUILD index 4adfe5c622a5..8ea9d219563b 100644 --- a/source/common/common/BUILD +++ b/source/common/common/BUILD @@ -462,6 +462,18 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "perf_tracing_lib", + srcs = ["perf_tracing.cc"], + hdrs = ["perf_tracing.h"], + deps = select({ + "//bazel:enable_perf_tracing": [ + "@com_github_google_perfetto//:perfetto", + ], + "//conditions:default": [], + }), +) + envoy_cc_library( name = "scalar_to_byte_vector_lib", hdrs = ["scalar_to_byte_vector.h"], diff --git a/source/common/common/base64.cc b/source/common/common/base64.cc index 9eab86b1c48b..e719eb9a4863 100644 --- a/source/common/common/base64.cc +++ b/source/common/common/base64.cc @@ -143,7 +143,7 @@ inline void encodeLast(uint64_t pos, uint8_t last_char, std::string& ret, } // namespace -std::string Base64::decode(const std::string& input) { +std::string Base64::decode(absl::string_view input) { if (input.length() % 4) { return EMPTY_STRING; } @@ -242,7 +242,7 @@ void Base64::completePadding(std::string& encoded) { } } -std::string Base64Url::decode(const std::string& input) { +std::string Base64Url::decode(absl::string_view input) { if (input.empty()) { return EMPTY_STRING; } diff --git a/source/common/common/base64.h b/source/common/common/base64.h index a69ffbf910a3..ed2181d79b66 100644 --- a/source/common/common/base64.h +++ b/source/common/common/base64.h @@ -44,7 +44,7 @@ class Base64 { * Note, decoded string may contain '\0' at any position, it should be treated as a sequence of * bytes. */ - static std::string decode(const std::string& input); + static std::string decode(absl::string_view input); /** * Base64 decode an input string. Padding is not required. @@ -82,7 +82,7 @@ class Base64Url { * Note, decoded string may contain '\0' at any position, it should be treated as a sequence of * bytes. */ - static std::string decode(const std::string& input); + static std::string decode(absl::string_view input); }; } // namespace Envoy diff --git a/source/common/common/dns_utils.cc b/source/common/common/dns_utils.cc index cbcce39380b9..9ddd49477726 100644 --- a/source/common/common/dns_utils.cc +++ b/source/common/common/dns_utils.cc @@ -35,7 +35,7 @@ generateAddressList(const std::list& responses, uint32_t p return addresses; } for (const auto& response : responses) { - auto address = Network::Utility::getAddressWithPort(*(response.address_), port); + auto address = Network::Utility::getAddressWithPort(*(response.addrInfo().address_), port); if (address) { addresses.push_back(address); } diff --git a/source/common/common/logger.cc b/source/common/common/logger.cc index bc4859fe8ee1..a418cc046e02 100644 --- a/source/common/common/logger.cc +++ b/source/common/common/logger.cc @@ -27,8 +27,15 @@ void SinkDelegate::logWithStableName(absl::string_view, absl::string_view, absl: SinkDelegate::~SinkDelegate() { // The previous delegate should have never been set or should have been reset by now via - // restoreDelegate(); + // restoreDelegate()/restoreTlsDelegate(); assert(previous_delegate_ == nullptr); + assert(previous_tls_delegate_ == nullptr); +} + +void SinkDelegate::setTlsDelegate() { + assert(previous_tls_delegate_ == nullptr); + previous_tls_delegate_ = log_sink_->tlsDelegate(); + log_sink_->setTlsDelegate(this); } void SinkDelegate::setDelegate() { @@ -38,6 +45,13 @@ void SinkDelegate::setDelegate() { log_sink_->setDelegate(this); } +void SinkDelegate::restoreTlsDelegate() { + // Ensures stacked allocation of delegates. + assert(log_sink_->tlsDelegate() == this); + log_sink_->setTlsDelegate(previous_tls_delegate_); + previous_tls_delegate_ = nullptr; +} + void SinkDelegate::restoreDelegate() { // Ensures stacked allocation of delegates. assert(log_sink_->delegate() == this); @@ -52,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; } @@ -80,6 +94,19 @@ void DelegatingLogSink::log(const spdlog::details::log_msg& msg) { } lock.Release(); + auto log_to_sink = [this, msg_view, msg](SinkDelegate& sink) { + if (should_escape_) { + sink.log(escapeLogLine(msg_view), msg); + } else { + sink.log(msg_view, msg); + } + }; + auto* tls_sink = tlsDelegate(); + if (tls_sink != nullptr) { + log_to_sink(*tls_sink); + return; + } + // Hold the sink mutex while performing the actual logging. This prevents the sink from being // swapped during an individual log event. // TODO(mattklein123): In production this lock will never be contended. In practice, thread @@ -87,11 +114,7 @@ void DelegatingLogSink::log(const spdlog::details::log_msg& msg) { // mechanism for this that does not require extra locking that we don't explicitly need in the // prod code. absl::ReaderMutexLock sink_lock(&sink_mutex_); - if (should_escape_) { - sink_->log(escapeLogLine(msg_view)); - } else { - sink_->log(msg_view); - } + log_to_sink(*sink_); } std::string DelegatingLogSink::escapeLogLine(absl::string_view msg_view) { @@ -111,6 +134,26 @@ DelegatingLogSinkSharedPtr DelegatingLogSink::init() { return delegating_sink; } +void DelegatingLogSink::flush() { + auto* tls_sink = tlsDelegate(); + if (tls_sink != nullptr) { + tls_sink->flush(); + return; + } + absl::ReaderMutexLock lock(&sink_mutex_); + sink_->flush(); +} + +SinkDelegate** DelegatingLogSink::tlsSink() { + static thread_local SinkDelegate* tls_sink = nullptr; + + return &tls_sink; +} + +void DelegatingLogSink::setTlsDelegate(SinkDelegate* sink) { *tlsSink() = sink; } + +SinkDelegate* DelegatingLogSink::tlsDelegate() { return *tlsSink(); } + static Context* current_context = nullptr; Context::Context(spdlog::level::level_enum log_level, const std::string& log_format, diff --git a/source/common/common/logger.h b/source/common/common/logger.h index 73425c4c827a..0d55c68fa557 100644 --- a/source/common/common/logger.h +++ b/source/common/common/logger.h @@ -109,27 +109,53 @@ 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: - // Swap the current log sink delegate for this one. This should be called by the derived class - // constructor immediately before returning. This is required to match restoreDelegate(), - // otherwise it's possible for the previous delegate to get set in the base class constructor, - // the derived class constructor throws, and cleanup becomes broken. + // Swap the current thread local log sink delegate for this one. This should be called by the + // derived class constructor immediately before returning. This is required to match + // restoreTlsDelegate(), otherwise it's possible for the previous delegate to get set in the base + // class constructor, the derived class constructor throws, and cleanup becomes broken. + void setTlsDelegate(); + + // Swap the current *global* log sink delegate for this one. This behaves as setTlsDelegate, but + // operates on the global log sink instead of the thread local one. void setDelegate(); - // Swap the current log sink (this) for the previous one. This should be called by the derived - // class destructor in the body. This is critical as otherwise it's possible for a log message - // to get routed to a partially destructed sink. + // Swap the current thread local log sink (this) for the previous one. This should be called by + // the derived class destructor in the body. This is critical as otherwise it's possible for a log + // message to get routed to a partially destructed sink. + void restoreTlsDelegate(); + + // Swap the current *global* log sink delegate for the previous one. This behaves as + // restoreTlsDelegate, but operates on the global sink instead of the thread local one. void restoreDelegate(); SinkDelegate* previousDelegate() { return previous_delegate_; } private: SinkDelegate* previous_delegate_{nullptr}; + SinkDelegate* previous_tls_delegate_{nullptr}; DelegatingLogSinkSharedPtr log_sink_; }; @@ -142,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; } @@ -165,15 +191,17 @@ class DelegatingLogSink : public spdlog::sinks::sink { template void logWithStableName(absl::string_view stable_name, absl::string_view level, absl::string_view component, Args... msg) { + auto tls_sink = tlsDelegate(); + if (tls_sink != nullptr) { + tls_sink->logWithStableName(stable_name, level, component, fmt::format(msg...)); + return; + } absl::ReaderMutexLock sink_lock(&sink_mutex_); sink_->logWithStableName(stable_name, level, component, fmt::format(msg...)); } // spdlog::sinks::sink void log(const spdlog::details::log_msg& msg) override; - void flush() override { - absl::ReaderMutexLock lock(&sink_mutex_); - sink_->flush(); - } + void flush() override; void set_pattern(const std::string& pattern) override { set_formatter(spdlog::details::make_unique(pattern)); } @@ -220,6 +248,9 @@ class DelegatingLogSink : public spdlog::sinks::sink { absl::ReaderMutexLock lock(&sink_mutex_); return sink_; } + SinkDelegate** tlsSink(); + void setTlsDelegate(SinkDelegate* sink); + SinkDelegate* tlsDelegate(); SinkDelegate* sink_ ABSL_GUARDED_BY(sink_mutex_){nullptr}; absl::Mutex sink_mutex_; diff --git a/source/common/common/logger_delegates.cc b/source/common/common/logger_delegates.cc index 8f008b296fb7..46371a5de2e1 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 f9e1d7da69cc..c85d0b1b46a0 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/source/common/common/perf_annotation.h b/source/common/common/perf_annotation.h index ab9ec215eae4..ecb4e8ca1bff 100644 --- a/source/common/common/perf_annotation.h +++ b/source/common/common/perf_annotation.h @@ -25,6 +25,10 @@ // See also, for a much more comprehensive study in performance annotation: // https://labs.vmware.com/vmtj/methodology-for-performance-analysis-of-vmware-vsphere-under-tier-1-applications // https://dl.acm.org/citation.cfm?id=1899945&dl=ACM&coll=DL +// +// See also, source/common/common/perf_tracing.h adding support for "Perfetto" - +// an open-source stack for performance instrumentation and trace analysis. +// It can give a better insight into execution flows and ordering of events. /** * Initiates a performance operation, storing its state in perf_var. A perf_var diff --git a/source/common/common/perf_tracing.cc b/source/common/common/perf_tracing.cc new file mode 100644 index 000000000000..ef08e6aee256 --- /dev/null +++ b/source/common/common/perf_tracing.cc @@ -0,0 +1,9 @@ +#ifdef ENVOY_PERFETTO + +#include "perf_tracing.h" + +// NOLINT(namespace-envoy) + +PERFETTO_TRACK_EVENT_STATIC_STORAGE(); + +#endif diff --git a/source/common/common/perf_tracing.h b/source/common/common/perf_tracing.h new file mode 100644 index 000000000000..cfdf4c4d38c8 --- /dev/null +++ b/source/common/common/perf_tracing.h @@ -0,0 +1,59 @@ +#pragma once + +#ifdef ENVOY_PERFETTO + +#include "perfetto.h" + +// `Perfetto` is an open-source stack for performance instrumentation and trace +// analysis. In Envoy we use it only as a library for recording app-level +// traces which can be later analyzed online at https://ui.perfetto.dev/ or with +// custom tools. +// +// See https://perfetto.dev/docs/instrumentation/track-events for more details. +// +// The support is enabled with +// bazel --define=perf_tracing=enabled ... +// In the absence of such directives the macros for instrumenting code for +// performance analysis will expand to nothing. +// +// The supported `Perfetto` macros are TRACE_EVENT, TRACE_COUNTER, +// TRACE_EVENT_BEGIN and TRACE_EVENT_END. Please be careful with the last two: +// all events on a given thread share the same stack. This means that it's not +// recommended to have a matching pair of TRACE_EVENT_BEGIN and TRACE_EVENT_END +// markers in separate functions, since an unrelated event might terminate the +// original event unexpectedly; for events that cross function boundaries it's +// usually best to emit them on a separate track. Unfortunately this may lead to +// excessive number of tracks if they are unique for every pair of emitted +// events. The existing visualization tools may not work well if the number of +// tracks is too big. In this case the resulting trace data needs to be processed +// differently. Alternatively, if you are interested in benchmarking only and don't +// need any tracing capabilities, then you can resort to the Performance Annotation +// system which supports cross-scoped events too, but doesn't require any +// post-processing to get a benchmark's final report. +// See source/common/common/perf_annotation.h for details. + +// NOLINT(namespace-envoy) + +PERFETTO_DEFINE_CATEGORIES( + perfetto::Category("core").SetDescription("Events from core modules"), + perfetto::Category("extensions").SetDescription("Events from extensions")); + +#else + +// Macros that expand to nothing when performance collection is disabled. These are contrived to +// work syntactically as a C++ statement (e.g. if (foo) TRACE_COUNTER(...) else TRACE_COUNTER(...)). + +#define TRACE_EVENT(category, name, ...) \ + do { \ + } while (false) +#define TRACE_EVENT_BEGIN(category, name, ...) \ + do { \ + } while (false) +#define TRACE_EVENT_END(category, ...) \ + do { \ + } while (false) +#define TRACE_COUNTER(category, track, ...) \ + do { \ + } while (false) + +#endif diff --git a/source/common/common/thread.cc b/source/common/common/thread.cc index 63cac85f6ca0..ca6a247ed495 100644 --- a/source/common/common/thread.cc +++ b/source/common/common/thread.cc @@ -20,15 +20,11 @@ namespace { // call-sites for isMainThread(), which might be a bit of work, but will make // tests more hermetic. struct ThreadIds { - // Determines whether we are currently running on the main-thread or - // test-thread. We need to allow for either one because we don't establish - // the full threading model in all unit tests. - bool inMainOrTestThread() const { + bool inMainThread() const { // We don't take the lock when testing the thread IDs, as they are atomic, // and are cleared when being released. All possible thread orderings // result in the correct result even without a lock. - std::thread::id id = std::this_thread::get_id(); - return main_thread_id_ == id || test_thread_id_ == id; + return std::this_thread::get_id() == main_thread_id_; } bool isMainThreadActive() const { @@ -53,20 +49,6 @@ struct ThreadIds { } } - // Call this when the TestThread exits. Nested semantics are supported, so - // that if multiple TestThread instances are declared, we unwind them - // properly. - void releaseTestThread() { - absl::MutexLock lock(&mutex_); - ASSERT(test_thread_use_count_ > 0); - ASSERT(std::this_thread::get_id() == test_thread_id_); - if (--test_thread_use_count_ == 0) { - // Clearing the thread ID when its use-count goes to zero allows us - // to read the atomic without taking a lock. - test_thread_id_ = std::thread::id{}; - } - } - // Declares current thread as the main one, or verifies that the current // thread matches any previous declarations. void registerMainThread() { @@ -78,16 +60,10 @@ struct ThreadIds { } } - // Declares current thread as the test thread, or verifies that the current - // thread matches any previous declarations. - void registerTestThread() { - absl::MutexLock lock(&mutex_); - if (++test_thread_use_count_ > 1) { - ASSERT(std::this_thread::get_id() == test_thread_id_); - } else { - test_thread_id_ = std::this_thread::get_id(); - } - } + // Methods to track how many SkipAssert objects are instantiated. + void incSkipAsserts() { ++skip_asserts_; } + void decSkipAsserts() { --skip_asserts_; } + bool skipAsserts() const { return skip_asserts_ > 0; } private: // The atomic thread IDs can be read without a mutex, but they are written @@ -95,26 +71,41 @@ struct ThreadIds { // avoids the possibility of two threads racing to claim being the main/test // thread. std::atomic main_thread_id_; - std::atomic test_thread_id_; int32_t main_thread_use_count_ GUARDED_BY(mutex_) = 0; - int32_t test_thread_use_count_ GUARDED_BY(mutex_) = 0; mutable absl::Mutex mutex_; + + std::atomic skip_asserts_{}; }; } // namespace -bool MainThread::isMainOrTestThread() { return ThreadIds::get().inMainOrTestThread(); } +bool MainThread::isMainThread() { return ThreadIds::get().inMainThread(); } bool MainThread::isMainThreadActive() { return ThreadIds::get().isMainThreadActive(); } -TestThread::TestThread() { ThreadIds::get().registerTestThread(); } - -TestThread::~TestThread() { ThreadIds::get().releaseTestThread(); } - MainThread::MainThread() { ThreadIds::get().registerMainThread(); } MainThread::~MainThread() { ThreadIds::get().releaseMainThread(); } +#if TEST_THREAD_SUPPORTED +bool TestThread::isTestThread() { + // Keep this implementation consistent with TEST_THREAD_SUPPORTED, defined in thread.h. + // https://stackoverflow.com/questions/4867839/how-can-i-tell-if-pthread-self-is-the-main-first-thread-in-the-process +#ifdef __linux__ + return getpid() == syscall(SYS_gettid); +#elif defined(__APPLE__) + return pthread_main_np() != 0; +#endif + // Note: final #else fallback omitted intentionally. +} +#endif + +SkipAsserts::SkipAsserts() { ThreadIds::get().incSkipAsserts(); } + +SkipAsserts::~SkipAsserts() { ThreadIds::get().decSkipAsserts(); } + +bool SkipAsserts::skip() { return ThreadIds::get().skipAsserts(); } + } // namespace Thread } // namespace Envoy diff --git a/source/common/common/thread.h b/source/common/common/thread.h index f1f415cf04d5..b3f2fa17652c 100644 --- a/source/common/common/thread.h +++ b/source/common/common/thread.h @@ -168,16 +168,29 @@ class AtomicPtr : private AtomicPtrArray { T* get(const MakeObject& make_object) { return BaseClass::get(0, make_object); } }; -// RAII object to declare the TestThread. This should be declared in main() or -// equivalent for any test binaries. -// -// Generally we expect TestThread to be instantiated only once on main() for -// each test binary, though nested instantiations are allowed as long as the -// thread ID does not change. +// We use platform-specific functions to determine whether the current thread is +// the "test thread". It is only valid to call isTestThread() on platforms where +// these functions are available. Currently this is available only on apple and +// linux. +#if defined(__linux__) || defined(__APPLE__) +#define TEST_THREAD_SUPPORTED 1 +#else +#define TEST_THREAD_SUPPORTED 0 +#endif + +// Context for determining whether we are in the test thread. class TestThread { public: - TestThread(); - ~TestThread(); +#if TEST_THREAD_SUPPORTED + /** + * @return whether the current thread is the test thread. + * + * Use of the macros ASSERT_IS_TEST_THREAD() and ASSERT_IS_NOT_TEST_THREAD() + * are preferred to avoid issues on platforms where detecting the test-thread + * is not supported. + */ + static bool isTestThread(); +#endif }; // RAII object to declare the MainThread. This should be declared in the thread @@ -196,10 +209,25 @@ class MainThread { MainThread(); ~MainThread(); +#if TEST_THREAD_SUPPORTED /** * @return whether the current thread is the main thread or test thread. + * + * Determines whether we are currently running on the main-thread or + * test-thread. We need to allow for either one because we don't establish + * the full threading model in all unit tests. + * + * Use of the macros ASSERT_IS_TEST_THREAD() and ASSERT_IS_NOT_TEST_THREAD() + * are preferred to avoid issues on platforms where detecting the test-thread + * is not supported. */ - static bool isMainOrTestThread(); + static bool isMainOrTestThread() { return isMainThread() || TestThread::isTestThread(); } +#endif + + /** + * @return whether the current thread is the main thread. + */ + static bool isMainThread(); /** * @return whether a MainThread has been instantiated. @@ -207,18 +235,84 @@ class MainThread { static bool isMainThreadActive(); }; -// To improve exception safety in data plane, we plan to forbid the use of raw try in the core code -// base. This macros uses main thread assertion to make sure that exceptions aren't thrown from -// worker thread. -#define TRY_ASSERT_MAIN_THREAD \ - try { \ - ASSERT(Thread::MainThread::isMainOrTestThread()); - #define END_TRY } // TODO(chaoqinli-1123): Remove this macros after we have removed all the exceptions from data // plane. #define TRY_NEEDS_AUDIT try +// These convenience macros assert properties of the threading system, when +// feasible. There is a platform-specific mechanism for determining whether the +// current thread is from main(), which we call the "test thread", and if that +// method is not available on the current platform we must skip the assertions. +// +// Note that the macros are all no-ops if there are any SkipAsserts instances +// allocated. +#ifdef NDEBUG + +#define ASSERT_IS_TEST_THREAD() +#define ASSERT_IS_MAIN_OR_TEST_THREAD() +#define ASSERT_IS_NOT_TEST_THREAD() +#define ASSERT_IS_NOT_MAIN_OR_TEST_THREAD() + +#elif TEST_THREAD_SUPPORTED + +#define ASSERT_IS_TEST_THREAD() \ + ASSERT(Thread::SkipAsserts::skip() || Thread::TestThread::isTestThread()) +#define ASSERT_IS_MAIN_OR_TEST_THREAD() \ + ASSERT(Thread::SkipAsserts::skip() || Thread::TestThread::isTestThread() || \ + Thread::MainThread::isMainThread()) +#define ASSERT_IS_NOT_TEST_THREAD() \ + ASSERT(Thread::SkipAsserts::skip() || !Thread::TestThread::isTestThread()) +#define ASSERT_IS_NOT_MAIN_OR_TEST_THREAD() \ + ASSERT(Thread::SkipAsserts::skip() || \ + (!Thread::MainThread::isMainThread() && !Thread::TestThread::isTestThread())) + +#else // !TEST_THREAD_SUPPORTED -- test-thread checks are skipped + +#define ASSERT_IS_TEST_THREAD() +#define ASSERT_IS_MAIN_OR_TEST_THREAD() +#define ASSERT_IS_NOT_TEST_THREAD() +#define ASSERT_IS_NOT_MAIN_OR_TEST_THREAD() ASSERT(!Thread::MainThread::isMainThread()) + +#endif + +/** + * To improve exception safety in data plane, we plan to forbid the use of raw + * try in the core code base. This macros uses main thread assertion to make + * sure that exceptions aren't thrown from worker thread. + */ +#define TRY_ASSERT_MAIN_THREAD \ + try { \ + ASSERT_IS_MAIN_OR_TEST_THREAD(); + +/** + * RAII class to override thread assertions checks in the macros: + * + * TRY_ASSERT_MAIN_THREAD + * ASSERT_IS_TEST_THREAD() + * ASSERT_IS_MAIN_OR_TEST_THREAD() + * ASSERT_IS_NOT_TEST_THREAD() + * ASSERT_IS_NOT_MAIN_OR_TEST_THREAD() + * + * Those macros will be no-ops while there is a SkipAsserts object + * alive. SkipAsserts declarations can be nested. + * + * The state of the assertion-skipping can also be checked by calling static + * method SkipAsserts::skip(). + * + * This class is intended to be instantiated on the stack in a limited scope. + */ +class SkipAsserts { +public: + SkipAsserts(); + ~SkipAsserts(); + + /** + * @return whether thread-related assertions should be skipped. + */ + static bool skip(); +}; + } // namespace Thread } // namespace Envoy diff --git a/source/common/common/utility.cc b/source/common/common/utility.cc index 06a2a7027fc4..82ad3dd520b0 100644 --- a/source/common/common/utility.cc +++ b/source/common/common/utility.cc @@ -1,5 +1,6 @@ #include "source/common/common/utility.h" +#include #include #include #include @@ -21,6 +22,7 @@ #include "absl/strings/ascii.h" #include "absl/strings/match.h" #include "absl/strings/str_join.h" +#include "absl/strings/str_replace.h" #include "absl/strings/str_split.h" #include "absl/time/time.h" #include "spdlog/spdlog.h" @@ -194,7 +196,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; @@ -268,8 +270,6 @@ uint64_t DateUtil::nowToSeconds(TimeSource& time_source) { .count(); } -const char StringUtil::WhitespaceChars[] = " \t\f\v\n\r"; - const char* StringUtil::strtoull(const char* str, uint64_t& out, int base) { if (strlen(str) == 0) { return nullptr; @@ -378,6 +378,7 @@ std::vector StringUtil::splitToken(absl::string_view source, bool keep_empty_string, bool trim_whitespace) { std::vector result; + if (keep_empty_string) { result = absl::StrSplit(source, absl::ByAnyChar(delimiters)); } else { @@ -435,7 +436,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. @@ -576,6 +577,26 @@ std::string StringUtil::removeCharacters(const absl::string_view& str, return absl::StrJoin(pieces, ""); } +bool StringUtil::hasEmptySpace(absl::string_view view) { + return view.find_first_of(WhitespaceChars) != absl::string_view::npos; +} + +namespace { + +using ReplacementMap = absl::flat_hash_map; + +const ReplacementMap& emptySpaceReplacement() { + CONSTRUCT_ON_FIRST_USE( + ReplacementMap, + ReplacementMap{{" ", "_"}, {"\t", "_"}, {"\f", "_"}, {"\v", "_"}, {"\n", "_"}, {"\r", "_"}}); +} + +} // namespace + +std::string StringUtil::replaceAllEmptySpace(absl::string_view view) { + return absl::StrReplaceAll(view, emptySpaceReplacement()); +} + bool Primes::isPrime(uint32_t x) { if (x && x < 4) { return true; // eliminates special-casing 2. diff --git a/source/common/common/utility.h b/source/common/common/utility.h index 24b29a127350..8fef9576901b 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 { @@ -237,7 +237,7 @@ class StringUtil { using CaseUnorderedSet = absl::flat_hash_set; - static const char WhitespaceChars[]; + static constexpr absl::string_view WhitespaceChars = " \t\f\v\n\r"; /** * Convert a string to an unsigned long, checking for error. @@ -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 " @@ -440,6 +440,21 @@ class StringUtil { */ static std::string removeCharacters(const absl::string_view& str, const IntervalSet& remove_characters); + + /** + * Check whether a string contains empty characters or space (' ', '\t', '\f', '\v', '\n', '\r'). + * @param view string. + * @return true if string contains ' ', '\t', '\f', '\v', '\n', '\r'. + */ + static bool hasEmptySpace(absl::string_view view); + + /** + * Replace all empty characters or space (' ', '\t', '\f', '\v', '\n', '\r') in the string with + * '_'. + * @param view string. + * @return std::string the string after replaced all empty characters or space. + */ + static std::string replaceAllEmptySpace(absl::string_view view); }; /** diff --git a/source/common/config/context_provider_impl.h b/source/common/config/context_provider_impl.h index 590b9c4eff2b..0cbda95cfc4d 100644 --- a/source/common/config/context_provider_impl.h +++ b/source/common/config/context_provider_impl.h @@ -20,7 +20,7 @@ class ContextProviderImpl : public ContextProvider { const xds::core::v3::ContextParams& nodeContext() const override { return node_context_; } const xds::core::v3::ContextParams& dynamicContext(absl::string_view resource_type_url) const override { - ASSERT(Thread::MainThread::isMainOrTestThread()); + ASSERT_IS_MAIN_OR_TEST_THREAD(); auto it = dynamic_context_.find(resource_type_url); if (it != dynamic_context_.end()) { return it->second; @@ -29,7 +29,7 @@ class ContextProviderImpl : public ContextProvider { }; void setDynamicContextParam(absl::string_view resource_type_url, absl::string_view key, absl::string_view value) override { - ASSERT(Thread::MainThread::isMainOrTestThread()); + ASSERT_IS_MAIN_OR_TEST_THREAD(); (*dynamic_context_[resource_type_url] .mutable_params())[toStdStringView(key)] = // NOLINT(std::string_view) toStdStringView(value); // NOLINT(std::string_view) @@ -37,14 +37,14 @@ class ContextProviderImpl : public ContextProvider { } void unsetDynamicContextParam(absl::string_view resource_type_url, absl::string_view key) override { - ASSERT(Thread::MainThread::isMainOrTestThread()); + ASSERT_IS_MAIN_OR_TEST_THREAD(); dynamic_context_[resource_type_url].mutable_params()->erase( toStdStringView(key)); // NOLINT(std::string_view) update_cb_helper_.runCallbacks(resource_type_url); } ABSL_MUST_USE_RESULT Common::CallbackHandlePtr addDynamicContextUpdateCallback(UpdateNotificationCb callback) const override { - ASSERT(Thread::MainThread::isMainOrTestThread()); + ASSERT_IS_MAIN_OR_TEST_THREAD(); return update_cb_helper_.add(callback); }; diff --git a/source/common/config/grpc_mux_impl.cc b/source/common/config/grpc_mux_impl.cc index 4242599901a7..f886e9019d64 100644 --- a/source/common/config/grpc_mux_impl.cc +++ b/source/common/config/grpc_mux_impl.cc @@ -64,7 +64,7 @@ void GrpcMuxImpl::onDynamicContextUpdate(absl::string_view resource_type_url) { void GrpcMuxImpl::start() { grpc_stream_.establishNewStream(); } -void GrpcMuxImpl::sendDiscoveryRequest(const std::string& type_url) { +void GrpcMuxImpl::sendDiscoveryRequest(absl::string_view type_url) { if (shutdown_) { return; } diff --git a/source/common/config/grpc_mux_impl.h b/source/common/config/grpc_mux_impl.h index e9d1a61828c7..7ae805663fc1 100644 --- a/source/common/config/grpc_mux_impl.h +++ b/source/common/config/grpc_mux_impl.h @@ -84,7 +84,7 @@ class GrpcMuxImpl : public GrpcMux, private: void drainRequests(); void setRetryTimer(); - void sendDiscoveryRequest(const std::string& type_url); + void sendDiscoveryRequest(absl::string_view type_url); struct GrpcMuxWatchImpl : public GrpcMuxWatch { GrpcMuxWatchImpl(const absl::flat_hash_set& resources, diff --git a/source/common/config/utility.cc b/source/common/config/utility.cc index 3c97063d91fa..aaaa0816abec 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 bdbf9f64b9c5..80bbdc69e587 100644 --- a/source/common/config/utility.h +++ b/source/common/config/utility.h @@ -194,7 +194,7 @@ class Utility { */ template static void checkTransportVersion(const Proto& api_config_source) { const auto transport_api_version = api_config_source.transport_api_version(); - ASSERT(Thread::MainThread::isMainOrTestThread()); + ASSERT_IS_MAIN_OR_TEST_THREAD(); if (transport_api_version == envoy::config::core::v3::ApiVersion::AUTO || transport_api_version == envoy::config::core::v3::ApiVersion::V2) { Runtime::LoaderSingleton::getExisting()->countDeprecatedFeatureUse(); @@ -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 71a03839b1a7..09eaa6532422 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 0b694392a4ae..dd69131e8e24 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/conn_pool/conn_pool_base.cc b/source/common/conn_pool/conn_pool_base.cc index 4cd623df5061..9850addcc30a 100644 --- a/source/common/conn_pool/conn_pool_base.cc +++ b/source/common/conn_pool/conn_pool_base.cc @@ -166,7 +166,7 @@ void ConnPoolImplBase::attachStreamToClient(Envoy::ConnectionPool::ActiveClient& AttachContext& context) { ASSERT(client.state() == Envoy::ConnectionPool::ActiveClient::State::READY); - if (!host_->cluster().resourceManager(priority_).requests().canCreate()) { + if (enforceMaxRequests() && !host_->cluster().resourceManager(priority_).requests().canCreate()) { ENVOY_LOG(debug, "max streams overflow"); onPoolFailure(client.real_host_description_, absl::string_view(), ConnectionPool::PoolFailureReason::Overflow, context); @@ -175,18 +175,23 @@ void ConnPoolImplBase::attachStreamToClient(Envoy::ConnectionPool::ActiveClient& } ENVOY_CONN_LOG(debug, "creating stream", client); + // Latch capacity before updating remaining streams. + uint64_t capacity = client.currentUnusedCapacity(); client.remaining_streams_--; if (client.remaining_streams_ == 0) { ENVOY_CONN_LOG(debug, "maximum streams per connection, DRAINING", client); host_->cluster().stats().upstream_cx_max_requests_.inc(); transitionActiveClientState(client, Envoy::ConnectionPool::ActiveClient::State::DRAINING); - } else if (client.numActiveStreams() + 1 >= client.concurrent_stream_limit_) { + } else if (capacity == 1) { // As soon as the new stream is created, the client will be maxed out. transitionActiveClientState(client, Envoy::ConnectionPool::ActiveClient::State::BUSY); } // Decrement the capacity, as there's one less stream available for serving. - state_.decrConnectingAndConnectedStreamCapacity(1); + // For HTTP/3, the capacity is updated in newStreamEncoder. + if (trackStreamCapacity()) { + state_.decrConnectingAndConnectedStreamCapacity(1); + } // Track the new active stream. state_.incrActiveStreams(1); num_active_streams_++; @@ -203,24 +208,30 @@ void ConnPoolImplBase::onStreamClosed(Envoy::ConnectionPool::ActiveClient& clien bool delay_attaching_stream) { ENVOY_CONN_LOG(debug, "destroying stream: {} remaining", client, client.numActiveStreams()); ASSERT(num_active_streams_ > 0); - // Reflect there's one less stream in flight. - bool had_negative_capacity = client.hadNegativeDeltaOnStreamClosed(); state_.decrActiveStreams(1); num_active_streams_--; host_->stats().rq_active_.dec(); host_->cluster().stats().upstream_rq_active_.dec(); host_->cluster().resourceManager(priority_).requests().dec(); - // If the effective client capacity was limited by concurrency, increase connecting capacity. - // If the effective client capacity was limited by max total streams, this will not result in an - // increment as no capacity is freed up. - if (client.remaining_streams_ > client.concurrent_stream_limit_ - client.numActiveStreams() - 1 || - had_negative_capacity) { - state_.incrConnectingAndConnectedStreamCapacity(1); + // We don't update the capacity for HTTP/3 as the stream count should only + // increase when a MAX_STREAMS frame is received. + if (trackStreamCapacity()) { + // If the effective client capacity was limited by concurrency, increase connecting capacity. + bool limited_by_concurrency = + client.remaining_streams_ > client.concurrent_stream_limit_ - client.numActiveStreams() - 1; + // The capacity calculated by concurrency could be negative if a SETTINGS frame lowered the + // number of allowed streams. In this case, effective client capacity was still limited by + // concurrency, compare client.concurrent_stream_limit_ and client.numActiveStreams() directly + // to avoid overflow. + bool negative_capacity = client.concurrent_stream_limit_ < client.numActiveStreams() + 1; + if (negative_capacity || limited_by_concurrency) { + state_.incrConnectingAndConnectedStreamCapacity(1); + } } 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) { + } else if (client.state() == ActiveClient::State::BUSY && client.currentUnusedCapacity() > 0) { transitionActiveClientState(client, ActiveClient::State::READY); if (!delay_attaching_stream) { onUpstreamReady(); @@ -296,6 +307,9 @@ void ConnPoolImplBase::onUpstreamReady() { state_.decrPendingStreams(1); pending_streams_.pop_back(); } + if (!pending_streams_.empty()) { + tryCreateNewConnections(); + } } std::list& ConnPoolImplBase::owningList(ActiveClient::State state) { @@ -481,7 +495,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 = @@ -495,7 +511,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/conn_pool/conn_pool_base.h b/source/common/conn_pool/conn_pool_base.h index 0c47346f5725..4bdf01a5aec0 100644 --- a/source/common/conn_pool/conn_pool_base.h +++ b/source/common/conn_pool/conn_pool_base.h @@ -49,14 +49,14 @@ 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_); } // Returns the application protocol, or absl::nullopt for TCP. virtual absl::optional protocol() const PURE; - int64_t currentUnusedCapacity() const { + virtual int64_t currentUnusedCapacity() const { int64_t remaining_concurrent_streams = static_cast(concurrent_stream_limit_) - numActiveStreams(); @@ -102,6 +102,11 @@ class ActiveClient : public LinkedObject, virtual void drain(); ConnPoolImplBase& parent_; + // The count of remaining streams allowed for this connection. + // This will start out as the total number of streams per connection if capped + // by configuration, or it will be set to std::numeric_limits::max() to be + // (functionally) unlimited. + // TODO: this could be moved to an optional to make it actually unlimited. uint32_t remaining_streams_; uint32_t concurrent_stream_limit_; Upstream::HostDescriptionConstSharedPtr real_host_description_; @@ -148,6 +153,10 @@ class ConnPoolImplBase : protected Logger::Loggable { virtual ~ConnPoolImplBase(); void deleteIsPendingImpl(); + // By default, the connection pool will track connected and connecting stream + // capacity as streams are created and destroyed. QUIC does custom stream + // accounting so will override this to false. + virtual bool trackStreamCapacity() { return true; } // A helper function to get the specific context type from the base class context. template T& typedContext(AttachContext& context) { @@ -234,6 +243,9 @@ class ConnPoolImplBase : protected Logger::Loggable { void decrClusterStreamCapacity(uint32_t delta) { state_.decrConnectingAndConnectedStreamCapacity(delta); } + void incrClusterStreamCapacity(uint32_t delta) { + state_.incrConnectingAndConnectedStreamCapacity(delta); + } void dumpState(std::ostream& os, int indent_level = 0) const { const char* spaces = spacesForLevel(indent_level); os << spaces << "ConnPoolImplBase " << this << DUMP_MEMBER(ready_clients_.size()) @@ -255,6 +267,14 @@ 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(); + protected: virtual void onConnected(Envoy::ConnectionPool::ActiveClient&) {} @@ -265,7 +285,6 @@ class ConnPoolImplBase : protected Logger::Loggable { NoConnectionRateLimited, CreatedButRateLimited, }; - // Creates up to 3 connections, based on the preconnect ratio. // Returns the ConnectionResult of the last attempt. ConnectionResult tryCreateNewConnections(); @@ -295,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_; @@ -309,6 +323,10 @@ class ConnPoolImplBase : protected Logger::Loggable { const Network::ConnectionSocket::OptionsSharedPtr socket_options_; const Network::TransportSocketOptionsConstSharedPtr transport_socket_options_; + // True if the max requests circuit breakers apply. + // This will be false for the TCP pool, true otherwise. + virtual bool enforceMaxRequests() const { return true; } + std::list idle_callbacks_; // When calling purgePendingStreams, this list will be used to hold the streams we are about @@ -342,7 +360,6 @@ class ConnPoolImplBase : protected Logger::Loggable { // True iff this object is in the deferred delete list. bool deferred_deleting_{false}; - void onUpstreamReady(); Event::SchedulableCallbackPtr upstream_ready_cb_; }; diff --git a/source/common/event/BUILD b/source/common/event/BUILD index 42f6547a67e4..22efdc958ac2 100644 --- a/source/common/event/BUILD +++ b/source/common/event/BUILD @@ -43,7 +43,6 @@ envoy_cc_library( "//envoy/network:listener_interface", "//source/common/common:assert_lib", "//source/common/common:thread_lib", - "//source/common/config:utility_lib", "//source/common/filesystem:watcher_lib", "//source/common/network:connection_lib", "//source/common/network:listener_lib", diff --git a/source/common/event/dispatcher_impl.cc b/source/common/event/dispatcher_impl.cc index 1f28e9a24fb8..a94b537ff45e 100644 --- a/source/common/event/dispatcher_impl.cc +++ b/source/common/event/dispatcher_impl.cc @@ -171,10 +171,11 @@ Filesystem::WatcherPtr DispatcherImpl::createFilesystemWatcher() { Network::ListenerPtr DispatcherImpl::createListener(Network::SocketSharedPtr&& socket, Network::TcpListenerCallbacks& cb, - bool bind_to_port) { + bool bind_to_port, + bool ignore_global_conn_limit) { ASSERT(isThreadSafe()); - return std::make_unique(*this, api_.randomGenerator(), - std::move(socket), cb, bind_to_port); + return std::make_unique( + *this, api_.randomGenerator(), std::move(socket), cb, bind_to_port, ignore_global_conn_limit); } Network::UdpListenerPtr diff --git a/source/common/event/dispatcher_impl.h b/source/common/event/dispatcher_impl.h index 513ebc9d1b2b..068638b72725 100644 --- a/source/common/event/dispatcher_impl.h +++ b/source/common/event/dispatcher_impl.h @@ -70,8 +70,8 @@ class DispatcherImpl : Logger::Loggable, uint32_t events) override; Filesystem::WatcherPtr createFilesystemWatcher() override; Network::ListenerPtr createListener(Network::SocketSharedPtr&& socket, - Network::TcpListenerCallbacks& cb, - bool bind_to_port) override; + Network::TcpListenerCallbacks& cb, bool bind_to_port, + bool ignore_global_conn_limit) override; Network::UdpListenerPtr createUdpListener(Network::SocketSharedPtr socket, Network::UdpListenerCallbacks& cb, const envoy::config::core::v3::UdpSocketConfig& config) override; diff --git a/source/common/event/file_event_impl.cc b/source/common/event/file_event_impl.cc index 5b83e694d548..4c68e6193fc4 100644 --- a/source/common/event/file_event_impl.cc +++ b/source/common/event/file_event_impl.cc @@ -55,6 +55,9 @@ void FileEventImpl::activate(uint32_t events) { void FileEventImpl::assignEvents(uint32_t events, event_base* base) { ASSERT(dispatcher_.isThreadSafe()); ASSERT(base != nullptr); + // TODO(antoniovicente) remove this once ConnectionImpl can + // handle Read and Close events delivered together. + ASSERT(!((events & FileReadyType::Read) && (events & FileReadyType::Closed))); enabled_events_ = events; event_assign( &raw_event_, base, fd_, @@ -120,7 +123,6 @@ void FileEventImpl::unregisterEventIfEmulatedEdge(uint32_t event) { ASSERT(dispatcher_.isThreadSafe()); // This constexpr if allows the compiler to optimize away the function on POSIX if constexpr (PlatformDefaultTriggerType == FileTriggerType::EmulatedEdge) { - ASSERT((event & (FileReadyType::Read | FileReadyType::Write)) == event); if (trigger_ == FileTriggerType::EmulatedEdge) { auto new_event_mask = enabled_events_ & ~event; updateEvents(new_event_mask); @@ -156,7 +158,6 @@ void FileEventImpl::mergeInjectedEventsAndRunCb(uint32_t events) { injected_activation_events_ = injected_activation_events_ & ~FileReadyType::Read; } } - events |= injected_activation_events_; injected_activation_events_ = 0; activation_cb_->cancel(); diff --git a/source/common/filter/config_discovery_impl.cc b/source/common/filter/config_discovery_impl.cc index 74acc6b8383e..7ff56f26471f 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,28 +122,25 @@ 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::Http::FilterFactoryCb factory_callback = - factory.createFilterFactoryFromProto(*message, stat_prefix_, factory_context_); ENVOY_LOG(debug, "Updating filter config {}", filter_config_name_); Common::applyToAllWithCleanup( filter_config_providers_, - [&factory_callback, &version_info](DynamicFilterConfigProviderImplBase* provider, - std::shared_ptr cleanup) { - provider->onConfigUpdate(factory_callback, version_info, [cleanup] {}); + [&message = message, &version_info](DynamicFilterConfigProviderImplBase* provider, + std::shared_ptr cleanup) { + provider->onConfigUpdate(*message, version_info, [cleanup] {}); }, [this]() { stats_.config_reload_.inc(); }); last_config_hash_ = new_hash; - last_config_ = factory_callback; + 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; } @@ -165,7 +158,7 @@ void FilterConfigSubscription::onConfigUpdate( [this]() { stats_.config_reload_.inc(); }); last_config_hash_ = 0; - last_config_ = absl::nullopt; + last_config_ = nullptr; last_type_url_ = ""; last_filter_is_terminal_ = false; last_filter_name_ = ""; @@ -222,7 +215,7 @@ void FilterConfigProviderManagerImplBase::applyLastOrDefaultConfig( // update arrives first. In this case, use the default config, increment a metric, // and the applied config eventually converges once ECDS update arrives. bool last_config_valid = false; - if (subscription->lastConfig().has_value()) { + if (subscription->lastConfig()) { TRY_ASSERT_MAIN_THREAD { provider.validateTypeUrl(subscription->lastTypeUrl()); provider.validateTerminalFilter(filter_config_name, subscription->lastFilterName(), @@ -235,7 +228,7 @@ void FilterConfigProviderManagerImplBase::applyLastOrDefaultConfig( subscription->incrementConflictCounter(); } if (last_config_valid) { - provider.onConfigUpdate(subscription->lastConfig().value(), subscription->lastVersionInfo(), + provider.onConfigUpdate(*subscription->lastConfig(), subscription->lastVersionInfo(), nullptr); } } @@ -246,49 +239,25 @@ 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); - } - - Envoy::Http::FilterFactoryCb default_config = nullptr; - if (config_source.has_default_config()) { - default_config = getDefaultConfig(config_source.default_config(), filter_config_name, - factory_context, stat_prefix, last_filter_in_filter_chain, - filter_chain_type, require_type_urls); - } - - auto provider = std::make_unique( - subscription, require_type_urls, factory_context, default_config, last_filter_in_filter_chain, - filter_chain_type); - - // 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}; } -Http::FilterFactoryCb HttpFilterConfigProviderManagerImpl::getDefaultConfig( +ProtobufTypes::MessagePtr HttpFilterConfigProviderManagerImpl::getDefaultConfig( const ProtobufWkt::Any& proto_config, 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, - const absl::flat_hash_set require_type_urls) const { + Server::Configuration::FactoryContext& factory_context, bool last_filter_in_filter_chain, + const std::string& filter_chain_type, + const absl::flat_hash_set& require_type_urls) const { auto* default_factory = Config::Utility::getFactoryByType( proto_config); @@ -304,7 +273,15 @@ Http::FilterFactoryCb HttpFilterConfigProviderManagerImpl::getDefaultConfig( filter_config_name, default_factory->name(), filter_chain_type, default_factory->isTerminalFilterByProto(*message, factory_context), last_filter_in_filter_chain); - return default_factory->createFilterFactoryFromProto(*message, stat_prefix, factory_context); + return message; +} + +Http::FilterFactoryCb HttpFilterConfigProviderManagerImpl::instantiateFilterFactory( + const Protobuf::Message& message, const std::string& stat_prefix, + Server::Configuration::FactoryContext& factory_context) const { + auto* factory = Registry::FactoryRegistry< + Server::Configuration::NamedHttpFilterConfigFactory>::getFactoryByType(message.GetTypeName()); + return factory->createFilterFactoryFromProto(message, stat_prefix, factory_context); } } // namespace Filter diff --git a/source/common/filter/config_discovery_impl.h b/source/common/filter/config_discovery_impl.h index 7646f4217699..3d708adaa75a 100644 --- a/source/common/filter/config_discovery_impl.h +++ b/source/common/filter/config_discovery_impl.h @@ -31,8 +31,7 @@ using FilterConfigSubscriptionSharedPtr = std::shared_ptr { +class DynamicFilterConfigProviderImplBase : public Config::DynamicExtensionConfigProviderBase { public: DynamicFilterConfigProviderImplBase(FilterConfigSubscriptionSharedPtr& subscription, const absl::flat_hash_set& require_type_urls, @@ -63,30 +62,32 @@ class DynamicFilterConfigProviderImplBase /** * 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, - Envoy::Http::FilterFactoryCb default_config, + ProtobufTypes::MessagePtr&& default_config, bool last_filter_in_filter_chain, - const std::string& filter_chain_type) + 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_(default_config ? absl::make_optional(default_config) - : absl::nullopt), - tls_(factory_context.threadLocal()) { + default_configuration_(std::move(default_config)), tls_(factory_context.threadLocal()), + factory_cb_fn_(factory_cb_fn) { tls_.set([](Event::Dispatcher&) { return std::make_shared(); }); }; // 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::DynamicExtensionConfigProvider - void onConfigUpdate(Envoy::Http::FilterFactoryCb config, const std::string&, + // Config::DynamicExtensionConfigProviderBase + void onConfigUpdate(const Protobuf::Message& message, const std::string&, Config::ConfigAppliedCb cb) override { + const FactoryCb config = factory_cb_fn_(message); tls_.runOnAllThreads( [config, cb](OptRef tls) { tls->config_ = config; @@ -102,12 +103,15 @@ class DynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImplBa } void onConfigRemoved(Config::ConfigAppliedCb applied_on_all_threads) override { + const absl::optional default_config = + default_configuration_ ? absl::make_optional(factory_cb_fn_(*default_configuration_)) + : absl::nullopt; tls_.runOnAllThreads( - [config = default_configuration_](OptRef tls) { tls->config_ = config; }, - [this, applied_on_all_threads]() { + [config = default_config](OptRef tls) { tls->config_ = config; }, + [this, default_config, applied_on_all_threads]() { // This happens after all workers have discarded the previous config so it can be safely // deleted on the main thread by an update with the new config. - this->current_config_ = default_configuration_; + this->current_config_ = default_config; if (applied_on_all_threads) { applied_on_all_threads(); } @@ -123,14 +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}; - const absl::optional default_configuration_; + absl::optional current_config_{absl::nullopt}; + const ProtobufTypes::MessagePtr default_configuration_; ThreadLocal::TypedSlot tls_; + const std::function factory_cb_fn_; }; /** @@ -168,7 +173,7 @@ class FilterConfigSubscription const Init::SharedTargetImpl& initTarget() { return init_target_; } const std::string& name() { return filter_config_name_; } - const absl::optional& lastConfig() { return last_config_; } + const Protobuf::Message* lastConfig() { return last_config_.get(); } const std::string& lastTypeUrl() { return last_type_url_; } const std::string& lastVersionInfo() { return last_version_info_; } const std::string& lastFilterName() { return last_filter_name_; } @@ -189,13 +194,12 @@ class FilterConfigSubscription const std::string filter_config_name_; uint64_t last_config_hash_{0ul}; - absl::optional last_config_{absl::nullopt}; + ProtobufTypes::MessagePtr last_config_; std::string last_type_url_; std::string last_version_info_; 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}; @@ -218,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_; }; @@ -237,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, @@ -254,39 +265,86 @@ 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); + } + + 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); + }); - FilterConfigProviderPtr - createStaticFilterConfigProvider(const Envoy::Http::FilterFactoryCb& config, + // 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: - virtual Http::FilterFactoryCb + virtual ProtobufTypes::MessagePtr getDefaultConfig(const ProtobufWkt::Any& proto_config, 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, - const absl::flat_hash_set require_type_urls) const PURE; + bool last_filter_in_filter_chain, const std::string& filter_chain_type, + const absl::flat_hash_set& require_type_urls) const PURE; + + 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: - Http::FilterFactoryCb + ProtobufTypes::MessagePtr getDefaultConfig(const ProtobufWkt::Any& proto_config, 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, - const absl::flat_hash_set require_type_urls) const override; + bool last_filter_in_filter_chain, const std::string& filter_chain_type, + const absl::flat_hash_set& require_type_urls) const override; + Http::FilterFactoryCb + instantiateFilterFactory(const Protobuf::Message& message, const std::string& stat_prefix, + Server::Configuration::FactoryContext& factory_context) const override; }; } // namespace Filter diff --git a/source/common/formatter/substitution_formatter.cc b/source/common/formatter/substitution_formatter.cc index 51da65e89e36..0cc4e370e5b3 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 @@ -685,26 +686,31 @@ class StreamInfoSslConnectionInfoFieldExtractor : public StreamInfoFormatter::Fi }; StreamInfoFormatter::StreamInfoFormatter(const std::string& field_name) { + // TODO: Change this huge if-else ladder to use a switch case instead. if (field_name == "REQUEST_DURATION") { field_extractor_ = std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { - return stream_info.lastDownstreamRxByteReceived(); + StreamInfo::TimingUtility timing(stream_info); + return timing.lastDownstreamRxByteReceived(); }); } else if (field_name == "REQUEST_TX_DURATION") { field_extractor_ = std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { - return stream_info.lastUpstreamTxByteSent(); + StreamInfo::TimingUtility timing(stream_info); + return timing.lastUpstreamTxByteSent(); }); } else if (field_name == "RESPONSE_DURATION") { field_extractor_ = std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { - return stream_info.firstUpstreamRxByteReceived(); + StreamInfo::TimingUtility timing(stream_info); + return timing.firstUpstreamRxByteReceived(); }); } else if (field_name == "RESPONSE_TX_DURATION") { field_extractor_ = std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { - auto downstream = stream_info.lastDownstreamTxByteSent(); - auto upstream = stream_info.firstUpstreamRxByteReceived(); + StreamInfo::TimingUtility timing(stream_info); + auto downstream = timing.lastDownstreamTxByteSent(); + auto upstream = timing.firstUpstreamRxByteReceived(); absl::optional result; if (downstream && upstream) { @@ -788,9 +794,13 @@ StreamInfoFormatter::StreamInfoFormatter(const std::string& field_name) { return StreamInfo::ResponseFlagUtils::toShortString(stream_info); }); } else if (field_name == "UPSTREAM_HOST") { - field_extractor_ = - StreamInfoAddressFieldExtractor::withPort([](const StreamInfo::StreamInfo& stream_info) { - return stream_info.upstreamHost() ? stream_info.upstreamHost()->address() : nullptr; + field_extractor_ = StreamInfoAddressFieldExtractor::withPort( + [](const StreamInfo::StreamInfo& stream_info) + -> std::shared_ptr { + if (stream_info.upstreamInfo() && stream_info.upstreamInfo()->upstreamHost()) { + return stream_info.upstreamInfo()->upstreamHost()->address(); + } + return nullptr; }); } else if (field_name == "UPSTREAM_CLUSTER") { field_extractor_ = std::make_unique( @@ -812,9 +822,18 @@ StreamInfoFormatter::StreamInfoFormatter(const std::string& field_name) { : absl::make_optional(upstream_cluster_name); }); } else if (field_name == "UPSTREAM_LOCAL_ADDRESS") { - field_extractor_ = - StreamInfoAddressFieldExtractor::withPort([](const StreamInfo::StreamInfo& stream_info) { - return stream_info.upstreamLocalAddress(); + field_extractor_ = StreamInfoAddressFieldExtractor::withPort( + [](const StreamInfo::StreamInfo& stream_info) + -> std::shared_ptr { + if (stream_info.upstreamInfo().has_value()) { + return stream_info.upstreamInfo().value().get().upstreamLocalAddress(); + } + return nullptr; + }); + } else if (field_name == "UPSTREAM_REQUEST_ATTEMPT_COUNT") { + field_extractor_ = std::make_unique( + [](const StreamInfo::StreamInfo& stream_info) { + return stream_info.attemptCount().value_or(0); }); } else if (field_name == "DOWNSTREAM_LOCAL_ADDRESS") { field_extractor_ = @@ -935,8 +954,9 @@ StreamInfoFormatter::StreamInfoFormatter(const std::string& field_name) { field_extractor_ = std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { absl::optional result; - if (!stream_info.upstreamTransportFailureReason().empty()) { - result = stream_info.upstreamTransportFailureReason(); + if (stream_info.upstreamInfo().has_value() && + !stream_info.upstreamInfo().value().get().upstreamTransportFailureReason().empty()) { + result = stream_info.upstreamInfo().value().get().upstreamTransportFailureReason(); } return result; }); @@ -952,6 +972,20 @@ StreamInfoFormatter::StreamInfoFormatter(const std::string& field_name) { } return absl::nullopt; }); + } else if (field_name == "VIRTUAL_CLUSTER_NAME") { + field_extractor_ = std::make_unique( + [](const StreamInfo::StreamInfo& stream_info) -> absl::optional { + return stream_info.virtualClusterName(); + }); + } 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)); } @@ -1335,14 +1369,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/grpc/BUILD b/source/common/grpc/BUILD index 8e3cc89fd7e7..6729a434b246 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/async_client_impl.cc b/source/common/grpc/async_client_impl.cc index dd63a6bac88f..f1da95844a40 100644 --- a/source/common/grpc/async_client_impl.cc +++ b/source/common/grpc/async_client_impl.cc @@ -9,6 +9,8 @@ #include "source/common/http/header_map_impl.h" #include "source/common/http/utility.h" +#include "absl/strings/str_cat.h" + namespace Envoy { namespace Grpc { @@ -54,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; } @@ -226,10 +228,14 @@ AsyncRequestImpl::AsyncRequestImpl(AsyncClientImpl& parent, absl::string_view se : AsyncStreamImpl(parent, service_full_name, method_name, *this, options), request_(std::move(request)), callbacks_(callbacks) { - current_span_ = parent_span.spawnChild(Tracing::EgressConfig::get(), - "async " + parent.remote_cluster_name_ + " egress", - parent.time_source_.systemTime()); + current_span_ = + parent_span.spawnChild(Tracing::EgressConfig::get(), + absl::StrCat("async ", service_full_name, ".", method_name, " egress"), + parent.time_source_.systemTime()); current_span_->setTag(Tracing::Tags::get().UpstreamCluster, parent.remote_cluster_name_); + current_span_->setTag(Tracing::Tags::get().UpstreamAddress, parent.host_name_.empty() + ? parent.remote_cluster_name_ + : parent.host_name_); current_span_->setTag(Tracing::Tags::get().Component, Tracing::Tags::get().Proxy); } diff --git a/source/common/grpc/async_client_manager_impl.cc b/source/common/grpc/async_client_manager_impl.cc index ff712fca14bf..72de83c82b4a 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 14531c02ce30..9fab1d8018b1 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/source/common/grpc/buffered_async_client.h b/source/common/grpc/buffered_async_client.h new file mode 100644 index 000000000000..7c0467342942 --- /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/source/common/grpc/google_async_client_impl.cc b/source/common/grpc/google_async_client_impl.cc index 8e4539536cdc..dea6cc94e869 100644 --- a/source/common/grpc/google_async_client_impl.cc +++ b/source/common/grpc/google_async_client_impl.cc @@ -13,6 +13,7 @@ #include "source/common/router/header_parser.h" #include "source/common/tracing/http_tracer_impl.h" +#include "absl/strings/str_cat.h" #include "grpcpp/support/proto_buffer_reader.h" namespace Envoy { @@ -80,7 +81,7 @@ GoogleAsyncClientImpl::GoogleAsyncClientImpl(Event::Dispatcher& dispatcher, const envoy::config::core::v3::GrpcService& config, Api::Api& api, const StatNames& stat_names) : dispatcher_(dispatcher), tls_(tls), stat_prefix_(config.google_grpc().stat_prefix()), - scope_(scope), + target_uri_(config.google_grpc().target_uri()), scope_(scope), per_stream_buffer_limit_bytes_(PROTOBUF_GET_WRAPPED_OR_DEFAULT( config.google_grpc(), per_stream_buffer_limit_bytes, DefaultBufferLimitBytes)), metadata_parser_( @@ -425,10 +426,12 @@ GoogleAsyncRequestImpl::GoogleAsyncRequestImpl( Tracing::Span& parent_span, const Http::AsyncClient::RequestOptions& options) : GoogleAsyncStreamImpl(parent, service_full_name, method_name, *this, options), request_(std::move(request)), callbacks_(callbacks) { - current_span_ = parent_span.spawnChild(Tracing::EgressConfig::get(), - "async " + parent.stat_prefix_ + " egress", - parent.timeSource().systemTime()); + current_span_ = + parent_span.spawnChild(Tracing::EgressConfig::get(), + absl::StrCat("async ", service_full_name, ".", method_name, " egress"), + parent.timeSource().systemTime()); current_span_->setTag(Tracing::Tags::get().UpstreamCluster, parent.stat_prefix_); + current_span_->setTag(Tracing::Tags::get().UpstreamAddress, parent.target_uri_); current_span_->setTag(Tracing::Tags::get().Component, Tracing::Tags::get().Proxy); } diff --git a/source/common/grpc/google_async_client_impl.h b/source/common/grpc/google_async_client_impl.h index 4977ed219132..bd43b1103e9f 100644 --- a/source/common/grpc/google_async_client_impl.h +++ b/source/common/grpc/google_async_client_impl.h @@ -198,6 +198,7 @@ class GoogleAsyncClientImpl final : public RawAsyncClient, Logger::Loggable active_streams_; const std::string stat_prefix_; + const std::string target_uri_; Stats::ScopeSharedPtr scope_; GoogleAsyncClientStats stats_; uint64_t per_stream_buffer_limit_bytes_; diff --git a/source/common/grpc/typed_async_client.h b/source/common/grpc/typed_async_client.h index af9e8df9492a..f41a7cc7de73 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); } diff --git a/source/common/http/BUILD b/source/common/http/BUILD index 6298fe50b3b2..10de8d765b97 100644 --- a/source/common/http/BUILD +++ b/source/common/http/BUILD @@ -296,6 +296,7 @@ envoy_cc_library( "//source/common/common:empty_string", "//source/common/common:enum_to_int", "//source/common/common:linked_object", + "//source/common/common:perf_tracing_lib", "//source/common/common:regex_lib", "//source/common/common:scope_tracker", "//source/common/common:utility_lib", @@ -346,6 +347,7 @@ envoy_cc_library( hdrs = ["hash_policy.h"], deps = [ ":utility_lib", + "//envoy/common:hashable_interface", "//envoy/http:hash_policy_interface", "@envoy_api//envoy/config/route/v3:pkg_cc_proto", ], diff --git a/source/common/http/async_client_impl.h b/source/common/http/async_client_impl.h index 608813cc0172..3ff696d9f21b 100644 --- a/source/common/http/async_client_impl.h +++ b/source/common/http/async_client_impl.h @@ -259,6 +259,7 @@ class AsyncStreamImpl : public AsyncClient::Stream, } const Router::VirtualHost& virtualHost() const override { return virtual_host_; } bool autoHostRewrite() const override { return false; } + bool appendXfh() const override { return false; } bool includeVirtualHostRateLimits() const override { return true; } const Router::PathMatchCriterion& pathMatchCriterion() const override { return path_match_criterion_; @@ -376,10 +377,9 @@ 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 {} - ResponseHeaderMapOptRef continueHeaders() const override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; } + // The async client won't pause if sending 1xx headers so simply swallow any. + void encode1xxHeaders(ResponseHeaderMapPtr&&) override {} + 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/codec_client.cc b/source/common/http/codec_client.cc index 0b7b4e3a115f..1fa2115354da 100644 --- a/source/common/http/codec_client.cc +++ b/source/common/http/codec_client.cc @@ -79,6 +79,10 @@ RequestEncoder& CodecClient::newStream(ResponseDecoder& response_decoder) { request->encoder_ = &codec_->newStream(*request); request->encoder_->getStream().addCallbacks(*request); LinkedList::moveIntoList(std::move(request), active_requests_); + + auto upstream_info = connection_->streamInfo().upstreamInfo(); + upstream_info->setUpstreamNumStreams(upstream_info->upstreamNumStreams() + 1); + disableIdleTimer(); return *active_requests_.front()->encoder_; } diff --git a/source/common/http/codec_client.h b/source/common/http/codec_client.h index 684c962380f3..093e28304402 100644 --- a/source/common/http/codec_client.h +++ b/source/common/http/codec_client.h @@ -158,6 +158,11 @@ class CodecClient : protected Logger::Loggable, codec_callbacks_->onSettings(settings); } } + void onMaxStreamsChanged(uint32_t num_streams) override { + if (codec_callbacks_) { + codec_callbacks_->onMaxStreamsChanged(num_streams); + } + } void onIdleTimeout() { host_->cluster().stats().upstream_cx_idle_timeout_.inc(); diff --git a/source/common/http/codec_wrappers.h b/source/common/http/codec_wrappers.h index c3660af26c00..37b62d35fbfa 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 5a27e44a5f32..ba4886d5a9f6 100644 --- a/source/common/http/conn_manager_impl.cc +++ b/source/common/http/conn_manager_impl.cc @@ -27,6 +27,7 @@ #include "source/common/common/empty_string.h" #include "source/common/common/enum_to_int.h" #include "source/common/common/fmt.h" +#include "source/common/common/perf_tracing.h" #include "source/common/common/scope_tracker.h" #include "source/common/common/utility.h" #include "source/common/http/codes.h" @@ -260,6 +261,13 @@ void ConnectionManagerImpl::doDeferredStreamDestroy(ActiveStream& stream) { read_callbacks_->connection().dispatcher().deferredDelete(stream.removeFromList(streams_)); + // The response_encoder should never be dangling (unless we're destroying a + // stream we are recreating) as the codec level stream will either outlive the + // ActiveStream, or be alive in deferred deletion queue at this point. + if (stream.response_encoder_) { + stream.response_encoder_->getStream().removeCallbacks(stream); + } + if (connection_idle_timer_ && streams_.empty()) { connection_idle_timer_->enableTimer(config_.idleTimeout().value()); } @@ -267,6 +275,7 @@ void ConnectionManagerImpl::doDeferredStreamDestroy(ActiveStream& stream) { RequestDecoder& ConnectionManagerImpl::newStream(ResponseEncoder& response_encoder, bool is_internally_created) { + TRACE_EVENT("core", "ConnectionManagerImpl::newStream"); if (connection_idle_timer_) { connection_idle_timer_->disableTimer(); } @@ -289,7 +298,7 @@ RequestDecoder& ConnectionManagerImpl::newStream(ResponseEncoder& response_encod new_stream->state_.saw_connection_close_ = true; // Prevent erroneous debug log of closing due to incoming connection close header. drain_state_ = DrainState::Closing; - } else { + } else if (drain_state_ == DrainState::NotDraining) { startDrainSequence(); } ENVOY_CONN_LOG(debug, "max requests per connection reached", read_callbacks_->connection()); @@ -318,7 +327,7 @@ void ConnectionManagerImpl::handleCodecError(absl::string_view error) { // GOAWAY. doConnectionClose(Network::ConnectionCloseType::FlushWriteAndDelay, StreamInfo::ResponseFlag::DownstreamProtocolError, - absl::StrCat("codec error:", error)); + absl::StrCat("codec_error:", StringUtil::replaceAllEmptySpace(error))); } void ConnectionManagerImpl::createCodec(Buffer::Instance& data) { @@ -885,7 +894,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 +1341,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 +1354,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, @@ -1464,7 +1472,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); } @@ -1531,6 +1540,10 @@ void ConnectionManagerImpl::ActiveStream::onResetStream(StreamResetReason reset_ filter_manager_.streamInfo().setResponseCodeDetails( StreamInfo::ResponseCodeDetails::get().Overload); } + if (Runtime::runtimeFeatureEnabled( + "envoy.reloadable_features.handle_stream_reset_during_hcm_encoding")) { + filter_manager_.onDownstreamReset(); + } connection_manager_.doDeferredStreamDestroy(*this); } diff --git a/source/common/http/conn_manager_impl.h b/source/common/http/conn_manager_impl.h index c9a5b9eb5e1e..11e59f4d6826 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; @@ -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()); @@ -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); } @@ -353,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/conn_manager_utility.cc b/source/common/http/conn_manager_utility.cc index c75401a9ede5..6c07a7dab104 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/conn_pool_base.cc b/source/common/http/conn_pool_base.cc index 295220d79db9..4f823caf777c 100644 --- a/source/common/http/conn_pool_base.cc +++ b/source/common/http/conn_pool_base.cc @@ -122,9 +122,11 @@ 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; } // As we don't increase stream limits when maxConcurrentStreams goes up, treat // a stream limit of 0 as a GOAWAY. diff --git a/source/common/http/conn_pool_base.h b/source/common/http/conn_pool_base.h index 47bdfdad7f3f..e3bdd93928f5 100644 --- a/source/common/http/conn_pool_base.h +++ b/source/common/http/conn_pool_base.h @@ -201,19 +201,6 @@ class MultiplexedActiveClientBase : public CodecClientCallbacks, void onGoAway(Http::GoAwayErrorCode error_code) override; void onSettings(ReceivedSettings& settings) override; - // As this is called once when the stream is closed, it's a good place to - // update the counter as one stream has been "returned" and the negative - // capacity should be reduced. - bool hadNegativeDeltaOnStreamClosed() override { - int ret = negative_capacity_ != 0; - if (negative_capacity_ > 0) { - negative_capacity_--; - } - return ret; - } - - uint64_t negative_capacity_{}; - protected: MultiplexedActiveClientBase(Envoy::Http::HttpConnPoolImplBase& parent, Upstream::Host::CreateConnectionData& data, diff --git a/source/common/http/filter_manager.cc b/source/common/http/filter_manager.cc index fc15f8041f22..6e1dae737c53 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,18 +390,18 @@ 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_.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, @@ -831,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); } } @@ -1014,26 +1013,31 @@ void FilterManager::sendDirectLocalReply( state_.non_100_response_headers_encoded_ = true; filter_manager_callbacks_.encodeHeaders(*filter_manager_callbacks_.responseHeaders(), end_stream); - + if (state_.saw_downstream_reset_) { + return; + } maybeEndEncode(end_stream); }, [&](Buffer::Instance& data, bool end_stream) -> void { filter_manager_callbacks_.encodeData(data, end_stream); + if (state_.saw_downstream_reset_) { + return; + } maybeEndEncode(end_stream); }}, 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 +1049,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( @@ -1145,13 +1149,16 @@ void FilterManager::encodeHeaders(ActiveStreamEncoderFilter* filter, ResponseHea sendLocalReply( Http::Code::BadGateway, status.message(), nullptr, absl::nullopt, absl::StrCat(StreamInfo::ResponseCodeDetails::get().FilterRemovedRequiredResponseHeaders, - "{", status.message(), "}")); + "{", StringUtil::replaceAllEmptySpace(status.message()), "}")); return; } const bool modified_end_stream = (end_stream && continue_data_entry == encoder_filters_.end()); state_.non_100_response_headers_encoded_ = true; filter_manager_callbacks_.encodeHeaders(headers, modified_end_stream); + if (state_.saw_downstream_reset_) { + return; + } maybeEndEncode(modified_end_stream); if (!modified_end_stream) { @@ -1291,6 +1298,9 @@ void FilterManager::encodeData(ActiveStreamEncoderFilter* filter, Buffer::Instan const bool modified_end_stream = end_stream && trailers_added_entry == encoder_filters_.end(); filter_manager_callbacks_.encodeData(data, modified_end_stream); + if (state_.saw_downstream_reset_) { + return; + } maybeEndEncode(modified_end_stream); // If trailers were adding during encodeData we need to trigger decodeTrailers in order @@ -1333,6 +1343,9 @@ void FilterManager::encodeTrailers(ActiveStreamEncoderFilter* filter, } filter_manager_callbacks_.encodeTrailers(trailers); + if (state_.saw_downstream_reset_) { + return; + } maybeEndEncode(true); } @@ -1525,11 +1538,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_.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 633fa6862d3d..e7cff1b731e8 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,8 +259,8 @@ struct ActiveStreamDecoderFilter : public ActiveStreamFilterBase, std::function modify_headers, const absl::optional grpc_status, absl::string_view details) override; - void encode100ContinueHeaders(ResponseHeaderMapPtr&& headers) override; - ResponseHeaderMapOptRef continueHeaders() const override; + void encode1xxHeaders(ResponseHeaderMapPtr&& headers) override; + ResponseHeaderMapOptRef informationalHeaders() const override; void encodeHeaders(ResponseHeaderMapPtr&& headers, bool end_stream, absl::string_view details) override; ResponseHeaderMapOptRef responseHeaders() const 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. @@ -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. @@ -631,9 +631,6 @@ class OverridableRemoteConnectionInfoSetterStreamInfo : public StreamInfo::Strea Ssl::ConnectionInfoConstSharedPtr sslConnection() const override { return StreamInfoImpl::downstreamAddressProvider().sslConnection(); } - Ssl::ConnectionInfoConstSharedPtr upstreamSslConnection() const override { - return StreamInfoImpl::upstreamSslConnection(); - } void dumpState(std::ostream& os, int indent_level) const override { StreamInfoImpl::dumpState(os, indent_level); @@ -643,6 +640,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_; @@ -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()); @@ -942,6 +942,8 @@ class FilterManager : public ScopeTrackedObject, void contextOnContinue(ScopeTrackedObjectStack& tracked_object_stack); + void onDownstreamReset() { state_.saw_downstream_reset_ = true; } + private: // Indicates which filter to start the iteration with. enum class FilterIterationStartState { AlwaysStartFromNext, CanStartFromCurrent }; @@ -970,7 +972,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 +1048,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,10 +1061,11 @@ 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) {} + decoder_filter_chain_aborted_(false), encoder_filter_chain_aborted_(false), + saw_downstream_reset_(false) {} uint32_t filter_call_state_{0}; @@ -1070,9 +1073,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. @@ -1085,6 +1088,7 @@ class FilterManager : public ScopeTrackedObject, // True when the filter chain iteration was aborted with local reply. bool decoder_filter_chain_aborted_ : 1; bool encoder_filter_chain_aborted_ : 1; + bool saw_downstream_reset_ : 1; // The following 3 members are booleans rather than part of the space-saving bitfield as they // are passed as arguments to functions expecting bools. Extend State using the bitfield diff --git a/source/common/http/hash_policy.cc b/source/common/http/hash_policy.cc index a2eda3f01579..1364404fda23 100644 --- a/source/common/http/hash_policy.cc +++ b/source/common/http/hash_policy.cc @@ -2,6 +2,7 @@ #include +#include "envoy/common/hashable.h" #include "envoy/config/route/v3/route_components.pb.h" #include "source/common/common/matchers.h" diff --git a/source/common/http/hash_policy.h b/source/common/http/hash_policy.h index 31368e552408..20425a0f4d46 100644 --- a/source/common/http/hash_policy.h +++ b/source/common/http/hash_policy.h @@ -8,8 +8,7 @@ namespace Envoy { namespace Http { /** - * Implementation of HashPolicy that reads from the proto route config and only currently supports - * hashing on an HTTP header. + * Implementation of HashPolicy that reads from the proto route config. */ class HashPolicyImpl : public HashPolicy { public: diff --git a/source/common/http/header_map_impl.cc b/source/common/http/header_map_impl.cc index bfd77c8dcfa3..1f7d51e357eb 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 5f64bc311c5c..441571b923f5 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/http/header_utility.cc b/source/common/http/header_utility.cc index 5ec89d6228c6..a7f630b041b4 100644 --- a/source/common/http/header_utility.cc +++ b/source/common/http/header_utility.cc @@ -201,6 +201,15 @@ bool HeaderUtility::authorityIsValid(const absl::string_view header_value) { header_value.size()) != 0; } +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"; +} + 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 8e1ae6decd1c..40a42c7da604 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/headers.h b/source/common/http/headers.h index ccae31bf706c..75b574599ef7 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/source/common/http/http1/codec_impl.cc b/source/common/http/http1/codec_impl.cc index bcca80fd2582..0ddc9c8a69aa 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(); } @@ -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); } @@ -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 @@ -414,9 +414,15 @@ void ResponseEncoderImpl::encodeHeaders(const ResponseHeaderMap& headers, bool e connection_.addIntToBuffer(numeric_status); connection_.addCharToBuffer(' '); - const char* status_string = CodeUtility::toString(static_cast(numeric_status)); - uint32_t status_string_len = strlen(status_string); - connection_.copyToBuffer(status_string, status_string_len); + StatefulHeaderKeyFormatterOptConstRef formatter(headers.formatter()); + + if (formatter.has_value() && !formatter->getReasonPhrase().empty()) { + connection_.addToBuffer(formatter->getReasonPhrase()); + } else { + const char* status_string = CodeUtility::toString(static_cast(numeric_status)); + uint32_t status_string_len = strlen(status_string); + connection_.copyToBuffer(status_string, status_string_len); + } connection_.addCharToBuffer('\r'); connection_.addCharToBuffer('\n'); @@ -996,18 +1002,17 @@ ServerConnectionImpl::ServerConnectionImpl( uint32_t ServerConnectionImpl::getHeadersSize() { // Add in the size of the request URL if processing request headers. - const uint32_t url_size = (!processing_trailers_ && active_request_.has_value()) - ? active_request_.value().request_url_.size() - : 0; + const uint32_t url_size = + (!processing_trailers_ && active_request_) ? active_request_->request_url_.size() : 0; return url_size + ConnectionImpl::getHeadersSize(); } void ServerConnectionImpl::onEncodeComplete() { - if (active_request_.value().remote_complete_) { + if (active_request_->remote_complete_) { // Only do this if remote is complete. If we are replying before the request is complete the // only logical thing to do is for higher level code to reset() / close the connection so we // leave the request around so that it can fire reset callbacks. - active_request_.reset(); + connection_.dispatcher().deferredDelete(std::move(active_request_)); } } @@ -1018,12 +1023,11 @@ Status ServerConnectionImpl::handlePath(RequestHeaderMap& headers, absl::string_ bool is_connect = (method == header_values.MethodValues.Connect); // The url is relative or a wildcard when the method is OPTIONS. Nothing to do here. - auto& active_request = active_request_.value(); - if (!is_connect && !active_request.request_url_.getStringView().empty() && - (active_request.request_url_.getStringView()[0] == '/' || + if (!is_connect && !active_request_->request_url_.getStringView().empty() && + (active_request_->request_url_.getStringView()[0] == '/' || (method == header_values.MethodValues.Options && - active_request.request_url_.getStringView()[0] == '*'))) { - headers.addViaMove(std::move(path), std::move(active_request.request_url_)); + active_request_->request_url_.getStringView()[0] == '*'))) { + headers.addViaMove(std::move(path), std::move(active_request_->request_url_)); return okStatus(); } @@ -1032,12 +1036,12 @@ Status ServerConnectionImpl::handlePath(RequestHeaderMap& headers, absl::string_ // CONNECT "urls" are actually host:port so look like absolute URLs to the above checks. // Absolute URLS in CONNECT requests will be rejected below by the URL class validation. if (!codec_settings_.allow_absolute_url_ && !is_connect) { - headers.addViaMove(std::move(path), std::move(active_request.request_url_)); + headers.addViaMove(std::move(path), std::move(active_request_->request_url_)); return okStatus(); } Utility::Url absolute_url; - if (!absolute_url.initialize(active_request.request_url_.getStringView(), is_connect)) { + if (!absolute_url.initialize(active_request_->request_url_.getStringView(), is_connect)) { RETURN_IF_ERROR(sendProtocolError(Http1ResponseCodeDetails::get().InvalidUrl)); return codecProtocolError("http/1.1 protocol error: invalid url in request line"); } @@ -1068,7 +1072,7 @@ Status ServerConnectionImpl::handlePath(RequestHeaderMap& headers, absl::string_ if (!absolute_url.pathAndQueryParams().empty()) { headers.setPath(absolute_url.pathAndQueryParams()); } - active_request.request_url_.clear(); + active_request_->request_url_.clear(); return okStatus(); } @@ -1076,8 +1080,7 @@ Envoy::StatusOr ServerConnectionImpl::onHeadersCompleteBase() { // Handle the case where response happens prior to request complete. It's up to upper layer code // to disconnect the connection but we shouldn't fire any more events since it doesn't make // sense. - if (active_request_.has_value()) { - auto& active_request = active_request_.value(); + if (active_request_) { auto& headers = absl::get(headers_or_trailers_); ENVOY_CONN_LOG(trace, "Server: onHeadersComplete size={}", connection_, headers->size()); @@ -1097,13 +1100,13 @@ Envoy::StatusOr ServerConnectionImpl::onHeadersCompleteBase() { // Inform the response encoder about any HEAD method, so it can set content // length and transfer encoding headers correctly. const Http::HeaderValues& header_values = Http::Headers::get(); - active_request.response_encoder_.setIsResponseToHeadRequest(parser_->methodName() == - header_values.MethodValues.Head); - active_request.response_encoder_.setIsResponseToConnectRequest( + active_request_->response_encoder_.setIsResponseToHeadRequest(parser_->methodName() == + header_values.MethodValues.Head); + active_request_->response_encoder_.setIsResponseToConnectRequest( parser_->methodName() == header_values.MethodValues.Connect); RETURN_IF_ERROR(handlePath(*headers, parser_->methodName())); - ASSERT(active_request.request_url_.empty()); + ASSERT(active_request_->request_url_.empty()); headers->setMethod(parser_->methodName()); @@ -1124,7 +1127,7 @@ Envoy::StatusOr ServerConnectionImpl::onHeadersCompleteBase() { if (parser_->isChunked() || (parser_->contentLength().has_value() && parser_->contentLength().value() > 0) || handling_upgrade_) { - active_request.request_decoder_->decodeHeaders(std::move(headers), false); + active_request_->request_decoder_->decodeHeaders(std::move(headers), false); // If the connection has been closed (or is closing) after decoding headers, pause the parser // so we return control to the caller. @@ -1141,13 +1144,12 @@ Envoy::StatusOr ServerConnectionImpl::onHeadersCompleteBase() { Status ServerConnectionImpl::onMessageBeginBase() { if (!resetStreamCalled()) { - ASSERT(!active_request_.has_value()); - active_request_.emplace(*this, std::move(bytes_meter_before_stream_)); - auto& active_request = active_request_.value(); + ASSERT(active_request_ == nullptr); + active_request_ = std::make_unique(*this, std::move(bytes_meter_before_stream_)); if (resetStreamCalled()) { return codecClientError("cannot create new streams after calling reset"); } - active_request.request_decoder_ = &callbacks_.newStream(active_request.response_encoder_); + active_request_->request_decoder_ = &callbacks_.newStream(active_request_->response_encoder_); // Check for pipelined request flood as we prepare to accept a new request. // Parse errors that happen prior to onMessageBegin result in stream termination, it is not @@ -1158,8 +1160,8 @@ Status ServerConnectionImpl::onMessageBeginBase() { } Status ServerConnectionImpl::onUrl(const char* data, size_t length) { - if (active_request_.has_value()) { - active_request_.value().request_url_.append(data, length); + if (active_request_) { + active_request_->request_url_.append(data, length); RETURN_IF_ERROR(checkMaxHeadersSize()); } @@ -1169,31 +1171,31 @@ Status ServerConnectionImpl::onUrl(const char* data, size_t length) { void ServerConnectionImpl::onBody(Buffer::Instance& data) { ASSERT(!deferred_end_stream_headers_); - if (active_request_.has_value()) { + if (active_request_) { ENVOY_CONN_LOG(trace, "body size={}", connection_, data.length()); - active_request_.value().request_decoder_->decodeData(data, false); + active_request_->request_decoder_->decodeData(data, false); } } ParserStatus ServerConnectionImpl::onMessageCompleteBase() { ASSERT(!handling_upgrade_); - if (active_request_.has_value()) { - auto& active_request = active_request_.value(); + if (active_request_) { + + // The request_decoder should be non-null after we've called the newStream on callbacks. + ASSERT(active_request_->request_decoder_); + active_request_->response_encoder_.readDisable(true); + active_request_->remote_complete_ = true; - if (active_request.request_decoder_) { - active_request.response_encoder_.readDisable(true); - } - active_request.remote_complete_ = true; if (deferred_end_stream_headers_) { - active_request.request_decoder_->decodeHeaders( + active_request_->request_decoder_->decodeHeaders( std::move(absl::get(headers_or_trailers_)), true); deferred_end_stream_headers_ = false; } else if (processing_trailers_) { - active_request.request_decoder_->decodeTrailers( + active_request_->request_decoder_->decodeTrailers( std::move(absl::get(headers_or_trailers_))); } else { Buffer::OwnedImpl buffer; - active_request.request_decoder_->decodeData(buffer, true); + active_request_->request_decoder_->decodeData(buffer, true); } // Reset to ensure no information from one requests persists to the next. @@ -1207,19 +1209,19 @@ ParserStatus ServerConnectionImpl::onMessageCompleteBase() { } void ServerConnectionImpl::onResetStream(StreamResetReason reason) { - active_request_.value().response_encoder_.runResetCallbacks(reason); - active_request_.reset(); + active_request_->response_encoder_.runResetCallbacks(reason); + connection_.dispatcher().deferredDelete(std::move(active_request_)); } Status ServerConnectionImpl::sendProtocolError(absl::string_view details) { // We do this here because we may get a protocol error before we have a logical stream. - if (!active_request_.has_value()) { + if (active_request_ == nullptr) { RETURN_IF_ERROR(onMessageBegin()); } - ASSERT(active_request_.has_value()); + ASSERT(active_request_); - active_request_.value().response_encoder_.setDetails(details); - if (!active_request_.value().response_encoder_.startedResponse()) { + active_request_->response_encoder_.setDetails(details); + if (!active_request_->response_encoder_.startedResponse()) { active_request_->request_decoder_->sendLocalReply( error_code_, CodeUtility::toString(error_code_), nullptr, absl::nullopt, details); } @@ -1227,13 +1229,13 @@ Status ServerConnectionImpl::sendProtocolError(absl::string_view details) { } void ServerConnectionImpl::onAboveHighWatermark() { - if (active_request_.has_value()) { - active_request_.value().response_encoder_.runHighWatermarkCallbacks(); + if (active_request_) { + active_request_->response_encoder_.runHighWatermarkCallbacks(); } } void ServerConnectionImpl::onBelowLowWatermark() { - if (active_request_.has_value()) { - active_request_.value().response_encoder_.runLowWatermarkCallbacks(); + if (active_request_) { + active_request_->response_encoder_.runLowWatermarkCallbacks(); } } @@ -1305,6 +1307,16 @@ RequestEncoder& ClientConnectionImpl::newStream(ResponseDecoder& response_decode return pending_response_.value().encoder_; } +Status ClientConnectionImpl::onStatus(const char* data, size_t length) { + auto& headers = absl::get(headers_or_trailers_); + StatefulHeaderKeyFormatterOptRef formatter(headers->formatter()); + if (formatter.has_value()) { + formatter->setReasonPhrase(absl::string_view(data, length)); + } + + return okStatus(); +} + Envoy::StatusOr ClientConnectionImpl::onHeadersCompleteBase() { ENVOY_CONN_LOG(trace, "status_code {}", connection_, parser_->statusCode()); @@ -1347,8 +1359,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 44df405a0a2a..94864dff09f0 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); } @@ -467,10 +467,11 @@ class ServerConnectionImpl : public ServerConnection, public ConnectionImpl { /** * An active HTTP/1.1 request. */ - struct ActiveRequest { + struct ActiveRequest : public Event::DeferredDeletable { ActiveRequest(ServerConnectionImpl& connection, StreamInfo::BytesMeterSharedPtr&& bytes_meter) : response_encoder_(connection, std::move(bytes_meter), connection.codec_settings_.stream_error_on_invalid_http_message_) {} + ~ActiveRequest() override = default; void dumpState(std::ostream& os, int indent_level) const; HeaderString request_url_; @@ -478,7 +479,7 @@ class ServerConnectionImpl : public ServerConnection, public ConnectionImpl { ResponseEncoderImpl response_encoder_; bool remote_complete_{}; }; - absl::optional& activeRequest() { return active_request_; } + ActiveRequest* activeRequest() { return active_request_.get(); } // ConnectionImpl ParserStatus onMessageCompleteBase() override; // Add the size of the request_url to the reported header size when processing request headers. @@ -498,10 +499,11 @@ class ServerConnectionImpl : public ServerConnection, public ConnectionImpl { // ParserCallbacks. Status onUrl(const char* data, size_t length) override; + Status onStatus(const char*, size_t) override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; } // ConnectionImpl void onEncodeComplete() override; StreamInfo::BytesMeter& getBytesMeter() override { - if (active_request_.has_value()) { + if (active_request_) { return *(active_request_->response_encoder_.getStream().bytesMeter()); } if (bytes_meter_before_stream_ == nullptr) { @@ -550,7 +552,7 @@ class ServerConnectionImpl : public ServerConnection, public ConnectionImpl { Status checkHeaderNameForUnderscores() override; ServerConnectionCallbacks& callbacks_; - absl::optional active_request_; + std::unique_ptr active_request_; const Buffer::OwnedBufferFragmentImpl::Releasor response_buffer_releasor_; uint32_t outbound_responses_{}; // This defaults to 2, which functionally disables pipelining. If any users @@ -592,6 +594,7 @@ class ClientConnectionImpl : public ClientConnection, public ConnectionImpl { // ParserCallbacks. Status onUrl(const char*, size_t) override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; } + Status onStatus(const char* data, size_t length) override; // ConnectionImpl Http::Status dispatch(Buffer::Instance& data) override; void onEncodeComplete() override {} diff --git a/source/common/http/http1/legacy_parser_impl.cc b/source/common/http/http1/legacy_parser_impl.cc index 840ba419099f..2da1d3bc177c 100644 --- a/source/common/http/http1/legacy_parser_impl.cc +++ b/source/common/http/http1/legacy_parser_impl.cc @@ -51,7 +51,11 @@ class LegacyHttpParserImpl::Impl { auto status = conn_impl->onUrl(at, length); return conn_impl->setAndCheckCallbackStatus(std::move(status)); }, - nullptr, // on_status + [](http_parser* parser, const char* at, size_t length) -> int { + auto* conn_impl = static_cast(parser->data); + auto status = conn_impl->onStatus(at, length); + return conn_impl->setAndCheckCallbackStatus(std::move(status)); + }, [](http_parser* parser, const char* at, size_t length) -> int { auto* conn_impl = static_cast(parser->data); auto status = conn_impl->onHeaderField(at, length); diff --git a/source/common/http/http1/parser.h b/source/common/http/http1/parser.h index 23f6795a1f75..324ae41cc414 100644 --- a/source/common/http/http1/parser.h +++ b/source/common/http/http1/parser.h @@ -72,6 +72,14 @@ class ParserCallbacks { */ virtual Status onHeaderValue(const char* data, size_t length) PURE; + /** + * Called when response status data is received. + * @param data supplies the start address. + * @param length supplies the length. + * @return Status representing success or failure. + */ + virtual Status onStatus(const char* data, size_t length) PURE; + /** * Called when headers are complete. A base routine happens first then a virtual dispatch is * invoked. Note that this only applies to headers and NOT trailers. End of diff --git a/source/common/http/http2/BUILD b/source/common/http/http2/BUILD index 3ec423ce65c1..c4c4bb1fb25d 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 9ae282779949..c681acb6525d 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,8 +211,27 @@ void ConnectionImpl::StreamImpl::buildHeaders(std::vector& final_hea }); } -void ConnectionImpl::ServerStreamImpl::encode100ContinueHeaders(const ResponseHeaderMap& headers) { - ASSERT(headers.Status()->value() == "100"); +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 @@ -352,9 +400,9 @@ 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_.decode100ContinueHeaders(std::move(headers)); + response_decoder_.decode1xxHeaders(std::move(headers)); } else { response_decoder_.decodeHeaders(std::move(headers), remote_end_stream_); } @@ -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; } @@ -546,8 +639,11 @@ void ConnectionImpl::StreamImpl::resetStream(StreamResetReason reason) { // If we submit a reset, nghttp2 will cancel outbound frames that have not yet been sent. // We want these frames to go out so we defer the reset until we send all of the frames that - // end the local stream. - if (useDeferredReset() && local_end_stream_ && !local_end_stream_sent_) { + // end the local stream. However, if we're resetting the stream due to + // overload, we should reset the stream as soon as possible to free used + // resources. + if (useDeferredReset() && local_end_stream_ && !local_end_stream_sent_ && + reason != StreamResetReason::OverloadManager) { ASSERT(parent_.getStream(stream_id_) != nullptr); parent_.pending_deferred_reset_streams_.emplace(stream_id_, this); deferred_reset_ = reason; @@ -566,14 +662,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::getMetadataEncoder() { +MetadataEncoder& ConnectionImpl::StreamImpl::getMetadataEncoderOld() { + if (metadata_encoder_old_ == nullptr) { + metadata_encoder_old_ = std::make_unique(); + } + return *metadata_encoder_old_; +} + +NewMetadataEncoder& ConnectionImpl::StreamImpl::getMetadataEncoder() { if (metadata_encoder_ == nullptr) { - metadata_encoder_ = std::make_unique(); + metadata_encoder_ = std::make_unique(); } return *metadata_encoder_; } @@ -608,8 +716,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 +751,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 +769,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 +832,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 +871,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 +891,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 +903,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::HTTP2_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 +920,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 +934,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 +1047,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 { @@ -1068,7 +1213,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); } @@ -1132,9 +1278,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 +1318,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 +1326,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 +1371,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 +1425,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 +1520,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 +1536,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 +1574,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 +1677,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 { @@ -1646,12 +1868,17 @@ 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); - 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)); @@ -1708,36 +1935,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); @@ -1756,8 +1971,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 +2006,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(); } @@ -1819,12 +2044,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 9a8bfc7b7c6b..4f775f2cb7b4 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 init(nghttp2_session* session, ConnectionImpl* connection, + 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(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; @@ -411,7 +442,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); @@ -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,12 +596,11 @@ 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. 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(); @@ -603,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_; }; /** @@ -636,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/http/http2/metadata_decoder.h b/source/common/http/http2/metadata_decoder.h index e5f91925d508..9971a35f8349 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 81002de8566d..544e85600dbf 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 9fe06ce6e459..1f1295677448 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/http/http3/codec_stats.h b/source/common/http/http3/codec_stats.h index 9e7ca5859b2f..aed84aeafe93 100644 --- a/source/common/http/http3/codec_stats.h +++ b/source/common/http/http3/codec_stats.h @@ -18,6 +18,7 @@ namespace Http3 { COUNTER(rx_reset) \ COUNTER(tx_reset) \ COUNTER(metadata_not_supported_error) \ + COUNTER(quic_version_h3_29) \ COUNTER(quic_version_rfc_v1) \ COUNTER(tx_flush_timeout) diff --git a/source/common/http/http3/conn_pool.cc b/source/common/http/http3/conn_pool.cc index fbc3e4503607..059806bc002b 100644 --- a/source/common/http/http3/conn_pool.cc +++ b/source/common/http/http3/conn_pool.cc @@ -30,17 +30,24 @@ ActiveClient::ActiveClient(Envoy::Http::HttpConnPoolImplBase& parent, parent.host()->cluster().stats().upstream_cx_http3_total_, data) { } +void ActiveClient::onMaxStreamsChanged(uint32_t num_streams) { + updateCapacity(num_streams); + if (state() == ActiveClient::State::BUSY && currentUnusedCapacity() != 0) { + 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); + } +} + void Http3ConnPoolImpl::setQuicConfigFromClusterConfig(const Upstream::ClusterInfo& cluster, quic::QuicConfig& quic_config) { - // TODO(alyssawilk) use and test other defaults. + Quic::convertQuicConfig(cluster.http3Options().quic_protocol_options(), quic_config); quic::QuicTime::Delta crypto_timeout = quic::QuicTime::Delta::FromMilliseconds(cluster.connectTimeout().count()); quic_config.set_max_time_before_crypto_handshake(crypto_timeout); - int32_t max_streams = getMaxStreams(cluster); - quic_config.SetMaxBidirectionalStreamsToSend(max_streams); - quic_config.SetMaxUnidirectionalStreamsToSend(max_streams); - Quic::configQuicInitialFlowControlWindow(cluster.http3Options().quic_protocol_options(), - quic_config); } Http3ConnPoolImpl::Http3ConnPoolImpl( @@ -85,9 +92,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); @@ -101,6 +109,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/http/http3/conn_pool.h b/source/common/http/http3/conn_pool.h index 6ae87420207e..d89c5fa69ab0 100644 --- a/source/common/http/http3/conn_pool.h +++ b/source/common/http/http3/conn_pool.h @@ -23,6 +23,78 @@ class ActiveClient : public MultiplexedActiveClientBase { public: ActiveClient(Envoy::Http::HttpConnPoolImplBase& parent, Upstream::Host::CreateConnectionData& data); + + // Http::ConnectionCallbacks + void onMaxStreamsChanged(uint32_t num_streams) override; + + RequestEncoder& newStreamEncoder(ResponseDecoder& response_decoder) override { + ASSERT(quiche_capacity_ != 0); + // Each time a quic stream is allocated the quic capacity needs to get + // decremented. See comments by quiche_capacity_. + updateCapacity(quiche_capacity_ - 1); + 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 { + return std::min(quiche_capacity_, effectiveConcurrentStreamLimit()); + } + + void updateCapacity(uint64_t new_quiche_capacity) { + // Each time we update the capacity make sure to reflect the update in the + // connection pool. + // + // Due to interplay between the max number of concurrent streams Envoy will + // allow and the max number of streams per connection this is not as simple + // as just updating based on the delta between quiche_capacity_ and + // new_quiche_capacity, so we use the delta between the actual calculated + // capacity before and after the update. + uint64_t old_capacity = currentUnusedCapacity(); + quiche_capacity_ = new_quiche_capacity; + uint64_t new_capacity = currentUnusedCapacity(); + + 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); + } + } + } + + // Unlike HTTP/2 and HTTP/1, rather than having a cap on the number of active + // streams, QUIC has a fixed number of streams available which is updated via + // the MAX_STREAMS frame. + // + // As such each time we create a new stream for QUIC, the capacity goes down + // by one, but unlike the other two codecs it is _not_ restored on stream + // closure. + // + // We track the QUIC capacity here, and overload currentUnusedCapacity so the + // connection pool can accurately keep track of when it is safe to create new + // streams. + // + // Though HTTP/3 should arguably start out with 0 stream capacity until the + // initial handshake is complete and MAX_STREAMS frame has been received, + // assume optimistically it will get ~100 streams, so that the connection pool + // won't fetch a connection for each incoming stream but will assume that the + // first connection will likely be able to serve 100. + // This number will be updated to the correct value before the connection is + // deemed connected, at which point further connections will be established if + // necessary. + uint64_t quiche_capacity_ = 100; }; // Http3 subclass of FixedHttpConnPoolImpl which exists to store quic data. @@ -45,6 +117,9 @@ class Http3ConnPoolImpl : public FixedHttpConnPoolImpl { quic::QuicConfig& quic_config); Quic::PersistentQuicInfoImpl& quicInfo() { return *quic_info_; } + // For HTTP/3 the base connection pool does not track stream capacity, rather + // the HTTP3 active client does. + bool trackStreamCapacity() override { return false; } private: // Store quic helpers which can be shared between connections and must live diff --git a/source/common/http/match_wrapper/config.cc b/source/common/http/match_wrapper/config.cc index 2292ca4e2d6f..f727a18072c0 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/http/utility.cc b/source/common/http/utility.cc index 6f2a55da1056..7d1bc1a8aa31 100644 --- a/source/common/http/utility.cc +++ b/source/common/http/utility.cc @@ -387,6 +387,14 @@ void Utility::appendVia(RequestOrResponseHeaderMap& headers, const std::string& headers.appendVia(via, ", "); } +void Utility::updateAuthority(RequestHeaderMap& headers, absl::string_view hostname, + const bool append_xfh) { + if (append_xfh && !headers.getHostValue().empty()) { + headers.appendForwardedHost(headers.getHostValue(), ","); + } + headers.setHost(hostname); +} + std::string Utility::createSslRedirectPath(const RequestHeaderMap& headers) { ASSERT(headers.Host()); ASSERT(headers.Path()); diff --git a/source/common/http/utility.h b/source/common/http/utility.h index a13922b7aee9..1d4b214da622 100644 --- a/source/common/http/utility.h +++ b/source/common/http/utility.h @@ -194,6 +194,14 @@ void appendXff(RequestHeaderMap& headers, const Network::Address::Instance& remo */ void appendVia(RequestOrResponseHeaderMap& headers, const std::string& via); +/** + * Update authority with the specified hostname. + * @param headers headers where authority should be updated. + * @param hostname hostname that authority should be updated with. + * @param append_xfh append the original authority to the x-forwarded-host header. + */ +void updateAuthority(RequestHeaderMap& headers, absl::string_view hostname, bool append_xfh); + /** * Creates an SSL (https) redirect path based on the input host and path headers. * @param headers supplies the request headers. diff --git a/source/common/network/BUILD b/source/common/network/BUILD index d75313240dd6..1f352e7e5963 100644 --- a/source/common/network/BUILD +++ b/source/common/network/BUILD @@ -15,7 +15,6 @@ envoy_cc_library( deps = [ ":socket_interface_lib", "//envoy/network:address_interface", - "//source/common/api:os_sys_calls_lib", "//source/common/common:assert_lib", "//source/common/common:safe_memcpy_lib", "//source/common/common:statusor_lib", @@ -64,6 +63,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", @@ -133,6 +133,8 @@ envoy_cc_library( srcs = ["hash_policy.cc"], hdrs = ["hash_policy.h"], deps = [ + "//envoy/common:hashable_interface", + "//envoy/network:connection_interface", "//envoy/network:hash_policy_interface", "//source/common/common:assert_lib", "//source/common/common:hash_lib", @@ -182,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/addr_family_aware_socket_option_impl.cc b/source/common/network/addr_family_aware_socket_option_impl.cc index 8c4c756a1093..a820d012f770 100644 --- a/source/common/network/addr_family_aware_socket_option_impl.cc +++ b/source/common/network/addr_family_aware_socket_option_impl.cc @@ -1,7 +1,7 @@ #include "source/common/network/addr_family_aware_socket_option_impl.h" #include "envoy/common/exception.h" -#include "envoy/common/platform.h" +#include "envoy/common/optref.h" #include "envoy/config/core/v3/base.pb.h" #include "source/common/api/os_sys_calls_impl.h" @@ -14,11 +14,12 @@ namespace Network { namespace { -SocketOptionImplOptRef getOptionForSocket(const Socket& socket, SocketOptionImpl& ipv4_option, - SocketOptionImpl& ipv6_option) { +OptRef getOptionForSocket(const Socket& socket, + const Socket::Option& ipv4_option, + const Socket::Option& ipv6_option) { auto version = socket.ipVersion(); if (!version.has_value()) { - return absl::nullopt; + return {}; } // If the FD is v4, we can only try the IPv4 variant. @@ -38,7 +39,7 @@ SocketOptionImplOptRef getOptionForSocket(const Socket& socket, SocketOptionImpl bool AddrFamilyAwareSocketOptionImpl::setOption( Socket& socket, envoy::config::core::v3::SocketOption::SocketState state) const { - return setIpSocketOption(socket, state, ipv4_option_, ipv6_option_); + return setIpSocketOption(socket, state, *ipv4_option_, *ipv6_option_); } absl::optional AddrFamilyAwareSocketOptionImpl::getOptionDetails( @@ -49,21 +50,20 @@ absl::optional AddrFamilyAwareSocketOptionImpl::getOpti return absl::nullopt; } - return option->get().getOptionDetails(socket, state); + return option.value().get().getOptionDetails(socket, state); } bool AddrFamilyAwareSocketOptionImpl::setIpSocketOption( Socket& socket, envoy::config::core::v3::SocketOption::SocketState state, - const std::unique_ptr& ipv4_option, - const std::unique_ptr& ipv6_option) { - auto option = getOptionForSocket(socket, *ipv4_option, *ipv6_option); + const Socket::Option& ipv4_option, const Socket::Option& ipv6_option) { + auto option = getOptionForSocket(socket, ipv4_option, ipv6_option); if (!option.has_value()) { ENVOY_LOG(warn, "Failed to set IP socket option on non-IP socket"); return false; } - return option->get().setOption(socket, state); + return option.value().get().setOption(socket, state); } } // namespace Network diff --git a/source/common/network/addr_family_aware_socket_option_impl.h b/source/common/network/addr_family_aware_socket_option_impl.h index ff7ada58a947..5c7c5975467e 100644 --- a/source/common/network/addr_family_aware_socket_option_impl.h +++ b/source/common/network/addr_family_aware_socket_option_impl.h @@ -29,6 +29,9 @@ class AddrFamilyAwareSocketOptionImpl : public Socket::Option, SocketOptionName ipv6_optname, absl::string_view ipv6_value) : ipv4_option_(std::make_unique(in_state, ipv4_optname, ipv4_value)), ipv6_option_(std::make_unique(in_state, ipv6_optname, ipv6_value)) {} + AddrFamilyAwareSocketOptionImpl(Socket::OptionConstPtr&& ipv4_option, + Socket::OptionConstPtr&& ipv6_option) + : ipv4_option_(std::move(ipv4_option)), ipv6_option_(std::move(ipv6_option)) {} // Socket::Option bool setOption(Socket& socket, @@ -41,6 +44,7 @@ class AddrFamilyAwareSocketOptionImpl : public Socket::Option, absl::optional
getOptionDetails(const Socket& socket, envoy::config::core::v3::SocketOption::SocketState state) const override; + bool isSupported() const override { return true; } /** * Set a socket option that applies at both IPv4 and IPv6 socket levels. When the underlying FD @@ -59,12 +63,12 @@ class AddrFamilyAwareSocketOptionImpl : public Socket::Option, */ static bool setIpSocketOption(Socket& socket, envoy::config::core::v3::SocketOption::SocketState state, - const std::unique_ptr& ipv4_option, - const std::unique_ptr& ipv6_option); + const Socket::Option& ipv4_option, + const Socket::Option& ipv6_option); private: - const std::unique_ptr ipv4_option_; - const std::unique_ptr ipv6_option_; + const Socket::OptionConstPtr ipv4_option_; + const Socket::OptionConstPtr ipv6_option_; }; } // namespace Network diff --git a/source/common/network/connection_impl.cc b/source/common/network/connection_impl.cc index edbd5adbe34a..2d4f847a8c45 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,8 +846,9 @@ 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()) { + stream_info_.setUpstreamInfo(std::make_shared()); // There are no meaningful socket options or source address semantics for // non-IP sockets, so skip. if (socket_->connectionInfoProviderSharedPtr()->remoteAddress()->ip() == nullptr) { @@ -890,6 +892,7 @@ void ClientConnectionImpl::connect() { socket_->connectionInfoProvider().remoteAddress()->asString()); const Api::SysCallIntResult result = socket_->connect(socket_->connectionInfoProvider().remoteAddress()); + stream_info_.upstreamInfo()->upstreamTiming().onUpstreamConnectStart(dispatcher_.timeSource()); if (result.return_value_ == 0) { // write will become ready. ASSERT(connecting_); @@ -918,5 +921,10 @@ void ClientConnectionImpl::connect() { } } +void ClientConnectionImpl::onConnected() { + stream_info_.upstreamInfo()->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 e20119ffb095..85756548dfba 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/network/dns_resolver/BUILD b/source/common/network/dns_resolver/BUILD index 10a556d92454..1c1284e246d3 100644 --- a/source/common/network/dns_resolver/BUILD +++ b/source/common/network/dns_resolver/BUILD @@ -9,12 +9,11 @@ licenses(["notice"]) # Apache 2 envoy_package() envoy_cc_library( - name = "dns_factory_lib", - srcs = ["dns_factory.cc"], - hdrs = ["dns_factory.h"], + name = "dns_factory_util_lib", + srcs = ["dns_factory_util.cc"], + hdrs = ["dns_factory_util.h"], deps = [ - "//envoy/api:api_interface", - "//source/common/config:utility_lib", + "//envoy/network:dns_resolver_interface", "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", "@envoy_api//envoy/config/cluster/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/common/dynamic_forward_proxy/v3:pkg_cc_proto", diff --git a/source/common/network/dns_resolver/dns_factory.cc b/source/common/network/dns_resolver/dns_factory_util.cc similarity index 98% rename from source/common/network/dns_resolver/dns_factory.cc rename to source/common/network/dns_resolver/dns_factory_util.cc index 4df5ea8e4392..d8d1712cab95 100644 --- a/source/common/network/dns_resolver/dns_factory.cc +++ b/source/common/network/dns_resolver/dns_factory_util.cc @@ -1,4 +1,4 @@ -#include "source/common/network/dns_resolver/dns_factory.h" +#include "source/common/network/dns_resolver/dns_factory_util.h" namespace Envoy { namespace Network { diff --git a/source/common/network/dns_resolver/dns_factory.h b/source/common/network/dns_resolver/dns_factory_util.h similarity index 86% rename from source/common/network/dns_resolver/dns_factory.h rename to source/common/network/dns_resolver/dns_factory_util.h index b1a8bf5fb404..5c9a92dfb2fb 100644 --- a/source/common/network/dns_resolver/dns_factory.h +++ b/source/common/network/dns_resolver/dns_factory_util.h @@ -1,40 +1,18 @@ #pragma once -#include "envoy/api/api.h" #include "envoy/config/bootstrap/v3/bootstrap.pb.h" #include "envoy/config/cluster/v3/cluster.pb.h" -#include "envoy/event/dispatcher.h" #include "envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.pb.h" #include "envoy/extensions/filters/udp/dns_filter/v3/dns_filter.pb.h" #include "envoy/extensions/network/dns_resolver/apple/v3/apple_dns_resolver.pb.h" #include "envoy/extensions/network/dns_resolver/cares/v3/cares_dns_resolver.pb.h" -#include "envoy/network/dns.h" +#include "envoy/network/dns_resolver.h" -#include "source/common/config/utility.h" #include "source/common/runtime/runtime_features.h" namespace Envoy { namespace Network { -constexpr absl::string_view CaresDnsResolver = "envoy.network.dns_resolver.cares"; -constexpr absl::string_view AppleDnsResolver = "envoy.network.dns_resolver.apple"; -constexpr absl::string_view DnsResolverCategory = "envoy.network.dns_resolver"; - -class DnsResolverFactory : public Config::TypedFactory { -public: - /** - * @returns a DnsResolver object. - * @param dispatcher: the local dispatcher thread - * @param api: API interface to interact with system resources - * @param typed_dns_resolver_config: the typed DNS resolver config - */ - virtual DnsResolverSharedPtr createDnsResolver( - Event::Dispatcher& dispatcher, Api::Api& api, - const envoy::config::core::v3::TypedExtensionConfig& typed_dns_resolver_config) const PURE; - - std::string category() const override { return std::string(DnsResolverCategory); } -}; - // Create a default c-ares DNS resolver typed config. void makeDefaultCaresDnsResolverConfig( envoy::config::core::v3::TypedExtensionConfig& typed_dns_resolver_config); @@ -153,7 +131,7 @@ template Network::DnsResolverFactory& createDnsResolverFactoryFromProto( const ConfigType& config, envoy::config::core::v3::TypedExtensionConfig& typed_dns_resolver_config) { - ASSERT(Thread::MainThread::isMainOrTestThread()); + ASSERT_IS_MAIN_OR_TEST_THREAD(); typed_dns_resolver_config = makeDnsResolverConfig(config); return createDnsResolverFactoryFromTypedConfig(typed_dns_resolver_config); } diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 40802f0919fb..3d6ebf229971 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 4a64bc420d67..d2235dc58170 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/source/common/network/hash_policy.cc b/source/common/network/hash_policy.cc index 2baf9fe8d0e5..7957c19c5ff9 100644 --- a/source/common/network/hash_policy.cc +++ b/source/common/network/hash_policy.cc @@ -1,6 +1,7 @@ #include "source/common/network/hash_policy.h" #include "envoy/common/exception.h" +#include "envoy/common/hashable.h" #include "envoy/type/v3/hash_policy.pb.h" #include "source/common/common/assert.h" @@ -10,8 +11,8 @@ namespace Network { class SourceIpHashMethod : public HashPolicyImpl::HashMethod { public: - absl::optional evaluate(const Network::Address::Instance* downstream_addr, - const Network::Address::Instance*) const override { + absl::optional evaluate(const Network::Connection& connection) const override { + const auto* downstream_addr = connection.connectionInfoProvider().remoteAddress().get(); if (downstream_addr && downstream_addr->ip()) { ASSERT(!downstream_addr->ip()->addressAsString().empty()); return HashUtil::xxHash64(downstream_addr->ip()->addressAsString()); @@ -21,6 +22,22 @@ class SourceIpHashMethod : public HashPolicyImpl::HashMethod { } }; +class FilterStateHashMethod : public HashPolicyImpl::HashMethod { +public: + FilterStateHashMethod(absl::string_view key) : key_(key) {} + + absl::optional evaluate(const Network::Connection& connection) const override { + const auto& filter_state = connection.streamInfo().filterState(); + if (filter_state.hasData(key_)) { + return filter_state.getDataReadOnly(key_).hash(); + } + return absl::nullopt; + } + +private: + const std::string key_; +}; + HashPolicyImpl::HashPolicyImpl( const absl::Span& hash_policies) { ASSERT(hash_policies.size() == 1); @@ -28,15 +45,16 @@ HashPolicyImpl::HashPolicyImpl( case envoy::type::v3::HashPolicy::PolicySpecifierCase::kSourceIp: hash_impl_ = std::make_unique(); break; + case envoy::type::v3::HashPolicy::PolicySpecifierCase::kFilterState: + hash_impl_ = std::make_unique(hash_policies[0]->filter_state().key()); + break; default: NOT_REACHED_GCOVR_EXCL_LINE; } } -absl::optional -HashPolicyImpl::generateHash(const Network::Address::Instance* downstream_addr, - const Network::Address::Instance* upstream_addr) const { - return hash_impl_->evaluate(downstream_addr, upstream_addr); +absl::optional HashPolicyImpl::generateHash(const Network::Connection& connection) const { + return hash_impl_->evaluate(connection); } } // namespace Network diff --git a/source/common/network/hash_policy.h b/source/common/network/hash_policy.h index 1b5d09489fcf..d2b0c1074495 100644 --- a/source/common/network/hash_policy.h +++ b/source/common/network/hash_policy.h @@ -1,5 +1,6 @@ #pragma once +#include "envoy/network/connection.h" #include "envoy/network/hash_policy.h" #include "envoy/type/v3/hash_policy.pb.h" @@ -15,16 +16,12 @@ class HashPolicyImpl : public Network::HashPolicy { explicit HashPolicyImpl(const absl::Span& hash_policy); // Network::HashPolicy - absl::optional - generateHash(const Network::Address::Instance* downstream_addr, - const Network::Address::Instance* upstream_addr) const override; + absl::optional generateHash(const Network::Connection& connection) const override; class HashMethod { public: virtual ~HashMethod() = default; - virtual absl::optional - evaluate(const Network::Address::Instance* downstream_addr, - const Network::Address::Instance* upstream_addr) const PURE; + virtual absl::optional evaluate(const Network::Connection& connection) const PURE; }; using HashMethodPtr = std::unique_ptr; diff --git a/source/common/network/io_socket_error_impl.cc b/source/common/network/io_socket_error_impl.cc index a3e955f4d68a..b2a4e93f9e72 100644 --- a/source/common/network/io_socket_error_impl.cc +++ b/source/common/network/io_socket_error_impl.cc @@ -18,6 +18,12 @@ IoSocketError* IoSocketError::getIoSocketInvalidAddressInstance() { return instance; } +IoSocketError* IoSocketError::getIoSocketEbadfInstance() { + static auto* instance = + new IoSocketError(SOCKET_ERROR_BADF, Api::IoError::IoErrorCode::NoSupport); + return instance; +} + IoSocketError* IoSocketError::getIoSocketEagainInstance() { static auto* instance = new IoSocketError(SOCKET_ERROR_AGAIN, Api::IoError::IoErrorCode::Again); return instance; diff --git a/source/common/network/io_socket_error_impl.h b/source/common/network/io_socket_error_impl.h index d17ce2a2f31c..392dd6129ebb 100644 --- a/source/common/network/io_socket_error_impl.h +++ b/source/common/network/io_socket_error_impl.h @@ -25,6 +25,8 @@ class IoSocketError : public Api::IoError { // deleter deleteIoError() below to avoid deallocating memory for this error. static IoSocketError* getIoSocketEagainInstance(); + static IoSocketError* getIoSocketEbadfInstance(); + // This error is introduced when Envoy create socket for unsupported address. It is either a bug, // or this Envoy instance received config which is not yet supported. This should not be fatal // error. diff --git a/source/common/network/io_socket_handle_impl.cc b/source/common/network/io_socket_handle_impl.cc index 452f57711b5e..020bd4b55c47 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/listen_socket_impl.cc b/source/common/network/listen_socket_impl.cc index 79bd106162ba..c24f024ad34c 100644 --- a/source/common/network/listen_socket_impl.cc +++ b/source/common/network/listen_socket_impl.cc @@ -45,7 +45,7 @@ void ListenSocketImpl::setupSocket(const Network::Socket::OptionsSharedPtr& opti } UdsListenSocket::UdsListenSocket(const Address::InstanceConstSharedPtr& address) - : ListenSocketImpl(ioHandleForAddr(Socket::Type::Stream, address), address) { + : ListenSocketImpl(ioHandleForAddr(Socket::Type::Stream, address, {}), address) { RELEASE_ASSERT(io_handle_->isOpen(), ""); bind(connection_info_provider_->localAddress()); } diff --git a/source/common/network/listen_socket_impl.h b/source/common/network/listen_socket_impl.h index ca6af3ee9052..a02948f04be0 100644 --- a/source/common/network/listen_socket_impl.h +++ b/source/common/network/listen_socket_impl.h @@ -58,8 +58,10 @@ template <> struct NetworkSocketTrait { template class NetworkListenSocket : public ListenSocketImpl { public: NetworkListenSocket(const Address::InstanceConstSharedPtr& address, - const Network::Socket::OptionsSharedPtr& options, bool bind_to_port) - : ListenSocketImpl(bind_to_port ? Network::ioHandleForAddr(T::type, address) : nullptr, + const Network::Socket::OptionsSharedPtr& options, bool bind_to_port, + const SocketCreationOptions& creation_options = {}) + : ListenSocketImpl(bind_to_port ? Network::ioHandleForAddr(T::type, address, creation_options) + : nullptr, address) { // Prebind is applied if the socket is bind to port. if (bind_to_port) { @@ -151,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, @@ -159,8 +185,9 @@ class ConnectionSocketImpl : public SocketImpl, public ConnectionSocket { : SocketImpl(std::move(io_handle), local_address, remote_address) {} ConnectionSocketImpl(Socket::Type type, const Address::InstanceConstSharedPtr& local_address, - const Address::InstanceConstSharedPtr& remote_address) - : SocketImpl(type, local_address, remote_address) { + const Address::InstanceConstSharedPtr& remote_address, + const SocketCreationOptions& options) + : SocketImpl(type, local_address, remote_address, options) { connection_info_provider_->setLocalAddress(local_address); } @@ -191,6 +218,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(); } @@ -233,7 +265,7 @@ class ClientSocketImpl : public ConnectionSocketImpl { public: ClientSocketImpl(const Address::InstanceConstSharedPtr& remote_address, const OptionsSharedPtr& options) - : ConnectionSocketImpl(Network::ioHandleForAddr(Socket::Type::Stream, remote_address), + : ConnectionSocketImpl(Network::ioHandleForAddr(Socket::Type::Stream, remote_address, {}), nullptr, remote_address) { if (options) { addOptions(options); diff --git a/source/common/network/socket_impl.cc b/source/common/network/socket_impl.cc index c19ac2542f24..3e16eeddfd3c 100644 --- a/source/common/network/socket_impl.cc +++ b/source/common/network/socket_impl.cc @@ -11,8 +11,9 @@ namespace Network { SocketImpl::SocketImpl(Socket::Type sock_type, const Address::InstanceConstSharedPtr& address_for_io_handle, - const Address::InstanceConstSharedPtr& remote_address) - : io_handle_(ioHandleForAddr(sock_type, address_for_io_handle)), + const Address::InstanceConstSharedPtr& remote_address, + const SocketCreationOptions& options) + : io_handle_(ioHandleForAddr(sock_type, address_for_io_handle, options)), connection_info_provider_( std::make_shared(nullptr, remote_address)), sock_type_(sock_type), addr_type_(address_for_io_handle->type()) {} @@ -29,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/socket_impl.h b/source/common/network/socket_impl.h index 8c33d96bce22..f95115314c81 100644 --- a/source/common/network/socket_impl.h +++ b/source/common/network/socket_impl.h @@ -1,6 +1,7 @@ #pragma once #include "envoy/network/socket.h" +#include "envoy/network/socket_interface.h" #include "source/common/common/assert.h" #include "source/common/common/dump_state_utils.h" @@ -53,8 +54,11 @@ 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; } + 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_; @@ -64,12 +68,14 @@ 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 { public: SocketImpl(Socket::Type socket_type, const Address::InstanceConstSharedPtr& address_for_io_handle, - const Address::InstanceConstSharedPtr& remote_address); + const Address::InstanceConstSharedPtr& remote_address, + const SocketCreationOptions& options); // Network::Socket ConnectionInfoSetter& connectionInfoProvider() override { return *connection_info_provider_; } diff --git a/source/common/network/socket_interface_impl.cc b/source/common/network/socket_interface_impl.cc index 1d87e3295ee9..5b46cb1eaf66 100644 --- a/source/common/network/socket_interface_impl.cc +++ b/source/common/network/socket_interface_impl.cc @@ -7,21 +7,39 @@ #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, - Address::IpVersion version, bool socket_v6only) const { + Address::IpVersion version, bool socket_v6only, + const SocketCreationOptions& options) const { + int protocol = 0; #if defined(__APPLE__) || defined(WIN32) + ASSERT(!options.mptcp_enabled_, "MPTCP is only supported on Linux"); int flags = 0; #else int flags = SOCK_NONBLOCK; + + if (options.mptcp_enabled_) { + ASSERT(socket_type == Socket::Type::Stream); + ASSERT(addr_type == Address::Type::Ip); + protocol = IPPROTO_MPTCP; + } #endif if (socket_type == Socket::Type::Stream) { @@ -46,7 +64,8 @@ IoHandlePtr SocketInterfaceImpl::socket(Socket::Type socket_type, Address::Type NOT_IMPLEMENTED_GCOVR_EXCL_LINE; } - const Api::SysCallSocketResult result = Api::OsSysCallsSingleton::get().socket(domain, flags, 0); + const Api::SysCallSocketResult result = + Api::OsSysCallsSingleton::get().socket(domain, flags, protocol); RELEASE_ASSERT(SOCKET_VALID(result.return_value_), fmt::format("socket(2) failed, got error: {}", errorDetails(result.errno_))); IoHandlePtr io_handle = makeSocket(result.return_value_, socket_v6only, domain); @@ -61,7 +80,8 @@ IoHandlePtr SocketInterfaceImpl::socket(Socket::Type socket_type, Address::Type } IoHandlePtr SocketInterfaceImpl::socket(Socket::Type socket_type, - const Address::InstanceConstSharedPtr addr) const { + const Address::InstanceConstSharedPtr addr, + const SocketCreationOptions& options) const { Address::IpVersion ip_version = addr->ip() ? addr->ip()->version() : Address::IpVersion::v4; int v6only = 0; if (addr->type() == Address::Type::Ip && ip_version == Address::IpVersion::v6) { @@ -69,7 +89,7 @@ IoHandlePtr SocketInterfaceImpl::socket(Socket::Type socket_type, } IoHandlePtr io_handle = - SocketInterfaceImpl::socket(socket_type, addr->type(), ip_version, v6only); + SocketInterfaceImpl::socket(socket_type, addr->type(), ip_version, v6only, options); if (addr->type() == Address::Type::Ip && ip_version == Address::IpVersion::v6) { // Setting IPV6_V6ONLY restricts the IPv6 socket to IPv6 connections only. const Api::SysCallIntResult result = io_handle->setOption( diff --git a/source/common/network/socket_interface_impl.h b/source/common/network/socket_interface_impl.h index ce3889230f85..780c0dc97b71 100644 --- a/source/common/network/socket_interface_impl.h +++ b/source/common/network/socket_interface_impl.h @@ -11,9 +11,9 @@ class SocketInterfaceImpl : public SocketInterfaceBase { public: // SocketInterface IoHandlePtr socket(Socket::Type socket_type, Address::Type addr_type, Address::IpVersion version, - bool socket_v6only) const override; - IoHandlePtr socket(Socket::Type socket_type, - const Address::InstanceConstSharedPtr addr) const override; + bool socket_v6only, const SocketCreationOptions& options) const override; + IoHandlePtr socket(Socket::Type socket_type, const Address::InstanceConstSharedPtr addr, + const SocketCreationOptions& options) const override; bool ipFamilySupported(int domain) override; // Server::Configuration::BootstrapExtensionFactory @@ -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/socket_option_impl.h b/source/common/network/socket_option_impl.h index fd42517c7bd9..8fe7574c729c 100644 --- a/source/common/network/socket_option_impl.h +++ b/source/common/network/socket_option_impl.h @@ -138,8 +138,7 @@ class SocketOptionImpl : public Socket::Option, Logger::Loggable getOptionDetails(const Socket& socket, envoy::config::core::v3::SocketOption::SocketState state) const override; - - bool isSupported() const; + bool isSupported() const override; /** * Set the option on the given socket. @@ -162,7 +161,5 @@ class SocketOptionImpl : public Socket::Option, Logger::Loggable value_; }; -using SocketOptionImplOptRef = absl::optional>; - } // namespace Network } // namespace Envoy diff --git a/source/common/network/tcp_listener_impl.cc b/source/common/network/tcp_listener_impl.cc index a3f79f72df6b..b73f26ab4df0 100644 --- a/source/common/network/tcp_listener_impl.cc +++ b/source/common/network/tcp_listener_impl.cc @@ -20,11 +20,11 @@ namespace Network { const absl::string_view TcpListenerImpl::GlobalMaxCxRuntimeKey = "overload.global_downstream_max_connections"; -bool TcpListenerImpl::rejectCxOverGlobalLimit() { +bool TcpListenerImpl::rejectCxOverGlobalLimit() const { // Enforce the global connection limit if necessary, immediately closing the accepted connection. Runtime::Loader* runtime = Runtime::LoaderSingleton::getExisting(); - if (runtime == nullptr) { + if (ignore_global_conn_limit_ || runtime == nullptr) { // The runtime singleton won't exist in most unit tests that do not need global downstream limit // enforcement. Therefore, there is no need to enforce limits if the singleton doesn't exist. // TODO(tonya11en): Revisit this once runtime is made globally available. @@ -98,9 +98,10 @@ void TcpListenerImpl::onSocketEvent(short flags) { TcpListenerImpl::TcpListenerImpl(Event::DispatcherImpl& dispatcher, Random::RandomGenerator& random, SocketSharedPtr socket, TcpListenerCallbacks& cb, - bool bind_to_port) + bool bind_to_port, bool ignore_global_conn_limit) : BaseListenerImpl(dispatcher, std::move(socket)), cb_(cb), random_(random), - bind_to_port_(bind_to_port), reject_fraction_(0.0) { + bind_to_port_(bind_to_port), reject_fraction_(0.0), + ignore_global_conn_limit_(ignore_global_conn_limit) { if (bind_to_port) { // Although onSocketEvent drains to completion, use level triggered mode to avoid potential // loss of the trigger due to transient accept errors. diff --git a/source/common/network/tcp_listener_impl.h b/source/common/network/tcp_listener_impl.h index 27ecfeec949b..f9978b73543e 100644 --- a/source/common/network/tcp_listener_impl.h +++ b/source/common/network/tcp_listener_impl.h @@ -17,7 +17,8 @@ namespace Network { class TcpListenerImpl : public BaseListenerImpl { public: TcpListenerImpl(Event::DispatcherImpl& dispatcher, Random::RandomGenerator& random, - SocketSharedPtr socket, TcpListenerCallbacks& cb, bool bind_to_port); + SocketSharedPtr socket, TcpListenerCallbacks& cb, bool bind_to_port, + bool ignore_global_conn_limit); ~TcpListenerImpl() override { if (bind_to_port_) { socket_->ioHandle().resetFileEvents(); @@ -37,11 +38,12 @@ class TcpListenerImpl : public BaseListenerImpl { // Returns true if global connection limit has been reached and the accepted socket should be // rejected/closed. If the accepted socket is to be admitted, false is returned. - static bool rejectCxOverGlobalLimit(); + bool rejectCxOverGlobalLimit() const; Random::RandomGenerator& random_; bool bind_to_port_; UnitFloat reject_fraction_; + const bool ignore_global_conn_limit_; }; } // namespace Network diff --git a/source/common/network/utility.cc b/source/common/network/utility.cc index 7cd7b19c00be..80d0aef51bd3 100644 --- a/source/common/network/utility.cc +++ b/source/common/network/utility.cc @@ -239,36 +239,22 @@ void Utility::throwWithMalformedIp(absl::string_view ip_address) { // need to be updated in the future. Discussion can be found at Github issue #939. Address::InstanceConstSharedPtr Utility::getLocalAddress(const Address::IpVersion version) { Address::InstanceConstSharedPtr ret; -#ifdef SUPPORTS_GETIFADDRS - struct ifaddrs* ifaddr; - struct ifaddrs* ifa; + if (Api::OsSysCallsSingleton::get().supportsGetifaddrs()) { + Api::InterfaceAddressVector interface_addresses{}; - const int rc = getifaddrs(&ifaddr); - RELEASE_ASSERT(!rc, ""); + const Api::SysCallIntResult rc = + Api::OsSysCallsSingleton::get().getifaddrs(interface_addresses); + RELEASE_ASSERT(!rc.return_value_, fmt::format("getiffaddrs error: {}", rc.errno_)); - // man getifaddrs(3) - for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == nullptr) { - continue; - } - - if ((ifa->ifa_addr->sa_family == AF_INET && version == Address::IpVersion::v4) || - (ifa->ifa_addr->sa_family == AF_INET6 && version == Address::IpVersion::v6)) { - const struct sockaddr_storage* addr = - reinterpret_cast(ifa->ifa_addr); - ret = Address::addressFromSockAddrOrThrow( - *addr, (version == Address::IpVersion::v4) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6)); - if (!isLoopbackAddress(*ret)) { + // man getifaddrs(3) + for (const auto& interface_address : interface_addresses) { + if (!isLoopbackAddress(*interface_address.interface_addr_)) { + ret = interface_address.interface_addr_; break; } } } - if (ifaddr) { - freeifaddrs(ifaddr); - } -#endif - // If the local address is not found above, then return the loopback address by default. if (ret == nullptr) { if (version == Address::IpVersion::v4) { @@ -534,6 +520,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/network/win32_redirect_records_option_impl.h b/source/common/network/win32_redirect_records_option_impl.h index efa88048d970..6ec1707aab77 100644 --- a/source/common/network/win32_redirect_records_option_impl.h +++ b/source/common/network/win32_redirect_records_option_impl.h @@ -25,8 +25,8 @@ class Win32RedirectRecordsOptionImpl : public Socket::Option, absl::optional
getOptionDetails(const Socket& socket, envoy::config::core::v3::SocketOption::SocketState) const override; + bool isSupported() const override; - bool isSupported() const; static const Network::SocketOptionName& optionName(); private: 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 000000000000..c74f5144691b --- /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 000000000000..b9465f71db54 --- /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/source/common/protobuf/utility.cc b/source/common/protobuf/utility.cc index 7e5d60d0752b..006182f0d7ac 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 e8f4a61cd90b..f4107c669c28 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/source/common/quic/BUILD b/source/common/quic/BUILD index a651426c188b..bab6a0c97cde 100644 --- a/source/common/quic/BUILD +++ b/source/common/quic/BUILD @@ -124,17 +124,6 @@ envoy_cc_library( ], ) -envoy_cc_library( - name = "envoy_quic_session_cache_lib", - srcs = ["envoy_quic_session_cache.cc"], - hdrs = ["envoy_quic_session_cache.h"], - external_deps = ["quiche_quic_platform"], - tags = ["nofips"], - deps = [ - "@com_github_google_quiche//:quic_core_crypto_crypto_handshake_lib", - ], -) - envoy_cc_library( name = "spdy_server_push_utils_for_envoy_lib", srcs = ["spdy_server_push_utils_for_envoy.cc"], @@ -173,7 +162,6 @@ envoy_cc_library( ":envoy_quic_connection_helper_lib", ":envoy_quic_proof_verifier_lib", ":envoy_quic_server_session_lib", - ":envoy_quic_session_cache_lib", ":envoy_quic_utils_lib", "//envoy/http:codec_interface", "//envoy/registry", diff --git a/source/common/quic/active_quic_listener.cc b/source/common/quic/active_quic_listener.cc index 304ff8bc7d34..5aa56cada9b6 100644 --- a/source/common/quic/active_quic_listener.cc +++ b/source/common/quic/active_quic_listener.cc @@ -55,10 +55,7 @@ ActiveQuicListener::ActiveQuicListener( kernel_worker_routing_(kernel_worker_routing), packets_to_read_to_connection_count_ratio_(packets_to_read_to_connection_count_ratio), crypto_server_stream_factory_(crypto_server_stream_factory) { - // This flag fix a QUICHE issue which may crash Envoy during connection close. - SetQuicReloadableFlag(quic_single_ack_in_packet2, true); - // Do not include 32-byte per-entry overhead while counting header size. - quiche::FlagRegistry::getInstance(); + ASSERT(GetQuicReloadableFlag(quic_single_ack_in_packet2)); ASSERT(!GetQuicFlag(FLAGS_quic_header_size_limit_includes_overhead)); if (Runtime::LoaderSingleton::getExisting()) { @@ -257,11 +254,7 @@ ActiveQuicListenerFactory::ActiveQuicListenerFactory( : 20000; quic_config_.set_max_time_before_crypto_handshake( quic::QuicTime::Delta::FromMilliseconds(max_time_before_crypto_handshake_ms)); - int32_t max_streams = - PROTOBUF_GET_WRAPPED_OR_DEFAULT(config.quic_protocol_options(), max_concurrent_streams, 100); - quic_config_.SetMaxBidirectionalStreamsToSend(max_streams); - quic_config_.SetMaxUnidirectionalStreamsToSend(max_streams); - configQuicInitialFlowControlWindow(config.quic_protocol_options(), quic_config_); + convertQuicConfig(config.quic_protocol_options(), quic_config_); // Initialize crypto stream factory. envoy::config::core::v3::TypedExtensionConfig crypto_stream_config; diff --git a/source/common/quic/client_connection_factory_impl.cc b/source/common/quic/client_connection_factory_impl.cc index 00d86543436f..c6465aca4453 100644 --- a/source/common/quic/client_connection_factory_impl.cc +++ b/source/common/quic/client_connection_factory_impl.cc @@ -1,8 +1,9 @@ #include "source/common/quic/client_connection_factory_impl.h" -#include "source/common/quic/envoy_quic_session_cache.h" #include "source/common/quic/quic_transport_socket_factory.h" +#include "quiche/quic/core/crypto/quic_client_session_cache.h" + namespace Envoy { namespace Quic { @@ -34,7 +35,7 @@ std::shared_ptr PersistentQuicInfoImpl::cryptoConf client_context_ = context; client_config_ = std::make_shared( std::make_unique(getContext(transport_socket_factory_)), - std::make_unique((time_source_))); + std::make_unique()); } // Return the latest client config. return client_config_; @@ -57,8 +58,7 @@ createQuicNetworkConnection(Http::PersistentQuicInfo& info, Event::Dispatcher& d Network::Address::InstanceConstSharedPtr server_addr, Network::Address::InstanceConstSharedPtr local_addr, QuicStatNames& quic_stat_names, Stats::Scope& scope) { - // This flag fix a QUICHE issue which may crash Envoy during connection close. - SetQuicReloadableFlag(quic_single_ack_in_packet2, true); + ASSERT(GetQuicReloadableFlag(quic_single_ack_in_packet2)); PersistentQuicInfoImpl* info_impl = reinterpret_cast(&info); auto config = info_impl->cryptoConfig(); if (config == nullptr) { diff --git a/source/common/quic/codec_impl.h b/source/common/quic/codec_impl.h index b5136bb1b631..399cf5dde889 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_connection.cc b/source/common/quic/envoy_quic_client_connection.cc index 0a50d81b11bf..8ad98aa0afb0 100644 --- a/source/common/quic/envoy_quic_client_connection.cc +++ b/source/common/quic/envoy_quic_client_connection.cc @@ -200,7 +200,7 @@ void EnvoyQuicClientConnection::onFileEvent(uint32_t events, if (connected() && (events & Event::FileReadyType::Read)) { Api::IoErrorPtr err = Network::Utility::readPacketsFromSocket( connection_socket.ioHandle(), *connection_socket.connectionInfoProvider().localAddress(), - *this, dispatcher_.timeSource(), true, packets_dropped_); + *this, dispatcher_.timeSource(), /*prefer_gro=*/false, packets_dropped_); if (err == nullptr) { // In the case where the path validation fails, the probing socket will be closed and its IO // events are no longer interesting. @@ -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 f24a67ce067c..fc11311619b8 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 27ddf56b189b..96461564ad28 100644 --- a/source/common/quic/envoy_quic_client_session.cc +++ b/source/common/quic/envoy_quic_client_session.cc @@ -16,13 +16,13 @@ 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), - host_name_(server_id.host()), 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); + crypto_config_(crypto_config), crypto_stream_factory_(crypto_stream_factory), + quic_stat_names_(quic_stat_names), scope_(scope) { + streamInfo().setUpstreamInfo(std::make_shared()); } EnvoyQuicClientSession::~EnvoyQuicClientSession() { @@ -30,9 +30,10 @@ EnvoyQuicClientSession::~EnvoyQuicClientSession() { network_connection_ = nullptr; } -absl::string_view EnvoyQuicClientSession::requestedServerName() const { return host_name_; } +absl::string_view EnvoyQuicClientSession::requestedServerName() const { return server_id().host(); } void EnvoyQuicClientSession::connect() { + streamInfo().upstreamInfo()->upstreamTiming().onUpstreamConnectStart(dispatcher_.timeSource()); dynamic_cast(network_connection_) ->setUpConnectionSocket( *static_cast(connection())->connectionSocket(), *this); @@ -82,12 +83,12 @@ void EnvoyQuicClientSession::OnRstStream(const quic::QuicRstStreamFrame& frame) /*from_self*/ false, /*is_upstream*/ true); } -void EnvoyQuicClientSession::SetDefaultEncryptionLevel(quic::EncryptionLevel level) { - quic::QuicSpdyClientSession::SetDefaultEncryptionLevel(level); - if (level == quic::ENCRYPTION_FORWARD_SECURE) { - // This is only reached once, when handshake is done. - raiseConnectionEvent(Network::ConnectionEvent::Connected); +void EnvoyQuicClientSession::OnCanCreateNewOutgoingStream(bool unidirectional) { + if (!http_connection_callbacks_ || unidirectional) { + return; } + uint32_t streams_available = streamsAvailable(); + http_connection_callbacks_->onMaxStreamsChanged(streams_available); } std::unique_ptr EnvoyQuicClientSession::CreateClientStream() { @@ -118,8 +119,26 @@ quic::QuicConnection* EnvoyQuicClientSession::quicConnection() { return initialized_ ? connection() : nullptr; } +uint64_t EnvoyQuicClientSession::streamsAvailable() { + 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; +} + void EnvoyQuicClientSession::OnTlsHandshakeComplete() { quic::QuicSpdyClientSession::OnTlsHandshakeComplete(); + + // 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); + streamInfo().upstreamInfo()->upstreamTiming().onUpstreamConnectComplete(dispatcher_.timeSource()); + streamInfo().upstreamInfo()->upstreamTiming().onUpstreamHandshakeComplete( + dispatcher_.timeSource()); + raiseConnectionEvent(Network::ConnectionEvent::Connected); } @@ -132,11 +151,35 @@ std::unique_ptr EnvoyQuicClientSession::Create void EnvoyQuicClientSession::setHttp3Options( const envoy::config::core::v3::Http3ProtocolOptions& http3_options) { QuicFilterManagerConnectionImpl::setHttp3Options(http3_options); - if (http3_options_->has_quic_protocol_options()) { - static_cast(connection()) - ->setMigratePortOnPathDegrading(PROTOBUF_GET_WRAPPED_OR_DEFAULT( - http3_options.quic_protocol_options(), num_timeouts_to_trigger_port_migration, 1)); + if (!http3_options_->has_quic_protocol_options()) { + return; } + static_cast(connection()) + ->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()) { + const uint64_t initial_interval = PROTOBUF_GET_MS_OR_DEFAULT( + http3_options_->quic_protocol_options().connection_keepalive(), initial_interval, 0); + const uint64_t max_interval = + PROTOBUF_GET_MS_OR_DEFAULT(http3_options_->quic_protocol_options().connection_keepalive(), + max_interval, quic::kPingTimeoutSecs); + // If the keepalive max_interval is configured to zero, disable the probe completely. + if (max_interval == 0u) { + disable_keepalive_ = true; + return; + } + connection()->set_ping_timeout(quic::QuicTime::Delta::FromMilliseconds(max_interval)); + if (max_interval > initial_interval && initial_interval > 0u) { + connection()->set_initial_retransmittable_on_wire_timeout( + quic::QuicTime::Delta::FromMilliseconds(initial_interval)); + } + } +} + +bool EnvoyQuicClientSession::ShouldKeepConnectionAlive() const { + // Do not probe at all if keepalive is disabled via config. + return !disable_keepalive_ && quic::QuicSpdyClientSession::ShouldKeepConnectionAlive(); } void EnvoyQuicClientSession::OnProofVerifyDetailsAvailable( diff --git a/source/common/quic/envoy_quic_client_session.h b/source/common/quic/envoy_quic_client_session.h index a859a9d41c46..59beb7083532 100644 --- a/source/common/quic/envoy_quic_client_session.h +++ b/source/common/quic/envoy_quic_client_session.h @@ -11,6 +11,8 @@ namespace Envoy { namespace Quic { +class EnvoyQuicClientSession; + // Act as a Network::ClientConnection to ClientCodec. // TODO(danzh) This class doesn't need to inherit Network::FilterManager // interface but need all other Network::Connection implementation in @@ -58,8 +60,9 @@ class EnvoyQuicClientSession : public QuicFilterManagerConnectionImpl, void MaybeSendRstStreamFrame(quic::QuicStreamId id, quic::QuicResetStreamError error, quic::QuicStreamOffset bytes_written) override; void OnRstStream(const quic::QuicRstStreamFrame& frame) override; + // quic::QuicSpdyClientSessionBase - void SetDefaultEncryptionLevel(quic::EncryptionLevel level) override; + bool ShouldKeepConnectionAlive() const override; // quic::ProofHandler void OnProofVerifyDetailsAvailable(const quic::ProofVerifyDetails& verify_details) override; @@ -73,6 +76,9 @@ class EnvoyQuicClientSession : public QuicFilterManagerConnectionImpl, // QuicFilterManagerConnectionImpl void setHttp3Options(const envoy::config::core::v3::Http3ProtocolOptions& http3_options) override; + // Notify any registered connection pool when new streams are available. + void OnCanCreateNewOutgoingStream(bool) override; + using quic::QuicSpdyClientSession::PerformActionOnActiveStreams; protected: @@ -95,15 +101,16 @@ class EnvoyQuicClientSession : public QuicFilterManagerConnectionImpl, quic::QuicConnection* quicConnection() override; private: + uint64_t streamsAvailable(); + // These callbacks are owned by network filters and quic session should outlive // them. Http::ConnectionCallbacks* http_connection_callbacks_{nullptr}; - // TODO(danzh) deprecate this field once server_id() is made const. - const std::string host_name_; std::shared_ptr crypto_config_; EnvoyQuicCryptoClientStreamFactoryInterface& crypto_stream_factory_; QuicStatNames& quic_stat_names_; Stats::Scope& scope_; + bool disable_keepalive_{false}; }; } // namespace Quic diff --git a/source/common/quic/envoy_quic_client_stream.cc b/source/common/quic/envoy_quic_client_stream.cc index ad26cf9e17d7..0e239658f1aa 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)) { @@ -286,7 +287,9 @@ void EnvoyQuicClientStream::ResetWithError(quic::QuicResetStreamError error) { stats_.tx_reset_.inc(); // Upper layers expect calling resetStream() to immediately raise reset callbacks. runResetCallbacks(quicRstErrorToEnvoyLocalResetReason(error.internal_code())); - quic::QuicSpdyClientStream::ResetWithError(error); + if (session()->connection()->connected()) { + quic::QuicSpdyClientStream::ResetWithError(error); + } } void EnvoyQuicClientStream::OnConnectionClosed(quic::QuicErrorCode error, diff --git a/source/common/quic/envoy_quic_client_stream.h b/source/common/quic/envoy_quic_client_stream.h index 165d751680f9..bb1084122215 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_dispatcher.cc b/source/common/quic/envoy_quic_dispatcher.cc index 37fd7472c9a6..bcc5c4eb91e7 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 97d04adb048d..a022c224f21d 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 diff --git a/source/common/quic/envoy_quic_server_session.cc b/source/common/quic/envoy_quic_server_session.cc index 206327d576fe..2f83f9b3201b 100644 --- a/source/common/quic/envoy_quic_server_session.cc +++ b/source/common/quic/envoy_quic_server_session.cc @@ -7,6 +7,8 @@ #include "source/common/quic/envoy_quic_proof_source.h" #include "source/common/quic/envoy_quic_server_stream.h" +#include "quic_filter_manager_connection_impl.h" + namespace Envoy { namespace Quic { @@ -20,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() { @@ -147,6 +149,27 @@ void EnvoyQuicServerSession::OnRstStream(const quic::QuicRstStreamFrame& frame) /*from_self*/ false, /*is_upstream*/ false); } +void EnvoyQuicServerSession::setHttp3Options( + const envoy::config::core::v3::Http3ProtocolOptions& http3_options) { + QuicFilterManagerConnectionImpl::setHttp3Options(http3_options); + if (http3_options_->has_quic_protocol_options() && + http3_options_->quic_protocol_options().has_connection_keepalive()) { + const uint64_t initial_interval = PROTOBUF_GET_MS_OR_DEFAULT( + http3_options_->quic_protocol_options().connection_keepalive(), initial_interval, 0); + const uint64_t max_interval = + PROTOBUF_GET_MS_OR_DEFAULT(http3_options_->quic_protocol_options().connection_keepalive(), + max_interval, quic::kPingTimeoutSecs); + if (max_interval == 0) { + return; + } + if (initial_interval > 0) { + connection()->set_ping_timeout(quic::QuicTime::Delta::FromMilliseconds(max_interval)); + connection()->set_initial_retransmittable_on_wire_timeout( + quic::QuicTime::Delta::FromMilliseconds(initial_interval)); + } + } +} + void EnvoyQuicServerSession::storeConnectionMapPosition(FilterChainToConnectionMap& connection_map, const Network::FilterChain& filter_chain, ConnectionMapIter position) { diff --git a/source/common/quic/envoy_quic_server_session.h b/source/common/quic/envoy_quic_server_session.h index 131f46c51beb..59bb2384e0f3 100644 --- a/source/common/quic/envoy_quic_server_session.h +++ b/source/common/quic/envoy_quic_server_session.h @@ -87,6 +87,8 @@ class EnvoyQuicServerSession : public quic::QuicServerSessionBase, const Network::FilterChain& filter_chain, ConnectionMapIter position); + void setHttp3Options(const envoy::config::core::v3::Http3ProtocolOptions& http3_options) override; + using quic::QuicSession::PerformActionOnActiveStreams; protected: diff --git a/source/common/quic/envoy_quic_server_stream.cc b/source/common/quic/envoy_quic_server_stream.cc index b93e3a216acb..41f4bf5d30af 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 5662b08fab7d..fe7cd5a19281 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/quic/envoy_quic_session_cache.cc b/source/common/quic/envoy_quic_session_cache.cc deleted file mode 100644 index b6ce80f7717c..000000000000 --- a/source/common/quic/envoy_quic_session_cache.cc +++ /dev/null @@ -1,171 +0,0 @@ -#include "source/common/quic/envoy_quic_session_cache.h" - -namespace Envoy { -namespace Quic { -namespace { - -// This value was chosen arbitrarily. We can make this configurable if needed. -// TODO(14829) ensure this is tested and scaled for upstream. -constexpr size_t MaxSessionCacheEntries = 1024; - -// Returns false if the SSL session doesn't exist or it is expired. -bool isSessionValid(SSL_SESSION* session, SystemTime now) { - if (session == nullptr) { - return false; - } - const time_t now_time_t = std::chrono::system_clock::to_time_t(now); - if (now_time_t < 0) { - return false; - } - const uint64_t now_u64 = static_cast(now_time_t); - const uint64_t session_time = SSL_SESSION_get_time(session); - const uint64_t session_expiration = session_time + SSL_SESSION_get_timeout(session); - // now_u64 may be slightly behind because of differences in how time is calculated at this layer - // versus BoringSSL. Add a second of wiggle room to account for this. - return session_time <= now_u64 + 1 && now_u64 < session_expiration; -} - -bool doApplicationStatesMatch(const quic::ApplicationState* state, - const quic::ApplicationState* other) { - if (state == other) { - return true; - } - if ((state != nullptr && other == nullptr) || (state == nullptr && other != nullptr)) { - return false; - } - return (*state == *other); -} - -} // namespace - -EnvoyQuicSessionCache::EnvoyQuicSessionCache(TimeSource& time_source) : time_source_(time_source) {} - -EnvoyQuicSessionCache::~EnvoyQuicSessionCache() = default; - -void EnvoyQuicSessionCache::Insert(const quic::QuicServerId& server_id, - bssl::UniquePtr session, - const quic::TransportParameters& params, - const quic::ApplicationState* application_state) { - auto it = cache_.find(server_id); - if (it == cache_.end()) { - // New server ID, add a new entry. - createAndInsertEntry(server_id, std::move(session), params, application_state); - return; - } - Entry& entry = it->second; - if (params == *entry.params && - doApplicationStatesMatch(application_state, entry.application_state.get())) { - // The states are both the same, so we only need to insert the session. - entry.pushSession(std::move(session)); - return; - } - // States are different, replace the entry. - cache_.erase(it); - createAndInsertEntry(server_id, std::move(session), params, application_state); -} - -std::unique_ptr -EnvoyQuicSessionCache::Lookup(const quic::QuicServerId& server_id, const SSL_CTX* /*ctx*/) { - auto it = cache_.find(server_id); - if (it == cache_.end()) { - return nullptr; - } - Entry& entry = it->second; - const SystemTime system_time = time_source_.systemTime(); - if (!isSessionValid(entry.peekSession(), system_time)) { - cache_.erase(it); - return nullptr; - } - auto state = std::make_unique(); - state->tls_session = entry.popSession(); - if (entry.params != nullptr) { - state->transport_params = std::make_unique(*entry.params); - } - if (entry.application_state != nullptr) { - state->application_state = std::make_unique(*entry.application_state); - } - return state; -} - -void EnvoyQuicSessionCache::ClearEarlyData(const quic::QuicServerId& server_id) { - auto it = cache_.find(server_id); - if (it == cache_.end()) { - return; - } - for (bssl::UniquePtr& session : it->second.sessions) { - if (session != nullptr) { - session.reset(SSL_SESSION_copy_without_early_data(session.get())); - } - } -} - -void EnvoyQuicSessionCache::prune() { - quic::QuicServerId oldest_id; - uint64_t oldest_expiration = std::numeric_limits::max(); - const SystemTime system_time = time_source_.systemTime(); - auto it = cache_.begin(); - while (it != cache_.end()) { - Entry& entry = it->second; - SSL_SESSION* session = entry.peekSession(); - if (!isSessionValid(session, system_time)) { - it = cache_.erase(it); - } else { - if (cache_.size() >= MaxSessionCacheEntries) { - // Only track the oldest session if we are at the size limit. - const uint64_t session_expiration = - SSL_SESSION_get_time(session) + SSL_SESSION_get_timeout(session); - if (session_expiration < oldest_expiration) { - oldest_expiration = session_expiration; - oldest_id = it->first; - } - } - ++it; - } - } - if (cache_.size() >= MaxSessionCacheEntries) { - cache_.erase(oldest_id); - } -} - -void EnvoyQuicSessionCache::createAndInsertEntry(const quic::QuicServerId& server_id, - bssl::UniquePtr session, - const quic::TransportParameters& params, - const quic::ApplicationState* application_state) { - prune(); - ASSERT(cache_.size() < MaxSessionCacheEntries); - Entry entry; - entry.pushSession(std::move(session)); - entry.params = std::make_unique(params); - if (application_state != nullptr) { - entry.application_state = std::make_unique(*application_state); - } - cache_.insert(std::make_pair(server_id, std::move(entry))); -} - -size_t EnvoyQuicSessionCache::size() const { return cache_.size(); } - -EnvoyQuicSessionCache::Entry::Entry() = default; -EnvoyQuicSessionCache::Entry::Entry(Entry&&) noexcept = default; -EnvoyQuicSessionCache::Entry::~Entry() = default; - -void EnvoyQuicSessionCache::Entry::pushSession(bssl::UniquePtr session) { - if (sessions[0] != nullptr) { - sessions[1] = std::move(sessions[0]); - } - sessions[0] = std::move(session); -} - -bssl::UniquePtr EnvoyQuicSessionCache::Entry::popSession() { - if (sessions[0] == nullptr) { - return nullptr; - } - bssl::UniquePtr session = std::move(sessions[0]); - sessions[0] = std::move(sessions[1]); - sessions[1] = nullptr; - return session; -} - -SSL_SESSION* EnvoyQuicSessionCache::Entry::peekSession() { return sessions[0].get(); } - -} // namespace Quic -} // namespace Envoy diff --git a/source/common/quic/envoy_quic_session_cache.h b/source/common/quic/envoy_quic_session_cache.h deleted file mode 100644 index c15fe0d0e658..000000000000 --- a/source/common/quic/envoy_quic_session_cache.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include "envoy/common/time.h" - -#include "quiche/quic/core/crypto/quic_crypto_client_config.h" - -namespace Envoy { -namespace Quic { - -// Implementation of quic::SessionCache using an Envoy time source. -class EnvoyQuicSessionCache : public quic::SessionCache { -public: - explicit EnvoyQuicSessionCache(TimeSource& time_source); - ~EnvoyQuicSessionCache() override; - - // From quic::SessionCache. - void Insert(const quic::QuicServerId& server_id, bssl::UniquePtr session, - const quic::TransportParameters& params, - const quic::ApplicationState* application_state) override; - std::unique_ptr Lookup(const quic::QuicServerId& server_id, - const SSL_CTX* ctx) override; - void ClearEarlyData(const quic::QuicServerId& server_id) override; - - // Returns number of entries in the cache. - size_t size() const; - -private: - struct Entry { - Entry(); - Entry(Entry&&) noexcept; - ~Entry(); - - // Adds a new session onto sessions, dropping the oldest one if two are - // already stored. - void pushSession(bssl::UniquePtr session); - - // Retrieves and removes the latest session from the entry. - bssl::UniquePtr popSession(); - - SSL_SESSION* peekSession(); - - // We only save the last two sessions per server as that is sufficient in practice. This is - // because we only need one to create a new connection, and that new connection should send - // us another ticket. We keep two instead of one in case that connection attempt fails. - bssl::UniquePtr sessions[2]; - std::unique_ptr params; - std::unique_ptr application_state; - }; - - // Remove all entries that are no longer valid. If all entries were valid but the cache is at its - // size limit, instead remove the oldest entry. This walks the entire list of entries. - void prune(); - - // Creates a new entry and insert into cache_. This walks the entire list of entries. - void createAndInsertEntry(const quic::QuicServerId& server_id, - bssl::UniquePtr session, - const quic::TransportParameters& params, - const quic::ApplicationState* application_state); - - std::map cache_; - TimeSource& time_source_; -}; - -} // namespace Quic -} // namespace Envoy diff --git a/source/common/quic/envoy_quic_utils.cc b/source/common/quic/envoy_quic_utils.cc index b49fefff5785..430c4e0bce7a 100644 --- a/source/common/quic/envoy_quic_utils.cc +++ b/source/common/quic/envoy_quic_utils.cc @@ -134,7 +134,7 @@ createConnectionSocket(const Network::Address::InstanceConstSharedPtr& peer_addr local_addr = Network::Utility::getLocalAddress(peer_addr->ip()->version()); } auto connection_socket = std::make_unique( - Network::Socket::Type::Datagram, local_addr, peer_addr); + Network::Socket::Type::Datagram, local_addr, peer_addr, Network::SocketCreationOptions{}); connection_socket->addOptions(Network::SocketOptionFactory::buildIpPacketInfoOptions()); connection_socket->addOptions(Network::SocketOptionFactory::buildRxQueueOverFlowOptions()); if (options != nullptr) { @@ -237,6 +237,14 @@ createServerConnectionSocket(Network::IoHandle& io_handle, return connection_socket; } +void convertQuicConfig(const envoy::config::core::v3::QuicProtocolOptions& config, + quic::QuicConfig& quic_config) { + int32_t max_streams = PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, max_concurrent_streams, 100); + quic_config.SetMaxBidirectionalStreamsToSend(max_streams); + quic_config.SetMaxUnidirectionalStreamsToSend(max_streams); + configQuicInitialFlowControlWindow(config, quic_config); +} + void configQuicInitialFlowControlWindow(const envoy::config::core::v3::QuicProtocolOptions& config, quic::QuicConfig& quic_config) { size_t stream_flow_control_window_to_send = PROTOBUF_GET_WRAPPED_OR_DEFAULT( diff --git a/source/common/quic/envoy_quic_utils.h b/source/common/quic/envoy_quic_utils.h index 67ffd0dcf70c..ccef576914f9 100644 --- a/source/common/quic/envoy_quic_utils.h +++ b/source/common/quic/envoy_quic_utils.h @@ -178,6 +178,10 @@ createServerConnectionSocket(Network::IoHandle& io_handle, const quic::QuicSocketAddress& peer_address, const std::string& hostname, absl::string_view alpn); +// Alter QuicConfig based on all the options in the supplied config. +void convertQuicConfig(const envoy::config::core::v3::QuicProtocolOptions& config, + quic::QuicConfig& quic_config); + // Set initial flow control windows in quic_config according to the given Envoy config. void configQuicInitialFlowControlWindow(const envoy::config::core::v3::QuicProtocolOptions& config, quic::QuicConfig& quic_config); diff --git a/source/common/quic/platform/BUILD b/source/common/quic/platform/BUILD index 60fcfe741ae0..715245d04c1a 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 550b91951484..000000000000 --- 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 27e2b8448a55..000000000000 --- 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 5ef754f7aea8..000000000000 --- 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 711372c021dd..000000000000 --- 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 2e45c63eba05..000000000000 --- 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 afdcee72b8c5..000000000000 --- 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 de6ac4ed2a75..000000000000 --- 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/quiche_flags_impl.cc b/source/common/quic/platform/quiche_flags_impl.cc index af607a83adc4..30da7e75db9e 100644 --- a/source/common/quic/platform/quiche_flags_impl.cc +++ b/source/common/quic/platform/quiche_flags_impl.cc @@ -32,8 +32,11 @@ absl::flat_hash_map makeFlagMap() { QUIC_FLAG(FLAGS_quic_restart_flag_http2_testonly_default_false, false) QUIC_FLAG(FLAGS_quic_restart_flag_http2_testonly_default_true, true) #undef QUIC_FLAG - // Disable IETF draft 29 implementation. Envoy only supports RFC-v1. + // Envoy only supports RFC-v1 in the long term, so disable IETF draft 29 implementation by + // default. FLAGS_quic_reloadable_flag_quic_disable_version_draft_29->setValue(true); + // This flag fixes a QUICHE issue which may crash Envoy during connection close. + FLAGS_quic_reloadable_flag_quic_single_ack_in_packet2->setValue(true); #define QUIC_PROTOCOL_FLAG(type, flag, ...) flags.emplace(FLAGS_##flag->name(), FLAGS_##flag); #include "quiche/quic/core/quic_protocol_flags_list.h" 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 29990935d94f..000000000000 --- 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 701aff548c1e..000000000000 --- 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 b572688364ca..000000000000 --- 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 70a00ad15217..000000000000 --- 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 5353033aee51..000000000000 --- 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 bc9b960d7070..000000000000 --- 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 diff --git a/source/common/quic/quic_filter_manager_connection_impl.cc b/source/common/quic/quic_filter_manager_connection_impl.cc index e1d7a7a52a46..8792457535b6 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) { @@ -179,9 +182,14 @@ void QuicFilterManagerConnectionImpl::onConnectionCloseEvent( // The connection was closed before it could be used. Stats are not recorded. return; } - if (version.transport_version == quic::QUIC_VERSION_IETF_RFC_V1) { + switch (version.transport_version) { + case quic::QUIC_VERSION_IETF_DRAFT_29: + codec_stats_->quic_version_h3_29_.inc(); + return; + case quic::QUIC_VERSION_IETF_RFC_V1: codec_stats_->quic_version_rfc_v1_.inc(); - } else { + return; + default: ENVOY_BUG(false, fmt::format("Unexpected QUIC version {}", quic::QuicVersionToString(version.transport_version))); } diff --git a/source/common/quic/quic_filter_manager_connection_impl.h b/source/common/quic/quic_filter_manager_connection_impl.h index 7b2138d5e1ee..7a613e4f6b62 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; @@ -60,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/source/common/quic/quic_transport_socket_factory.h b/source/common/quic/quic_transport_socket_factory.h index 7c98e1ef29e6..c2328324a6b3 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/source/common/router/BUILD b/source/common/router/BUILD index cc7b7b979edc..90ac3acb21d9 100644 --- a/source/common/router/BUILD +++ b/source/common/router/BUILD @@ -102,6 +102,7 @@ envoy_cc_library( "//envoy/router:context_interface", "//envoy/router:router_interface", "//envoy/stats:stats_macros", + "//source/common/config:utility_lib", ], ) diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index 705911b97a3e..d3120fcb533b 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -405,7 +405,8 @@ RouteEntryImplBase::RouteEntryImplBase(const VirtualHostImpl& vhost, route.route().has_host_rewrite_path_regex() ? route.route().host_rewrite_path_regex().substitution() : ""), - cluster_name_(route.route().cluster()), cluster_header_name_(route.route().cluster_header()), + append_xfh_(route.route().append_x_forwarded_host()), cluster_name_(route.route().cluster()), + cluster_header_name_(route.route().cluster_header()), cluster_not_found_response_code_(ConfigUtility::parseClusterNotFoundResponseCode( route.route().cluster_not_found_response_code())), timeout_(PROTOBUF_GET_MS_OR_DEFAULT(route.route(), timeout, DEFAULT_ROUTE_TIMEOUT_MS)), @@ -670,7 +671,7 @@ void RouteEntryImplBase::finalizeRequestHeaders(Http::RequestHeaderMap& headers, } if (!host_rewrite_.empty()) { - headers.setHost(host_rewrite_); + Http::Utility::updateAuthority(headers, host_rewrite_, append_xfh_); } else if (auto_host_rewrite_header_) { const auto header = headers.get(*auto_host_rewrite_header_); if (!header.empty()) { @@ -678,14 +679,16 @@ void RouteEntryImplBase::finalizeRequestHeaders(Http::RequestHeaderMap& headers, // value is used. const absl::string_view header_value = header[0]->value().getStringView(); if (!header_value.empty()) { - headers.setHost(header_value); + Http::Utility::updateAuthority(headers, header_value, append_xfh_); } } } else if (host_rewrite_path_regex_ != nullptr) { const std::string path(headers.getPathValue()); absl::string_view just_path(Http::PathUtil::removeQueryAndFragment(path)); - headers.setHost( - host_rewrite_path_regex_->replaceAll(just_path, host_rewrite_path_regex_substitution_)); + Http::Utility::updateAuthority( + headers, + host_rewrite_path_regex_->replaceAll(just_path, host_rewrite_path_regex_substitution_), + append_xfh_); } // Handle path rewrite @@ -1427,7 +1430,7 @@ VirtualHostImpl::VirtualClusterEntry::VirtualClusterEntry( const envoy::config::route::v3::VirtualCluster& virtual_cluster, Stats::Scope& scope, const VirtualClusterStatNames& stat_names) : StatNameProvider(virtual_cluster.name(), scope.symbolTable()), - VirtualClusterBase(stat_name_storage_.statName(), + VirtualClusterBase(virtual_cluster.name(), stat_name_storage_.statName(), scope.scopeFromStatName(stat_name_storage_.statName()), stat_names) { if (virtual_cluster.headers().empty()) { throw EnvoyException("virtual clusters must define 'headers'"); diff --git a/source/common/router/config_impl.h b/source/common/router/config_impl.h index 4b6126587d24..e6402afc3b4d 100644 --- a/source/common/router/config_impl.h +++ b/source/common/router/config_impl.h @@ -236,16 +236,20 @@ class VirtualHostImpl : public VirtualHost, Logger::Loggable struct VirtualClusterBase : public VirtualCluster { public: - VirtualClusterBase(Stats::StatName stat_name, Stats::ScopePtr&& scope, - const VirtualClusterStatNames& stat_names) - : stat_name_(stat_name), scope_(std::move(scope)), + VirtualClusterBase(const absl::optional& name, Stats::StatName stat_name, + Stats::ScopePtr&& scope, const VirtualClusterStatNames& stat_names) + : name_(name), stat_name_(stat_name), scope_(std::move(scope)), stats_(generateStats(*scope_, stat_names)) {} // Router::VirtualCluster + // name_ and stat_name_ are two different representations for the same string, retained in + // memory to avoid symbol-table locks that would be needed when converting on-the-fly. + const absl::optional& name() const override { return name_; } Stats::StatName statName() const override { return stat_name_; } VirtualClusterStats& stats() const override { return stats_; } private: + const absl::optional name_; const Stats::StatName stat_name_; Stats::ScopePtr scope_; mutable VirtualClusterStats stats_; @@ -259,8 +263,8 @@ class VirtualHostImpl : public VirtualHost, Logger::Loggable struct CatchAllVirtualCluster : public VirtualClusterBase { CatchAllVirtualCluster(Stats::Scope& scope, const VirtualClusterStatNames& stat_names) - : VirtualClusterBase(stat_names.other_, scope.scopeFromStatName(stat_names.other_), - stat_names) {} + : VirtualClusterBase(absl::nullopt, stat_names.other_, + scope.scopeFromStatName(stat_names.other_), stat_names) {} }; static const std::shared_ptr SSL_REDIRECT_ROUTE; @@ -572,6 +576,7 @@ class RouteEntryImplBase : public RouteEntry, } const VirtualHost& virtualHost() const override { return vhost_; } bool autoHostRewrite() const override { return auto_host_rewrite_; } + bool appendXfh() const override { return append_xfh_; } const std::multimap& opaqueConfig() const override { return opaque_config_; } @@ -726,6 +731,7 @@ class RouteEntryImplBase : public RouteEntry, const VirtualHost& virtualHost() const override { return parent_->virtualHost(); } bool autoHostRewrite() const override { return parent_->autoHostRewrite(); } + bool appendXfh() const override { return parent_->appendXfh(); } bool includeVirtualHostRateLimits() const override { return parent_->includeVirtualHostRateLimits(); } @@ -882,6 +888,7 @@ class RouteEntryImplBase : public RouteEntry, const absl::optional auto_host_rewrite_header_; const Regex::CompiledMatcherPtr host_rewrite_path_regex_; const std::string host_rewrite_path_regex_substitution_; + const bool append_xfh_; const std::string cluster_name_; const Http::LowerCaseString cluster_header_name_; const Http::Code cluster_not_found_response_code_; diff --git a/source/common/router/context_impl.cc b/source/common/router/context_impl.cc index 2a2df3b234e3..42ad06090c58 100644 --- a/source/common/router/context_impl.cc +++ b/source/common/router/context_impl.cc @@ -1,10 +1,14 @@ #include "source/common/router/context_impl.h" +#include "source/common/config/utility.h" + namespace Envoy { namespace Router { ContextImpl::ContextImpl(Stats::SymbolTable& symbol_table) - : stat_names_(symbol_table), virtual_cluster_stat_names_(symbol_table) {} + : stat_names_(symbol_table), virtual_cluster_stat_names_(symbol_table), + generic_conn_pool_factory_(Envoy::Config::Utility::getFactoryByName( + "envoy.filters.connection_pools.http.generic")) {} } // namespace Router } // namespace Envoy diff --git a/source/common/router/context_impl.h b/source/common/router/context_impl.h index fd36cbdd3a5a..49d133b68468 100644 --- a/source/common/router/context_impl.h +++ b/source/common/router/context_impl.h @@ -4,6 +4,8 @@ #include "envoy/router/router.h" #include "envoy/stats/stats_macros.h" +#include "source/common/common/assert.h" + namespace Envoy { namespace Router { @@ -38,10 +40,15 @@ class ContextImpl : public Context { const VirtualClusterStatNames& virtualClusterStatNames() const override { return virtual_cluster_stat_names_; } + GenericConnPoolFactory& genericConnPoolFactory() override { + ASSERT(generic_conn_pool_factory_ != nullptr); + return *generic_conn_pool_factory_; + } private: const StatNames stat_names_; const VirtualClusterStatNames virtual_cluster_stat_names_; + GenericConnPoolFactory* generic_conn_pool_factory_; }; } // namespace Router diff --git a/source/common/router/delegating_route_impl.cc b/source/common/router/delegating_route_impl.cc index 422b43248dff..ddf1899837c2 100644 --- a/source/common/router/delegating_route_impl.cc +++ b/source/common/router/delegating_route_impl.cc @@ -126,6 +126,8 @@ bool DelegatingRouteEntry::autoHostRewrite() const { return base_route_->routeEntry()->autoHostRewrite(); } +bool DelegatingRouteEntry::appendXfh() const { return base_route_->routeEntry()->appendXfh(); } + const MetadataMatchCriteria* DelegatingRouteEntry::metadataMatchCriteria() const { return base_route_->routeEntry()->metadataMatchCriteria(); } diff --git a/source/common/router/delegating_route_impl.h b/source/common/router/delegating_route_impl.h index f9696e8fc744..048c21b00318 100644 --- a/source/common/router/delegating_route_impl.h +++ b/source/common/router/delegating_route_impl.h @@ -95,6 +95,7 @@ class DelegatingRouteEntry : public Router::RouteEntry { const VirtualCluster* virtualCluster(const Http::HeaderMap& headers) const override; const VirtualHost& virtualHost() const override; bool autoHostRewrite() const override; + bool appendXfh() const override; const MetadataMatchCriteria* metadataMatchCriteria() const override; const std::multimap& opaqueConfig() const override; bool includeVirtualHostRateLimits() const override; diff --git a/source/common/router/header_formatter.cc b/source/common/router/header_formatter.cc index bcc32857d5db..f1b5e04e2ac9 100644 --- a/source/common/router/header_formatter.cc +++ b/source/common/router/header_formatter.cc @@ -96,8 +96,8 @@ parseMetadataField(absl::string_view params_str, bool upstream = true) { return [upstream, params](const Envoy::StreamInfo::StreamInfo& stream_info) -> std::string { const envoy::config::core::v3::Metadata* metadata = nullptr; - if (upstream) { - Upstream::HostDescriptionConstSharedPtr host = stream_info.upstreamHost(); + if (upstream && stream_info.upstreamInfo()) { + Upstream::HostDescriptionConstSharedPtr host = stream_info.upstreamInfo()->upstreamHost(); if (!host) { return std::string(); } @@ -241,6 +241,14 @@ 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 == "VIRTUAL_CLUSTER_NAME") { + field_extractor_ = [](const Envoy::StreamInfo::StreamInfo& stream_info) -> std::string { + return stream_info.virtualClusterName().value_or(""); + }; } else if (field_name == "DOWNSTREAM_REMOTE_ADDRESS") { field_extractor_ = [](const StreamInfo::StreamInfo& stream_info) { return stream_info.downstreamAddressProvider().remoteAddress()->asString(); @@ -326,8 +334,8 @@ StreamInfoHeaderFormatter::StreamInfoHeaderFormatter(absl::string_view field_nam field_extractor_ = parseSubstitutionFormatField(field_name, formatter_map_); } else if (field_name == "UPSTREAM_REMOTE_ADDRESS") { field_extractor_ = [](const Envoy::StreamInfo::StreamInfo& stream_info) -> std::string { - if (stream_info.upstreamHost()) { - return stream_info.upstreamHost()->address()->asString(); + if (stream_info.upstreamInfo() && stream_info.upstreamInfo()->upstreamHost()) { + return stream_info.upstreamInfo()->upstreamHost()->address()->asString(); } return ""; }; diff --git a/source/common/router/rds_impl.cc b/source/common/router/rds_impl.cc index 62f47d9221a1..00cf44c82907 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 4c4df57d4482..8b99ce2f741e 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 61d451ce6291..baec90c90bbc 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 fc07c9f9e3f9..721e46095dfc 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/common/router/router.cc b/source/common/router/router.cc index 054b6a6858ec..40b289a3c019 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -205,29 +205,55 @@ FilterUtility::finalTimeout(const RouteEntry& route, Http::RequestHeaderMap& req timeout.per_try_timeout_ = std::chrono::milliseconds(0); } + setTimeoutHeaders(0, timeout, route, request_headers, insert_envoy_expected_request_timeout_ms, + grpc_request, per_try_timeout_hedging_enabled); + + return timeout; +} + +void FilterUtility::setTimeoutHeaders(uint64_t elapsed_time, + const FilterUtility::TimeoutData& timeout, + const RouteEntry& route, + Http::RequestHeaderMap& request_headers, + bool insert_envoy_expected_request_timeout_ms, + bool grpc_request, bool per_try_timeout_hedging_enabled) { + + const uint64_t global_timeout = timeout.global_timeout_.count(); + // See if there is any timeout to write in the expected timeout header. uint64_t expected_timeout = timeout.per_try_timeout_.count(); + // Use the global timeout if no per try timeout was specified or if we're // doing hedging when there are per try timeouts. Either of these scenarios // mean that the upstream server can use the full global timeout. if (per_try_timeout_hedging_enabled || expected_timeout == 0) { - expected_timeout = timeout.global_timeout_.count(); - } - - if (insert_envoy_expected_request_timeout_ms && expected_timeout > 0) { - request_headers.setEnvoyExpectedRequestTimeoutMs(expected_timeout); + expected_timeout = global_timeout; } - // If we've configured max_grpc_timeout, override the grpc-timeout header with - // the expected timeout. This ensures that the optional per try timeout is reflected - // in grpc-timeout, ensuring that the upstream gRPC server is aware of the actual timeout. // If the expected timeout is 0 set no timeout, as Envoy treats 0 as infinite timeout. - if (grpc_request && !route.usingNewTimeouts() && route.maxGrpcTimeout() && - expected_timeout != 0) { - Grpc::Common::toGrpcTimeout(std::chrono::milliseconds(expected_timeout), request_headers); - } + if (expected_timeout > 0) { - return timeout; + if (global_timeout > 0) { + if (elapsed_time >= global_timeout) { + // We are out of time, but 0 would be an infinite timeout. So instead we send a 1ms timeout + // and assume the timers armed by onRequestComplete() will fire very soon. + expected_timeout = 1; + } else { + expected_timeout = std::min(expected_timeout, global_timeout - elapsed_time); + } + } + + if (insert_envoy_expected_request_timeout_ms) { + request_headers.setEnvoyExpectedRequestTimeoutMs(expected_timeout); + } + + // If we've configured max_grpc_timeout, override the grpc-timeout header with + // the expected timeout. This ensures that the optional per try timeout is reflected + // in grpc-timeout, ensuring that the upstream gRPC server is aware of the actual timeout. + if (grpc_request && !route.usingNewTimeouts() && route.maxGrpcTimeout()) { + Grpc::Common::toGrpcTimeout(std::chrono::milliseconds(expected_timeout), request_headers); + } + } } absl::optional @@ -453,6 +479,9 @@ Http::FilterHeadersStatus Filter::decodeHeaders(Http::RequestHeaderMap& headers, // Set up stat prefixes, etc. request_vcluster_ = route_entry_->virtualCluster(headers); + if (request_vcluster_ != nullptr) { + callbacks_->streamInfo().setVirtualClusterName(request_vcluster_->name()); + } ENVOY_STREAM_LOG(debug, "cluster '{}' match for URL '{}'", *callbacks_, route_entry_->clusterName(), headers.getPathValue()); @@ -467,7 +496,7 @@ Http::FilterHeadersStatus Filter::decodeHeaders(Http::RequestHeaderMap& headers, std::string(res.entry_->value().getStringView())); const std::string details = absl::StrCat(StreamInfo::ResponseCodeDetails::get().InvalidEnvoyRequestHeaders, "{", - res.entry_->key().getStringView(), "}"); + StringUtil::replaceAllEmptySpace(res.entry_->key().getStringView()), "}"); callbacks_->sendLocalReply(Http::Code::BadRequest, body, nullptr, absl::nullopt, details); return Http::FilterHeadersStatus::StopIteration; } @@ -697,8 +726,7 @@ Filter::createConnPool(Upstream::ThreadLocalCluster& thread_local_cluster) { cluster_->upstreamConfig().value().DebugString())); } if (!factory) { - factory = &Envoy::Config::Utility::getAndCheckFactoryByName( - "envoy.filters.connection_pools.http.generic"); + factory = &config_.router_context_.genericConnPoolFactory(); } bool should_tcp_proxy = false; @@ -895,12 +923,6 @@ void Filter::onDestroy() { void Filter::onResponseTimeout() { ENVOY_STREAM_LOG(debug, "upstream timeout", *callbacks_); - // If we had an upstream request that got a "good" response, save its - // upstream timing information into the downstream stream info. - if (final_upstream_request_) { - callbacks_->streamInfo().setUpstreamTiming(final_upstream_request_->upstreamTiming()); - } - // Reset any upstream requests that are still in flight. while (!upstream_requests_.empty()) { UpstreamRequestPtr upstream_request = @@ -1164,13 +1186,12 @@ void Filter::onUpstreamReset(Http::StreamResetReason reset_reason, ? ", transport failure reason: " : "", transport_failure_reason); - callbacks_->streamInfo().setUpstreamTransportFailureReason(transport_failure_reason); const std::string& basic_details = downstream_response_started_ ? StreamInfo::ResponseCodeDetails::get().LateUpstreamReset : StreamInfo::ResponseCodeDetails::get().EarlyUpstreamReset; - const std::string details = absl::StrCat( + const std::string details = StringUtil::replaceAllEmptySpace(absl::StrCat( basic_details, "{", Http::Utility::resetReasonToString(reset_reason), - transport_failure_reason.empty() ? "" : absl::StrCat(",", transport_failure_reason), "}"); + transport_failure_reason.empty() ? "" : absl::StrCat(",", transport_failure_reason), "}")); onUpstreamAbort(error_code, response_flags, body, dropped, details); } @@ -1225,10 +1246,11 @@ void Filter::handleNon5xxResponseHeaders(absl::optionalencode100ContinueHeaders(std::move(headers)); + if (!downstream_1xx_headers_encoded_) { + downstream_1xx_headers_encoded_ = true; + callbacks_->encode1xxHeaders(std::move(headers)); } } @@ -1406,14 +1428,8 @@ void Filter::onUpstreamHeaders(uint64_t response_code, Http::ResponseHeaderMapPt downstream_response_started_ = true; final_upstream_request_ = &upstream_request; - // In upstream request hedging scenarios the upstream connection ID set in onPoolReady might not - // be the connection ID of the upstream connection that ended up receiving upstream headers. Thus - // reset the upstream connection ID here with the ID of the connection that ultimately was the - // transport for the final upstream request. - if (final_upstream_request_->streamInfo().upstreamConnectionId().has_value()) { - callbacks_->streamInfo().setUpstreamConnectionId( - final_upstream_request_->streamInfo().upstreamConnectionId().value()); - } + // Make sure that for request hedging, we end up with the correct final upstream info. + callbacks_->streamInfo().setUpstreamInfo(final_upstream_request_->streamInfo().upstreamInfo()); resetOtherUpstreams(upstream_request); if (end_stream) { onUpstreamComplete(upstream_request); @@ -1470,8 +1486,6 @@ void Filter::onUpstreamComplete(UpstreamRequest& upstream_request) { if (!downstream_end_stream_) { upstream_request.resetStream(); } - callbacks_->streamInfo().setUpstreamTiming(final_upstream_request_->upstreamTiming()); - Event::Dispatcher& dispatcher = callbacks_->dispatcher(); std::chrono::milliseconds response_time = std::chrono::duration_cast( dispatcher.timeSource().monotonicTime() - downstream_request_complete_time_); @@ -1694,6 +1708,25 @@ void Filter::doRetry() { downstream_headers_->setEnvoyAttemptCount(attempt_count_); } + if (Runtime::runtimeFeatureEnabled( + "envoy.reloadable_features.update_expected_rq_timeout_on_retry")) { + // If not enabled, then it will re-use the previous headers (if any.) + + // The request timeouts only account for time elapsed since the downstream request completed + // which might not have happened yet (in which case zero time has elapsed.) + std::chrono::milliseconds elapsed_time = std::chrono::milliseconds::zero(); + + if (DateUtil::timePointValid(downstream_request_complete_time_)) { + Event::Dispatcher& dispatcher = callbacks_->dispatcher(); + elapsed_time = std::chrono::duration_cast( + dispatcher.timeSource().monotonicTime() - downstream_request_complete_time_); + } + + FilterUtility::setTimeoutHeaders(elapsed_time.count(), timeout_, *route_entry_, + *downstream_headers_, !config_.suppress_envoy_headers_, + grpc_request_, hedging_params_.hedge_on_per_try_timeout_); + } + UpstreamRequest* upstream_request_tmp = upstream_request.get(); LinkedList::moveIntoList(std::move(upstream_request), upstream_requests_); upstream_requests_.front()->encodeHeaders(!callbacks_->decodingBuffer() && diff --git a/source/common/router/router.h b/source/common/router/router.h index e9d0234aed5d..fe5050f74fa2 100644 --- a/source/common/router/router.h +++ b/source/common/router/router.h @@ -147,6 +147,21 @@ class FilterUtility { bool per_try_timeout_hedging_enabled, bool respect_expected_rq_timeout); + /** + * Set the x-envoy-expected-request-timeout-ms and grpc-timeout headers if needed. + * @param elapsed_time time elapsed since completion of the downstream request + * @param timeout final TimeoutData to use for the request + * @param request_headers the request headers to modify + * @param insert_envoy_expected_request_timeout_ms insert + * x-envoy-expected-request-timeout-ms? + * @param grpc_request tells if the request is a gRPC request. + * @param per_try_timeout_headging_enabled is request hedging enabled? + */ + static void setTimeoutHeaders(uint64_t elapsed_time, const FilterUtility::TimeoutData& timeout, + const RouteEntry& route, Http::RequestHeaderMap& request_headers, + bool insert_envoy_expected_request_timeout_ms, bool grpc_request, + bool per_try_timeout_hedging_enabled); + /** * Try to parse a header entry that may have a timeout field * @@ -188,8 +203,8 @@ class FilterConfig { const Protobuf::RepeatedPtrField& strict_check_headers, TimeSource& time_source, Http::Context& http_context, Router::Context& router_context) - : scope_(scope), local_info_(local_info), cm_(cm), runtime_(runtime), random_(random), - stats_(router_context.statNames(), scope, stat_prefix), + : router_context_(router_context), scope_(scope), local_info_(local_info), cm_(cm), + runtime_(runtime), random_(random), stats_(router_context_.statNames(), scope, stat_prefix), emit_dynamic_stats_(emit_dynamic_stats), start_child_span_(start_child_span), suppress_envoy_headers_(suppress_envoy_headers), respect_expected_rq_timeout_(respect_expected_rq_timeout), @@ -224,6 +239,7 @@ class FilterConfig { ShadowWriter& shadowWriter() { return *shadow_writer_; } TimeSource& timeSource() { return time_source_; } + Router::Context& router_context_; Stats::Scope& scope_; const LocalInfo::LocalInfo& local_info_; Upstream::ClusterManager& cm_; @@ -258,8 +274,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 +317,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 +450,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 +578,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 34ccc3f38c5f..61a45b0b1dae 100644 --- a/source/common/router/upstream_request.cc +++ b/source/common/router/upstream_request.cc @@ -60,6 +60,8 @@ UpstreamRequest::UpstreamRequest(RouterFilterInterface& parent, span_->setTag(Tracing::Tags::get().RetryCount, std::to_string(parent.attemptCount() - 1)); } } + stream_info_.setUpstreamInfo(std::make_shared()); + parent_.callbacks()->streamInfo().setUpstreamInfo(stream_info_.upstreamInfo()); stream_info_.healthCheck(parent_.callbacks()->streamInfo().healthCheck()); absl::optional cluster_info = @@ -115,7 +117,6 @@ UpstreamRequest::~UpstreamRequest() { } } - stream_info_.setUpstreamTiming(upstream_timing_); stream_info_.onRequestComplete(); for (const auto& upstream_log : parent_.config().upstream_logs_) { upstream_log->log(parent_.downstreamHeaders(), upstream_headers_.get(), @@ -129,12 +130,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 +144,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 @@ -162,7 +163,7 @@ void UpstreamRequest::decodeHeaders(Http::ResponseHeaderMapPtr&& headers, bool e // TODO(rodaine): This is actually measuring after the headers are parsed and not the first // byte. - upstream_timing_.onFirstUpstreamRxByteReceived(parent_.callbacks()->dispatcher().timeSource()); + upstreamTiming().onFirstUpstreamRxByteReceived(parent_.callbacks()->dispatcher().timeSource()); maybeEndDecode(end_stream); awaiting_headers_ = false; @@ -219,15 +220,15 @@ void UpstreamRequest::decodeMetadata(Http::MetadataMapPtr&& metadata_map) { void UpstreamRequest::maybeEndDecode(bool end_stream) { if (end_stream) { - upstream_timing_.onLastUpstreamRxByteReceived(parent_.callbacks()->dispatcher().timeSource()); + upstreamTiming().onLastUpstreamRxByteReceived(parent_.callbacks()->dispatcher().timeSource()); decode_complete_ = true; } } void UpstreamRequest::onUpstreamHostSelected(Upstream::HostDescriptionConstSharedPtr host) { - stream_info_.onUpstreamHostSelected(host); + StreamInfo::UpstreamInfo& upstream_info = *streamInfo().upstreamInfo(); + upstream_info.setUpstreamHost(host); upstream_host_ = host; - parent_.callbacks()->streamInfo().onUpstreamHostSelected(host); parent_.onUpstreamHostSelected(host); } @@ -260,7 +261,7 @@ void UpstreamRequest::encodeData(Buffer::Instance& data, bool end_stream) { stream_info_.addBytesSent(data.length()); upstream_->encodeData(data, end_stream); if (end_stream) { - upstream_timing_.onLastUpstreamTxByteSent(parent_.callbacks()->dispatcher().timeSource()); + upstreamTiming().onLastUpstreamTxByteSent(parent_.callbacks()->dispatcher().timeSource()); } } } @@ -277,7 +278,7 @@ void UpstreamRequest::encodeTrailers(const Http::RequestTrailerMap& trailers) { ENVOY_STREAM_LOG(trace, "proxying trailers", *parent_.callbacks()); upstream_->encodeTrailers(trailers); - upstream_timing_.onLastUpstreamTxByteSent(parent_.callbacks()->dispatcher().timeSource()); + upstreamTiming().onLastUpstreamTxByteSent(parent_.callbacks()->dispatcher().timeSource()); } } @@ -427,22 +428,23 @@ void UpstreamRequest::onPoolReady( stream_info_.protocol(protocol.value()); } - stream_info_.setUpstreamFilterState(std::make_shared( - info.filterState().parent()->parent(), StreamInfo::FilterState::LifeSpan::Request)); - parent_.callbacks()->streamInfo().setUpstreamFilterState( - std::make_shared(info.filterState().parent()->parent(), - StreamInfo::FilterState::LifeSpan::Request)); - stream_info_.setUpstreamLocalAddress(upstream_local_address); - parent_.callbacks()->streamInfo().setUpstreamLocalAddress(upstream_local_address); + StreamInfo::UpstreamInfo& upstream_info = *stream_info_.upstreamInfo(); + parent_.callbacks()->streamInfo().setUpstreamInfo(stream_info_.upstreamInfo()); + if (info.upstreamInfo().has_value()) { + auto& upstream_timing = info.upstreamInfo().value().get().upstreamTiming(); + upstreamTiming().upstream_connect_start_ = upstream_timing.upstream_connect_start_; + upstreamTiming().upstream_connect_complete_ = upstream_timing.upstream_connect_complete_; + upstreamTiming().upstream_handshake_complete_ = upstream_timing.upstream_handshake_complete_; + upstream_info.setUpstreamNumStreams(info.upstreamInfo().value().get().upstreamNumStreams()); + } - stream_info_.setUpstreamSslConnection(info.downstreamAddressProvider().sslConnection()); - parent_.callbacks()->streamInfo().setUpstreamSslConnection( - info.downstreamAddressProvider().sslConnection()); + upstream_info.setUpstreamFilterState(std::make_shared( + info.filterState().parent()->parent(), StreamInfo::FilterState::LifeSpan::Request)); + upstream_info.setUpstreamLocalAddress(upstream_local_address); + upstream_info.setUpstreamSslConnection(info.downstreamAddressProvider().sslConnection()); if (info.downstreamAddressProvider().connectionID().has_value()) { - stream_info_.setUpstreamConnectionId(info.downstreamAddressProvider().connectionID().value()); - parent_.callbacks()->streamInfo().setUpstreamConnectionId( - info.downstreamAddressProvider().connectionID().value()); + upstream_info.setUpstreamConnectionId(info.downstreamAddressProvider().connectionID().value()); } stream_info_.setUpstreamBytesMeter(upstream_->bytesMeter()); @@ -463,14 +465,15 @@ void UpstreamRequest::onPoolReady( calling_encode_headers_ = true; auto* headers = parent_.downstreamHeaders(); if (parent_.routeEntry()->autoHostRewrite() && !host->hostname().empty()) { - parent_.downstreamHeaders()->setHost(host->hostname()); + Http::Utility::updateAuthority(*parent_.downstreamHeaders(), host->hostname(), + parent_.routeEntry()->appendXfh()); } if (span_ != nullptr) { span_->injectContext(*parent_.downstreamHeaders()); } - upstream_timing_.onFirstUpstreamTxByteSent(parent_.callbacks()->dispatcher().timeSource()); + upstreamTiming().onFirstUpstreamTxByteSent(parent_.callbacks()->dispatcher().timeSource()); // Make sure that when we are forwarding CONNECT payload we do not do so until // the upstream has accepted the CONNECT request. @@ -503,7 +506,7 @@ void UpstreamRequest::onPoolReady( stream_info_.setResponseFlag(StreamInfo::ResponseFlag::DownstreamProtocolError); const std::string details = absl::StrCat(StreamInfo::ResponseCodeDetails::get().FilterRemovedRequiredRequestHeaders, - "{", status.message(), "}"); + "{", StringUtil::replaceAllEmptySpace(status.message()), "}"); parent_.callbacks()->sendLocalReply(Http::Code::ServiceUnavailable, status.message(), nullptr, absl::nullopt, details); return; @@ -541,7 +544,7 @@ void UpstreamRequest::encodeBodyAndTrailers() { } if (encode_complete_) { - upstream_timing_.onLastUpstreamTxByteSent(parent_.callbacks()->dispatcher().timeSource()); + upstreamTiming().onLastUpstreamTxByteSent(parent_.callbacks()->dispatcher().timeSource()); } } } diff --git a/source/common/router/upstream_request.h b/source/common/router/upstream_request.h index 112c2a5216e6..de02565a6758 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; @@ -104,7 +104,6 @@ class UpstreamRequest : public Logger::Loggable, outlier_detection_timeout_recorded_ = recorded; } bool outlierDetectionTimeoutRecorded() { return outlier_detection_timeout_recorded_; } - const StreamInfo::UpstreamTiming& upstreamTiming() { return upstream_timing_; } void retried(bool value) { retried_ = value; } bool retried() { return retried_; } bool grpcRqSuccessDeferred() { return grpc_rq_success_deferred_; } @@ -122,6 +121,9 @@ class UpstreamRequest : public Logger::Loggable, StreamInfo::StreamInfo& streamInfo() { return stream_info_; } private: + StreamInfo::UpstreamTiming& upstreamTiming() { + return stream_info_.upstreamInfo()->upstreamTiming(); + } bool shouldSendEndStream() { // Only encode end stream if the full request has been received, the body // has been sent, and any trailers or metadata have also been sent. @@ -147,7 +149,6 @@ class UpstreamRequest : public Logger::Loggable, DownstreamWatermarkManager downstream_watermark_manager_{*this}; Tracing::SpanPtr span_; StreamInfo::StreamInfoImpl stream_info_; - StreamInfo::UpstreamTiming upstream_timing_; const MonotonicTime start_time_; // This is wrapped in an optional, since we want to avoid computing zero size headers when in // reality we just didn't get a response back. diff --git a/source/common/runtime/BUILD b/source/common/runtime/BUILD index ce7a517e9a4a..25d026525622 100644 --- a/source/common/runtime/BUILD +++ b/source/common/runtime/BUILD @@ -74,7 +74,6 @@ envoy_cc_library( "//source/common/protobuf:utility_lib", "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", - "@envoy_api//envoy/service/discovery/v2:pkg_cc_proto", "@envoy_api//envoy/service/discovery/v3:pkg_cc_proto", "@envoy_api//envoy/service/runtime/v3:pkg_cc_proto", "@envoy_api//envoy/type/v3:pkg_cc_proto", diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 966d1e7d9f21..67410b41205d 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -62,8 +62,7 @@ constexpr const char* runtime_features[] = { "envoy.reloadable_features.disable_tls_inspector_injection", "envoy.reloadable_features.fix_added_trailers", "envoy.reloadable_features.grpc_bridge_stats_disabled", - "envoy.reloadable_features.grpc_web_fix_non_proto_encoded_response_handling", - "envoy.reloadable_features.grpc_json_transcoder_adhere_to_buffer_limits", + "envoy.reloadable_features.handle_stream_reset_during_hcm_encoding", "envoy.reloadable_features.hash_multiple_header_values", "envoy.reloadable_features.health_check.graceful_goaway_handling", "envoy.reloadable_features.http2_consume_stream_refused_errors", @@ -71,28 +70,29 @@ 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", "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.proxy_102_103", + "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", "envoy.reloadable_features.udp_listener_updates_filter_chain_in_place", "envoy.reloadable_features.udp_per_event_loop_read_limit", "envoy.reloadable_features.unquote_log_string_values", + "envoy.reloadable_features.update_expected_rq_timeout_on_retry", "envoy.reloadable_features.use_dns_ttl", "envoy.reloadable_features.use_observable_cluster_name", "envoy.reloadable_features.validate_connect", "envoy.reloadable_features.vhds_heartbeats", - "envoy.reloadable_features.wasm_cluster_name_envoy_grpc", - "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. - "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. @@ -110,18 +110,16 @@ 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", - // 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. "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/source/common/runtime/runtime_impl.cc b/source/common/runtime/runtime_impl.cc index bf5ad44ffe5e..824054f8412d 100644 --- a/source/common/runtime/runtime_impl.cc +++ b/source/common/runtime/runtime_impl.cc @@ -6,7 +6,6 @@ #include "envoy/config/bootstrap/v3/bootstrap.pb.h" #include "envoy/config/core/v3/config_source.pb.h" #include "envoy/event/dispatcher.h" -#include "envoy/service/discovery/v2/rtds.pb.h" #include "envoy/service/discovery/v3/discovery.pb.h" #include "envoy/thread_local/thread_local.h" #include "envoy/type/v3/percent.pb.h" diff --git a/source/common/ssl/BUILD b/source/common/ssl/BUILD index 714c426b930f..4526fe428f73 100644 --- a/source/common/ssl/BUILD +++ b/source/common/ssl/BUILD @@ -32,6 +32,7 @@ envoy_cc_library( "//envoy/ssl:certificate_validation_context_config_interface", "//source/common/common:empty_string", "//source/common/config:datasource_lib", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", "@envoy_api//envoy/type/matcher/v3:pkg_cc_proto", ], diff --git a/source/common/ssl/certificate_validation_context_config_impl.cc b/source/common/ssl/certificate_validation_context_config_impl.cc index 40dc20f6ef3a..376f2b65a085 100644 --- a/source/common/ssl/certificate_validation_context_config_impl.cc +++ b/source/common/ssl/certificate_validation_context_config_impl.cc @@ -1,10 +1,13 @@ #include "source/common/ssl/certificate_validation_context_config_impl.h" #include "envoy/common/exception.h" +#include "envoy/config/core/v3/extension.pb.h" #include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h" +#include "envoy/extensions/transport_sockets/tls/v3/common.pb.h" #include "source/common/common/empty_string.h" #include "source/common/common/fmt.h" +#include "source/common/common/logger.h" #include "source/common/config/datasource.h" namespace Envoy { @@ -22,8 +25,7 @@ CertificateValidationContextConfigImpl::CertificateValidationContextConfigImpl( certificate_revocation_list_path_( Config::DataSource::getPath(config.crl()) .value_or(certificate_revocation_list_.empty() ? EMPTY_STRING : INLINE_STRING)), - subject_alt_name_matchers_(config.match_subject_alt_names().begin(), - config.match_subject_alt_names().end()), + subject_alt_name_matchers_(getSubjectAltNameMatchers(config)), verify_certificate_hash_list_(config.verify_certificate_hash().begin(), config.verify_certificate_hash().end()), verify_certificate_spki_list_(config.verify_certificate_spki().begin(), @@ -35,7 +37,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", @@ -51,5 +53,34 @@ CertificateValidationContextConfigImpl::CertificateValidationContextConfigImpl( } } +std::vector +CertificateValidationContextConfigImpl::getSubjectAltNameMatchers( + const envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext& config) { + if (!config.match_typed_subject_alt_names().empty() && + !config.match_subject_alt_names().empty()) { + throw EnvoyException("SAN-based verification using both match_typed_subject_alt_names and " + "the deprecated match_subject_alt_names is not allowed"); + } + std::vector + subject_alt_name_matchers(config.match_typed_subject_alt_names().begin(), + config.match_typed_subject_alt_names().end()); + // Handle deprecated string type san matchers without san type specified, by + // creating a matcher for each supported type. + for (const envoy::type::matcher::v3::StringMatcher& matcher : config.match_subject_alt_names()) { + static constexpr std::array< + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SanType, 4> + san_types{envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::DNS, + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::URI, + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::EMAIL, + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::IP_ADDRESS}; + for (const auto san_type : san_types) { + subject_alt_name_matchers.emplace_back(); + subject_alt_name_matchers.back().set_san_type(san_type); + *subject_alt_name_matchers.back().mutable_matcher() = matcher; + } + } + return subject_alt_name_matchers; +} + } // namespace Ssl } // namespace Envoy diff --git a/source/common/ssl/certificate_validation_context_config_impl.h b/source/common/ssl/certificate_validation_context_config_impl.h index 7cf045a35184..6e00605ff5cd 100644 --- a/source/common/ssl/certificate_validation_context_config_impl.h +++ b/source/common/ssl/certificate_validation_context_config_impl.h @@ -4,6 +4,7 @@ #include "envoy/api/api.h" #include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h" +#include "envoy/extensions/transport_sockets/tls/v3/common.pb.h" #include "envoy/ssl/certificate_validation_context_config.h" #include "envoy/type/matcher/v3/string.pb.h" @@ -24,7 +25,7 @@ class CertificateValidationContextConfigImpl : public CertificateValidationConte const std::string& certificateRevocationListPath() const final { return certificate_revocation_list_path_; } - const std::vector& + const std::vector& subjectAltNameMatchers() const override { return subject_alt_name_matchers_; } @@ -48,12 +49,18 @@ class CertificateValidationContextConfigImpl : public CertificateValidationConte Api::Api& api() const override { return api_; } + bool onlyVerifyLeafCertificateCrl() const override { return only_verify_leaf_cert_crl_; } + private: + static std::vector + getSubjectAltNameMatchers( + const envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext& config); const std::string ca_cert_; const std::string ca_cert_path_; const std::string certificate_revocation_list_; const std::string certificate_revocation_list_path_; - const std::vector subject_alt_name_matchers_; + const std::vector + subject_alt_name_matchers_; const std::vector verify_certificate_hash_list_; const std::vector verify_certificate_spki_list_; const bool allow_expired_certificate_; @@ -61,6 +68,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/common/ssl/tls_certificate_config_impl.cc b/source/common/ssl/tls_certificate_config_impl.cc index fd0332c126af..0d4c1dfb83bd 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 074ddcb955a4..b93d429fc989 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/common/stats/BUILD b/source/common/stats/BUILD index 7e3ef609fba5..122d5d66ca4a 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/allocator_impl.cc b/source/common/stats/allocator_impl.cc index 9e8a37705e4d..d0103b1065c7 100644 --- a/source/common/stats/allocator_impl.cc +++ b/source/common/stats/allocator_impl.cc @@ -3,6 +3,7 @@ #include #include +#include "envoy/stats/sink.h" #include "envoy/stats/stats.h" #include "envoy/stats/symbol_table.h" @@ -144,6 +145,7 @@ class CounterImpl : public StatsSharedImpl { void removeFromSetLockHeld() ABSL_EXCLUSIVE_LOCKS_REQUIRED(alloc_.mutex_) override { const size_t count = alloc_.counters_.erase(statName()); ASSERT(count == 1); + alloc_.sinked_counters_.erase(this); } // Stats::Counter @@ -188,6 +190,7 @@ class GaugeImpl : public StatsSharedImpl { void removeFromSetLockHeld() override ABSL_EXCLUSIVE_LOCKS_REQUIRED(alloc_.mutex_) { const size_t count = alloc_.gauges_.erase(statName()); ASSERT(count == 1); + alloc_.sinked_gauges_.erase(this); } // Stats::Gauge @@ -260,6 +263,7 @@ class TextReadoutImpl : public StatsSharedImpl { void removeFromSetLockHeld() ABSL_EXCLUSIVE_LOCKS_REQUIRED(alloc_.mutex_) override { const size_t count = alloc_.text_readouts_.erase(statName()); ASSERT(count == 1); + alloc_.sinked_text_readouts_.erase(this); } // Stats::TextReadout @@ -289,6 +293,11 @@ CounterSharedPtr AllocatorImpl::makeCounter(StatName name, StatName tag_extracte } auto counter = CounterSharedPtr(makeCounterInternal(name, tag_extracted_name, stat_name_tags)); counters_.insert(counter.get()); + // Add counter to sinked_counters_ if it matches the sink predicate. + if (sink_predicates_ != nullptr && sink_predicates_->includeCounter(*counter)) { + auto val = sinked_counters_.insert(counter.get()); + ASSERT(val.second); + } return counter; } @@ -305,6 +314,11 @@ GaugeSharedPtr AllocatorImpl::makeGauge(StatName name, StatName tag_extracted_na auto gauge = GaugeSharedPtr(new GaugeImpl(name, *this, tag_extracted_name, stat_name_tags, import_mode)); gauges_.insert(gauge.get()); + // Add gauge to sinked_gauges_ if it matches the sink predicate. + if (sink_predicates_ != nullptr && sink_predicates_->includeGauge(*gauge)) { + auto val = sinked_gauges_.insert(gauge.get()); + ASSERT(val.second); + } return gauge; } @@ -320,6 +334,11 @@ TextReadoutSharedPtr AllocatorImpl::makeTextReadout(StatName name, StatName tag_ auto text_readout = TextReadoutSharedPtr(new TextReadoutImpl(name, *this, tag_extracted_name, stat_name_tags)); text_readouts_.insert(text_readout.get()); + // Add text_readout to sinked_text_readouts_ if it matches the sink predicate. + if (sink_predicates_ != nullptr && sink_predicates_->includeTextReadout(*text_readout)) { + auto val = sinked_text_readouts_.insert(text_readout.get()); + ASSERT(val.second); + } return text_readout; } @@ -336,8 +355,7 @@ Counter* AllocatorImpl::makeCounterInternal(StatName name, StatName tag_extracte return new CounterImpl(name, *this, tag_extracted_name, stat_name_tags); } -void AllocatorImpl::forEachCounter(std::function f_size, - std::function f_stat) const { +void AllocatorImpl::forEachCounter(SizeFn f_size, StatFn f_stat) const { Thread::LockGuard lock(mutex_); if (f_size != nullptr) { f_size(counters_.size()); @@ -347,8 +365,7 @@ void AllocatorImpl::forEachCounter(std::function f_size, } } -void AllocatorImpl::forEachGauge(std::function f_size, - std::function f_stat) const { +void AllocatorImpl::forEachGauge(SizeFn f_size, StatFn f_stat) const { Thread::LockGuard lock(mutex_); if (f_size != nullptr) { f_size(gauges_.size()); @@ -358,8 +375,7 @@ void AllocatorImpl::forEachGauge(std::function f_size, } } -void AllocatorImpl::forEachTextReadout(std::function f_size, - std::function f_stat) const { +void AllocatorImpl::forEachTextReadout(SizeFn f_size, StatFn f_stat) const { Thread::LockGuard lock(mutex_); if (f_size != nullptr) { f_size(text_readouts_.size()); @@ -369,6 +385,69 @@ void AllocatorImpl::forEachTextReadout(std::function f_size, } } +void AllocatorImpl::forEachSinkedCounter(SizeFn f_size, StatFn f_stat) const { + if (sink_predicates_ != nullptr) { + Thread::LockGuard lock(mutex_); + f_size(sinked_counters_.size()); + for (auto counter : sinked_counters_) { + f_stat(*counter); + } + } else { + forEachCounter(f_size, f_stat); + } +} + +void AllocatorImpl::forEachSinkedGauge(SizeFn f_size, StatFn f_stat) const { + if (sink_predicates_ != nullptr) { + Thread::LockGuard lock(mutex_); + f_size(sinked_gauges_.size()); + for (auto gauge : sinked_gauges_) { + f_stat(*gauge); + } + } else { + forEachGauge(f_size, f_stat); + } +} + +void AllocatorImpl::forEachSinkedTextReadout(SizeFn f_size, StatFn f_stat) const { + if (sink_predicates_ != nullptr) { + Thread::LockGuard lock(mutex_); + f_size(sinked_text_readouts_.size()); + for (auto text_readout : sinked_text_readouts_) { + f_stat(*text_readout); + } + } else { + forEachTextReadout(f_size, f_stat); + } +} + +void AllocatorImpl::setSinkPredicates(std::unique_ptr&& sink_predicates) { + Thread::LockGuard lock(mutex_); + ASSERT(sink_predicates_ == nullptr); + sink_predicates_ = std::move(sink_predicates); + sinked_counters_.clear(); + sinked_gauges_.clear(); + sinked_text_readouts_.clear(); + // Add counters to the set of sinked counters. + for (auto& counter : counters_) { + if (sink_predicates_->includeCounter(*counter)) { + sinked_counters_.emplace(counter); + } + } + // Add gauges to the set of sinked gauges. + for (auto& gauge : gauges_) { + if (sink_predicates_->includeGauge(*gauge)) { + sinked_gauges_.insert(gauge); + } + } + // Add text_readouts to the set of sinked text readouts. + for (auto& text_readout : text_readouts_) { + if (sink_predicates_->includeTextReadout(*text_readout)) { + sinked_text_readouts_.insert(text_readout); + } + } +} + void AllocatorImpl::markCounterForDeletion(const CounterSharedPtr& counter) { Thread::LockGuard lock(mutex_); auto iter = counters_.find(counter->statName()); @@ -380,6 +459,7 @@ void AllocatorImpl::markCounterForDeletion(const CounterSharedPtr& counter) { // Duplicates are ASSERTed in ~AllocatorImpl. deleted_counters_.emplace_back(*iter); counters_.erase(iter); + sinked_counters_.erase(counter.get()); } void AllocatorImpl::markGaugeForDeletion(const GaugeSharedPtr& gauge) { @@ -393,6 +473,7 @@ void AllocatorImpl::markGaugeForDeletion(const GaugeSharedPtr& gauge) { // Duplicates are ASSERTed in ~AllocatorImpl. deleted_gauges_.emplace_back(*iter); gauges_.erase(iter); + sinked_gauges_.erase(gauge.get()); } void AllocatorImpl::markTextReadoutForDeletion(const TextReadoutSharedPtr& text_readout) { @@ -406,6 +487,7 @@ void AllocatorImpl::markTextReadoutForDeletion(const TextReadoutSharedPtr& text_ // Duplicates are ASSERTed in ~AllocatorImpl. deleted_text_readouts_.emplace_back(*iter); text_readouts_.erase(iter); + sinked_text_readouts_.erase(text_readout.get()); } } // namespace Stats diff --git a/source/common/stats/allocator_impl.h b/source/common/stats/allocator_impl.h index 806e7dc8612f..86a1e1aa664c 100644 --- a/source/common/stats/allocator_impl.h +++ b/source/common/stats/allocator_impl.h @@ -2,7 +2,9 @@ #include +#include "envoy/common/optref.h" #include "envoy/stats/allocator.h" +#include "envoy/stats/sink.h" #include "envoy/stats/stats.h" #include "envoy/stats/symbol_table.h" @@ -33,15 +35,17 @@ class AllocatorImpl : public Allocator { SymbolTable& symbolTable() override { return symbol_table_; } const SymbolTable& constSymbolTable() const override { return symbol_table_; } - void forEachCounter(std::function, - std::function) const override; + void forEachCounter(SizeFn, StatFn) const override; - void forEachGauge(std::function, - std::function) const override; + void forEachGauge(SizeFn, StatFn) const override; - void forEachTextReadout(std::function, - std::function) const override; + void forEachTextReadout(SizeFn, StatFn) const override; + void forEachSinkedCounter(SizeFn f_size, StatFn f_stat) const override; + void forEachSinkedGauge(SizeFn f_size, StatFn f_stat) const override; + void forEachSinkedTextReadout(SizeFn f_size, StatFn f_stat) const override; + + void setSinkPredicates(std::unique_ptr&& sink_predicates) override; #ifndef ENVOY_CONFIG_COVERAGE void debugPrint(); #endif @@ -93,6 +97,14 @@ class AllocatorImpl : public Allocator { std::vector deleted_gauges_ ABSL_GUARDED_BY(mutex_); std::vector deleted_text_readouts_ ABSL_GUARDED_BY(mutex_); + template using StatPointerSet = absl::flat_hash_set; + // Stat pointers that participate in the flush to sink process. + StatPointerSet sinked_counters_ ABSL_GUARDED_BY(mutex_); + StatPointerSet sinked_gauges_ ABSL_GUARDED_BY(mutex_); + StatPointerSet sinked_text_readouts_ ABSL_GUARDED_BY(mutex_); + + // Predicates used to filter stats to be flushed. + std::unique_ptr sink_predicates_; SymbolTable& symbol_table_; Thread::ThreadSynchronizer sync_; diff --git a/source/common/stats/custom_stat_namespaces_impl.cc b/source/common/stats/custom_stat_namespaces_impl.cc index 89c95344fc2b..f5acb34ebcfb 100644 --- a/source/common/stats/custom_stat_namespaces_impl.cc +++ b/source/common/stats/custom_stat_namespaces_impl.cc @@ -7,18 +7,18 @@ namespace Envoy { namespace Stats { bool CustomStatNamespacesImpl::registered(const absl::string_view name) const { - ASSERT(Thread::MainThread::isMainOrTestThread()); + ASSERT_IS_MAIN_OR_TEST_THREAD(); return namespaces_.find(name) != namespaces_.end(); } void CustomStatNamespacesImpl::registerStatNamespace(const absl::string_view name) { - ASSERT(Thread::MainThread::isMainOrTestThread()); + ASSERT_IS_MAIN_OR_TEST_THREAD(); namespaces_.insert(std::string(name)); }; absl::optional CustomStatNamespacesImpl::stripRegisteredPrefix(const absl::string_view stat_name) const { - ASSERT(Thread::MainThread::isMainOrTestThread()); + ASSERT_IS_MAIN_OR_TEST_THREAD(); if (!namespaces_.empty()) { const auto pos = stat_name.find_first_of('.'); if (pos != std::string::npos && registered(stat_name.substr(0, pos))) { diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index ebff944da7ef..cc44d8e811ca 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -101,9 +101,10 @@ template class IsolatedStatsCache { return true; } - void forEachStat(std::function f_size, - std::function f_stat) const { - f_size(stats_.size()); + void forEachStat(SizeFn f_size, std::function f_stat) const { + if (f_size != nullptr) { + f_size(stats_.size()); + } for (auto const& stat : stats_) { f_stat(*stat.second); } @@ -214,21 +215,30 @@ class IsolatedStoreImpl : public StoreImpl { return textReadoutFromStatName(storage.statName()); } - void forEachCounter(std::function f_size, - std::function f_stat) const override { + void forEachCounter(SizeFn f_size, StatFn f_stat) const override { counters_.forEachStat(f_size, f_stat); } - void forEachGauge(std::function f_size, - std::function f_stat) const override { + void forEachGauge(SizeFn f_size, StatFn f_stat) const override { gauges_.forEachStat(f_size, f_stat); } - void forEachTextReadout(std::function f_size, - std::function f_stat) const override { + void forEachTextReadout(SizeFn f_size, StatFn f_stat) const override { text_readouts_.forEachStat(f_size, f_stat); } + void forEachSinkedCounter(SizeFn f_size, StatFn f_stat) const override { + forEachCounter(f_size, f_stat); + } + + void forEachSinkedGauge(SizeFn f_size, StatFn f_stat) const override { + forEachGauge(f_size, f_stat); + } + + void forEachSinkedTextReadout(SizeFn f_size, StatFn f_stat) const override { + forEachTextReadout(f_size, f_stat); + } + private: IsolatedStoreImpl(std::unique_ptr&& symbol_table); diff --git a/source/common/stats/tag_producer_impl.cc b/source/common/stats/tag_producer_impl.cc index 85384e7eb2ec..b21f5895d4ae 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 ff5dc3d34fb1..1c05c2083d24 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 b73ff3d81fe2..550bdf81180b 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 b3e56e7737b3..d0961cf0f3e8 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/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index 97960182096c..f0c32033cecc 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -954,24 +954,38 @@ bool ParentHistogramImpl::usedLockHeld() const { return false; } -void ThreadLocalStoreImpl::forEachCounter(std::function f_size, - std::function f_stat) const { - Thread::LockGuard lock(lock_); +void ThreadLocalStoreImpl::forEachCounter(SizeFn f_size, StatFn f_stat) const { alloc_.forEachCounter(f_size, f_stat); } -void ThreadLocalStoreImpl::forEachGauge(std::function f_size, - std::function f_stat) const { - Thread::LockGuard lock(lock_); +void ThreadLocalStoreImpl::forEachGauge(SizeFn f_size, StatFn f_stat) const { alloc_.forEachGauge(f_size, f_stat); } -void ThreadLocalStoreImpl::forEachTextReadout( - std::function f_size, - std::function f_stat) const { - Thread::LockGuard lock(lock_); +void ThreadLocalStoreImpl::forEachTextReadout(SizeFn f_size, StatFn f_stat) const { alloc_.forEachTextReadout(f_size, f_stat); } +void ThreadLocalStoreImpl::forEachSinkedCounter(SizeFn f_size, StatFn f_stat) const { + alloc_.forEachSinkedCounter(f_size, f_stat); +} + +void ThreadLocalStoreImpl::forEachSinkedGauge(SizeFn f_size, StatFn f_stat) const { + alloc_.forEachSinkedGauge(f_size, f_stat); +} + +void ThreadLocalStoreImpl::forEachSinkedTextReadout(SizeFn f_size, + StatFn f_stat) const { + alloc_.forEachSinkedTextReadout(f_size, f_stat); +} + +void ThreadLocalStoreImpl::setSinkPredicates(std::unique_ptr&& sink_predicates) { + ASSERT(sink_predicates != nullptr); + if (sink_predicates != nullptr) { + sink_predicates_.emplace(*sink_predicates); + alloc_.setSinkPredicates(std::move(sink_predicates)); + } +} + } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index 742e1fc3c04d..a22a04d00f98 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -244,14 +244,9 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo std::vector textReadouts() const override; std::vector histograms() const override; - void forEachCounter(std::function f_size, - std::function f_stat) const override; - - void forEachGauge(std::function f_size, - std::function f_stat) const override; - - void forEachTextReadout(std::function f_size, - std::function f_stat) const override; + void forEachCounter(SizeFn f_size, StatFn f_stat) const override; + void forEachGauge(SizeFn f_size, StatFn f_stat) const override; + void forEachTextReadout(SizeFn f_size, StatFn f_stat) const override; // Stats::StoreRoot void addSink(Sink& sink) override { timer_sinks_.push_back(sink); } @@ -267,6 +262,12 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo Histogram& tlsHistogram(ParentHistogramImpl& parent, uint64_t id); + void forEachSinkedCounter(SizeFn f_size, StatFn f_stat) const override; + void forEachSinkedGauge(SizeFn f_size, StatFn f_stat) const override; + void forEachSinkedTextReadout(SizeFn f_size, StatFn f_stat) const override; + + void setSinkPredicates(std::unique_ptr&& sink_predicates) override; + /** * @return a thread synchronizer object used for controlling thread behavior in tests. */ @@ -500,6 +501,7 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo StatNameHashSet* tls_rejected_stats); TlsCache& tlsCache() { return **tls_cache_; } + OptRef sink_predicates_; Allocator& alloc_; Event::Dispatcher* main_thread_dispatcher_{}; using TlsCacheSlot = ThreadLocal::TypedSlotPtr; diff --git a/source/common/stream_info/stream_info_impl.h b/source/common/stream_info/stream_info_impl.h index 403ab403154b..8e0a6a85bbe7 100644 --- a/source/common/stream_info/stream_info_impl.h +++ b/source/common/stream_info/stream_info_impl.h @@ -14,6 +14,7 @@ #include "source/common/common/assert.h" #include "source/common/common/dump_state_utils.h" #include "source/common/common/macros.h" +#include "source/common/common/utility.h" #include "source/common/network/socket_impl.h" #include "source/common/stream_info/filter_state_impl.h" @@ -22,17 +23,63 @@ namespace Envoy { namespace StreamInfo { -namespace { +struct UpstreamInfoImpl : public UpstreamInfo { + void setUpstreamConnectionId(uint64_t id) override { upstream_connection_id_ = id; } + + absl::optional upstreamConnectionId() const override { return upstream_connection_id_; } + + void + setUpstreamSslConnection(const Ssl::ConnectionInfoConstSharedPtr& ssl_connection_info) override { + upstream_ssl_info_ = ssl_connection_info; + } + + Ssl::ConnectionInfoConstSharedPtr upstreamSslConnection() const override { + return upstream_ssl_info_; + } + UpstreamTiming& upstreamTiming() override { return upstream_timing_; } + const UpstreamTiming& upstreamTiming() const override { return upstream_timing_; } + const Network::Address::InstanceConstSharedPtr& upstreamLocalAddress() const override { + return upstream_local_address_; + } + void setUpstreamLocalAddress( + const Network::Address::InstanceConstSharedPtr& upstream_local_address) override { + upstream_local_address_ = upstream_local_address; + } + 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 setUpstreamHost(Upstream::HostDescriptionConstSharedPtr host) override { + upstream_host_ = host; + } + const FilterStateSharedPtr& upstreamFilterState() const override { + return upstream_filter_state_; + } + void setUpstreamFilterState(const FilterStateSharedPtr& filter_state) override { + upstream_filter_state_ = filter_state; + } -using ReplacementMap = absl::flat_hash_map; + Upstream::HostDescriptionConstSharedPtr upstreamHost() const override { return upstream_host_; } -const ReplacementMap& emptySpaceReplacement() { - CONSTRUCT_ON_FIRST_USE( - ReplacementMap, - ReplacementMap{{" ", "_"}, {"\t", "_"}, {"\f", "_"}, {"\v", "_"}, {"\n", "_"}, {"\r", "_"}}); -} + void dumpState(std::ostream& os, int indent_level = 0) const override { + const char* spaces = spacesForLevel(indent_level); + os << spaces << "UpstreamInfoImpl " << this << DUMP_OPTIONAL_MEMBER(upstream_connection_id_) + << "\n"; + } + void setUpstreamNumStreams(uint64_t num_streams) override { num_streams_ = num_streams; } + uint64_t upstreamNumStreams() const override { return num_streams_; } -} // namespace + Upstream::HostDescriptionConstSharedPtr upstream_host_{}; + Network::Address::InstanceConstSharedPtr upstream_local_address_; + UpstreamTiming upstream_timing_; + Ssl::ConnectionInfoConstSharedPtr upstream_ssl_info_; + absl::optional upstream_connection_id_; + std::string upstream_transport_failure_reason_; + FilterStateSharedPtr upstream_filter_state_; + size_t num_streams_{}; +}; struct StreamInfoImpl : public StreamInfo { StreamInfoImpl( @@ -71,55 +118,15 @@ struct StreamInfoImpl : public StreamInfo { start_time_monotonic_); } - void setUpstreamConnectionId(uint64_t id) override { upstream_connection_id_ = id; } - - 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(); - } - - void setUpstreamTiming(const UpstreamTiming& upstream_timing) override { - upstream_timing_ = upstream_timing; - } - - 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(first_downstream_tx_byte_sent_); - } + void setUpstreamInfo(std::shared_ptr info) override { upstream_info_ = info; } - void onFirstDownstreamTxByteSent() override { - ASSERT(!first_downstream_tx_byte_sent_); - first_downstream_tx_byte_sent_ = time_source_.monotonicTime(); - } - - absl::optional lastDownstreamTxByteSent() const override { - return duration(last_downstream_tx_byte_sent_); - } + std::shared_ptr upstreamInfo() override { return upstream_info_; } - void onLastDownstreamTxByteSent() override { - ASSERT(!last_downstream_tx_byte_sent_); - last_downstream_tx_byte_sent_ = time_source_.monotonicTime(); + OptRef upstreamInfo() const override { + if (!upstream_info_) { + return {}; + } + return *upstream_info_; } absl::optional requestComplete() const override { @@ -131,6 +138,19 @@ struct StreamInfoImpl : public StreamInfo { final_time_ = time_source_.monotonicTime(); } + DownstreamTiming& downstreamTiming() override { + if (!downstream_timing_.has_value()) { + downstream_timing_ = DownstreamTiming(); + } + return downstream_timing_.value(); + } + OptRef downstreamTiming() const override { + if (!downstream_timing_.has_value()) { + return {}; + } + return {*downstream_timing_}; + } + void addBytesReceived(uint64_t bytes_received) override { bytes_received_ += bytes_received; } uint64_t bytesReceived() const override { return bytes_received_; } @@ -148,7 +168,8 @@ struct StreamInfoImpl : public StreamInfo { void setResponseCode(uint32_t code) override { response_code_ = code; } void setResponseCodeDetails(absl::string_view rc_details) override { - response_code_details_.emplace(absl::StrReplaceAll(rc_details, emptySpaceReplacement())); + ASSERT(!StringUtil::hasEmptySpace(rc_details)); + response_code_details_.emplace(rc_details); } const absl::optional& connectionTerminationDetails() const override { @@ -175,25 +196,18 @@ struct StreamInfoImpl : public StreamInfo { 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 setRouteName(absl::string_view route_name) override { route_name_ = std::string(route_name); } const std::string& getRouteName() const override { return route_name_; } - void setUpstreamLocalAddress( - const Network::Address::InstanceConstSharedPtr& upstream_local_address) override { - upstream_local_address_ = upstream_local_address; + void setVirtualClusterName(const absl::optional& virtual_cluster_name) override { + virtual_cluster_name_ = virtual_cluster_name; } - const Network::Address::InstanceConstSharedPtr& upstreamLocalAddress() const override { - return upstream_local_address_; + const absl::optional& virtualClusterName() const override { + return virtual_cluster_name_; } bool healthCheck() const override { return health_check_request_; } @@ -204,14 +218,6 @@ struct StreamInfoImpl : public StreamInfo { return *downstream_connection_info_provider_; } - void setUpstreamSslConnection(const Ssl::ConnectionInfoConstSharedPtr& connection_info) override { - upstream_ssl_info_ = connection_info; - } - - Ssl::ConnectionInfoConstSharedPtr upstreamSslConnection() const override { - return upstream_ssl_info_; - } - Router::RouteConstSharedPtr route() const override { return route_; } envoy::config::core::v3::Metadata& dynamicMetadata() override { return metadata_; }; @@ -224,21 +230,6 @@ struct StreamInfoImpl : public StreamInfo { const FilterStateSharedPtr& filterState() override { return filter_state_; } const FilterState& filterState() const override { return *filter_state_; } - const FilterStateSharedPtr& upstreamFilterState() const override { - return upstream_filter_state_; - } - void setUpstreamFilterState(const 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; } @@ -258,10 +249,11 @@ struct StreamInfoImpl : public StreamInfo { void dumpState(std::ostream& os, int indent_level = 0) const { const char* spaces = spacesForLevel(indent_level); - os << spaces << "StreamInfoImpl " << this << DUMP_OPTIONAL_MEMBER(upstream_connection_id_) - << DUMP_OPTIONAL_MEMBER(protocol_) << DUMP_OPTIONAL_MEMBER(response_code_) - << DUMP_OPTIONAL_MEMBER(response_code_details_) << DUMP_OPTIONAL_MEMBER(attempt_count_) - << DUMP_MEMBER(health_check_request_) << DUMP_MEMBER(route_name_) << "\n"; + os << spaces << "StreamInfoImpl " << this << DUMP_OPTIONAL_MEMBER(protocol_) + << DUMP_OPTIONAL_MEMBER(response_code_) << DUMP_OPTIONAL_MEMBER(response_code_details_) + << DUMP_OPTIONAL_MEMBER(attempt_count_) << DUMP_MEMBER(health_check_request_) + << DUMP_MEMBER(route_name_); + DUMP_DETAILS(upstream_info_); } void setUpstreamClusterInfo( @@ -278,7 +270,6 @@ struct StreamInfoImpl : public StreamInfo { } const std::string& filterChainName() const override { return filter_chain_name_; } - void setAttemptCount(uint32_t attempt_count) override { attempt_count_ = attempt_count; } absl::optional attemptCount() const override { return attempt_count_; } @@ -297,7 +288,6 @@ struct StreamInfoImpl : public StreamInfo { upstream_bytes_meter->addWireBytesReceived(upstream_bytes_meter_->wireBytesReceived()); upstream_bytes_meter->addHeaderBytesSent(upstream_bytes_meter_->headerBytesSent()); upstream_bytes_meter->addHeaderBytesReceived(upstream_bytes_meter_->headerBytesReceived()); - upstream_bytes_meter_ = upstream_bytes_meter; } @@ -312,10 +302,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_; @@ -323,15 +309,15 @@ struct StreamInfoImpl : public StreamInfo { absl::optional response_code_details_; absl::optional connection_termination_details_; uint64_t response_flags_{}; - Upstream::HostDescriptionConstSharedPtr upstream_host_{}; bool health_check_request_{}; Router::RouteConstSharedPtr route_; envoy::config::core::v3::Metadata metadata_{}; FilterStateSharedPtr filter_state_; - FilterStateSharedPtr upstream_filter_state_; std::string route_name_; - absl::optional upstream_connection_id_; absl::optional attempt_count_; + // TODO(agrawroh): Check if the owner of this storage outlives the StreamInfo. We should only copy + // the string if it could outlive the StreamInfo. + absl::optional virtual_cluster_name_; private: static Network::ConnectionInfoProviderSharedPtr emptyDownstreamAddressProvider() { @@ -352,16 +338,13 @@ struct StreamInfoImpl : public StreamInfo { : emptyDownstreamAddressProvider()), trace_reason_(Tracing::Reason::NotTraceable) {} + std::shared_ptr upstream_info_; uint64_t bytes_received_{}; uint64_t bytes_sent_{}; - Network::Address::InstanceConstSharedPtr upstream_local_address_; const Network::ConnectionInfoProviderSharedPtr downstream_connection_info_provider_; - Ssl::ConnectionInfoConstSharedPtr upstream_ssl_info_; - std::string requested_server_name_; const Http::RequestHeaderMap* request_headers_{}; Http::RequestIdStreamInfoProviderSharedPtr request_id_provider_; - UpstreamTiming upstream_timing_; - std::string upstream_transport_failure_reason_; + absl::optional downstream_timing_; absl::optional upstream_cluster_info_; std::string filter_chain_name_; Tracing::Reason trace_reason_; diff --git a/source/common/stream_info/utility.cc b/source/common/stream_info/utility.cc index bda068767290..00c84c6bbe39 100644 --- a/source/common/stream_info/utility.cc +++ b/source/common/stream_info/utility.cc @@ -43,6 +43,79 @@ absl::optional ResponseFlagUtils::toResponseFlag(absl::string_view return absl::nullopt; } +OptRef getUpstreamTiming(const StreamInfo& stream_info) { + OptRef info = stream_info.upstreamInfo(); + if (!info.has_value()) { + return {}; + } + return info.value().get().upstreamTiming(); +} + +absl::optional duration(const absl::optional& time, + const StreamInfo& stream_info) { + if (!time.has_value()) { + return absl::nullopt; + } + return std::chrono::duration_cast(time.value() - + stream_info.startTimeMonotonic()); +} + +absl::optional TimingUtility::firstUpstreamTxByteSent() { + OptRef timing = getUpstreamTiming(stream_info_); + if (!timing) { + return absl::nullopt; + } + return duration(timing.value().get().first_upstream_tx_byte_sent_, stream_info_); +} + +absl::optional TimingUtility::lastUpstreamTxByteSent() { + OptRef timing = getUpstreamTiming(stream_info_); + if (!timing) { + return absl::nullopt; + } + return duration(timing.value().get().last_upstream_tx_byte_sent_, stream_info_); +} + +absl::optional TimingUtility::firstUpstreamRxByteReceived() { + OptRef timing = getUpstreamTiming(stream_info_); + if (!timing) { + return absl::nullopt; + } + return duration(timing.value().get().first_upstream_rx_byte_received_, stream_info_); +} + +absl::optional TimingUtility::lastUpstreamRxByteReceived() { + OptRef timing = getUpstreamTiming(stream_info_); + if (!timing) { + return absl::nullopt; + } + return duration(timing.value().get().last_upstream_rx_byte_received_, stream_info_); +} + +absl::optional TimingUtility::firstDownstreamTxByteSent() { + OptRef timing = stream_info_.downstreamTiming(); + if (!timing) { + return absl::nullopt; + } + return duration(timing.value().get().firstDownstreamTxByteSent(), stream_info_); +} + +absl::optional TimingUtility::lastDownstreamTxByteSent() { + OptRef timing = stream_info_.downstreamTiming(); + if (!timing) { + return absl::nullopt; + } + return duration(timing.value().get().lastDownstreamTxByteSent(), stream_info_); +} + +absl::optional TimingUtility::lastDownstreamRxByteReceived() { + OptRef timing = stream_info_.downstreamTiming(); + if (!timing) { + return absl::nullopt; + } + return duration(timing.value().get().lastDownstreamRxByteReceived(), stream_info_); +} + const std::string& Utility::formatDownstreamAddressNoPort(const Network::Address::Instance& address) { if (address.type() == Network::Address::Type::Ip) { diff --git a/source/common/stream_info/utility.h b/source/common/stream_info/utility.h index f6c39b8bd25a..4c38b01f5414 100644 --- a/source/common/stream_info/utility.h +++ b/source/common/stream_info/utility.h @@ -83,6 +83,22 @@ class ResponseFlagUtils { static absl::flat_hash_map getFlagMap(); }; +class TimingUtility { +public: + TimingUtility(const StreamInfo& info) : stream_info_(info) {} + + absl::optional firstUpstreamTxByteSent(); + absl::optional lastUpstreamTxByteSent(); + absl::optional firstUpstreamRxByteReceived(); + absl::optional lastUpstreamRxByteReceived(); + absl::optional firstDownstreamTxByteSent(); + absl::optional lastDownstreamTxByteSent(); + absl::optional lastDownstreamRxByteReceived(); + +private: + const StreamInfo& stream_info_; +}; + /** * Utility class for StreamInfo. */ diff --git a/source/common/tcp/conn_pool.h b/source/common/tcp/conn_pool.h index d3a4a6d81d37..760ed61e1dce 100644 --- a/source/common/tcp/conn_pool.h +++ b/source/common/tcp/conn_pool.h @@ -217,6 +217,7 @@ class ConnPoolImpl : public Envoy::ConnectionPool::ConnPoolImplBase, callbacks->onPoolFailure(reason, failure_reason, host_description); } + bool enforceMaxRequests() const override { return false; } // These two functions exist for testing parity between old and new Tcp Connection Pools. virtual void onConnReleased(Envoy::ConnectionPool::ActiveClient&) {} virtual void onConnDestroyed() {} diff --git a/source/common/tcp_proxy/tcp_proxy.cc b/source/common/tcp_proxy/tcp_proxy.cc index b22fa5e956e5..7f0e231fa182 100644 --- a/source/common/tcp_proxy/tcp_proxy.cc +++ b/source/common/tcp_proxy/tcp_proxy.cc @@ -88,7 +88,6 @@ Config::Config(const envoy::extensions::filters::network::tcp_proxy::v3::TcpProx upstream_drain_manager_slot_(context.threadLocal().allocateSlot()), shared_config_(std::make_shared(config, context)), random_generator_(context.api().randomGenerator()) { - upstream_drain_manager_slot_->set([](Event::Dispatcher&) { ThreadLocal::ThreadLocalObjectSharedPtr drain_manager = std::make_shared(); @@ -196,7 +195,7 @@ void Filter::initialize(Network::ReadFilterCallbacks& callbacks, bool set_connec // in onData(). This will get re-enabled when the upstream connection is // established. read_callbacks_->connection().readDisable(true); - + getStreamInfo().setUpstreamInfo(std::make_shared()); config_->stats().downstream_cx_total_.inc(); if (set_connection_stats) { read_callbacks_->connection().setConnectionStats( @@ -439,7 +438,7 @@ void Filter::onGenericPoolFailure(ConnectionPool::PoolFailureReason reason, Upstream::HostDescriptionConstSharedPtr host) { generic_conn_pool_.reset(); read_callbacks_->upstreamHost(host); - getStreamInfo().onUpstreamHostSelected(host); + getStreamInfo().upstreamInfo()->setUpstreamHost(host); switch (reason) { case ConnectionPool::PoolFailureReason::Overflow: @@ -468,13 +467,14 @@ void Filter::onGenericPoolReady(StreamInfo::StreamInfo* info, upstream_ = std::move(upstream); generic_conn_pool_.reset(); read_callbacks_->upstreamHost(host); - getStreamInfo().onUpstreamHostSelected(host); - getStreamInfo().setUpstreamLocalAddress(local_address); - getStreamInfo().setUpstreamSslConnection(ssl_info); + StreamInfo::UpstreamInfo& upstream_info = *getStreamInfo().upstreamInfo(); + upstream_info.setUpstreamHost(host); + upstream_info.setUpstreamLocalAddress(local_address); + upstream_info.setUpstreamSslConnection(ssl_info); onUpstreamConnection(); read_callbacks_->continueReading(); if (info) { - read_callbacks_->connection().streamInfo().setUpstreamFilterState(info->filterState()); + upstream_info.setUpstreamFilterState(info->filterState()); } } @@ -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 3b64e58f5add..22a49739c4eb 100644 --- a/source/common/tcp_proxy/tcp_proxy.h +++ b/source/common/tcp_proxy/tcp_proxy.h @@ -259,9 +259,7 @@ class Filter : public Network::ReadFilter, absl::optional computeHashKey() override { auto hash_policy = config_->hashPolicy(); if (hash_policy) { - return hash_policy->generateHash( - downstreamConnection()->connectionInfoProvider().remoteAddress().get(), - downstreamConnection()->connectionInfoProvider().localAddress().get()); + return hash_policy->generateHash(*downstreamConnection()); } return {}; @@ -382,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/tcp_proxy/upstream.cc b/source/common/tcp_proxy/upstream.cc index 898272920600..4fdb838540cd 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 017257b5bcaa..fbae213d57fb 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,19 +151,21 @@ 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 { 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); @@ -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/source/common/thread_local/thread_local_impl.cc b/source/common/thread_local/thread_local_impl.cc index 99f473a7cc07..f054730f847c 100644 --- a/source/common/thread_local/thread_local_impl.cc +++ b/source/common/thread_local/thread_local_impl.cc @@ -16,13 +16,13 @@ namespace ThreadLocal { thread_local InstanceImpl::ThreadLocalData InstanceImpl::thread_local_data_; InstanceImpl::~InstanceImpl() { - ASSERT(Thread::MainThread::isMainOrTestThread()); + ASSERT_IS_MAIN_OR_TEST_THREAD(); ASSERT(shutdown_); thread_local_data_.data_.clear(); } SlotPtr InstanceImpl::allocateSlot() { - ASSERT(Thread::MainThread::isMainOrTestThread()); + ASSERT_IS_MAIN_OR_TEST_THREAD(); ASSERT(!shutdown_); if (free_slot_indexes_.empty()) { @@ -91,7 +91,7 @@ void InstanceImpl::SlotImpl::runOnAllThreads(const UpdateCb& cb) { } void InstanceImpl::SlotImpl::set(InitializeCb cb) { - ASSERT(Thread::MainThread::isMainOrTestThread()); + ASSERT_IS_MAIN_OR_TEST_THREAD(); ASSERT(!parent_.shutdown_); for (Event::Dispatcher& dispatcher : parent_.registered_threads_) { @@ -105,7 +105,7 @@ void InstanceImpl::SlotImpl::set(InitializeCb cb) { } void InstanceImpl::registerThread(Event::Dispatcher& dispatcher, bool main_thread) { - ASSERT(Thread::MainThread::isMainOrTestThread()); + ASSERT_IS_MAIN_OR_TEST_THREAD(); ASSERT(!shutdown_); if (main_thread) { @@ -119,7 +119,7 @@ void InstanceImpl::registerThread(Event::Dispatcher& dispatcher, bool main_threa } void InstanceImpl::removeSlot(uint32_t slot) { - ASSERT(Thread::MainThread::isMainOrTestThread()); + ASSERT_IS_MAIN_OR_TEST_THREAD(); // When shutting down, we do not post slot removals to other threads. This is because the other // threads have already shut down and the dispatcher is no longer alive. There is also no reason @@ -146,7 +146,7 @@ void InstanceImpl::removeSlot(uint32_t slot) { } void InstanceImpl::runOnAllThreads(Event::PostCb cb) { - ASSERT(Thread::MainThread::isMainOrTestThread()); + ASSERT_IS_MAIN_OR_TEST_THREAD(); ASSERT(!shutdown_); for (Event::Dispatcher& dispatcher : registered_threads_) { @@ -158,7 +158,7 @@ void InstanceImpl::runOnAllThreads(Event::PostCb cb) { } void InstanceImpl::runOnAllThreads(Event::PostCb cb, Event::PostCb all_threads_complete_cb) { - ASSERT(Thread::MainThread::isMainOrTestThread()); + ASSERT_IS_MAIN_OR_TEST_THREAD(); ASSERT(!shutdown_); // Handle main thread first so that when the last worker thread wins, we could just call the // all_threads_complete_cb method. Parallelism of main thread execution is being traded off @@ -185,7 +185,7 @@ void InstanceImpl::setThreadLocal(uint32_t index, ThreadLocalObjectSharedPtr obj } void InstanceImpl::shutdownGlobalThreading() { - ASSERT(Thread::MainThread::isMainOrTestThread()); + ASSERT_IS_MAIN_OR_TEST_THREAD(); ASSERT(!shutdown_); shutdown_ = true; } diff --git a/source/common/tracing/http_tracer_impl.cc b/source/common/tracing/http_tracer_impl.cc index 84e7168a68e9..d9ed9a86d18a 100644 --- a/source/common/tracing/http_tracer_impl.cc +++ b/source/common/tracing/http_tracer_impl.cc @@ -95,39 +95,40 @@ template static void addGrpcResponseTags(Span& span, const T& headers) static void annotateVerbose(Span& span, const StreamInfo::StreamInfo& stream_info) { const auto start_time = stream_info.startTime(); - if (stream_info.lastDownstreamRxByteReceived()) { + StreamInfo::TimingUtility timing(stream_info); + if (timing.lastDownstreamRxByteReceived()) { span.log(start_time + std::chrono::duration_cast( - *stream_info.lastDownstreamRxByteReceived()), + *timing.lastDownstreamRxByteReceived()), Tracing::Logs::get().LastDownstreamRxByteReceived); } - if (stream_info.firstUpstreamTxByteSent()) { + if (timing.firstUpstreamTxByteSent()) { span.log(start_time + std::chrono::duration_cast( - *stream_info.firstUpstreamTxByteSent()), + *timing.firstUpstreamTxByteSent()), Tracing::Logs::get().FirstUpstreamTxByteSent); } - if (stream_info.lastUpstreamTxByteSent()) { - span.log(start_time + std::chrono::duration_cast( - *stream_info.lastUpstreamTxByteSent()), + if (timing.lastUpstreamTxByteSent()) { + span.log(start_time + + std::chrono::duration_cast(*timing.lastUpstreamTxByteSent()), Tracing::Logs::get().LastUpstreamTxByteSent); } - if (stream_info.firstUpstreamRxByteReceived()) { + if (timing.firstUpstreamRxByteReceived()) { span.log(start_time + std::chrono::duration_cast( - *stream_info.firstUpstreamRxByteReceived()), + *timing.firstUpstreamRxByteReceived()), Tracing::Logs::get().FirstUpstreamRxByteReceived); } - if (stream_info.lastUpstreamRxByteReceived()) { + if (timing.lastUpstreamRxByteReceived()) { span.log(start_time + std::chrono::duration_cast( - *stream_info.lastUpstreamRxByteReceived()), + *timing.lastUpstreamRxByteReceived()), Tracing::Logs::get().LastUpstreamRxByteReceived); } - if (stream_info.firstDownstreamTxByteSent()) { + if (timing.firstDownstreamTxByteSent()) { span.log(start_time + std::chrono::duration_cast( - *stream_info.firstDownstreamTxByteSent()), + *timing.firstDownstreamTxByteSent()), Tracing::Logs::get().FirstDownstreamTxByteSent); } - if (stream_info.lastDownstreamTxByteSent()) { + if (timing.lastDownstreamTxByteSent()) { span.log(start_time + std::chrono::duration_cast( - *stream_info.lastDownstreamTxByteSent()), + *timing.lastDownstreamTxByteSent()), Tracing::Logs::get().LastDownstreamTxByteSent); } } @@ -197,9 +198,9 @@ void HttpTracerUtility::finalizeUpstreamSpan(Span& span, Tracing::Tags::get().HttpProtocol, Formatter::SubstitutionFormatUtils::protocolToStringOrDefault(stream_info.protocol())); - if (stream_info.upstreamHost()) { + if (stream_info.upstreamInfo() && stream_info.upstreamInfo()->upstreamHost()) { span.setTag(Tracing::Tags::get().UpstreamAddress, - stream_info.upstreamHost()->address()->asStringView()); + stream_info.upstreamInfo()->upstreamHost()->address()->asStringView()); } setCommonTags(span, response_headers, response_trailers, stream_info, tracing_config); @@ -214,10 +215,11 @@ void HttpTracerUtility::setCommonTags(Span& span, const Http::ResponseHeaderMap* span.setTag(Tracing::Tags::get().Component, Tracing::Tags::get().Proxy); - if (nullptr != stream_info.upstreamHost()) { - span.setTag(Tracing::Tags::get().UpstreamCluster, stream_info.upstreamHost()->cluster().name()); + if (stream_info.upstreamInfo() && stream_info.upstreamInfo()->upstreamHost()) { + span.setTag(Tracing::Tags::get().UpstreamCluster, + stream_info.upstreamInfo()->upstreamHost()->cluster().name()); span.setTag(Tracing::Tags::get().UpstreamClusterName, - stream_info.upstreamHost()->cluster().observabilityName()); + stream_info.upstreamInfo()->upstreamHost()->cluster().observabilityName()); } // Post response data. @@ -350,21 +352,27 @@ void MetadataCustomTag::apply(Span& span, const CustomTagContext& ctx) const { const envoy::config::core::v3::Metadata* MetadataCustomTag::metadata(const CustomTagContext& ctx) const { - const StreamInfo::StreamInfo& info = ctx.stream_info; + const StreamInfo::StreamInfo& stream_info = ctx.stream_info; switch (kind_) { case envoy::type::metadata::v3::MetadataKind::KindCase::kRequest: - return &info.dynamicMetadata(); + return &stream_info.dynamicMetadata(); case envoy::type::metadata::v3::MetadataKind::KindCase::kRoute: { - Router::RouteConstSharedPtr route = info.route(); + Router::RouteConstSharedPtr route = stream_info.route(); return route ? &route->metadata() : nullptr; } case envoy::type::metadata::v3::MetadataKind::KindCase::kCluster: { - const auto& hostPtr = info.upstreamHost(); - return hostPtr ? &hostPtr->cluster().metadata() : nullptr; + if (stream_info.upstreamInfo().has_value() && + stream_info.upstreamInfo().value().get().upstreamHost()) { + return &stream_info.upstreamInfo().value().get().upstreamHost()->cluster().metadata(); + } + return nullptr; } case envoy::type::metadata::v3::MetadataKind::KindCase::kHost: { - const auto& hostPtr = info.upstreamHost(); - return hostPtr ? hostPtr->metadata().get() : nullptr; + if (stream_info.upstreamInfo().has_value() && + stream_info.upstreamInfo().value().get().upstreamHost()) { + return stream_info.upstreamInfo().value().get().upstreamHost()->metadata().get(); + } + return nullptr; } default: NOT_REACHED_GCOVR_EXCL_LINE; diff --git a/source/common/upstream/BUILD b/source/common/upstream/BUILD index 023986cf2b02..3e3e5b358357 100644 --- a/source/common/upstream/BUILD +++ b/source/common/upstream/BUILD @@ -666,7 +666,7 @@ envoy_cc_library( "//source/common/network:resolver_lib", "//source/common/network:socket_option_factory_lib", "//source/common/network:utility_lib", - "//source/common/network/dns_resolver:dns_factory_lib", + "//source/common/network/dns_resolver:dns_factory_util_lib", "//source/common/protobuf", "//source/common/protobuf:utility_lib", "//source/server:transport_socket_config_lib", diff --git a/source/common/upstream/cluster_factory_impl.cc b/source/common/upstream/cluster_factory_impl.cc index 2ed161baca57..fdebb5066bc1 100644 --- a/source/common/upstream/cluster_factory_impl.cc +++ b/source/common/upstream/cluster_factory_impl.cc @@ -5,7 +5,7 @@ #include "source/common/http/utility.h" #include "source/common/network/address_impl.h" -#include "source/common/network/dns_resolver/dns_factory.h" +#include "source/common/network/dns_resolver/dns_factory_util.h" #include "source/common/network/resolver_impl.h" #include "source/common/network/socket_option_factory.h" #include "source/common/upstream/health_checker_impl.h" diff --git a/source/common/upstream/health_checker_impl.cc b/source/common/upstream/health_checker_impl.cc index 6bb52b6b90ca..ebd9b4c6e4c7 100644 --- a/source/common/upstream/health_checker_impl.cc +++ b/source/common/upstream/health_checker_impl.cc @@ -298,7 +298,8 @@ void HttpHealthCheckerImpl::HttpActiveHealthCheckSession::onInterval() { host_->transportSocketFactory().implementsSecureTransport()); StreamInfo::StreamInfoImpl stream_info(protocol_, parent_.dispatcher_.timeSource(), local_connection_info_provider_); - stream_info.onUpstreamHostSelected(host_); + stream_info.setUpstreamInfo(std::make_shared()); + stream_info.upstreamInfo()->setUpstreamHost(host_); parent_.request_headers_parser_->evaluateHeaders(*request_headers, stream_info); auto status = request_encoder->encodeHeaders(*request_headers, true); // Encoding will only fail if required request headers are missing. diff --git a/source/common/upstream/health_checker_impl.h b/source/common/upstream/health_checker_impl.h index cb8d62a3d4a6..3dd90a6dc140 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/common/upstream/load_balancer_impl.cc b/source/common/upstream/load_balancer_impl.cc index 5cd361b1da58..f9aad9016978 100644 --- a/source/common/upstream/load_balancer_impl.cc +++ b/source/common/upstream/load_balancer_impl.cc @@ -959,7 +959,7 @@ double EdfLoadBalancerBase::applySlowStartFactor(double host_weight, const Host& aggression_ = aggression_runtime_ != absl::nullopt ? aggression_runtime_.value().value() : 1.0; if (aggression_ < 0.0) { ENVOY_LOG_EVERY_POW_2(error, "Invalid runtime value provided for aggression parameter, " - "agression cannot be less than 0.0"); + "aggression cannot be less than 0.0"); } aggression_ = std::max(0.0, aggression_); diff --git a/source/common/upstream/load_balancer_impl.h b/source/common/upstream/load_balancer_impl.h index b7efb873eac3..33bfbf758029 100644 --- a/source/common/upstream/load_balancer_impl.h +++ b/source/common/upstream/load_balancer_impl.h @@ -42,8 +42,9 @@ class LoadBalancerBase : public LoadBalancer { // Pool selection not implemented. absl::optional - selectPool(Upstream::LoadBalancerContext* /*context*/, const Upstream::Host& /*host*/, - std::vector& /*hash_key*/) override { + selectExistingConnection(Upstream::LoadBalancerContext* /*context*/, + const Upstream::Host& /*host*/, + std::vector& /*hash_key*/) override { return absl::nullopt; } // Lifetime tracking not implemented. diff --git a/source/common/upstream/logical_dns_cluster.cc b/source/common/upstream/logical_dns_cluster.cc index c572f11d307a..6e34bbb8fab9 100644 --- a/source/common/upstream/logical_dns_cluster.cc +++ b/source/common/upstream/logical_dns_cluster.cc @@ -122,11 +122,12 @@ void LogicalDnsCluster::startResolve() { // not stabilize back to 0 hosts. if (status == Network::DnsResolver::ResolutionStatus::Success && !response.empty()) { info_->stats().update_success_.inc(); + const auto addrinfo = response.front().addrInfo(); // TODO(mattklein123): Move port handling into the DNS interface. uint32_t port = Network::Utility::portFromTcpUrl(dns_url_); - ASSERT(response.front().address_ != nullptr); + ASSERT(addrinfo.address_ != nullptr); Network::Address::InstanceConstSharedPtr new_address = - Network::Utility::getAddressWithPort(*(response.front().address_), port); + Network::Utility::getAddressWithPort(*(response.front().addrInfo().address_), port); auto address_list = DnsUtils::generateAddressList(response, port); if (!logical_host_) { @@ -159,8 +160,8 @@ void LogicalDnsCluster::startResolve() { // reset failure backoff strategy because there was a success. failure_backoff_strategy_->reset(); - if (respect_dns_ttl_ && response.front().ttl_ != std::chrono::seconds(0)) { - final_refresh_rate = response.front().ttl_; + if (respect_dns_ttl_ && addrinfo.ttl_ != std::chrono::seconds(0)) { + final_refresh_rate = addrinfo.ttl_; } ENVOY_LOG(debug, "DNS refresh rate reset for {}, refresh rate {} ms", dns_address, final_refresh_rate.count()); diff --git a/source/common/upstream/original_dst_cluster.h b/source/common/upstream/original_dst_cluster.h index 1f135d130012..589991eb612f 100644 --- a/source/common/upstream/original_dst_cluster.h +++ b/source/common/upstream/original_dst_cluster.h @@ -58,8 +58,9 @@ class OriginalDstCluster : public ClusterImplBase { HostConstSharedPtr peekAnotherHost(LoadBalancerContext*) override { return nullptr; } // Pool selection not implemented for OriginalDstCluster absl::optional - selectPool(Upstream::LoadBalancerContext* /*context*/, const Upstream::Host& /*host*/, - std::vector& /*hash_key*/) override { + selectExistingConnection(Upstream::LoadBalancerContext* /*context*/, + const Upstream::Host& /*host*/, + std::vector& /*hash_key*/) override { return absl::nullopt; } // Lifetime tracking not implemented for OriginalDstCluster diff --git a/source/common/upstream/strict_dns_cluster.cc b/source/common/upstream/strict_dns_cluster.cc index 44cb3e83485a..d641c31e781d 100644 --- a/source/common/upstream/strict_dns_cluster.cc +++ b/source/common/upstream/strict_dns_cluster.cc @@ -121,12 +121,13 @@ void StrictDnsClusterImpl::ResolveTarget::startResolve() { std::chrono::seconds ttl_refresh_rate = std::chrono::seconds::max(); absl::flat_hash_set all_new_hosts; for (const auto& resp : response) { + const auto& addrinfo = resp.addrInfo(); // TODO(mattklein123): Currently the DNS interface does not consider port. We need to // make a new address that has port in it. We need to both support IPv6 as well as // potentially move port handling into the DNS interface itself, which would work better // for SRV. - ASSERT(resp.address_ != nullptr); - auto address = Network::Utility::getAddressWithPort(*(resp.address_), port_); + ASSERT(addrinfo.address_ != nullptr); + auto address = Network::Utility::getAddressWithPort(*(addrinfo.address_), port_); if (all_new_hosts.count(address->asString()) > 0) { continue; } @@ -139,7 +140,7 @@ void StrictDnsClusterImpl::ResolveTarget::startResolve() { lb_endpoint_.endpoint().health_check_config(), locality_lb_endpoints_.priority(), lb_endpoint_.health_status(), parent_.time_source_)); all_new_hosts.emplace(address->asString()); - ttl_refresh_rate = min(ttl_refresh_rate, resp.ttl_); + ttl_refresh_rate = min(ttl_refresh_rate, addrinfo.ttl_); } HostVector hosts_added; diff --git a/source/common/upstream/subset_lb.h b/source/common/upstream/subset_lb.h index 4960bc730aa4..977285287948 100644 --- a/source/common/upstream/subset_lb.h +++ b/source/common/upstream/subset_lb.h @@ -44,8 +44,9 @@ class SubsetLoadBalancer : public LoadBalancer, Logger::Loggable - selectPool(Upstream::LoadBalancerContext* /*context*/, const Upstream::Host& /*host*/, - std::vector& /*hash_key*/) override { + selectExistingConnection(Upstream::LoadBalancerContext* /*context*/, + const Upstream::Host& /*host*/, + std::vector& /*hash_key*/) override { return absl::nullopt; } // Lifetime tracking not implemented. diff --git a/source/common/upstream/thread_aware_lb_impl.h b/source/common/upstream/thread_aware_lb_impl.h index 2d48d96d4403..efd6f005bbd2 100644 --- a/source/common/upstream/thread_aware_lb_impl.h +++ b/source/common/upstream/thread_aware_lb_impl.h @@ -93,8 +93,9 @@ class ThreadAwareLoadBalancerBase : public LoadBalancerBase, public ThreadAwareL HostConstSharedPtr peekAnotherHost(LoadBalancerContext*) override { return nullptr; } // Pool selection not implemented. absl::optional - selectPool(Upstream::LoadBalancerContext* /*context*/, const Upstream::Host& /*host*/, - std::vector& /*hash_key*/) override { + selectExistingConnection(Upstream::LoadBalancerContext* /*context*/, + const Upstream::Host& /*host*/, + std::vector& /*hash_key*/) override { return absl::nullopt; } // Lifetime tracking not implemented. @@ -126,8 +127,9 @@ class ThreadAwareLoadBalancerBase : public LoadBalancerBase, public ThreadAwareL // Preconnect not implemented for hash based load balancing HostConstSharedPtr peekAnotherHost(LoadBalancerContext*) override { return nullptr; } absl::optional - selectPool(Upstream::LoadBalancerContext* /*context*/, const Upstream::Host& /*host*/, - std::vector& /*hash_key*/) override { + selectExistingConnection(Upstream::LoadBalancerContext* /*context*/, + const Upstream::Host& /*host*/, + std::vector& /*hash_key*/) override { return absl::nullopt; } OptRef lifetimeCallbacks() override { diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index 353b1d572f12..4a57f8d18b20 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( @@ -836,72 +839,42 @@ ClusterInfoImpl::ClusterInfoImpl( "HttpProtocolOptions can be specified"); } - switch (config.lb_policy()) { - case envoy::config::cluster::v3::Cluster::ROUND_ROBIN: - lb_type_ = LoadBalancerType::RoundRobin; - break; - case envoy::config::cluster::v3::Cluster::LEAST_REQUEST: - lb_type_ = LoadBalancerType::LeastRequest; - break; - case envoy::config::cluster::v3::Cluster::RANDOM: - lb_type_ = LoadBalancerType::Random; - break; - case envoy::config::cluster::v3::Cluster::RING_HASH: - lb_type_ = LoadBalancerType::RingHash; - break; - case envoy::config::cluster::v3::Cluster::MAGLEV: - lb_type_ = LoadBalancerType::Maglev; - break; - case envoy::config::cluster::v3::Cluster::CLUSTER_PROVIDED: - if (config.has_lb_subset_config()) { - throw EnvoyException( - fmt::format("cluster: LB policy {} cannot be combined with lb_subset_config", - envoy::config::cluster::v3::Cluster::LbPolicy_Name(config.lb_policy()))); - } - - lb_type_ = LoadBalancerType::ClusterProvided; - break; - case envoy::config::cluster::v3::Cluster::LOAD_BALANCING_POLICY_CONFIG: { - if (config.has_lb_subset_config()) { - throw EnvoyException( - fmt::format("cluster: LB policy {} cannot be combined with lb_subset_config", - envoy::config::cluster::v3::Cluster::LbPolicy_Name(config.lb_policy()))); - } - - if (config.has_common_lb_config()) { - throw EnvoyException( - fmt::format("cluster: LB policy {} cannot be combined with common_lb_config", - envoy::config::cluster::v3::Cluster::LbPolicy_Name(config.lb_policy()))); - } - - if (!config.has_load_balancing_policy()) { - throw EnvoyException( - fmt::format("cluster: LB policy {} requires load_balancing_policy to be set", - envoy::config::cluster::v3::Cluster::LbPolicy_Name(config.lb_policy()))); - } - - for (const auto& policy : config.load_balancing_policy().policies()) { - TypedLoadBalancerFactory* factory = - Config::Utility::getAndCheckFactory( - policy.typed_extension_config(), /*is_optional=*/true); - if (factory != nullptr) { - load_balancing_policy_ = policy; - load_balancer_factory_ = factory; - break; + // If load_balancing_policy is set we will use it directly, ignoring lb_policy. + if (config.has_load_balancing_policy()) { + configureLbPolicies(config); + } else { + switch (config.lb_policy()) { + case envoy::config::cluster::v3::Cluster::ROUND_ROBIN: + lb_type_ = LoadBalancerType::RoundRobin; + break; + case envoy::config::cluster::v3::Cluster::LEAST_REQUEST: + lb_type_ = LoadBalancerType::LeastRequest; + break; + case envoy::config::cluster::v3::Cluster::RANDOM: + lb_type_ = LoadBalancerType::Random; + break; + case envoy::config::cluster::v3::Cluster::RING_HASH: + lb_type_ = LoadBalancerType::RingHash; + break; + case envoy::config::cluster::v3::Cluster::MAGLEV: + lb_type_ = LoadBalancerType::Maglev; + break; + case envoy::config::cluster::v3::Cluster::CLUSTER_PROVIDED: + if (config.has_lb_subset_config()) { + throw EnvoyException( + fmt::format("cluster: LB policy {} cannot be combined with lb_subset_config", + envoy::config::cluster::v3::Cluster::LbPolicy_Name(config.lb_policy()))); } - } - if (load_balancer_factory_ == nullptr) { - throw EnvoyException(fmt::format( - "Didn't find a registered load balancer factory implementation for cluster: '{}'", - name_)); + lb_type_ = LoadBalancerType::ClusterProvided; + break; + case envoy::config::cluster::v3::Cluster::LOAD_BALANCING_POLICY_CONFIG: { + configureLbPolicies(config); + break; + } + default: + NOT_REACHED_GCOVR_EXCL_LINE; } - - lb_type_ = LoadBalancerType::LoadBalancingPolicyConfig; - break; - } - default: - NOT_REACHED_GCOVR_EXCL_LINE; } if (config.lb_subset_config().locality_weight_aware() && @@ -960,6 +933,45 @@ ClusterInfoImpl::ClusterInfoImpl( } } +// Configures the load balancer based on config.load_balancing_policy +void ClusterInfoImpl::configureLbPolicies(const envoy::config::cluster::v3::Cluster& config) { + if (config.has_lb_subset_config()) { + throw EnvoyException( + fmt::format("cluster: LB policy {} cannot be combined with lb_subset_config", + envoy::config::cluster::v3::Cluster::LbPolicy_Name(config.lb_policy()))); + } + + if (config.has_common_lb_config()) { + throw EnvoyException( + fmt::format("cluster: LB policy {} cannot be combined with common_lb_config", + envoy::config::cluster::v3::Cluster::LbPolicy_Name(config.lb_policy()))); + } + + if (!config.has_load_balancing_policy()) { + throw EnvoyException( + fmt::format("cluster: LB policy {} requires load_balancing_policy to be set", + envoy::config::cluster::v3::Cluster::LbPolicy_Name(config.lb_policy()))); + } + + for (const auto& policy : config.load_balancing_policy().policies()) { + TypedLoadBalancerFactory* factory = + Config::Utility::getAndCheckFactory( + policy.typed_extension_config(), /*is_optional=*/true); + if (factory != nullptr) { + load_balancing_policy_ = policy; + load_balancer_factory_ = factory; + break; + } + } + + if (load_balancer_factory_ == nullptr) { + throw EnvoyException(fmt::format( + "Didn't find a registered load balancer factory implementation for cluster: '{}'", name_)); + } + + lb_type_ = LoadBalancerType::LoadBalancingPolicyConfig; +} + ProtocolOptionsConfigConstSharedPtr ClusterInfoImpl::extensionProtocolOptions(const std::string& name) const { auto i = extension_protocol_options_.find(name); @@ -1777,6 +1789,8 @@ getDnsLookupFamilyFromEnum(envoy::config::cluster::v3::Cluster::DnsLookupFamily return Network::DnsLookupFamily::Auto; case envoy::config::cluster::v3::Cluster::V4_PREFERRED: return Network::DnsLookupFamily::V4Preferred; + case envoy::config::cluster::v3::Cluster::ALL: + return Network::DnsLookupFamily::All; default: NOT_REACHED_GCOVR_EXCL_LINE; } diff --git a/source/common/upstream/upstream_impl.h b/source/common/upstream/upstream_impl.h index 8c4c4d8661c6..56486741bcb1 100644 --- a/source/common/upstream/upstream_impl.h +++ b/source/common/upstream/upstream_impl.h @@ -634,6 +634,7 @@ class ClusterInfoImpl : public ClusterInfo, const envoy::config::core::v3::HttpProtocolOptions& commonHttpProtocolOptions() const override { return http_protocol_options_->common_http_protocol_options_; } + void configureLbPolicies(const envoy::config::cluster::v3::Cluster& config); ProtocolOptionsConfigConstSharedPtr extensionProtocolOptions(const std::string& name) const override; LoadBalancerType lbType() const override { return lb_type_; } diff --git a/source/docs/h2_metadata.md b/source/docs/h2_metadata.md index 71d5eba1a32c..133fdbb57be8 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/access_loggers/common/grpc_access_logger.h b/source/extensions/access_loggers/common/grpc_access_logger.h index 921186875fbe..446dd2698f67 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/filters/cel/BUILD b/source/extensions/access_loggers/filters/cel/BUILD new file mode 100644 index 000000000000..4e53f30780d7 --- /dev/null +++ b/source/extensions/access_loggers/filters/cel/BUILD @@ -0,0 +1,63 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_extension", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_extension_package() + +envoy_cc_extension( + name = "cel_lib", + srcs = ["cel.cc"], + hdrs = ["cel.h"], + extra_visibility = [ + "//test:__subpackages__", + ], + deps = [ + "//envoy/access_log:access_log_interface", + "//envoy/http:header_map_interface", + "//envoy/stream_info:stream_info_interface", + "//source/common/access_log:access_log_lib", + "//source/common/config:utility_lib", + "//source/common/protobuf", + "//source/common/protobuf:utility_lib", + "//source/extensions/filters/common/expr:evaluator_lib", + ], +) + +envoy_cc_extension( + name = "config", + srcs = ["config.cc"], + hdrs = ["config.h"], + copts = select({ + "//bazel:windows_x86_64": [], # TODO: fix the windows ANTLR build + "//conditions:default": [ + "-DUSE_CEL_PARSER", + ], + }), + extra_visibility = [ + "//test:__subpackages__", + ], + deps = [ + ":cel_lib", + "//envoy/access_log:access_log_interface", + "//envoy/http:header_map_interface", + "//envoy/registry", + "//envoy/stream_info:stream_info_interface", + "//source/common/access_log:access_log_lib", + "//source/common/config:utility_lib", + "//source/common/protobuf", + "//source/common/protobuf:utility_lib", + "//source/extensions/filters/common/expr:evaluator_lib", + "@envoy_api//envoy/extensions/access_loggers/filters/cel/v3:pkg_cc_proto", + ] + select( + { + "//bazel:windows_x86_64": [], + "//conditions:default": [ + "@com_google_cel_cpp//parser", + ], + }, + ), +) diff --git a/source/extensions/access_loggers/filters/cel/cel.cc b/source/extensions/access_loggers/filters/cel/cel.cc new file mode 100644 index 000000000000..a9c87da2e1a0 --- /dev/null +++ b/source/extensions/access_loggers/filters/cel/cel.cc @@ -0,0 +1,35 @@ +#include "source/extensions/access_loggers/filters/cel/cel.h" + +namespace Envoy { +namespace Extensions { +namespace AccessLoggers { +namespace Filters { +namespace CEL { + +namespace Expr = Envoy::Extensions::Filters::Common::Expr; + +CELAccessLogExtensionFilter::CELAccessLogExtensionFilter( + Expr::Builder& builder, const google::api::expr::v1alpha1::Expr& input_expr) + : parsed_expr_(input_expr) { + compiled_expr_ = Expr::createExpression(builder, parsed_expr_); +} + +bool CELAccessLogExtensionFilter::evaluate( + const StreamInfo::StreamInfo& stream_info, const Http::RequestHeaderMap& request_headers, + const Http::ResponseHeaderMap& response_headers, + const Http::ResponseTrailerMap& response_trailers) const { + Protobuf::Arena arena; + auto eval_status = Expr::evaluate(*compiled_expr_, arena, stream_info, &request_headers, + &response_headers, &response_trailers); + if (!eval_status.has_value() || eval_status.value().IsError()) { + return false; + } + auto result = eval_status.value(); + return result.IsBool() ? result.BoolOrDie() : false; +} + +} // namespace CEL +} // namespace Filters +} // namespace AccessLoggers +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/access_loggers/filters/cel/cel.h b/source/extensions/access_loggers/filters/cel/cel.h new file mode 100644 index 000000000000..911f04106b60 --- /dev/null +++ b/source/extensions/access_loggers/filters/cel/cel.h @@ -0,0 +1,36 @@ +#include "envoy/access_log/access_log.h" +#include "envoy/http/header_map.h" +#include "envoy/stream_info/stream_info.h" + +#include "source/common/access_log/access_log_impl.h" +#include "source/common/config/utility.h" +#include "source/common/protobuf/message_validator_impl.h" +#include "source/common/protobuf/protobuf.h" +#include "source/common/protobuf/utility.h" +#include "source/extensions/filters/common/expr/evaluator.h" + +namespace Envoy { +namespace Extensions { +namespace AccessLoggers { +namespace Filters { +namespace CEL { + +class CELAccessLogExtensionFilter : public AccessLog::Filter { +public: + CELAccessLogExtensionFilter(Extensions::Filters::Common::Expr::Builder&, + const google::api::expr::v1alpha1::Expr&); + + bool evaluate(const StreamInfo::StreamInfo& info, const Http::RequestHeaderMap& request_headers, + const Http::ResponseHeaderMap& response_headers, + const Http::ResponseTrailerMap& response_trailers) const override; + +private: + const google::api::expr::v1alpha1::Expr parsed_expr_; + Extensions::Filters::Common::Expr::ExpressionPtr compiled_expr_; +}; + +} // namespace CEL +} // namespace Filters +} // namespace AccessLoggers +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/access_loggers/filters/cel/config.cc b/source/extensions/access_loggers/filters/cel/config.cc new file mode 100644 index 000000000000..0df41fc19c52 --- /dev/null +++ b/source/extensions/access_loggers/filters/cel/config.cc @@ -0,0 +1,65 @@ +#include "source/extensions/access_loggers/filters/cel/config.h" + +#include "envoy/extensions/access_loggers/filters/cel/v3/cel.pb.h" + +#include "source/extensions/access_loggers/filters/cel/cel.h" + +#if defined(USE_CEL_PARSER) +#include "parser/parser.h" +#endif + +namespace Envoy { +namespace Extensions { +namespace AccessLoggers { +namespace Filters { +namespace CEL { + +Envoy::AccessLog::FilterPtr CELAccessLogExtensionFilterFactory::createFilter( + const envoy::config::accesslog::v3::ExtensionFilter& config, Runtime::Loader&, + Random::RandomGenerator&) { + + // TODO(douglas-reid): use factory_context validation. likely needs update to + // createFilter signature to pass in validation visitor. + auto factory_config = Config::Utility::translateToFactoryConfig( + config, Envoy::ProtobufMessage::getNullValidationVisitor(), *this); + +#if defined(USE_CEL_PARSER) + envoy::extensions::access_loggers::filters::cel::v3::ExpressionFilter cel_config = + *dynamic_cast( + factory_config.get()); + + auto parse_status = google::api::expr::parser::Parse(cel_config.expression()); + if (!parse_status.ok()) { + throw EnvoyException("Not able to parse filter expression: " + + parse_status.status().ToString()); + } + + return std::make_unique(getOrCreateBuilder(), + parse_status.value().expr()); +#else + throw EnvoyException("CEL is not available for use in this environment."); +#endif +} + +ProtobufTypes::MessagePtr CELAccessLogExtensionFilterFactory::createEmptyConfigProto() { + return std::make_unique(); +} + +Extensions::Filters::Common::Expr::Builder& +CELAccessLogExtensionFilterFactory::getOrCreateBuilder() { + if (expr_builder_ == nullptr) { + expr_builder_ = Extensions::Filters::Common::Expr::createBuilder(nullptr); + } + return *expr_builder_; +} + +/** + * Static registration for the CELAccessLogExtensionFilter. @see RegisterFactory. + */ +REGISTER_FACTORY(CELAccessLogExtensionFilterFactory, Envoy::AccessLog::ExtensionFilterFactory); + +} // namespace CEL +} // namespace Filters +} // namespace AccessLoggers +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/access_loggers/filters/cel/config.h b/source/extensions/access_loggers/filters/cel/config.h new file mode 100644 index 000000000000..0266af6fbb0a --- /dev/null +++ b/source/extensions/access_loggers/filters/cel/config.h @@ -0,0 +1,36 @@ +#include "envoy/access_log/access_log.h" +#include "envoy/http/header_map.h" +#include "envoy/registry/registry.h" +#include "envoy/stream_info/stream_info.h" + +#include "source/common/access_log/access_log_impl.h" +#include "source/common/config/utility.h" +#include "source/common/protobuf/message_validator_impl.h" +#include "source/common/protobuf/protobuf.h" +#include "source/common/protobuf/utility.h" +#include "source/extensions/filters/common/expr/evaluator.h" + +namespace Envoy { +namespace Extensions { +namespace AccessLoggers { +namespace Filters { +namespace CEL { + +class CELAccessLogExtensionFilterFactory : public Envoy::AccessLog::ExtensionFilterFactory { +public: + Envoy::AccessLog::FilterPtr + createFilter(const envoy::config::accesslog::v3::ExtensionFilter& config, Runtime::Loader&, + Random::RandomGenerator&) override; + ProtobufTypes::MessagePtr createEmptyConfigProto() override; + std::string name() const override { return "envoy.access_loggers.extension_filters.cel"; } + +private: + Extensions::Filters::Common::Expr::Builder& getOrCreateBuilder(); + Extensions::Filters::Common::Expr::BuilderPtr expr_builder_; +}; + +} // namespace CEL +} // namespace Filters +} // namespace AccessLoggers +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/access_loggers/grpc/BUILD b/source/extensions/access_loggers/grpc/BUILD index 1174056f7b79..4d73da7b49ad 100644 --- a/source/extensions/access_loggers/grpc/BUILD +++ b/source/extensions/access_loggers/grpc/BUILD @@ -97,11 +97,6 @@ envoy_cc_extension( name = "http_config", srcs = ["http_config.cc"], hdrs = ["http_config.h"], - # TODO(#9953) clean up. - extra_visibility = [ - "//test/common/access_log:__subpackages__", - "//test/integration:__subpackages__", - ], deps = [ ":config_utils", "//envoy/server:access_log_config_interface", @@ -118,11 +113,6 @@ envoy_cc_extension( name = "tcp_config", srcs = ["tcp_config.cc"], hdrs = ["tcp_config.h"], - # TODO(#9953) clean up. - extra_visibility = [ - "//test/common/access_log:__subpackages__", - "//test/integration:__subpackages__", - ], deps = [ ":config_utils", "//envoy/server:access_log_config_interface", 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 ca45d2c5acaf..bc94345b938e 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 c502f4365d89..34aab4cf6edc 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/grpc/grpc_access_log_utils.cc b/source/extensions/access_loggers/grpc/grpc_access_log_utils.cc index 01ccb77d85fc..408173a16ebb 100644 --- a/source/extensions/access_loggers/grpc/grpc_access_log_utils.cc +++ b/source/extensions/access_loggers/grpc/grpc_access_log_utils.cc @@ -5,6 +5,7 @@ #include "envoy/upstream/upstream.h" #include "source/common/network/utility.h" +#include "source/common/stream_info/utility.h" namespace Envoy { namespace Extensions { @@ -201,68 +202,72 @@ void Utility::extractCommonAccessLogProperties( stream_info.startTime().time_since_epoch()) .count())); - absl::optional dur = stream_info.lastDownstreamRxByteReceived(); + StreamInfo::TimingUtility timing(stream_info); + absl::optional dur = timing.lastDownstreamRxByteReceived(); if (dur) { common_access_log.mutable_time_to_last_rx_byte()->MergeFrom( Protobuf::util::TimeUtil::NanosecondsToDuration(dur.value().count())); } - dur = stream_info.firstUpstreamTxByteSent(); + dur = timing.firstUpstreamTxByteSent(); if (dur) { common_access_log.mutable_time_to_first_upstream_tx_byte()->MergeFrom( Protobuf::util::TimeUtil::NanosecondsToDuration(dur.value().count())); } - dur = stream_info.lastUpstreamTxByteSent(); + dur = timing.lastUpstreamTxByteSent(); if (dur) { common_access_log.mutable_time_to_last_upstream_tx_byte()->MergeFrom( Protobuf::util::TimeUtil::NanosecondsToDuration(dur.value().count())); } - dur = stream_info.firstUpstreamRxByteReceived(); + dur = timing.firstUpstreamRxByteReceived(); if (dur) { common_access_log.mutable_time_to_first_upstream_rx_byte()->MergeFrom( Protobuf::util::TimeUtil::NanosecondsToDuration(dur.value().count())); } - dur = stream_info.lastUpstreamRxByteReceived(); + dur = timing.lastUpstreamRxByteReceived(); if (dur) { common_access_log.mutable_time_to_last_upstream_rx_byte()->MergeFrom( Protobuf::util::TimeUtil::NanosecondsToDuration(dur.value().count())); } - dur = stream_info.firstDownstreamTxByteSent(); + dur = timing.firstDownstreamTxByteSent(); if (dur) { common_access_log.mutable_time_to_first_downstream_tx_byte()->MergeFrom( Protobuf::util::TimeUtil::NanosecondsToDuration(dur.value().count())); } - dur = stream_info.lastDownstreamTxByteSent(); + dur = timing.lastDownstreamTxByteSent(); if (dur) { common_access_log.mutable_time_to_last_downstream_tx_byte()->MergeFrom( Protobuf::util::TimeUtil::NanosecondsToDuration(dur.value().count())); } - if (stream_info.upstreamHost() != nullptr) { - Network::Utility::addressToProtobufAddress( - *stream_info.upstreamHost()->address(), - *common_access_log.mutable_upstream_remote_address()); - common_access_log.set_upstream_cluster(stream_info.upstreamHost()->cluster().name()); + if (stream_info.upstreamInfo().has_value()) { + const auto& upstream_info = stream_info.upstreamInfo().value().get(); + if (upstream_info.upstreamHost() != nullptr) { + Network::Utility::addressToProtobufAddress( + *upstream_info.upstreamHost()->address(), + *common_access_log.mutable_upstream_remote_address()); + common_access_log.set_upstream_cluster(upstream_info.upstreamHost()->cluster().name()); + } + if (upstream_info.upstreamLocalAddress() != nullptr) { + Network::Utility::addressToProtobufAddress( + *upstream_info.upstreamLocalAddress(), + *common_access_log.mutable_upstream_local_address()); + } + if (!upstream_info.upstreamTransportFailureReason().empty()) { + common_access_log.set_upstream_transport_failure_reason( + upstream_info.upstreamTransportFailureReason()); + } } - if (!stream_info.getRouteName().empty()) { common_access_log.set_route_name(stream_info.getRouteName()); } - if (stream_info.upstreamLocalAddress() != nullptr) { - Network::Utility::addressToProtobufAddress(*stream_info.upstreamLocalAddress(), - *common_access_log.mutable_upstream_local_address()); - } responseFlagsToAccessLogResponseFlags(common_access_log, stream_info); - if (!stream_info.upstreamTransportFailureReason().empty()) { - common_access_log.set_upstream_transport_failure_reason( - stream_info.upstreamTransportFailureReason()); - } if (stream_info.dynamicMetadata().filter_metadata_size() > 0) { common_access_log.mutable_metadata()->MergeFrom(stream_info.dynamicMetadata()); } diff --git a/source/extensions/access_loggers/grpc/http_config.cc b/source/extensions/access_loggers/grpc/http_config.cc index 5d3b79510067..fa69f555b48b 100644 --- a/source/extensions/access_loggers/grpc/http_config.cc +++ b/source/extensions/access_loggers/grpc/http_config.cc @@ -27,10 +27,6 @@ AccessLog::InstanceSharedPtr HttpGrpcAccessLogFactory::createAccessLogInstance( const envoy::extensions::access_loggers::grpc::v3::HttpGrpcAccessLogConfig&>( config, context.messageValidationVisitor()); - const auto service_config = proto_config.common_config().grpc_service(); - if (service_config.has_envoy_grpc()) { - context.clusterManager().checkActiveStaticCluster(service_config.envoy_grpc().cluster_name()); - } return std::make_shared( std::move(filter), proto_config, context.threadLocal(), GrpcCommon::getGrpcAccessLoggerCacheSingleton(context)); diff --git a/source/extensions/access_loggers/grpc/tcp_config.cc b/source/extensions/access_loggers/grpc/tcp_config.cc index 495cdfa3738c..65c2416c74b7 100644 --- a/source/extensions/access_loggers/grpc/tcp_config.cc +++ b/source/extensions/access_loggers/grpc/tcp_config.cc @@ -27,10 +27,6 @@ AccessLog::InstanceSharedPtr TcpGrpcAccessLogFactory::createAccessLogInstance( const envoy::extensions::access_loggers::grpc::v3::TcpGrpcAccessLogConfig&>( config, context.messageValidationVisitor()); - const auto service_config = proto_config.common_config().grpc_service(); - if (service_config.has_envoy_grpc()) { - context.clusterManager().checkActiveStaticCluster(service_config.envoy_grpc().cluster_name()); - } return std::make_shared(std::move(filter), proto_config, context.threadLocal(), GrpcCommon::getGrpcAccessLoggerCacheSingleton(context)); } diff --git a/source/extensions/access_loggers/open_telemetry/BUILD b/source/extensions/access_loggers/open_telemetry/BUILD index 1599f25a4c9c..fef41675f552 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 38b6a5d644b9..3acfe950c85c 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 7a36bad2639e..b35db3bd32d9 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/grpc_access_log_impl.cc b/source/extensions/access_loggers/open_telemetry/grpc_access_log_impl.cc index 38d9616922a9..0c08ce1194e1 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 85aa0ad8d694..0fa389d75b1d 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/source/extensions/access_loggers/open_telemetry/substitution_formatter.cc b/source/extensions/access_loggers/open_telemetry/substitution_formatter.cc new file mode 100644 index 000000000000..d590ca2d5ca2 --- /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 000000000000..dcbe0c050c54 --- /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/source/extensions/clusters/aggregate/cluster.cc b/source/extensions/clusters/aggregate/cluster.cc index 632cec0034cb..c76a42ca95f8 100644 --- a/source/extensions/clusters/aggregate/cluster.cc +++ b/source/extensions/clusters/aggregate/cluster.cc @@ -189,11 +189,11 @@ AggregateClusterLoadBalancer::peekAnotherHost(Upstream::LoadBalancerContext* con } absl::optional -AggregateClusterLoadBalancer::selectPool(Upstream::LoadBalancerContext* context, - const Upstream::Host& host, - std::vector& hash_key) { +AggregateClusterLoadBalancer::selectExistingConnection(Upstream::LoadBalancerContext* context, + const Upstream::Host& host, + std::vector& hash_key) { if (load_balancer_) { - return load_balancer_->selectPool(context, host, hash_key); + return load_balancer_->selectExistingConnection(context, host, hash_key); } return absl::nullopt; } diff --git a/source/extensions/clusters/aggregate/cluster.h b/source/extensions/clusters/aggregate/cluster.h index ef997b0d2734..6d961fd2e0f3 100644 --- a/source/extensions/clusters/aggregate/cluster.h +++ b/source/extensions/clusters/aggregate/cluster.h @@ -80,8 +80,9 @@ class AggregateClusterLoadBalancer : public Upstream::LoadBalancer, Upstream::HostConstSharedPtr chooseHost(Upstream::LoadBalancerContext* context) override; Upstream::HostConstSharedPtr peekAnotherHost(Upstream::LoadBalancerContext*) override; absl::optional - selectPool(Upstream::LoadBalancerContext* /*context*/, const Upstream::Host& /*host*/, - std::vector& /*hash_key*/) override; + selectExistingConnection(Upstream::LoadBalancerContext* /*context*/, + const Upstream::Host& /*host*/, + std::vector& /*hash_key*/) override; OptRef lifetimeCallbacks() override; private: @@ -103,8 +104,9 @@ class AggregateClusterLoadBalancer : public Upstream::LoadBalancer, return nullptr; } absl::optional - selectPool(Upstream::LoadBalancerContext* /*context*/, const Upstream::Host& /*host*/, - std::vector& /*hash_key*/) override { + selectExistingConnection(Upstream::LoadBalancerContext* /*context*/, + const Upstream::Host& /*host*/, + std::vector& /*hash_key*/) override { return {}; } OptRef lifetimeCallbacks() override { diff --git a/source/extensions/clusters/dynamic_forward_proxy/cluster.cc b/source/extensions/clusters/dynamic_forward_proxy/cluster.cc index 4778672f50f6..915e0a4f2d8e 100644 --- a/source/extensions/clusters/dynamic_forward_proxy/cluster.cc +++ b/source/extensions/clusters/dynamic_forward_proxy/cluster.cc @@ -178,8 +178,9 @@ Cluster::LoadBalancer::chooseHost(Upstream::LoadBalancerContext* context) { } absl::optional -Cluster::LoadBalancer::selectPool(Upstream::LoadBalancerContext* /*context*/, - const Upstream::Host& host, std::vector& hash_key) { +Cluster::LoadBalancer::selectExistingConnection(Upstream::LoadBalancerContext* /*context*/, + const Upstream::Host& host, + std::vector& hash_key) { const std::string& hostname = host.hostname(); if (hostname.empty()) { return absl::nullopt; diff --git a/source/extensions/clusters/dynamic_forward_proxy/cluster.h b/source/extensions/clusters/dynamic_forward_proxy/cluster.h index 949bf8dcb6d7..bea82bdd93ba 100644 --- a/source/extensions/clusters/dynamic_forward_proxy/cluster.h +++ b/source/extensions/clusters/dynamic_forward_proxy/cluster.h @@ -66,8 +66,8 @@ class Cluster : public Upstream::BaseDynamicClusterImpl, return nullptr; } absl::optional - selectPool(Upstream::LoadBalancerContext* context, const Upstream::Host& host, - std::vector& hash_key) override; + selectExistingConnection(Upstream::LoadBalancerContext* context, const Upstream::Host& host, + std::vector& hash_key) override; OptRef lifetimeCallbacks() override; // Envoy::Http::ConnectionPool::ConnectionLifetimeCallbacks diff --git a/source/extensions/clusters/redis/redis_cluster.cc b/source/extensions/clusters/redis/redis_cluster.cc index e497a62a9ccf..9a64b58050a0 100644 --- a/source/extensions/clusters/redis/redis_cluster.cc +++ b/source/extensions/clusters/redis/redis_cluster.cc @@ -275,8 +275,10 @@ void RedisCluster::RedisDiscoverySession::registerDiscoveryAddress( // Since the address from DNS does not have port, we need to make a new address that has // port in it. for (const Network::DnsResponse& res : response) { - ASSERT(res.address_ != nullptr); - discovery_address_list_.push_back(Network::Utility::getAddressWithPort(*(res.address_), port)); + const auto& addrinfo = res.addrInfo(); + ASSERT(addrinfo.address_ != nullptr); + discovery_address_list_.push_back( + Network::Utility::getAddressWithPort(*(addrinfo.address_), port)); } } diff --git a/source/extensions/clusters/redis/redis_cluster_lb.h b/source/extensions/clusters/redis/redis_cluster_lb.h index 87a2c39b218e..a4eb2352df6c 100644 --- a/source/extensions/clusters/redis/redis_cluster_lb.h +++ b/source/extensions/clusters/redis/redis_cluster_lb.h @@ -190,8 +190,9 @@ class RedisClusterLoadBalancerFactory : public ClusterSlotUpdateCallBack, } // Pool selection not implemented. absl::optional - selectPool(Upstream::LoadBalancerContext* /*context*/, const Upstream::Host& /*host*/, - std::vector& /*hash_key*/) override { + selectExistingConnection(Upstream::LoadBalancerContext* /*context*/, + const Upstream::Host& /*host*/, + std::vector& /*hash_key*/) override { return absl::nullopt; } // Lifetime tracking not implemented. diff --git a/source/extensions/common/aws/BUILD b/source/extensions/common/aws/BUILD index f8857b60a553..31bd0c8fa72a 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 a071e2914967..9f7bf92b4eb0 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 301ead3e40ee..208133ce2679 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 a0ba42fe4eb8..b1929086ad6d 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 36b34da02a9f..e7cfdac2abb1 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/common/dynamic_forward_proxy/BUILD b/source/extensions/common/dynamic_forward_proxy/BUILD index d8abc11812b0..ceb356d83150 100644 --- a/source/extensions/common/dynamic_forward_proxy/BUILD +++ b/source/extensions/common/dynamic_forward_proxy/BUILD @@ -49,7 +49,7 @@ envoy_cc_library( "//source/common/config:utility_lib", "//source/common/network:resolver_lib", "//source/common/network:utility_lib", - "//source/common/network/dns_resolver:dns_factory_lib", + "//source/common/network/dns_resolver:dns_factory_util_lib", "@envoy_api//envoy/extensions/common/dynamic_forward_proxy/v3:pkg_cc_proto", ], ) diff --git a/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.cc b/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.cc index 05801becc104..aac0b88172e1 100644 --- a/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.cc +++ b/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.cc @@ -6,7 +6,7 @@ #include "source/common/common/stl_helpers.h" #include "source/common/config/utility.h" #include "source/common/http/utility.h" -#include "source/common/network/dns_resolver/dns_factory.h" +#include "source/common/network/dns_resolver/dns_factory_util.h" #include "source/common/network/resolver_impl.h" #include "source/common/network/utility.h" @@ -295,7 +295,7 @@ void DnsCacheImpl::finishResolve(const std::string& host, ENVOY_LOG_EVENT(debug, "dns_cache_finish_resolve", "main thread resolve complete for host '{}': {}", host, accumulateToString(response, [](const auto& dns_response) { - return dns_response.address_->asString(); + return dns_response.addrInfo().address_->asString(); })); const bool from_cache = resolution_time.has_value(); @@ -325,10 +325,10 @@ void DnsCacheImpl::finishResolve(const std::string& host, // If the DNS resolver successfully resolved with an empty response list, the dns cache does not // update. This ensures that a potentially previously resolved address does not stabilize back to // 0 hosts. - const auto new_address = !response.empty() - ? Network::Utility::getAddressWithPort(*(response.front().address_), - primary_host_info->port_) - : nullptr; + const auto new_address = + !response.empty() ? Network::Utility::getAddressWithPort( + *(response.front().addrInfo().address_), primary_host_info->port_) + : nullptr; auto address_list = DnsUtils::generateAddressList(response, primary_host_info->port_); // Only the change the address if: @@ -349,11 +349,12 @@ void DnsCacheImpl::finishResolve(const std::string& host, if (new_address) { // Update the cache entry and staleness any time the ttl changes. if (!from_cache) { - addCacheEntry(host, new_address, address_list, response.front().ttl_); + addCacheEntry(host, new_address, address_list, response.front().addrInfo().ttl_); } if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.use_dns_ttl")) { // Arbitrarily cap DNS re-resolution at 5s to avoid constant DNS queries. - dns_ttl = std::max(std::chrono::seconds(5), response.front().ttl_); + dns_ttl = + std::max(std::chrono::seconds(5), response.front().addrInfo().ttl_); } primary_host_info->host_info_->updateStale(resolution_time.value(), dns_ttl); } @@ -543,7 +544,7 @@ void DnsCacheImpl::loadCacheEntries( if (responses.empty()) { return KeyValueStore::Iterate::Break; } - createHost(key, responses.front().address_->ip()->port()); + createHost(key, responses.front().addrInfo().address_->ip()->port()); finishResolve(key, Network::DnsResolver::ResolutionStatus::Success, std::move(responses), resolution_time); stats_.cache_load_.inc(); diff --git a/source/extensions/common/wasm/BUILD b/source/extensions/common/wasm/BUILD index bf5b6a7273fc..93f33d4a6570 100644 --- a/source/extensions/common/wasm/BUILD +++ b/source/extensions/common/wasm/BUILD @@ -82,7 +82,7 @@ envoy_cc_library( "//source/common/http:message_lib", "//source/common/http:utility_lib", "//source/common/tracing:http_tracer_lib", - "//source/common/network/dns_resolver:dns_factory_lib", + "//source/common/network/dns_resolver:dns_factory_util_lib", "//source/extensions/common/wasm/ext:declare_property_cc_proto", "//source/extensions/common/wasm/ext:envoy_null_vm_wasm_api", "//source/extensions/filters/common/expr:context_lib", diff --git a/source/extensions/common/wasm/context.cc b/source/extensions/common/wasm/context.cc index 07447110f210..fb22c6f0594c 100644 --- a/source/extensions/common/wasm/context.cc +++ b/source/extensions/common/wasm/context.cc @@ -88,6 +88,13 @@ Http::RequestHeaderMapPtr buildRequestHeaderMapFromPairs(const Pairs& pairs) { template static uint32_t headerSize(const P& p) { return p ? p->size() : 0; } +Upstream::HostDescriptionConstSharedPtr getHost(const StreamInfo::StreamInfo* info) { + if (info && info->upstreamInfo() && info->upstreamInfo().value().get().upstreamHost()) { + return info->upstreamInfo().value().get().upstreamHost(); + } + return nullptr; +} + } // namespace // Test support. @@ -194,8 +201,8 @@ void Context::onResolveDns(uint32_t token, Envoy::Network::DnsResolver::Resoluti // N * null-terminated addresses uint32_t s = 4; // length for (auto& e : response) { - s += 4; // for TTL - s += e.address_->asStringView().size() + 1; // null terminated. + s += 4; // for TTL + s += e.addrInfo().address_->asStringView().size() + 1; // null terminated. } auto buffer = std::unique_ptr(new char[s]); char* b = buffer.get(); @@ -203,14 +210,14 @@ void Context::onResolveDns(uint32_t token, Envoy::Network::DnsResolver::Resoluti safeMemcpyUnsafeDst(b, &n); b += sizeof(uint32_t); for (auto& e : response) { - uint32_t ttl = e.ttl_.count(); + uint32_t ttl = e.addrInfo().ttl_.count(); safeMemcpyUnsafeDst(b, &ttl); b += sizeof(uint32_t); }; for (auto& e : response) { - memcpy(b, e.address_->asStringView().data(), // NOLINT(safe-memcpy) - e.address_->asStringView().size()); - b += e.address_->asStringView().size(); + memcpy(b, e.addrInfo().address_->asStringView().data(), // NOLINT(safe-memcpy) + e.addrInfo().address_->asStringView().size()); + b += e.addrInfo().address_->asStringView().size(); *b++ = 0; }; buffer_.set(std::move(buffer), s); @@ -443,9 +450,12 @@ Context::findValue(absl::string_view name, Protobuf::Arena* arena, bool last) co const CelState* state; if (info->filterState().hasData(key)) { state = &info->filterState().getDataReadOnly(key); - } else if (info->upstreamFilterState() && - info->upstreamFilterState()->hasData(key)) { - state = &info->upstreamFilterState()->getDataReadOnly(key); + } else if (info->upstreamInfo().has_value() && + info->upstreamInfo().value().get().upstreamFilterState() && + info->upstreamInfo().value().get().upstreamFilterState()->hasData(key)) { + state = + &info->upstreamInfo().value().get().upstreamFilterState()->getDataReadOnly( + key); } else { return {}; } @@ -522,8 +532,8 @@ Context::findValue(absl::string_view name, Protobuf::Arena* arena, bool last) co } break; case PropertyToken::CLUSTER_NAME: - if (info && info->upstreamHost()) { - return CelValue::CreateString(&info->upstreamHost()->cluster().name()); + if (getHost(info)) { + return CelValue::CreateString(&getHost(info)->cluster().name()); } else if (info && info->route() && info->route()->routeEntry()) { return CelValue::CreateString(&info->route()->routeEntry()->clusterName()); } else if (info && info->upstreamClusterInfo().has_value() && @@ -532,8 +542,8 @@ Context::findValue(absl::string_view name, Protobuf::Arena* arena, bool last) co } break; case PropertyToken::CLUSTER_METADATA: - if (info && info->upstreamHost()) { - return CelProtoWrapper::CreateMessage(&info->upstreamHost()->cluster().metadata(), arena); + if (getHost(info)) { + return CelProtoWrapper::CreateMessage(&getHost(info)->cluster().metadata(), arena); } else if (info && info->upstreamClusterInfo().has_value() && info->upstreamClusterInfo().value()) { return CelProtoWrapper::CreateMessage(&info->upstreamClusterInfo().value()->metadata(), @@ -541,8 +551,8 @@ Context::findValue(absl::string_view name, Protobuf::Arena* arena, bool last) co } break; case PropertyToken::UPSTREAM_HOST_METADATA: - if (info && info->upstreamHost() && info->upstreamHost()->metadata()) { - return CelProtoWrapper::CreateMessage(info->upstreamHost()->metadata().get(), arena); + if (getHost(info)) { + return CelProtoWrapper::CreateMessage(getHost(info)->metadata().get(), arena); } break; case PropertyToken::ROUTE_NAME: @@ -565,9 +575,12 @@ Context::findValue(absl::string_view name, Protobuf::Arena* arena, bool last) co case PropertyToken::PLUGIN_VM_ID: return CelValue::CreateStringView(toAbslStringView(wasm()->vm_id())); case PropertyToken::FILTER_STATE: - return Protobuf::Arena::Create(arena, - info->filterState()) - ->Produce(arena); + if (info) { + return Protobuf::Arena::Create(arena, + info->filterState()) + ->Produce(arena); + } + break; } return {}; } @@ -974,19 +987,15 @@ WasmResult Context::grpcCall(std::string_view grpc_service, std::string_view ser uint32_t* token_ptr) { GrpcService service_proto; if (!service_proto.ParseFromArray(grpc_service.data(), grpc_service.size())) { - if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.wasm_cluster_name_envoy_grpc")) { - auto cluster_name = std::string(grpc_service.substr(0, grpc_service.size())); - const auto thread_local_cluster = clusterManager().getThreadLocalCluster(cluster_name); - if (thread_local_cluster == nullptr) { - // TODO(shikugawa): The reason to keep return status as `BadArgument` is not to force - // callers to change their own codebase with ABI 0.1.x. We should treat this failure as - // `BadArgument` after ABI 0.2.x will have released. - return WasmResult::ParseFailure; - } - service_proto.mutable_envoy_grpc()->set_cluster_name(cluster_name); - } else { + auto cluster_name = std::string(grpc_service.substr(0, grpc_service.size())); + const auto thread_local_cluster = clusterManager().getThreadLocalCluster(cluster_name); + if (thread_local_cluster == nullptr) { + // TODO(shikugawa): The reason to keep return status as `BadArgument` is not to force + // callers to change their own codebase with ABI 0.1.x. We should treat this failure as + // `BadArgument` after ABI 0.2.x will have released. return WasmResult::ParseFailure; } + service_proto.mutable_envoy_grpc()->set_cluster_name(cluster_name); } uint32_t token = wasm()->nextGrpcCallId(); auto& handler = grpc_call_request_[token]; @@ -1023,19 +1032,15 @@ WasmResult Context::grpcStream(std::string_view grpc_service, std::string_view s uint32_t* token_ptr) { GrpcService service_proto; if (!service_proto.ParseFromArray(grpc_service.data(), grpc_service.size())) { - if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.wasm_cluster_name_envoy_grpc")) { - auto cluster_name = std::string(grpc_service.substr(0, grpc_service.size())); - const auto thread_local_cluster = clusterManager().getThreadLocalCluster(cluster_name); - if (thread_local_cluster == nullptr) { - // TODO(shikugawa): The reason to keep return status as `BadArgument` is not to force - // callers to change their own codebase with ABI 0.1.x. We should treat this failure as - // `BadArgument` after ABI 0.2.x will have released. - return WasmResult::ParseFailure; - } - service_proto.mutable_envoy_grpc()->set_cluster_name(cluster_name); - } else { + auto cluster_name = std::string(grpc_service.substr(0, grpc_service.size())); + const auto thread_local_cluster = clusterManager().getThreadLocalCluster(cluster_name); + if (thread_local_cluster == nullptr) { + // TODO(shikugawa): The reason to keep return status as `BadArgument` is not to force + // callers to change their own codebase with ABI 0.1.x. We should treat this failure as + // `BadArgument` after ABI 0.2.x will have released. return WasmResult::ParseFailure; } + service_proto.mutable_envoy_grpc()->set_cluster_name(cluster_name); } uint32_t token = wasm()->nextGrpcStreamId(); auto& handler = grpc_stream_[token]; @@ -1644,7 +1649,8 @@ WasmResult Context::sendLocalResponse(uint32_t response_code, std::string_view b // sendLocalReply() will fail. Net net, this is safe. wasm()->addAfterVmCallAction([this, response_code, body_text = std::string(body_text), modify_headers = std::move(modify_headers), grpc_status, - details = std::string(details)] { + details = StringUtil::replaceAllEmptySpace( + absl::string_view(details.data(), details.size()))] { decoder_callbacks_->sendLocalReply(static_cast(response_code), body_text, modify_headers, grpc_status, details); }); @@ -1715,7 +1721,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 b163c96c9eda..46dadc4b7677 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/common/wasm/wasm.cc b/source/extensions/common/wasm/wasm.cc index fbc06528dbfa..4fc6c6a36caf 100644 --- a/source/extensions/common/wasm/wasm.cc +++ b/source/extensions/common/wasm/wasm.cc @@ -6,7 +6,7 @@ #include "envoy/event/deferred_deletable.h" #include "source/common/common/logger.h" -#include "source/common/network/dns_resolver/dns_factory.h" +#include "source/common/network/dns_resolver/dns_factory_util.h" #include "source/extensions/common/wasm/plugin.h" #include "source/extensions/common/wasm/stats_handler.h" diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index 632c4ffc22a8..af6c53608439 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -5,6 +5,7 @@ EXTENSIONS = { # "envoy.access_loggers.file": "//source/extensions/access_loggers/file:config", + "envoy.access_loggers.extension_filters.cel": "//source/extensions/access_loggers/filters/cel:config", "envoy.access_loggers.http_grpc": "//source/extensions/access_loggers/grpc:http_config", "envoy.access_loggers.tcp_grpc": "//source/extensions/access_loggers/grpc:tcp_config", "envoy.access_loggers.open_telemetry": "//source/extensions/access_loggers/open_telemetry:config", @@ -173,6 +174,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", # @@ -196,6 +198,7 @@ EXTENSIONS = { "envoy.transport_sockets.raw_buffer": "//source/extensions/transport_sockets/raw_buffer:config", "envoy.transport_sockets.tap": "//source/extensions/transport_sockets/tap:config", "envoy.transport_sockets.starttls": "//source/extensions/transport_sockets/starttls:config", + "envoy.transport_sockets.tcp_stats": "//source/extensions/transport_sockets/tcp_stats:config", # # Retry host predicates diff --git a/source/extensions/extensions_metadata.yaml b/source/extensions/extensions_metadata.yaml index d888ec60f39d..3cf47195acf2 100644 --- a/source/extensions/extensions_metadata.yaml +++ b/source/extensions/extensions_metadata.yaml @@ -3,6 +3,11 @@ envoy.access_loggers.file: - envoy.access_loggers security_posture: robust_to_untrusted_downstream status: stable +envoy.access_loggers.extension_filters.cel: + categories: + - envoy.access_loggers.extension_filters + security_posture: unknown + status: alpha envoy.access_loggers.http_grpc: categories: - envoy.access_loggers @@ -393,6 +398,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 @@ -633,6 +643,12 @@ envoy.transport_sockets.tap: - envoy.transport_sockets.upstream security_posture: requires_trusted_downstream_and_upstream status: alpha +envoy.transport_sockets.tcp_stats: + categories: + - envoy.transport_sockets.downstream + - envoy.transport_sockets.upstream + security_posture: robust_to_untrusted_downstream_and_upstream + status: alpha envoy.transport_sockets.tls: categories: - envoy.transport_sockets.downstream diff --git a/source/extensions/filters/common/expr/context.cc b/source/extensions/filters/common/expr/context.cc index ac0a47bd98d3..b2c4ed14eae3 100644 --- a/source/extensions/filters/common/expr/context.cc +++ b/source/extensions/filters/common/expr/context.cc @@ -208,31 +208,32 @@ absl::optional ConnectionWrapper::operator[](CelValue key) const { } absl::optional UpstreamWrapper::operator[](CelValue key) const { - if (!key.IsString()) { + if (!key.IsString() || !info_.upstreamInfo().has_value()) { return {}; } auto value = key.StringOrDie().value(); if (value == Address) { - auto upstream_host = info_.upstreamHost(); + auto upstream_host = info_.upstreamInfo().value().get().upstreamHost(); if (upstream_host != nullptr && upstream_host->address() != nullptr) { return CelValue::CreateStringView(upstream_host->address()->asStringView()); } } else if (value == Port) { - auto upstream_host = info_.upstreamHost(); + auto upstream_host = info_.upstreamInfo().value().get().upstreamHost(); if (upstream_host != nullptr && upstream_host->address() != nullptr && upstream_host->address()->ip() != nullptr) { return CelValue::CreateInt64(upstream_host->address()->ip()->port()); } } else if (value == UpstreamLocalAddress) { - auto upstream_local_address = info_.upstreamLocalAddress(); + auto upstream_local_address = info_.upstreamInfo().value().get().upstreamLocalAddress(); if (upstream_local_address != nullptr) { return CelValue::CreateStringView(upstream_local_address->asStringView()); } } else if (value == UpstreamTransportFailureReason) { - return CelValue::CreateStringView(info_.upstreamTransportFailureReason()); + return CelValue::CreateStringView( + info_.upstreamInfo().value().get().upstreamTransportFailureReason()); } - auto ssl_info = info_.upstreamSslConnection(); + auto ssl_info = info_.upstreamInfo().value().get().upstreamSslConnection(); if (ssl_info != nullptr) { return extractSslInfo(*ssl_info, value); } diff --git a/source/extensions/filters/common/ext_authz/ext_authz.h b/source/extensions/filters/common/ext_authz/ext_authz.h index 861faac9b1bd..ee30b3b70fc9 100644 --- a/source/extensions/filters/common/ext_authz/ext_authz.h +++ b/source/extensions/filters/common/ext_authz/ext_authz.h @@ -33,6 +33,17 @@ struct TracingConstantValues { using TracingConstants = ConstSingleton; +/** + * Possible constant response code details values for a check call. + */ +struct ResponseCodeDetailsValues { + // The ext_authz filter denied the downstream request/connection. + const std::string AuthzDenied = "ext_authz_denied"; + // The ext_authz filter encountered a failure, and was configured to fail-closed. + const std::string AuthzError = "ext_authz_error"; +}; +using ResponseCodeDetails = ConstSingleton; + /** * Constant auth related HTTP headers. All lower case. This group of headers can * contain prefix override headers. diff --git a/source/extensions/filters/common/lua/wrappers.cc b/source/extensions/filters/common/lua/wrappers.cc index bfb2a1c47325..9963a446cb77 100644 --- a/source/extensions/filters/common/lua/wrappers.cc +++ b/source/extensions/filters/common/lua/wrappers.cc @@ -68,6 +68,7 @@ int BufferWrapper::luaSetBytes(lua_State* state) { data_.drain(data_.length()); absl::string_view bytes = getStringViewFromLuaString(state, 2); data_.add(bytes); + headers_.setContentLength(data_.length()); lua_pushnumber(state, data_.length()); return 1; } diff --git a/source/extensions/filters/common/lua/wrappers.h b/source/extensions/filters/common/lua/wrappers.h index 344467d39ff2..391120bd3f45 100644 --- a/source/extensions/filters/common/lua/wrappers.h +++ b/source/extensions/filters/common/lua/wrappers.h @@ -16,7 +16,8 @@ namespace Lua { */ class BufferWrapper : public BaseLuaObject { public: - BufferWrapper(Buffer::Instance& data) : data_(data) {} + BufferWrapper(Http::RequestOrResponseHeaderMap& headers, Buffer::Instance& data) + : data_(data), headers_(headers) {} static ExportedFunctions exportedFunctions() { return {{"length", static_luaLength}, @@ -46,6 +47,7 @@ class BufferWrapper : public BaseLuaObject { DECLARE_LUA_FUNCTION(BufferWrapper, luaSetBytes); Buffer::Instance& data_; + Http::RequestOrResponseHeaderMap& headers_; }; class MetadataMapWrapper; diff --git a/source/extensions/filters/common/original_src/original_src_socket_option.h b/source/extensions/filters/common/original_src/original_src_socket_option.h index 8e86dc87d719..8e1ffc16b020 100644 --- a/source/extensions/filters/common/original_src/original_src_socket_option.h +++ b/source/extensions/filters/common/original_src/original_src_socket_option.h @@ -36,6 +36,7 @@ class OriginalSrcSocketOption : public Network::Socket::Option { absl::optional
getOptionDetails(const Network::Socket& socket, envoy::config::core::v3::SocketOption::SocketState state) const override; + bool isSupported() const override { return true; } private: Network::Address::InstanceConstSharedPtr src_address_; 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 3a01a8a7afab..27dcbb004e16 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/source/extensions/filters/common/rbac/utility.cc b/source/extensions/filters/common/rbac/utility.cc index 3e2697745cfa..a581de80131b 100644 --- a/source/extensions/filters/common/rbac/utility.cc +++ b/source/extensions/filters/common/rbac/utility.cc @@ -20,9 +20,7 @@ generateStats(const std::string& prefix, const std::string& shadow_prefix, Stats std::string responseDetail(const std::string& policy_id) { // Replace whitespaces in policy_id with '_' to avoid breaking the access log (inconsistent number // of segments between log entries when the separator is whitespace). - const absl::flat_hash_map replacement{ - {" ", "_"}, {"\t", "_"}, {"\f", "_"}, {"\v", "_"}, {"\n", "_"}, {"\r", "_"}}; - std::string sanitized = absl::StrReplaceAll(policy_id, replacement); + std::string sanitized = StringUtil::replaceAllEmptySpace(policy_id); return fmt::format("rbac_access_denied_matched_policy[{}]", sanitized); } diff --git a/source/extensions/filters/http/admission_control/admission_control.cc b/source/extensions/filters/http/admission_control/admission_control.cc index dd9a3579cd9a..1038e9361013 100644 --- a/source/extensions/filters/http/admission_control/admission_control.cc +++ b/source/extensions/filters/http/admission_control/admission_control.cc @@ -99,7 +99,7 @@ Http::FilterHeadersStatus AdmissionControlFilter::decodeHeaders(Http::RequestHea stats_.rq_rejected_.inc(); decoder_callbacks_->sendLocalReply(Http::Code::ServiceUnavailable, "", nullptr, absl::nullopt, - "denied by admission control"); + "denied_by_admission_control"); return Http::FilterHeadersStatus::StopIteration; } diff --git a/source/extensions/filters/http/alternate_protocols_cache/filter.cc b/source/extensions/filters/http/alternate_protocols_cache/filter.cc index 2d87b6fbef7a..663b79b3d3fd 100644 --- a/source/extensions/filters/http/alternate_protocols_cache/filter.cc +++ b/source/extensions/filters/http/alternate_protocols_cache/filter.cc @@ -64,7 +64,8 @@ Http::FilterHeadersStatus Filter::encodeHeaders(Http::ResponseHeaderMap& headers // Envoy routes request to upstream hosts not to origin servers directly. This choice would // allow HTTP/3 to be used on a per-upstream host basis, even for origins which are load // balanced across them. - Upstream::HostDescriptionConstSharedPtr host = encoder_callbacks_->streamInfo().upstreamHost(); + Upstream::HostDescriptionConstSharedPtr host = + encoder_callbacks_->streamInfo().upstreamInfo()->upstreamHost(); const uint32_t port = host->address()->ip()->port(); const std::string& hostname = host->hostname(); Http::AlternateProtocolsCache::Origin origin(Http::Headers::get().SchemeValues.Https, hostname, diff --git a/source/extensions/filters/http/aws_lambda/config.cc b/source/extensions/filters/http/aws_lambda/config.cc index a3b1838ff194..45a9cfe75d82 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 fd1ecabed322..942ba57aff1f 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 0d2e9bc97c1f..d9a1a5c74a7b 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/filters/http/bandwidth_limit/bandwidth_limit.cc b/source/extensions/filters/http/bandwidth_limit/bandwidth_limit.cc index 7da418578629..2e7178d5a26a 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 876ef673e39f..482fde9a05bf 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; @@ -108,7 +116,7 @@ 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/pass_through_filter.h b/source/extensions/filters/http/common/pass_through_filter.h index 027a73f8f155..5e657af5b5c4 100644 --- a/source/extensions/filters/http/common/pass_through_filter.h +++ b/source/extensions/filters/http/common/pass_through_filter.h @@ -36,7 +36,7 @@ class PassThroughEncoderFilter : public virtual StreamEncoderFilter { void onDestroy() override {} // Http::StreamEncoderFilter - Http::FilterHeadersStatus encode100ContinueHeaders(Http::ResponseHeaderMap&) override { + Http::FilterHeadersStatus encode1xxHeaders(Http::ResponseHeaderMap&) override { return Http::FilterHeadersStatus::Continue; } Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap&, bool) override { diff --git a/source/extensions/filters/http/common/stream_rate_limiter.cc b/source/extensions/filters/http/common/stream_rate_limiter.cc index 6763adbeb241..a4e215a5f1d0 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 b8aed9ac8a72..42b6c21015f9 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/composite/filter.cc b/source/extensions/filters/http/composite/filter.cc index 3cf7ba81dd52..93d094c07678 100644 --- a/source/extensions/filters/http/composite/filter.cc +++ b/source/extensions/filters/http/composite/filter.cc @@ -49,8 +49,8 @@ void Filter::decodeComplete() { } } -Http::FilterHeadersStatus Filter::encode100ContinueHeaders(Http::ResponseHeaderMap& headers) { - return delegateFilterActionOr(delegated_filter_, &StreamEncoderFilter::encode100ContinueHeaders, +Http::FilterHeadersStatus Filter::encode1xxHeaders(Http::ResponseHeaderMap& headers) { + return delegateFilterActionOr(delegated_filter_, &StreamEncoderFilter::encode1xxHeaders, Http::FilterHeadersStatus::Continue, headers); } @@ -147,8 +147,8 @@ void Filter::StreamFilterWrapper::decodeComplete() { } Http::FilterHeadersStatus -Filter::StreamFilterWrapper::encode100ContinueHeaders(Http::ResponseHeaderMap& headers) { - return delegateFilterActionOr(encoder_filter_, &StreamEncoderFilter::encode100ContinueHeaders, +Filter::StreamFilterWrapper::encode1xxHeaders(Http::ResponseHeaderMap& headers) { + return delegateFilterActionOr(encoder_filter_, &StreamEncoderFilter::encode1xxHeaders, Http::FilterHeadersStatus::Continue, headers); } Http::FilterHeadersStatus diff --git a/source/extensions/filters/http/composite/filter.h b/source/extensions/filters/http/composite/filter.h index a225653a0e6f..25ad35b172c6 100644 --- a/source/extensions/filters/http/composite/filter.h +++ b/source/extensions/filters/http/composite/filter.h @@ -44,7 +44,7 @@ class Filter : public Http::StreamFilter, void decodeComplete() 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; @@ -106,7 +106,7 @@ class Filter : public Http::StreamFilter, void decodeComplete() 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/cors/cors_filter.h b/source/extensions/filters/http/cors/cors_filter.h index 5bcef49783f0..6dab644befe8 100644 --- a/source/extensions/filters/http/cors/cors_filter.h +++ b/source/extensions/filters/http/cors/cors_filter.h @@ -61,7 +61,7 @@ class CorsFilter : public Http::StreamFilter { void setDecoderFilterCallbacks(Http::StreamDecoderFilterCallbacks& callbacks) override; // 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/dynamic_forward_proxy/proxy_filter.cc b/source/extensions/filters/http/dynamic_forward_proxy/proxy_filter.cc index a25f157ea9ea..9be370a251d3 100644 --- a/source/extensions/filters/http/dynamic_forward_proxy/proxy_filter.cc +++ b/source/extensions/filters/http/dynamic_forward_proxy/proxy_filter.cc @@ -12,15 +12,29 @@ 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"; }; +struct RcDetailsValues { + const std::string DnsCacheOverflow = "dns_cache_overflow"; + const std::string PendingRequestOverflow = "dynamic_forward_proxy_pending_request_overflow"; +}; + using CustomClusterType = envoy::config::cluster::v3::Cluster::CustomClusterType; using ResponseStrings = ConstSingleton; +using RcDetails = ConstSingleton; using LoadDnsCacheEntryStatus = Common::DynamicForwardProxy::DnsCache::LoadDnsCacheEntryStatus; @@ -77,7 +91,7 @@ Http::FilterHeadersStatus ProxyFilter::decodeHeaders(Http::RequestHeaderMap& hea ENVOY_STREAM_LOG(debug, "pending request overflow", *this->decoder_callbacks_); this->decoder_callbacks_->sendLocalReply( Http::Code::ServiceUnavailable, ResponseStrings::get().PendingRequestOverflow, nullptr, - absl::nullopt, ResponseStrings::get().PendingRequestOverflow); + absl::nullopt, RcDetails::get().PendingRequestOverflow); return Http::FilterHeadersStatus::StopIteration; } @@ -110,6 +124,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 +147,7 @@ Http::FilterHeadersStatus ProxyFilter::decodeHeaders(Http::RequestHeaderMap& hea addHostAddressToFilterState(host.value()->address()); } + latchTime(decoder_callbacks_, DNS_END); return Http::FilterHeadersStatus::Continue; } case LoadDnsCacheEntryStatus::Loading: @@ -143,7 +159,7 @@ Http::FilterHeadersStatus ProxyFilter::decodeHeaders(Http::RequestHeaderMap& hea ENVOY_STREAM_LOG(debug, "DNS cache overflow", *decoder_callbacks_); decoder_callbacks_->sendLocalReply(Http::Code::ServiceUnavailable, ResponseStrings::get().DnsCacheOverflow, nullptr, - absl::nullopt, ResponseStrings::get().DnsCacheOverflow); + absl::nullopt, RcDetails::get().DnsCacheOverflow); return Http::FilterHeadersStatus::StopIteration; } NOT_REACHED_GCOVR_EXCL_LINE; @@ -183,6 +199,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 a0920e553c70..0bc97e646d32 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/source/extensions/filters/http/dynamo/dynamo_filter.h b/source/extensions/filters/http/dynamo/dynamo_filter.h index c7249f7fcdd5..5984a15e7d84 100644 --- a/source/extensions/filters/http/dynamo/dynamo_filter.h +++ b/source/extensions/filters/http/dynamo/dynamo_filter.h @@ -42,7 +42,7 @@ class DynamoFilter : public Http::StreamFilter { } // Http::StreamEncoderFilter - Http::FilterHeadersStatus encode100ContinueHeaders(Http::ResponseHeaderMap&) override { + Http::FilterHeadersStatus encode1xxHeaders(Http::ResponseHeaderMap&) override { return Http::FilterHeadersStatus::Continue; } Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap&, bool) override; diff --git a/source/extensions/filters/http/ext_authz/ext_authz.cc b/source/extensions/filters/http/ext_authz/ext_authz.cc index bd6f7a14e569..9dbb86c01563 100644 --- a/source/extensions/filters/http/ext_authz/ext_authz.cc +++ b/source/extensions/filters/http/ext_authz/ext_authz.cc @@ -13,14 +13,6 @@ namespace Extensions { namespace HttpFilters { namespace ExtAuthz { -struct RcDetailsValues { - // The ext_authz filter denied the downstream request. - const std::string AuthzDenied = "ext_authz_denied"; - // The ext_authz filter encountered a failure, and was configured to fail-closed. - const std::string AuthzError = "ext_authz_error"; -}; -using RcDetails = ConstSingleton; - void FilterConfigPerRoute::merge(const FilterConfigPerRoute& other) { // We only merge context extensions here, and leave boolean flags untouched since those flags are // not used from the merged config. @@ -91,8 +83,9 @@ Http::FilterHeadersStatus Filter::decodeHeaders(Http::RequestHeaderMap& headers, *decoder_callbacks_); decoder_callbacks_->streamInfo().setResponseFlag( StreamInfo::ResponseFlag::UnauthorizedExternalService); - decoder_callbacks_->sendLocalReply(config_->statusOnError(), EMPTY_STRING, nullptr, - absl::nullopt, RcDetails::get().AuthzError); + decoder_callbacks_->sendLocalReply( + config_->statusOnError(), EMPTY_STRING, nullptr, absl::nullopt, + Filters::Common::ExtAuthz::ResponseCodeDetails::get().AuthzError); return Http::FilterHeadersStatus::StopIteration; } return Http::FilterHeadersStatus::Continue; @@ -154,7 +147,7 @@ Http::FilterTrailersStatus Filter::decodeTrailers(Http::RequestTrailerMap&) { return Http::FilterTrailersStatus::Continue; } -Http::FilterHeadersStatus Filter::encode100ContinueHeaders(Http::ResponseHeaderMap&) { +Http::FilterHeadersStatus Filter::encode1xxHeaders(Http::ResponseHeaderMap&) { return Http::FilterHeadersStatus::Continue; } @@ -353,6 +346,9 @@ void Filter::onComplete(Filters::Common::ExtAuthz::ResponsePtr&& response) { config_->httpContext().codeStats().chargeResponseStat(info, false); } + // setResponseFlag must be called before sendLocalReply + decoder_callbacks_->streamInfo().setResponseFlag( + StreamInfo::ResponseFlag::UnauthorizedExternalService); decoder_callbacks_->sendLocalReply( response->status_code, response->body, [&headers = response->headers_to_set, @@ -371,9 +367,7 @@ void Filter::onComplete(Filters::Common::ExtAuthz::ResponsePtr&& response) { response_headers.addCopy(header.first, header.second); } }, - absl::nullopt, RcDetails::get().AuthzDenied); - decoder_callbacks_->streamInfo().setResponseFlag( - StreamInfo::ResponseFlag::UnauthorizedExternalService); + absl::nullopt, Filters::Common::ExtAuthz::ResponseCodeDetails::get().AuthzDenied); break; } @@ -396,8 +390,9 @@ void Filter::onComplete(Filters::Common::ExtAuthz::ResponsePtr&& response) { *decoder_callbacks_, enumToInt(config_->statusOnError())); decoder_callbacks_->streamInfo().setResponseFlag( StreamInfo::ResponseFlag::UnauthorizedExternalService); - decoder_callbacks_->sendLocalReply(config_->statusOnError(), EMPTY_STRING, nullptr, - absl::nullopt, RcDetails::get().AuthzError); + decoder_callbacks_->sendLocalReply( + config_->statusOnError(), EMPTY_STRING, nullptr, absl::nullopt, + Filters::Common::ExtAuthz::ResponseCodeDetails::get().AuthzError); } break; } diff --git a/source/extensions/filters/http/ext_authz/ext_authz.h b/source/extensions/filters/http/ext_authz/ext_authz.h index cd75def13e92..80c934e48c90 100644 --- a/source/extensions/filters/http/ext_authz/ext_authz.h +++ b/source/extensions/filters/http/ext_authz/ext_authz.h @@ -257,7 +257,7 @@ class Filter : public 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/ext_proc/ext_proc.cc b/source/extensions/filters/http/ext_proc/ext_proc.cc index 1eca01fa4ab7..8068b1a93a7d 100644 --- a/source/extensions/filters/http/ext_proc/ext_proc.cc +++ b/source/extensions/filters/http/ext_proc/ext_proc.cc @@ -25,7 +25,7 @@ using Http::RequestTrailerMap; using Http::ResponseHeaderMap; using Http::ResponseTrailerMap; -static const std::string ErrorPrefix = "ext_proc error"; +static const std::string ErrorPrefix = "ext_proc_error"; static const int DefaultImmediateStatus = 200; static const std::string FilterName = "envoy.filters.http.ext_proc"; @@ -536,7 +536,7 @@ void Filter::onGrpcError(Grpc::Status::GrpcStatus status) { cleanUpTimers(); ImmediateResponse errorResponse; errorResponse.mutable_status()->set_code(envoy::type::v3::StatusCode::InternalServerError); - errorResponse.set_details(absl::StrFormat("%s: gRPC error %i", ErrorPrefix, status)); + errorResponse.set_details(absl::StrFormat("%s_gRPC_error_%i", ErrorPrefix, status)); sendImmediateResponse(errorResponse); } } @@ -572,7 +572,7 @@ void Filter::onMessageTimeout() { encoding_state_.setCallbackState(ProcessorState::CallbackState::Idle); ImmediateResponse errorResponse; errorResponse.mutable_status()->set_code(envoy::type::v3::StatusCode::InternalServerError); - errorResponse.set_details(absl::StrFormat("%s: per-message timeout exceeded", ErrorPrefix)); + errorResponse.set_details(absl::StrFormat("%s_per-message_timeout_exceeded", ErrorPrefix)); sendImmediateResponse(errorResponse); } } @@ -609,8 +609,9 @@ void Filter::sendImmediateResponse(const ImmediateResponse& response) { sent_immediate_response_ = true; ENVOY_LOG(debug, "Sending local reply with status code {}", status_code); + const auto details = StringUtil::replaceAllEmptySpace(response.details()); encoder_callbacks_->sendLocalReply(static_cast(status_code), response.body(), - mutate_headers, grpc_status, response.details()); + mutate_headers, grpc_status, details); } static ProcessingMode allDisabledMode() { diff --git a/source/extensions/filters/http/fault/fault_filter.cc b/source/extensions/filters/http/fault/fault_filter.cc index 7499e47dd5b8..809b396b2b0f 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/source/extensions/filters/http/fault/fault_filter.h b/source/extensions/filters/http/fault/fault_filter.h index f96756f16c13..1d8fea719ca4 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; namespace { -constexpr absl::string_view buffer_limits_runtime_feature = - "envoy.reloadable_features.grpc_json_transcoder_adhere_to_buffer_limits"; - const Http::LowerCaseString& trailerHeader() { CONSTRUCT_ON_FIRST_USE(Http::LowerCaseString, "trailer"); } @@ -215,6 +212,7 @@ JsonTranscoderConfig::JsonTranscoderConfig( NOT_REACHED_GCOVR_EXCL_LINE; } pmb.SetQueryParamUnescapePlus(proto_config.query_param_unescape_plus()); + pmb.SetMatchUnregisteredCustomVerb(proto_config.match_unregistered_custom_verb()); path_matcher_ = pmb.Build(); @@ -466,7 +464,8 @@ Http::FilterHeadersStatus JsonTranscoderFilter::decodeHeaders(Http::RequestHeade decoder_callbacks_->sendLocalReply( static_cast(http_code), status.message().ToString(), nullptr, absl::nullopt, absl::StrCat(RcDetails::get().GrpcTranscodeFailedEarly, "{", - MessageUtil::codeEnumToString(status.code()), "}")); + StringUtil::replaceAllEmptySpace(MessageUtil::codeEnumToString(status.code())), + "}")); return Http::FilterHeadersStatus::StopIteration; } @@ -746,7 +745,10 @@ bool JsonTranscoderFilter::checkIfTranscoderFailed(const std::string& details) { Http::Code::BadRequest, absl::string_view(request_status.message().data(), request_status.message().size()), nullptr, absl::nullopt, - absl::StrCat(details, "{", MessageUtil::codeEnumToString(request_status.code()), "}")); + absl::StrCat( + details, "{", + StringUtil::replaceAllEmptySpace(MessageUtil::codeEnumToString(request_status.code())), + "}")); return true; } @@ -893,10 +895,6 @@ bool JsonTranscoderFilter::maybeConvertGrpcStatus(Grpc::Status::GrpcStatus grpc_ } bool JsonTranscoderFilter::decoderBufferLimitReached(uint64_t buffer_length) { - if (!Runtime::runtimeFeatureEnabled(buffer_limits_runtime_feature)) { - return false; - } - if (buffer_length > decoder_callbacks_->decoderBufferLimit()) { ENVOY_LOG(debug, "Request rejected because the transcoder's internal buffer size exceeds the " @@ -915,10 +913,6 @@ bool JsonTranscoderFilter::decoderBufferLimitReached(uint64_t buffer_length) { } bool JsonTranscoderFilter::encoderBufferLimitReached(uint64_t buffer_length) { - if (!Runtime::runtimeFeatureEnabled(buffer_limits_runtime_feature)) { - return false; - } - if (buffer_length > encoder_callbacks_->encoderBufferLimit()) { ENVOY_LOG(debug, "Response not transcoded because the transcoder's internal buffer size exceeds the " diff --git a/source/extensions/filters/http/grpc_json_transcoder/json_transcoder_filter.h b/source/extensions/filters/http/grpc_json_transcoder/json_transcoder_filter.h index 95d71aeaada9..9906f3f83eba 100644 --- a/source/extensions/filters/http/grpc_json_transcoder/json_transcoder_filter.h +++ b/source/extensions/filters/http/grpc_json_transcoder/json_transcoder_filter.h @@ -153,7 +153,7 @@ class JsonTranscoderFilter : public Http::StreamFilter, public Logger::Loggable< void setDecoderFilterCallbacks(Http::StreamDecoderFilterCallbacks& callbacks) override; // 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/grpc_stats/BUILD b/source/extensions/filters/http/grpc_stats/BUILD index d3727239b710..d2836eea5524 100644 --- a/source/extensions/filters/http/grpc_stats/BUILD +++ b/source/extensions/filters/http/grpc_stats/BUILD @@ -22,6 +22,7 @@ envoy_cc_extension( "//source/common/grpc:common_lib", "//source/common/grpc:context_lib", "//source/common/runtime:runtime_lib", + "//source/common/stream_info:utility_lib", "//source/extensions/filters/http/common:factory_base_lib", "//source/extensions/filters/http/common:pass_through_filter_lib", "@envoy_api//envoy/extensions/filters/http/grpc_stats/v3:pkg_cc_proto", diff --git a/source/extensions/filters/http/grpc_stats/grpc_stats_filter.cc b/source/extensions/filters/http/grpc_stats/grpc_stats_filter.cc index 7280a419d21d..3e604ddf1f4f 100644 --- a/source/extensions/filters/http/grpc_stats/grpc_stats_filter.cc +++ b/source/extensions/filters/http/grpc_stats/grpc_stats_filter.cc @@ -9,6 +9,7 @@ #include "source/common/grpc/context_impl.h" #include "source/common/runtime/runtime_impl.h" #include "source/common/stats/symbol_table_impl.h" +#include "source/common/stream_info/utility.h" #include "source/extensions/filters/http/common/pass_through_filter.h" namespace Envoy { @@ -254,13 +255,16 @@ class GrpcStatsFilter : public Http::PassThroughFilter { } void maybeChargeUpstreamStat() { - if (config_->enable_upstream_stats_ && - decoder_callbacks_->streamInfo().lastUpstreamTxByteSent().has_value() && - decoder_callbacks_->streamInfo().lastUpstreamRxByteReceived().has_value()) { + if (!config_->enable_upstream_stats_) { + return; + } + StreamInfo::TimingUtility timing(decoder_callbacks_->streamInfo()); + if (config_->enable_upstream_stats_ && timing.lastUpstreamTxByteSent().has_value() && + timing.lastUpstreamRxByteReceived().has_value()) { std::chrono::milliseconds chrono_duration = std::chrono::duration_cast( - decoder_callbacks_->streamInfo().lastUpstreamRxByteReceived().value() - - decoder_callbacks_->streamInfo().lastUpstreamTxByteSent().value()); + timing.lastUpstreamRxByteReceived().value() - + timing.lastUpstreamTxByteSent().value()); config_->context_.chargeUpstreamStat(*cluster_, request_names_, chrono_duration); } } diff --git a/source/extensions/filters/http/grpc_web/grpc_web_filter.cc b/source/extensions/filters/http/grpc_web/grpc_web_filter.cc index ca474514a450..734fe8cf12cc 100644 --- a/source/extensions/filters/http/grpc_web/grpc_web_filter.cc +++ b/source/extensions/filters/http/grpc_web/grpc_web_filter.cc @@ -13,7 +13,6 @@ #include "source/common/grpc/context_impl.h" #include "source/common/http/headers.h" #include "source/common/http/utility.h" -#include "source/common/runtime/runtime_features.h" namespace Envoy { namespace Extensions { @@ -112,11 +111,8 @@ bool GrpcWebFilter::hasProtoEncodedGrpcWebContentType( // If response headers do not contain valid response headers, it needs transformation. bool GrpcWebFilter::needsTransformationForNonProtoEncodedResponse(Http::ResponseHeaderMap& headers, bool end_stream) const { - return Runtime::runtimeFeatureEnabled( - "envoy.reloadable_features.grpc_web_fix_non_proto_encoded_response_handling") && - // We transform the response unless it is already a gRPC or proto-encoded gRPC-Web - // response. - !Grpc::Common::isGrpcResponseHeaders(headers, end_stream) && + // We transform the response unless it is already a gRPC or proto-encoded gRPC-Web response. + return !Grpc::Common::isGrpcResponseHeaders(headers, end_stream) && !isProtoEncodedGrpcWebResponseHeaders(headers); } diff --git a/source/extensions/filters/http/grpc_web/grpc_web_filter.h b/source/extensions/filters/http/grpc_web/grpc_web_filter.h index 11c3a75a374d..fabad0f214ed 100644 --- a/source/extensions/filters/http/grpc_web/grpc_web_filter.h +++ b/source/extensions/filters/http/grpc_web/grpc_web_filter.h @@ -35,7 +35,7 @@ class GrpcWebFilter : public Http::StreamFilter, NonCopyable { } // Implements StreamEncoderFilter. - Http::FilterHeadersStatus encode100ContinueHeaders(Http::ResponseHeaderMap&) override { + Http::FilterHeadersStatus encode1xxHeaders(Http::ResponseHeaderMap&) override { return Http::FilterHeadersStatus::Continue; } Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap&, bool) override; diff --git a/source/extensions/filters/http/header_to_metadata/header_to_metadata_filter.h b/source/extensions/filters/http/header_to_metadata/header_to_metadata_filter.h index b135f412bca8..97b12f114c27 100644 --- a/source/extensions/filters/http/header_to_metadata/header_to_metadata_filter.h +++ b/source/extensions/filters/http/header_to_metadata/header_to_metadata_filter.h @@ -151,7 +151,7 @@ class HeaderToMetadataFilter : public Http::StreamFilter, void setDecoderFilterCallbacks(Http::StreamDecoderFilterCallbacks& callbacks) override; // StreamEncoderFilter - Http::FilterHeadersStatus encode100ContinueHeaders(Http::ResponseHeaderMap&) override { + Http::FilterHeadersStatus encode1xxHeaders(Http::ResponseHeaderMap&) override { return Http::FilterHeadersStatus::Continue; } Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap& headers, bool) override; diff --git a/source/extensions/filters/http/health_check/health_check.h b/source/extensions/filters/http/health_check/health_check.h index 674ed59af58e..99ebb4b2e17e 100644 --- a/source/extensions/filters/http/health_check/health_check.h +++ b/source/extensions/filters/http/health_check/health_check.h @@ -81,7 +81,7 @@ class HealthCheckFilter : public Http::StreamFilter { } // 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/jwt_authn/filter.cc b/source/extensions/filters/http/jwt_authn/filter.cc index 768e58e73d35..89e343ac9c0e 100644 --- a/source/extensions/filters/http/jwt_authn/filter.cc +++ b/source/extensions/filters/http/jwt_authn/filter.cc @@ -33,8 +33,8 @@ constexpr absl::string_view kRcDetailJwtAuthnPrefix = "jwt_authn_access_denied"; std::string generateRcDetails(absl::string_view error_msg) { // Replace space with underscore since RCDetails may be written to access log. // Some log processors assume each log segment is separated by whitespace. - return absl::StrCat(kRcDetailJwtAuthnPrefix, "{", - absl::StrJoin(absl::StrSplit(error_msg, ' '), "_"), "}"); + return absl::StrCat(kRcDetailJwtAuthnPrefix, "{", StringUtil::replaceAllEmptySpace(error_msg), + "}"); } } // namespace diff --git a/source/extensions/filters/http/kill_request/kill_request_filter.h b/source/extensions/filters/http/kill_request/kill_request_filter.h index b97c66fa14b1..2458621cf88e 100644 --- a/source/extensions/filters/http/kill_request/kill_request_filter.h +++ b/source/extensions/filters/http/kill_request/kill_request_filter.h @@ -58,7 +58,7 @@ class KillRequestFilter : public Http::StreamFilter, Logger::Loggable wrapper( - Filters::Common::Lua::BufferWrapper::create(coroutine_.luaState(), data), true); + Filters::Common::Lua::BufferWrapper::create(coroutine_.luaState(), headers_, data), true); state_ = State::Running; coroutine_.resume(1, yield_callback_); } else if (state_ == State::WaitForBody && end_stream_) { @@ -458,9 +459,10 @@ int StreamHandleWrapper::luaBody(lua_State* state) { callbacks_.addData(body); } - body_wrapper_.reset(Filters::Common::Lua::BufferWrapper::create( - state, const_cast(*callbacks_.bufferedBody())), - true); + body_wrapper_.reset( + Filters::Common::Lua::BufferWrapper::create( + state, headers_, const_cast(*callbacks_.bufferedBody())), + true); } return 1; } @@ -720,11 +722,10 @@ void Filter::onDestroy() { } } -Http::FilterHeadersStatus Filter::doHeaders(StreamHandleRef& handle, - Filters::Common::Lua::CoroutinePtr& coroutine, - FilterCallbacks& callbacks, int function_ref, - PerLuaCodeSetup* setup, Http::HeaderMap& headers, - bool end_stream) { +Http::FilterHeadersStatus +Filter::doHeaders(StreamHandleRef& handle, Filters::Common::Lua::CoroutinePtr& coroutine, + FilterCallbacks& callbacks, int function_ref, PerLuaCodeSetup* setup, + Http::RequestOrResponseHeaderMap& headers, bool end_stream) { if (function_ref == LUA_REFNIL) { return Http::FilterHeadersStatus::Continue; } diff --git a/source/extensions/filters/http/lua/lua_filter.h b/source/extensions/filters/http/lua/lua_filter.h index 17c933862796..c67e4f74560e 100644 --- a/source/extensions/filters/http/lua/lua_filter.h +++ b/source/extensions/filters/http/lua/lua_filter.h @@ -133,9 +133,9 @@ class StreamHandleWrapper : public Filters::Common::Lua::BaseLuaObject { } // Http::StreamEncoderFilter - Http::FilterHeadersStatus encode100ContinueHeaders(Http::ResponseHeaderMap&) override { + Http::FilterHeadersStatus encode1xxHeaders(Http::ResponseHeaderMap&) override { return Http::FilterHeadersStatus::Continue; } Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap& headers, @@ -538,8 +538,8 @@ class Filter : public Http::StreamFilter, Logger::Loggable { Http::FilterHeadersStatus doHeaders(StreamHandleRef& handle, Filters::Common::Lua::CoroutinePtr& coroutine, FilterCallbacks& callbacks, int function_ref, - PerLuaCodeSetup* setup, Http::HeaderMap& headers, - bool end_stream); + PerLuaCodeSetup* setup, + Http::RequestOrResponseHeaderMap& headers, bool end_stream); Http::FilterDataStatus doData(StreamHandleRef& handle, Buffer::Instance& data, bool end_stream); Http::FilterTrailersStatus doTrailers(StreamHandleRef& handle, Http::HeaderMap& trailers); diff --git a/source/extensions/filters/http/oauth2/filter.cc b/source/extensions/filters/http/oauth2/filter.cc index 4f1def409668..446e3394de77 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 5b26f62b6446..06838a5b65ff 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 709668c90344..f93087b9dcc6 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 24335ff6c390..c597e1f1dd34 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 09d305dcbef4..48645dce7b99 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/source/extensions/filters/http/ratelimit/ratelimit.cc b/source/extensions/filters/http/ratelimit/ratelimit.cc index c007471d7779..869801a04e1e 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; } @@ -200,13 +200,13 @@ void Filter::complete(Filters::Common::RateLimit::LimitStatus status, if (status == Filters::Common::RateLimit::LimitStatus::OverLimit && config_->runtime().snapshot().featureEnabled("ratelimit.http_filter_enforcing", 100)) { state_ = State::Responded; + callbacks_->streamInfo().setResponseFlag(StreamInfo::ResponseFlag::RateLimited); callbacks_->sendLocalReply( Http::Code::TooManyRequests, response_body, [this](Http::HeaderMap& headers) { populateResponseHeaders(headers, /*from_local_reply=*/true); }, config_->rateLimitedGrpcStatus(), RcDetails::get().RateLimited); - callbacks_->streamInfo().setResponseFlag(StreamInfo::ResponseFlag::RateLimited); } else if (status == Filters::Common::RateLimit::LimitStatus::Error) { if (config_->failureModeAllow()) { cluster_->statsScope().counterFromStatName(stat_names.failure_mode_allowed_).inc(); @@ -216,9 +216,9 @@ void Filter::complete(Filters::Common::RateLimit::LimitStatus status, } } else { state_ = State::Responded; + callbacks_->streamInfo().setResponseFlag(StreamInfo::ResponseFlag::RateLimitServiceError); callbacks_->sendLocalReply(Http::Code::InternalServerError, response_body, nullptr, absl::nullopt, RcDetails::get().RateLimitError); - callbacks_->streamInfo().setResponseFlag(StreamInfo::ResponseFlag::RateLimitServiceError); } } else if (!initiating_call_) { appendRequestHeaders(req_headers_to_add); diff --git a/source/extensions/filters/http/ratelimit/ratelimit.h b/source/extensions/filters/http/ratelimit/ratelimit.h index bee2d04ff2e0..07dc086fd1a3 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 a6ea1a5059f2..1414b077cd6e 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/source/extensions/filters/listener/http_inspector/http_inspector.cc b/source/extensions/filters/listener/http_inspector/http_inspector.cc index 59b28e1febcb..8f77cbcf771e 100644 --- a/source/extensions/filters/listener/http_inspector/http_inspector.cc +++ b/source/extensions/filters/listener/http_inspector/http_inspector.cc @@ -61,10 +61,6 @@ Network::FilterStatus Filter::onAccept(Network::ListenerFilterCallbacks& cb) { cb.dispatcher(), [this](uint32_t events) { ENVOY_LOG(trace, "http inspector event: {}", events); - // inspector is always peeking and can never determine EOF. - // Use this event type to avoid listener timeout on the OS supporting - // FileReadyType::Closed. - bool end_stream = events & Event::FileReadyType::Closed; const ParseState parse_state = onRead(); switch (parse_state) { @@ -78,19 +74,11 @@ Network::FilterStatus Filter::onAccept(Network::ListenerFilterCallbacks& cb) { cb_->continueFilterChain(true); break; case ParseState::Continue: - if (end_stream) { - // Parser fails to determine http but the end of stream is reached. Fallback to - // non-http. - done(false); - cb_->socket().ioHandle().resetFileEvents(); - cb_->continueFilterChain(true); - } // do nothing but wait for the next event break; } }, - Event::PlatformDefaultTriggerType, - Event::FileReadyType::Read | Event::FileReadyType::Closed); + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); return Network::FilterStatus::StopIteration; } NOT_REACHED_GCOVR_EXCL_LINE; @@ -107,6 +95,11 @@ ParseState Filter::onRead() { return ParseState::Error; } + // Remote closed + if (result.return_value_ == 0) { + return ParseState::Error; + } + const auto parse_state = parseHttpHeader(absl::string_view(reinterpret_cast(buf_), result.return_value_)); switch (parse_state) { diff --git a/source/extensions/filters/listener/original_dst/BUILD b/source/extensions/filters/listener/original_dst/BUILD index 59207733fd74..4a3d275588e2 100644 --- a/source/extensions/filters/listener/original_dst/BUILD +++ b/source/extensions/filters/listener/original_dst/BUILD @@ -29,10 +29,6 @@ envoy_cc_library( envoy_cc_extension( name = "config", srcs = ["config.cc"], - # TODO(#9953) clean up. - extra_visibility = [ - "//test/integration:__subpackages__", - ], deps = [ ":original_dst_lib", "//envoy/registry", diff --git a/source/extensions/filters/listener/proxy_protocol/BUILD b/source/extensions/filters/listener/proxy_protocol/BUILD index 404e11118676..eaca916f34c3 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/source/extensions/filters/listener/tls_inspector/BUILD b/source/extensions/filters/listener/tls_inspector/BUILD index cc137ea1a5cc..a540cc428504 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 a53a448ac309..9fad2181fc1a 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 fb0f06e6c2d5..2924b8ae5ee0 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; @@ -93,12 +104,6 @@ Network::FilterStatus Filter::onAccept(Network::ListenerFilterCallbacks& cb) { socket.ioHandle().initializeFileEvent( cb.dispatcher(), [this](uint32_t events) { - if (events & Event::FileReadyType::Closed) { - config_->stats().connection_closed_.inc(); - done(false); - return; - } - ASSERT(events == Event::FileReadyType::Read); ParseState parse_state = onRead(); switch (parse_state) { @@ -113,8 +118,7 @@ Network::FilterStatus Filter::onAccept(Network::ListenerFilterCallbacks& cb) { break; } }, - Event::PlatformDefaultTriggerType, - Event::FileReadyType::Read | Event::FileReadyType::Closed); + Event::PlatformDefaultTriggerType, Event::FileReadyType::Read); return Network::FilterStatus::StopIteration; } NOT_REACHED_GCOVR_EXCL_LINE; @@ -176,6 +180,11 @@ ParseState Filter::onRead() { return ParseState::Error; } + if (result.return_value_ == 0) { + config_->stats().connection_closed_.inc(); + return ParseState::Error; + } + // Because we're doing a MSG_PEEK, data we've seen before gets returned every time, so // skip over what we've already processed. if (static_cast(result.return_value_) > read_) { @@ -235,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 f7adb93076e5..0e32ef2517c9 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( parent_.stats().request_time_ms_, parent.timeSystem())), - request_id_(-1), stream_id_(parent.randomGenerator().random()), + stream_id_(parent.randomGenerator().random()), stream_info_(parent.timeSystem(), parent_.connection().connectionInfoProviderSharedPtr()), pending_stream_decoded_(false), local_response_sent_(false) { parent_.stats().request_active_.inc(); @@ -346,7 +346,6 @@ FilterStatus ActiveMessage::applyEncoderFilters(ActiveMessageEncoderFilter* filt void ActiveMessage::sendLocalReply(const DubboFilters::DirectResponse& response, bool end_stream) { ASSERT(metadata_); - metadata_->setRequestId(request_id_); parent_.sendLocalReply(*metadata_, response, end_stream); if (end_stream) { diff --git a/source/extensions/filters/network/dubbo_proxy/active_message.h b/source/extensions/filters/network/dubbo_proxy/active_message.h index 5e860be3ddc2..c310e85b8ab2 100644 --- a/source/extensions/filters/network/dubbo_proxy/active_message.h +++ b/source/extensions/filters/network/dubbo_proxy/active_message.h @@ -202,8 +202,6 @@ class ActiveMessage : public LinkedObject, std::list encoder_filters_; std::function encoder_filter_action_; - int32_t request_id_; - // This value is used in the calculation of the weighted cluster. uint64_t stream_id_; StreamInfo::StreamInfoImpl stream_info_; diff --git a/source/extensions/filters/network/echo/BUILD b/source/extensions/filters/network/echo/BUILD index e0a91ea30b2c..67db7f18a967 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/source/extensions/filters/network/ext_authz/ext_authz.cc b/source/extensions/filters/network/ext_authz/ext_authz.cc index b59a2bf04a4c..4f9da9175fc1 100644 --- a/source/extensions/filters/network/ext_authz/ext_authz.cc +++ b/source/extensions/filters/network/ext_authz/ext_authz.cc @@ -94,6 +94,12 @@ void Filter::onComplete(Filters::Common::ExtAuthz::ResponsePtr&& response) { !config_->failureModeAllow())) { config_->stats().cx_closed_.inc(); filter_callbacks_->connection().close(Network::ConnectionCloseType::NoFlush); + filter_callbacks_->connection().streamInfo().setResponseFlag( + StreamInfo::ResponseFlag::UnauthorizedExternalService); + filter_callbacks_->connection().streamInfo().setResponseCodeDetails( + response->status == Filters::Common::ExtAuthz::CheckStatus::Denied + ? Filters::Common::ExtAuthz::ResponseCodeDetails::get().AuthzDenied + : Filters::Common::ExtAuthz::ResponseCodeDetails::get().AuthzError); } else { // Let the filter chain continue. filter_return_ = FilterReturn::Continue; diff --git a/source/extensions/filters/network/http_connection_manager/config.h b/source/extensions/filters/network/http_connection_manager/config.h index e480599fefd9..3464659a14c0 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/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 000000000000..810944a4ac04 --- /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 000000000000..f7fde9203170 --- /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 000000000000..08777ae395a1 --- /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 000000000000..bf2b3409a7c4 --- /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 000000000000..3578943d52fb --- /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/source/extensions/filters/network/thrift_proxy/header_transport_impl.cc b/source/extensions/filters/network/thrift_proxy/header_transport_impl.cc index cbfb42b0dc08..978b7fb9c00a 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 de44db1948b2..9e8b7ae3f26e 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/source/extensions/filters/network/thrift_proxy/router/upstream_request.cc b/source/extensions/filters/network/thrift_proxy/router/upstream_request.cc index b1cededafe95..6947a9856d0e 100644 --- a/source/extensions/filters/network/thrift_proxy/router/upstream_request.cc +++ b/source/extensions/filters/network/thrift_proxy/router/upstream_request.cc @@ -270,7 +270,7 @@ void UpstreamRequest::onResetStream(ConnectionPool::PoolFailureReason reason) { stats_.incResponseLocalException(parent_.cluster()); parent_.sendLocalReply(AppException(AppExceptionType::InternalError, "thrift upstream request: too many connections"), - true); + false /* Don't close the downstream connection. */); break; case ConnectionPool::PoolFailureReason::LocalConnectionFailure: upstream_host_->outlierDetector().putResult( diff --git a/source/extensions/filters/udp/dns_filter/BUILD b/source/extensions/filters/udp/dns_filter/BUILD index dff84e520c8a..b6f2e60d1002 100644 --- a/source/extensions/filters/udp/dns_filter/BUILD +++ b/source/extensions/filters/udp/dns_filter/BUILD @@ -39,6 +39,7 @@ envoy_cc_library( "//source/common/config:datasource_lib", "//source/common/network:address_lib", "//source/common/network:utility_lib", + "//source/common/network/dns_resolver:dns_factory_util_lib", "//source/common/protobuf:message_validator_lib", "//source/common/runtime:runtime_lib", "//source/common/upstream:cluster_manager_lib", diff --git a/source/extensions/filters/udp/dns_filter/dns_filter.cc b/source/extensions/filters/udp/dns_filter/dns_filter.cc index 30991185aff9..f93343b4c7dd 100644 --- a/source/extensions/filters/udp/dns_filter/dns_filter.cc +++ b/source/extensions/filters/udp/dns_filter/dns_filter.cc @@ -5,7 +5,7 @@ #include "source/common/config/datasource.h" #include "source/common/network/address_impl.h" -#include "source/common/network/dns_resolver/dns_factory.h" +#include "source/common/network/dns_resolver/dns_factory_util.h" #include "source/common/protobuf/message_validator_impl.h" #include "source/extensions/filters/udp/dns_filter/dns_filter_utils.h" diff --git a/source/extensions/filters/udp/dns_filter/dns_filter_resolver.cc b/source/extensions/filters/udp/dns_filter/dns_filter_resolver.cc index 76160c497434..0caee4defb8c 100644 --- a/source/extensions/filters/udp/dns_filter/dns_filter_resolver.cc +++ b/source/extensions/filters/udp/dns_filter/dns_filter_resolver.cc @@ -90,10 +90,12 @@ void DnsFilterResolver::resolveExternalQuery(DnsQueryContextPtr context, if (status == Network::DnsResolver::ResolutionStatus::Success) { ctx.resolved_hosts.reserve(response.size()); for (const auto& resp : response) { - ASSERT(resp.address_ != nullptr); + const auto& addrinfo = resp.addrInfo(); + ASSERT(addrinfo.address_ != nullptr); ENVOY_LOG(trace, "Resolved address: {} for {}", - resp.address_->ip()->addressAsString(), ctx.query_rec->name_); - ctx.resolved_hosts.emplace_back(std::move(resp.address_)); + addrinfo.address_->ip()->addressAsString(), + ctx.query_rec->name_); + ctx.resolved_hosts.emplace_back(std::move(addrinfo.address_)); } } // Invoke the filter callback notifying it of resolved addresses diff --git a/source/extensions/filters/udp/dns_filter/dns_filter_resolver.h b/source/extensions/filters/udp/dns_filter/dns_filter_resolver.h index 65dca46aa2f4..4dbd364b59b3 100644 --- a/source/extensions/filters/udp/dns_filter/dns_filter_resolver.h +++ b/source/extensions/filters/udp/dns_filter/dns_filter_resolver.h @@ -3,7 +3,7 @@ #include "envoy/event/dispatcher.h" #include "envoy/network/dns.h" -#include "source/common/network/dns_resolver/dns_factory.h" +#include "source/common/network/dns_resolver/dns_factory_util.h" #include "source/extensions/filters/udp/dns_filter/dns_parser.h" namespace Envoy { diff --git a/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.cc b/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.cc index 9be3f1003a37..d67e52e6b410 100644 --- a/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.cc +++ b/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.cc @@ -27,8 +27,13 @@ void UdpProxyFilter::onClusterAddOrUpdate(Upstream::ThreadLocalCluster& cluster) } ENVOY_LOG(debug, "udp proxy: attaching to cluster {}", cluster.info()->name()); - ASSERT(cluster_info_ == absl::nullopt || &cluster_info_.value().cluster_ != &cluster); - cluster_info_.emplace(*this, cluster); + ASSERT(cluster_info_ == absl::nullopt || &cluster_info_.value()->cluster_ != &cluster); + + if (config_->usingPerPacketLoadBalancing()) { + cluster_info_.emplace(std::make_unique(*this, cluster)); + } else { + cluster_info_.emplace(std::make_unique(*this, cluster)); + } } void UdpProxyFilter::onClusterRemoval(const std::string& cluster) { @@ -46,7 +51,7 @@ Network::FilterStatus UdpProxyFilter::onData(Network::UdpRecvData& data) { return Network::FilterStatus::StopIteration; } - return cluster_info_.value().onData(data); + return cluster_info_.value()->onData(data); } Network::FilterStatus UdpProxyFilter::onReceiveError(Api::IoError::IoErrorCode) { @@ -56,9 +61,10 @@ Network::FilterStatus UdpProxyFilter::onReceiveError(Api::IoError::IoErrorCode) } UdpProxyFilter::ClusterInfo::ClusterInfo(UdpProxyFilter& filter, - Upstream::ThreadLocalCluster& cluster) + Upstream::ThreadLocalCluster& cluster, + SessionStorageType&& sessions) : filter_(filter), cluster_(cluster), - cluster_stats_(generateStats(cluster.info()->statsScope())), + cluster_stats_(generateStats(cluster.info()->statsScope())), sessions_(std::move(sessions)), member_update_cb_handle_(cluster.prioritySet().addMemberUpdateCb( [this](const Upstream::HostVector&, const Upstream::HostVector& hosts_removed) { for (const auto& host : hosts_removed) { @@ -85,36 +91,84 @@ UdpProxyFilter::ClusterInfo::~ClusterInfo() { ASSERT(host_to_sessions_.empty()); } -Network::FilterStatus UdpProxyFilter::ClusterInfo::onData(Network::UdpRecvData& data) { +void UdpProxyFilter::ClusterInfo::removeSession(const ActiveSession* session) { + // First remove from the host to sessions map. + ASSERT(host_to_sessions_[&session->host()].count(session) == 1); + auto host_sessions_it = host_to_sessions_.find(&session->host()); + host_sessions_it->second.erase(session); + if (host_sessions_it->second.empty()) { + host_to_sessions_.erase(host_sessions_it); + } + + // Now remove it from the primary map. + ASSERT(sessions_.count(session) == 1); + sessions_.erase(session); +} + +UdpProxyFilter::ActiveSession* +UdpProxyFilter::ClusterInfo::createSession(Network::UdpRecvData::LocalPeerAddresses&& addresses, + const Upstream::HostConstSharedPtr& optional_host) { + if (!cluster_.info() + ->resourceManager(Upstream::ResourcePriority::Default) + .connections() + .canCreate()) { + ENVOY_LOG(debug, "cannot create new connection."); + cluster_.info()->stats().upstream_cx_overflow_.inc(); + return nullptr; + } + + if (optional_host) { + return createSessionWithHost(std::move(addresses), optional_host); + } + + auto host = chooseHost(addresses.peer_); + if (host == nullptr) { + ENVOY_LOG(debug, "cannot find any valid host."); + cluster_.info()->stats().upstream_cx_none_healthy_.inc(); + return nullptr; + } + return createSessionWithHost(std::move(addresses), host); +} + +UdpProxyFilter::ActiveSession* UdpProxyFilter::ClusterInfo::createSessionWithHost( + Network::UdpRecvData::LocalPeerAddresses&& addresses, + const Upstream::HostConstSharedPtr& host) { + ASSERT(host); + auto new_session = std::make_unique(*this, std::move(addresses), host); + auto new_session_ptr = new_session.get(); + sessions_.emplace(std::move(new_session)); + host_to_sessions_[host.get()].emplace(new_session_ptr); + return new_session_ptr; +} + +Upstream::HostConstSharedPtr UdpProxyFilter::ClusterInfo::chooseHost( + const Network::Address::InstanceConstSharedPtr& peer_address) const { + UdpLoadBalancerContext context(filter_.config_->hashPolicy(), peer_address); + Upstream::HostConstSharedPtr host = cluster_.loadBalancer().chooseHost(&context); + return host; +} + +UdpProxyFilter::StickySessionClusterInfo::StickySessionClusterInfo( + UdpProxyFilter& filter, Upstream::ThreadLocalCluster& cluster) + : ClusterInfo(filter, cluster, + SessionStorageType(1, HeterogeneousActiveSessionHash(false), + HeterogeneousActiveSessionEqual(false))) {} + +Network::FilterStatus UdpProxyFilter::StickySessionClusterInfo::onData(Network::UdpRecvData& data) { const auto active_session_it = sessions_.find(data.addresses_); ActiveSession* active_session; if (active_session_it == sessions_.end()) { - if (!cluster_.info() - ->resourceManager(Upstream::ResourcePriority::Default) - .connections() - .canCreate()) { - cluster_.info()->stats().upstream_cx_overflow_.inc(); + active_session = createSession(std::move(data.addresses_)); + if (active_session == nullptr) { return Network::FilterStatus::StopIteration; } - - UdpLoadBalancerContext context(filter_.config_->hashPolicy(), data.addresses_.peer_); - Upstream::HostConstSharedPtr host = cluster_.loadBalancer().chooseHost(&context); - if (host == nullptr) { - ENVOY_LOG(debug, "cannot find any valid host. failed to create a session."); - cluster_.info()->stats().upstream_cx_none_healthy_.inc(); - return Network::FilterStatus::StopIteration; - } - - active_session = createSession(std::move(data.addresses_), host); } else { active_session = active_session_it->get(); if (active_session->host().health() == Upstream::Host::Health::Unhealthy) { // If a host becomes unhealthy, we optimally would like to replace it with a new session // to a healthy host. We may eventually want to make this behavior configurable, but for now // this will be the universal behavior. - - UdpLoadBalancerContext context(filter_.config_->hashPolicy(), data.addresses_.peer_); - Upstream::HostConstSharedPtr host = cluster_.loadBalancer().chooseHost(&context); + auto host = chooseHost(data.addresses_.peer_); if (host != nullptr && host->health() != Upstream::Host::Health::Unhealthy && host.get() != &active_session->host()) { ENVOY_LOG(debug, "upstream session unhealthy, recreating the session"); @@ -132,28 +186,40 @@ Network::FilterStatus UdpProxyFilter::ClusterInfo::onData(Network::UdpRecvData& return Network::FilterStatus::StopIteration; } -UdpProxyFilter::ActiveSession* -UdpProxyFilter::ClusterInfo::createSession(Network::UdpRecvData::LocalPeerAddresses&& addresses, - const Upstream::HostConstSharedPtr& host) { - auto new_session = std::make_unique(*this, std::move(addresses), host); - auto new_session_ptr = new_session.get(); - sessions_.emplace(std::move(new_session)); - host_to_sessions_[host.get()].emplace(new_session_ptr); - return new_session_ptr; -} +UdpProxyFilter::PerPacketLoadBalancingClusterInfo::PerPacketLoadBalancingClusterInfo( + UdpProxyFilter& filter, Upstream::ThreadLocalCluster& cluster) + : ClusterInfo(filter, cluster, + SessionStorageType(1, HeterogeneousActiveSessionHash(true), + HeterogeneousActiveSessionEqual(true))) {} + +Network::FilterStatus +UdpProxyFilter::PerPacketLoadBalancingClusterInfo::onData(Network::UdpRecvData& data) { + auto host = chooseHost(data.addresses_.peer_); + if (host == nullptr) { + ENVOY_LOG(debug, "cannot find any valid host."); + cluster_.info()->stats().upstream_cx_none_healthy_.inc(); + return Network::FilterStatus::StopIteration; + } -void UdpProxyFilter::ClusterInfo::removeSession(const ActiveSession* session) { - // First remove from the host to sessions map. - ASSERT(host_to_sessions_[&session->host()].count(session) == 1); - auto host_sessions_it = host_to_sessions_.find(&session->host()); - host_sessions_it->second.erase(session); - if (host_sessions_it->second.empty()) { - host_to_sessions_.erase(host_sessions_it); + ENVOY_LOG(debug, "selected {} host as upstream.", host->address()->asStringView()); + + LocalPeerHostAddresses key{data.addresses_, *host}; + const auto active_session_it = sessions_.find(key); + ActiveSession* active_session; + if (active_session_it == sessions_.end()) { + active_session = createSession(std::move(data.addresses_), host); + if (active_session == nullptr) { + return Network::FilterStatus::StopIteration; + } + } else { + active_session = active_session_it->get(); + ENVOY_LOG(trace, "found already existing session on host {}.", + active_session->host().address()->asStringView()); } - // Now remove it from the primary map. - ASSERT(sessions_.count(session) == 1); - sessions_.erase(session); + active_session->write(*data.buffer_); + + return Network::FilterStatus::StopIteration; } UdpProxyFilter::ActiveSession::ActiveSession(ClusterInfo& cluster, diff --git a/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.h b/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.h index 727192c55e96..99c9708543e5 100644 --- a/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.h +++ b/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.h @@ -70,6 +70,7 @@ class UdpProxyFilterConfig { : cluster_manager_(cluster_manager), time_source_(time_source), cluster_(config.cluster()), session_timeout_(PROTOBUF_GET_MS_OR_DEFAULT(config, idle_timeout, 60 * 1000)), use_original_src_ip_(config.use_original_src_ip()), + use_per_packet_load_balancing_(config.use_per_packet_load_balancing()), stats_(generateStats(config.stat_prefix(), root_scope)), // Default prefer_gro to true for upstream client traffic. upstream_socket_config_(config.upstream_socket_config(), true) { @@ -87,6 +88,7 @@ class UdpProxyFilterConfig { Upstream::ClusterManager& clusterManager() const { return cluster_manager_; } std::chrono::milliseconds sessionTimeout() const { return session_timeout_; } bool usingOriginalSrcIp() const { return use_original_src_ip_; } + bool usingPerPacketLoadBalancing() const { return use_per_packet_load_balancing_; } const Udp::HashPolicy* hashPolicy() const { return hash_policy_.get(); } UdpProxyDownstreamStats& stats() const { return stats_; } TimeSource& timeSource() const { return time_source_; } @@ -107,6 +109,7 @@ class UdpProxyFilterConfig { const std::string cluster_; const std::chrono::milliseconds session_timeout_; const bool use_original_src_ip_; + const bool use_per_packet_load_balancing_; std::unique_ptr hash_policy_; mutable UdpProxyDownstreamStats stats_; const Network::ResolvedUdpSocketConfig upstream_socket_config_; @@ -200,6 +203,11 @@ class UdpProxyFilter : public Network::UdpListenerReadFilter, using ActiveSessionPtr = std::unique_ptr; + struct LocalPeerHostAddresses { + const Network::UdpRecvData::LocalPeerAddresses& local_peer_addresses_; + const Upstream::Host& host_; + }; + struct HeterogeneousActiveSessionHash { // Specifying is_transparent indicates to the library infrastructure that // type-conversions should not be applied when calling find(), but instead @@ -210,31 +218,52 @@ class UdpProxyFilter : public Network::UdpListenerReadFilter, // using it in the context of absl. using is_transparent = void; // NOLINT(readability-identifier-naming) + HeterogeneousActiveSessionHash(const bool consider_host) : consider_host_(consider_host) {} + size_t operator()(const Network::UdpRecvData::LocalPeerAddresses& value) const { return absl::Hash()(value); } - size_t operator()(const ActiveSessionPtr& value) const { - return absl::Hash()(value->addresses()); + size_t operator()(const LocalPeerHostAddresses& value) const { + auto hash = this->operator()(value.local_peer_addresses_); + if (consider_host_) { + hash = absl::HashOf(hash, value.host_.address()->asStringView()); + } + return hash; } size_t operator()(const ActiveSession* value) const { - return absl::Hash()(value->addresses()); + LocalPeerHostAddresses key{value->addresses(), value->host()}; + return this->operator()(key); } + size_t operator()(const ActiveSessionPtr& value) const { return this->operator()(value.get()); } + + private: + const bool consider_host_; }; struct HeterogeneousActiveSessionEqual { // See description for HeterogeneousActiveSessionHash::is_transparent. using is_transparent = void; // NOLINT(readability-identifier-naming) + HeterogeneousActiveSessionEqual(const bool consider_host) : consider_host_(consider_host) {} + bool operator()(const ActiveSessionPtr& lhs, const Network::UdpRecvData::LocalPeerAddresses& rhs) const { return lhs->addresses() == rhs; } - bool operator()(const ActiveSessionPtr& lhs, const ActiveSessionPtr& rhs) const { - return lhs->addresses() == rhs->addresses(); + bool operator()(const ActiveSessionPtr& lhs, const LocalPeerHostAddresses& rhs) const { + return this->operator()(lhs, rhs.local_peer_addresses_) && + (consider_host_ ? &lhs->host() == &rhs.host_ : true); } bool operator()(const ActiveSessionPtr& lhs, const ActiveSession* rhs) const { - return lhs->addresses() == rhs->addresses(); + LocalPeerHostAddresses key{rhs->addresses(), rhs->host()}; + return this->operator()(lhs, key); } + bool operator()(const ActiveSessionPtr& lhs, const ActiveSessionPtr& rhs) const { + return this->operator()(lhs, rhs.get()); + } + + private: + const bool consider_host_; }; /** @@ -242,36 +271,69 @@ class UdpProxyFilter : public Network::UdpListenerReadFilter, * we will very likely support different types of routing to multiple upstream clusters. */ class ClusterInfo { + protected: + using SessionStorageType = absl::flat_hash_set; + public: - ClusterInfo(UdpProxyFilter& filter, Upstream::ThreadLocalCluster& cluster); - ~ClusterInfo(); - Network::FilterStatus onData(Network::UdpRecvData& data); + ClusterInfo(UdpProxyFilter& filter, Upstream::ThreadLocalCluster& cluster, + SessionStorageType&& sessions); + virtual ~ClusterInfo(); + virtual Network::FilterStatus onData(Network::UdpRecvData& data) PURE; void removeSession(const ActiveSession* session); UdpProxyFilter& filter_; Upstream::ThreadLocalCluster& cluster_; UdpProxyUpstreamStats cluster_stats_; - private: + protected: ActiveSession* createSession(Network::UdpRecvData::LocalPeerAddresses&& addresses, - const Upstream::HostConstSharedPtr& host); + const Upstream::HostConstSharedPtr& optional_host = nullptr); + Upstream::HostConstSharedPtr + chooseHost(const Network::Address::InstanceConstSharedPtr& peer_address) const; + + SessionStorageType sessions_; + + private: static UdpProxyUpstreamStats generateStats(Stats::Scope& scope) { const auto final_prefix = "udp"; return {ALL_UDP_PROXY_UPSTREAM_STATS(POOL_COUNTER_PREFIX(scope, final_prefix))}; } + ActiveSession* createSessionWithHost(Network::UdpRecvData::LocalPeerAddresses&& addresses, + const Upstream::HostConstSharedPtr& host); Envoy::Common::CallbackHandlePtr member_update_cb_handle_; - absl::flat_hash_set - sessions_; absl::flat_hash_map> host_to_sessions_; }; + using ClusterInfoPtr = std::unique_ptr; + + /** + * Performs forwarding and replying data to one upstream host, selected when the first datagram + * for a session is received. If the upstream host becomes unhealthy, a new one is selected. + */ + class StickySessionClusterInfo : public ClusterInfo { + public: + StickySessionClusterInfo(UdpProxyFilter& filter, Upstream::ThreadLocalCluster& cluster); + Network::FilterStatus onData(Network::UdpRecvData& data) override; + }; + + /** + * On each data chunk selects another host using underlying load balancing method and communicates + * with that host. + */ + class PerPacketLoadBalancingClusterInfo : public ClusterInfo { + public: + PerPacketLoadBalancingClusterInfo(UdpProxyFilter& filter, + Upstream::ThreadLocalCluster& cluster); + Network::FilterStatus onData(Network::UdpRecvData& data) override; + }; + virtual Network::SocketPtr createSocket(const Upstream::HostConstSharedPtr& host) { // Virtual so this can be overridden in unit tests. return std::make_unique(Network::Socket::Type::Datagram, host->address(), - nullptr); + nullptr, Network::SocketCreationOptions{}); } // Upstream::ClusterUpdateCallbacks @@ -283,7 +345,7 @@ class UdpProxyFilter : public Network::UdpListenerReadFilter, // Right now we support a single cluster to route to. It is highly likely in the future that // we will support additional routing options either using filter chain matching, weighting, // etc. - absl::optional cluster_info_; + absl::optional cluster_info_; }; } // namespace UdpProxy diff --git a/source/extensions/grpc_credentials/aws_iam/config.cc b/source/extensions/grpc_credentials/aws_iam/config.cc index c70140a437cb..6253e6cc5cbc 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/source/extensions/http/header_formatters/preserve_case/preserve_case_formatter.cc b/source/extensions/http/header_formatters/preserve_case/preserve_case_formatter.cc index a27b35478f1e..5e1ee269286d 100644 --- a/source/extensions/http/header_formatters/preserve_case/preserve_case_formatter.cc +++ b/source/extensions/http/header_formatters/preserve_case/preserve_case_formatter.cc @@ -4,12 +4,17 @@ #include "envoy/extensions/http/header_formatters/preserve_case/v3/preserve_case.pb.validate.h" #include "envoy/registry/registry.h" +#include "source/common/protobuf/message_validator_impl.h" + namespace Envoy { namespace Extensions { namespace Http { namespace HeaderFormatters { namespace PreserveCase { +PreserveCaseHeaderFormatter::PreserveCaseHeaderFormatter(const bool forward_reason_phrase) + : forward_reason_phrase_(forward_reason_phrase) {} + std::string PreserveCaseHeaderFormatter::format(absl::string_view key) const { const auto remembered_key_itr = original_header_keys_.find(key); // TODO(mattklein123): We can avoid string copies here if the formatter interface allowed us @@ -34,12 +39,28 @@ void PreserveCaseHeaderFormatter::processKey(absl::string_view key) { original_header_keys_.emplace(key); } +void PreserveCaseHeaderFormatter::setReasonPhrase(absl::string_view reason_phrase) { + if (forward_reason_phrase_) { + reason_phrase_ = std::string(reason_phrase); + } +}; + +absl::string_view PreserveCaseHeaderFormatter::getReasonPhrase() const { + return absl::string_view(reason_phrase_); +}; + class PreserveCaseFormatterFactory : public Envoy::Http::StatefulHeaderKeyFormatterFactory { public: + PreserveCaseFormatterFactory(const bool forward_reason_phrase) + : forward_reason_phrase_(forward_reason_phrase) {} + // Envoy::Http::StatefulHeaderKeyFormatterFactory Envoy::Http::StatefulHeaderKeyFormatterPtr create() override { - return std::make_unique(); + return std::make_unique(forward_reason_phrase_); } + +private: + const bool forward_reason_phrase_; }; class PreserveCaseFormatterFactoryConfig @@ -47,10 +68,17 @@ class PreserveCaseFormatterFactoryConfig public: // Envoy::Http::StatefulHeaderKeyFormatterFactoryConfig std::string name() const override { return "preserve_case"; } + Envoy::Http::StatefulHeaderKeyFormatterFactorySharedPtr - createFromProto(const Protobuf::Message&) override { - return std::make_shared(); + createFromProto(const Protobuf::Message& message) override { + auto config = + MessageUtil::downcastAndValidate( + message, ProtobufMessage::getStrictValidationVisitor()); + + return std::make_shared(config.forward_reason_phrase()); } + ProtobufTypes::MessagePtr createEmptyConfigProto() override { return std::make_unique(); diff --git a/source/extensions/http/header_formatters/preserve_case/preserve_case_formatter.h b/source/extensions/http/header_formatters/preserve_case/preserve_case_formatter.h index d2e3f7da00ff..07ef7747d039 100644 --- a/source/extensions/http/header_formatters/preserve_case/preserve_case_formatter.h +++ b/source/extensions/http/header_formatters/preserve_case/preserve_case_formatter.h @@ -1,5 +1,6 @@ #pragma once +#include "envoy/extensions/http/header_formatters/preserve_case/v3/preserve_case.pb.h" #include "envoy/http/header_formatter.h" #include "source/common/common/utility.h" @@ -13,11 +14,17 @@ namespace PreserveCase { class PreserveCaseHeaderFormatter : public Envoy::Http::StatefulHeaderKeyFormatter { public: // Envoy::Http::StatefulHeaderKeyFormatter + PreserveCaseHeaderFormatter(const bool forward_reason_phrase); + std::string format(absl::string_view key) const override; void processKey(absl::string_view key) override; + void setReasonPhrase(absl::string_view reason_phrase) override; + absl::string_view getReasonPhrase() const override; private: StringUtil::CaseUnorderedSet original_header_keys_; + bool forward_reason_phrase_{false}; + std::string reason_phrase_; }; } // namespace PreserveCase diff --git a/source/extensions/io_socket/user_space/file_event_impl.cc b/source/extensions/io_socket/user_space/file_event_impl.cc index dcb89c659529..32c0f0a63843 100644 --- a/source/extensions/io_socket/user_space/file_event_impl.cc +++ b/source/extensions/io_socket/user_space/file_event_impl.cc @@ -57,8 +57,8 @@ void FileEventImpl::setEnabled(uint32_t events) { } ENVOY_LOG( trace, - "User space file event {} set enabled events {} and events {} is active. Will {} reschedule.", - static_cast(this), events, was_enabled ? "not " : ""); + "User space file event {} set enabled events {} and events {} is active. Will {}reschedule.", + static_cast(this), events, events_to_notify, was_enabled ? "not " : ""); } void FileEventImpl::activateIfEnabled(uint32_t events) { diff --git a/source/extensions/io_socket/user_space/io_handle_impl.cc b/source/extensions/io_socket/user_space/io_handle_impl.cc index 55f425ec542c..042a659b3fff 100644 --- a/source/extensions/io_socket/user_space/io_handle_impl.cc +++ b/source/extensions/io_socket/user_space/io_handle_impl.cc @@ -79,6 +79,10 @@ Api::IoCallUint64Result IoHandleImpl::close() { ENVOY_LOG(trace, "socket {} close after peer closed.", static_cast(this)); } } + if (user_file_event_) { + // No event callback should be handled after close completes. + user_file_event_.reset(); + } closed_ = true; return Api::ioCallUint64ResultNoError(); } @@ -150,7 +154,7 @@ Api::IoCallUint64Result IoHandleImpl::writev(const Buffer::RawSlice* slices, uin } if (is_input_empty) { return Api::ioCallUint64ResultNoError(); - }; + } if (!isOpen()) { return {0, Api::IoErrorPtr(new Network::IoSocketError(SOCKET_ERROR_BADF), Network::IoSocketError::deleteIoError)}; diff --git a/source/extensions/network/dns_resolver/apple/BUILD b/source/extensions/network/dns_resolver/apple/BUILD index c318b9d89b96..d8873ce661a6 100644 --- a/source/extensions/network/dns_resolver/apple/BUILD +++ b/source/extensions/network/dns_resolver/apple/BUILD @@ -29,7 +29,7 @@ envoy_cc_extension( "//source/common/common:linked_object", "//source/common/network:address_lib", "//source/common/network:utility_lib", - "//source/common/network/dns_resolver:dns_factory_lib", + "//source/common/network/dns_resolver:dns_factory_util_lib", "//source/common/singleton:threadsafe_singleton", ], ) diff --git a/source/extensions/network/dns_resolver/apple/apple_dns_impl.cc b/source/extensions/network/dns_resolver/apple/apple_dns_impl.cc index 72a2c408f2ec..be54536d00c5 100644 --- a/source/extensions/network/dns_resolver/apple/apple_dns_impl.cc +++ b/source/extensions/network/dns_resolver/apple/apple_dns_impl.cc @@ -15,7 +15,7 @@ #include "source/common/common/assert.h" #include "source/common/common/fmt.h" #include "source/common/network/address_impl.h" -#include "source/common/network/dns_resolver/dns_factory.h" +#include "source/common/network/dns_resolver/dns_factory_util.h" #include "source/common/network/utility.h" #include "absl/strings/str_join.h" @@ -141,7 +141,8 @@ AppleDnsResolverImpl::PendingResolution::PendingResolution(AppleDnsResolverImpl& const std::string& dns_name, DnsLookupFamily dns_lookup_family) : parent_(parent), callback_(callback), dispatcher_(dispatcher), dns_name_(dns_name), - pending_cb_({ResolutionStatus::Success, {}, {}}), dns_lookup_family_(dns_lookup_family) {} + pending_response_({ResolutionStatus::Success, {}, {}, {}}), + dns_lookup_family_(dns_lookup_family) {} AppleDnsResolverImpl::PendingResolution::~PendingResolution() { ENVOY_LOG(debug, "Destroying PendingResolution for {}", dns_name_); @@ -183,7 +184,7 @@ void AppleDnsResolverImpl::PendingResolution::onEventCallback(uint32_t events) { // Similar to receiving an error in onDNSServiceGetAddrInfoReply, an error while processing fd // events indicates that the sd_ref state is broken. // Therefore, finish resolving with an error. - pending_cb_.status_ = ResolutionStatus::Failure; + pending_response_.status_ = ResolutionStatus::Failure; finishResolve(); } } @@ -191,29 +192,39 @@ void AppleDnsResolverImpl::PendingResolution::onEventCallback(uint32_t events) { std::list& AppleDnsResolverImpl::PendingResolution::finalAddressList() { switch (dns_lookup_family_) { case DnsLookupFamily::V4Only: - return pending_cb_.v4_responses_; + return pending_response_.v4_responses_; case DnsLookupFamily::V6Only: - return pending_cb_.v6_responses_; + return pending_response_.v6_responses_; case DnsLookupFamily::Auto: // Per API docs only give v4 if v6 is not available. - if (pending_cb_.v6_responses_.empty()) { - return pending_cb_.v4_responses_; + if (pending_response_.v6_responses_.empty()) { + return pending_response_.v4_responses_; } - return pending_cb_.v6_responses_; + return pending_response_.v6_responses_; case DnsLookupFamily::V4Preferred: // Per API docs only give v6 if v4 is not available. - if (pending_cb_.v4_responses_.empty()) { - return pending_cb_.v6_responses_; + if (pending_response_.v4_responses_.empty()) { + return pending_response_.v6_responses_; } - return pending_cb_.v4_responses_; + return pending_response_.v4_responses_; + case DnsLookupFamily::All: + ASSERT(pending_response_.all_responses_.empty()); + pending_response_.all_responses_.insert(pending_response_.all_responses_.end(), + pending_response_.v4_responses_.begin(), + pending_response_.v4_responses_.end()); + pending_response_.all_responses_.insert(pending_response_.all_responses_.end(), + pending_response_.v6_responses_.begin(), + pending_response_.v6_responses_.end()); + return pending_response_.all_responses_; } NOT_REACHED_GCOVR_EXCL_LINE; } void AppleDnsResolverImpl::PendingResolution::finishResolve() { ENVOY_LOG_EVENT(debug, "apple_dns_resolution_complete", - "dns resolution for {} completed with status {}", dns_name_, pending_cb_.status_); - callback_(pending_cb_.status_, std::move(finalAddressList())); + "dns resolution for {} completed with status {}", dns_name_, + pending_response_.status_); + callback_(pending_response_.status_, std::move(finalAddressList())); if (owned_) { ENVOY_LOG(debug, "Resolution for {} completed (async)", dns_name_); @@ -235,6 +246,7 @@ DNSServiceErrorType AppleDnsResolverImpl::PendingResolution::dnsServiceGetAddrIn break; case DnsLookupFamily::Auto: case DnsLookupFamily::V4Preferred: + case DnsLookupFamily::All: /* We want to make sure we don't get any address that is not routable. Passing 0 * to apple's `DNSServiceGetAddrInfo` will make a best attempt to filter out IPv6 * or IPv4 addresses depending on what's routable, per Apple's documentation: @@ -291,9 +303,9 @@ void AppleDnsResolverImpl::PendingResolution::onDNSServiceGetAddrInfoReply( if (error_code != kDNSServiceErr_NoError) { parent_.chargeGetAddrInfoErrorStats(error_code); - pending_cb_.status_ = ResolutionStatus::Failure; - pending_cb_.v4_responses_.clear(); - pending_cb_.v6_responses_.clear(); + pending_response_.status_ = ResolutionStatus::Failure; + pending_response_.v4_responses_.clear(); + pending_response_.v6_responses_.clear(); finishResolve(); // Note: Nothing can follow this call to flushPendingQueries due to deletion of this @@ -308,12 +320,12 @@ void AppleDnsResolverImpl::PendingResolution::onDNSServiceGetAddrInfoReply( ASSERT(address, "invalid to add null address"); auto dns_response = buildDnsResponse(address, ttl); ENVOY_LOG(debug, "Address to add address={}, ttl={}", - dns_response.address_->ip()->addressAsString(), ttl); - if (dns_response.address_->ip()->ipv4()) { - pending_cb_.v4_responses_.push_back(dns_response); + dns_response.addrInfo().address_->ip()->addressAsString(), ttl); + if (dns_response.addrInfo().address_->ip()->ipv4()) { + pending_response_.v4_responses_.push_back(dns_response); } else { - ASSERT(dns_response.address_->ip()->ipv6()); - pending_cb_.v6_responses_.push_back(dns_response); + ASSERT(dns_response.addrInfo().address_->ip()->ipv6()); + pending_response_.v6_responses_.push_back(dns_response); } } diff --git a/source/extensions/network/dns_resolver/apple/apple_dns_impl.h b/source/extensions/network/dns_resolver/apple/apple_dns_impl.h index 214a60bc01b4..421d14fe29e7 100644 --- a/source/extensions/network/dns_resolver/apple/apple_dns_impl.h +++ b/source/extensions/network/dns_resolver/apple/apple_dns_impl.h @@ -109,10 +109,11 @@ class AppleDnsResolverImpl : public DnsResolver, protected Logger::Loggable v4_responses_; std::list v6_responses_; + std::list all_responses_; }; AppleDnsResolverImpl& parent_; @@ -128,7 +129,7 @@ class AppleDnsResolverImpl : public DnsResolver, protected Logger::Loggable address_list; - ResolutionStatus resolution_status; if (status == ARES_SUCCESS) { - resolution_status = ResolutionStatus::Success; + pending_response_.status_ = ResolutionStatus::Success; + if (addrinfo != nullptr && addrinfo->nodes != nullptr) { if (addrinfo->nodes->ai_family == AF_INET) { for (const ares_addrinfo_node* ai = addrinfo->nodes; ai != nullptr; ai = ai->ai_next) { @@ -146,7 +150,7 @@ void DnsResolverImpl::PendingResolution::onAresGetAddrInfoCallback(int status, i address.sin_port = 0; address.sin_addr = reinterpret_cast(ai->ai_addr)->sin_addr; - address_list.emplace_back( + pending_response_.address_list_.emplace_back( DnsResponse(std::make_shared(&address), std::chrono::seconds(ai->ai_ttl))); } @@ -157,21 +161,19 @@ void DnsResolverImpl::PendingResolution::onAresGetAddrInfoCallback(int status, i address.sin6_family = AF_INET6; address.sin6_port = 0; address.sin6_addr = reinterpret_cast(ai->ai_addr)->sin6_addr; - address_list.emplace_back( + pending_response_.address_list_.emplace_back( DnsResponse(std::make_shared(address), std::chrono::seconds(ai->ai_ttl))); } } } - if (!address_list.empty()) { + if (!pending_response_.address_list_.empty() && dns_lookup_family_ != DnsLookupFamily::All) { completed_ = true; } ASSERT(addrinfo != nullptr); ares_freeaddrinfo(addrinfo); - } else { - resolution_status = ResolutionStatus::Failure; } if (timeouts > 0) { @@ -179,45 +181,22 @@ void DnsResolverImpl::PendingResolution::onAresGetAddrInfoCallback(int status, i } if (completed_) { - if (!cancelled_) { - // Use a raw try here because it is used in both main thread and filter. - // Can not convert to use status code as there may be unexpected exceptions in server fuzz - // tests, which must be handled. Potential exception may come from getAddressWithPort() or - // portFromTcpUrl(). - // TODO(chaoqin-li1123): remove try catch pattern here once we figure how to handle unexpected - // exception in fuzz tests. - ENVOY_LOG_EVENT(debug, "cares_dns_resolution_complete", - "dns resolution for {} completed with status {}", dns_name_, - resolution_status); - - TRY_NEEDS_AUDIT { callback_(resolution_status, std::move(address_list)); } - catch (const EnvoyException& e) { - ENVOY_LOG(critical, "EnvoyException in c-ares callback: {}", e.what()); - dispatcher_.post([s = std::string(e.what())] { throw EnvoyException(s); }); - } - catch (const std::exception& e) { - ENVOY_LOG(critical, "std::exception in c-ares callback: {}", e.what()); - dispatcher_.post([s = std::string(e.what())] { throw EnvoyException(s); }); - } - catch (...) { - ENVOY_LOG(critical, "Unknown exception in c-ares callback"); - dispatcher_.post([] { throw EnvoyException("unknown"); }); - } - } - if (owned_) { - delete this; - return; - } + finishResolve(); + // Nothing can follow a call to finishResolve due to the deletion of this object upon + // finishResolve(). + return; } - if (!completed_ && fallback_if_failed_) { - fallback_if_failed_ = false; + if (dual_resolution_) { + dual_resolution_ = false; + // Perform a second lookup for DnsLookupFamily::Auto and DnsLookupFamily::V4Preferred, given + // that the first lookup failed to return any addresses. Note that DnsLookupFamily::All issues + // both lookups concurrently so there is no need to fire a second lookup here. if (dns_lookup_family_ == DnsLookupFamily::Auto) { - getAddrInfo(AF_INET); - } else { - ASSERT(dns_lookup_family_ == DnsLookupFamily::V4Preferred); - getAddrInfo(AF_INET6); + startResolutionImpl(AF_INET); + } else if (dns_lookup_family_ == DnsLookupFamily::V4Preferred) { + startResolutionImpl(AF_INET6); } // Note: Nothing can follow this call to getAddrInfo due to deletion of this @@ -226,6 +205,40 @@ void DnsResolverImpl::PendingResolution::onAresGetAddrInfoCallback(int status, i } } +void DnsResolverImpl::PendingResolution::finishResolve() { + if (!cancelled_) { + // Use a raw try here because it is used in both main thread and filter. + // Can not convert to use status code as there may be unexpected exceptions in server fuzz + // tests, which must be handled. Potential exception may come from getAddressWithPort() or + // portFromTcpUrl(). + // TODO(chaoqin-li1123): remove try catch pattern here once we figure how to handle unexpected + // exception in fuzz tests. + ENVOY_LOG_EVENT(debug, "cares_dns_resolution_complete", + "dns resolution for {} completed with status {}", dns_name_, + pending_response_.status_); + + TRY_NEEDS_AUDIT { + callback_(pending_response_.status_, std::move(pending_response_.address_list_)); + } + catch (const EnvoyException& e) { + ENVOY_LOG(critical, "EnvoyException in c-ares callback: {}", e.what()); + dispatcher_.post([s = std::string(e.what())] { throw EnvoyException(s); }); + } + catch (const std::exception& e) { + ENVOY_LOG(critical, "std::exception in c-ares callback: {}", e.what()); + dispatcher_.post([s = std::string(e.what())] { throw EnvoyException(s); }); + } + catch (...) { + ENVOY_LOG(critical, "Unknown exception in c-ares callback"); + dispatcher_.post([] { throw EnvoyException("unknown"); }); + } + } + if (owned_) { + delete this; + return; + } +} + void DnsResolverImpl::updateAresTimer() { // Update the timeout for events. timeval timeout; @@ -283,19 +296,9 @@ ActiveDnsQuery* DnsResolverImpl::resolve(const std::string& dns_name, initializeChannel(&options.options_, options.optmask_); } - auto pending_resolution = std::make_unique( + auto pending_resolution = std::make_unique( *this, callback, dispatcher_, channel_, dns_name, dns_lookup_family); - if (dns_lookup_family == DnsLookupFamily::Auto || - dns_lookup_family == DnsLookupFamily::V4Preferred) { - pending_resolution->fallback_if_failed_ = true; - } - - if (dns_lookup_family == DnsLookupFamily::V4Only || - dns_lookup_family == DnsLookupFamily::V4Preferred) { - pending_resolution->getAddrInfo(AF_INET); - } else { - pending_resolution->getAddrInfo(AF_INET6); - } + pending_resolution->startResolution(); if (pending_resolution->completed_) { // Resolution does not need asynchronous behavior or network events. For @@ -313,7 +316,46 @@ ActiveDnsQuery* DnsResolverImpl::resolve(const std::string& dns_name, } } -void DnsResolverImpl::PendingResolution::getAddrInfo(int family) { +DnsResolverImpl::AddrInfoPendingResolution::AddrInfoPendingResolution( + DnsResolverImpl& parent, ResolveCb callback, Event::Dispatcher& dispatcher, + ares_channel channel, const std::string& dns_name, DnsLookupFamily dns_lookup_family) + : PendingResolution(parent, callback, dispatcher, channel, dns_name), + dns_lookup_family_(dns_lookup_family) { + if (dns_lookup_family == DnsLookupFamily::Auto || + dns_lookup_family == DnsLookupFamily::V4Preferred || + dns_lookup_family == DnsLookupFamily::All) { + dual_resolution_ = true; + } + + switch (dns_lookup_family_) { + case DnsLookupFamily::V4Only: + case DnsLookupFamily::V4Preferred: + family_ = AF_INET; + break; + case DnsLookupFamily::V6Only: + case DnsLookupFamily::Auto: + family_ = AF_INET6; + break; + // NOTE: DnsLookupFamily::All performs both lookups concurrently as addresses from both families + // are being requested. + case DnsLookupFamily::All: + lookup_all_ = true; + break; + default: + NOT_REACHED_GCOVR_EXCL_LINE; + } +} + +void DnsResolverImpl::AddrInfoPendingResolution::startResolution() { + if (lookup_all_) { + startResolutionImpl(AF_INET); + startResolutionImpl(AF_INET6); + } else { + startResolutionImpl(family_); + } +} + +void DnsResolverImpl::AddrInfoPendingResolution::startResolutionImpl(int family) { struct ares_addrinfo_hints hints = {}; hints.ai_family = family; @@ -326,7 +368,8 @@ void DnsResolverImpl::PendingResolution::getAddrInfo(int family) { ares_getaddrinfo( channel_, dns_name_.c_str(), /* service */ nullptr, &hints, [](void* arg, int status, int timeouts, ares_addrinfo* addrinfo) { - static_cast(arg)->onAresGetAddrInfoCallback(status, timeouts, addrinfo); + static_cast(arg)->onAresGetAddrInfoCallback(status, timeouts, + addrinfo); }, this); } diff --git a/source/extensions/network/dns_resolver/cares/dns_impl.h b/source/extensions/network/dns_resolver/cares/dns_impl.h index 19532bbf18bb..4950e3ba1272 100644 --- a/source/extensions/network/dns_resolver/cares/dns_impl.h +++ b/source/extensions/network/dns_resolver/cares/dns_impl.h @@ -38,51 +38,82 @@ class DnsResolverImpl : public DnsResolver, protected Logger::Loggable address_list_; + }; + + // Note: pending_response_ is constructed with ResolutionStatus::Failure by default and + // __only__ changed to ResolutionStatus::Success if there is an ARES_SUCCESS reply. + // In the dual_resolution case __any__ ARES_SUCCESS reply will result in a + // ResolutionStatus::Success callback. + PendingResponse pending_response_{ResolutionStatus::Failure, {}}; + }; + + class AddrInfoPendingResolution final : public PendingResolution { + public: + AddrInfoPendingResolution(DnsResolverImpl& parent, ResolveCb callback, + Event::Dispatcher& dispatcher, ares_channel channel, + const std::string& dns_name, DnsLookupFamily dns_lookup_family); + + /** + * ares_getaddrinfo query callback. + * @param status return status of call to ares_getaddrinfo. + * @param timeouts the number of times the request timed out. + * @param addrinfo structure to store address info. + */ + void onAresGetAddrInfoCallback(int status, int timeouts, ares_addrinfo* addrinfo); + + /** + * wrapper function of call to ares_getaddrinfo. + */ + void startResolution(); + + private: + void startResolutionImpl(int family); + + // Perform a second resolution under certain conditions. If dns_lookup_family_ is V4Preferred + // or Auto: perform a second resolution if the first one fails. If dns_lookup_family_ is All: + // perform resolutions on both families concurrently. + bool dual_resolution_ = false; + // Whether or not to lookup both V4 and V6 address. + bool lookup_all_ = false; + int family_ = AF_INET; const DnsLookupFamily dns_lookup_family_; }; diff --git a/source/extensions/resource_monitors/fixed_heap/fixed_heap_monitor.cc b/source/extensions/resource_monitors/fixed_heap/fixed_heap_monitor.cc index 106c44620b68..6060adab4c97 100644 --- a/source/extensions/resource_monitors/fixed_heap/fixed_heap_monitor.cc +++ b/source/extensions/resource_monitors/fixed_heap/fixed_heap_monitor.cc @@ -21,7 +21,7 @@ FixedHeapMonitor::FixedHeapMonitor( ASSERT(max_heap_ > 0); } -void FixedHeapMonitor::updateResourceUsage(Server::ResourceMonitor::Callbacks& callbacks) { +void FixedHeapMonitor::updateResourceUsage(Server::ResourceUpdateCallbacks& callbacks) { const size_t physical = stats_->reservedHeapBytes(); const size_t unmapped = stats_->unmappedHeapBytes(); ASSERT(physical >= unmapped); diff --git a/source/extensions/resource_monitors/fixed_heap/fixed_heap_monitor.h b/source/extensions/resource_monitors/fixed_heap/fixed_heap_monitor.h index a54162ebb31f..84ed32c80e79 100644 --- a/source/extensions/resource_monitors/fixed_heap/fixed_heap_monitor.h +++ b/source/extensions/resource_monitors/fixed_heap/fixed_heap_monitor.h @@ -31,7 +31,7 @@ class FixedHeapMonitor : public Server::ResourceMonitor { const envoy::extensions::resource_monitors::fixed_heap::v3::FixedHeapConfig& config, std::unique_ptr stats = std::make_unique()); - void updateResourceUsage(Server::ResourceMonitor::Callbacks& callbacks) override; + void updateResourceUsage(Server::ResourceUpdateCallbacks& callbacks) override; private: const uint64_t max_heap_; diff --git a/source/extensions/resource_monitors/injected_resource/injected_resource_monitor.cc b/source/extensions/resource_monitors/injected_resource/injected_resource_monitor.cc index d6797ac85ec2..a2799ac6ff15 100644 --- a/source/extensions/resource_monitors/injected_resource/injected_resource_monitor.cc +++ b/source/extensions/resource_monitors/injected_resource/injected_resource_monitor.cc @@ -23,7 +23,7 @@ InjectedResourceMonitor::InjectedResourceMonitor( void InjectedResourceMonitor::onFileChanged() { file_changed_ = true; } -void InjectedResourceMonitor::updateResourceUsage(Server::ResourceMonitor::Callbacks& callbacks) { +void InjectedResourceMonitor::updateResourceUsage(Server::ResourceUpdateCallbacks& callbacks) { if (file_changed_) { file_changed_ = false; TRY_ASSERT_MAIN_THREAD { diff --git a/source/extensions/resource_monitors/injected_resource/injected_resource_monitor.h b/source/extensions/resource_monitors/injected_resource/injected_resource_monitor.h index 86210c112a07..59bf32375c56 100644 --- a/source/extensions/resource_monitors/injected_resource/injected_resource_monitor.h +++ b/source/extensions/resource_monitors/injected_resource/injected_resource_monitor.h @@ -25,7 +25,7 @@ class InjectedResourceMonitor : public Server::ResourceMonitor { Server::Configuration::ResourceMonitorFactoryContext& context); // Server::ResourceMonitor - void updateResourceUsage(Server::ResourceMonitor::Callbacks& callbacks) override; + void updateResourceUsage(Server::ResourceUpdateCallbacks& callbacks) override; protected: virtual void onFileChanged(); diff --git a/source/extensions/stat_sinks/common/statsd/statsd.cc b/source/extensions/stat_sinks/common/statsd/statsd.cc index 2bcd37f19312..a69733b87b58 100644 --- a/source/extensions/stat_sinks/common/statsd/statsd.cc +++ b/source/extensions/stat_sinks/common/statsd/statsd.cc @@ -31,7 +31,7 @@ namespace Statsd { UdpStatsdSink::WriterImpl::WriterImpl(UdpStatsdSink& parent) : parent_(parent), io_handle_(Network::ioHandleForAddr(Network::Socket::Type::Datagram, - parent_.server_address_)) {} + parent_.server_address_, {})) {} void UdpStatsdSink::WriterImpl::write(const std::string& message) { // TODO(mattklein123): We can avoid this const_cast pattern by having a constant variant of diff --git a/source/extensions/tracers/xray/daemon_broker.cc b/source/extensions/tracers/xray/daemon_broker.cc index 6d4406b5d51e..8b16383dc8ef 100644 --- a/source/extensions/tracers/xray/daemon_broker.cc +++ b/source/extensions/tracers/xray/daemon_broker.cc @@ -29,7 +29,7 @@ std::string createHeader(const std::string& format, uint32_t version) { DaemonBrokerImpl::DaemonBrokerImpl(const std::string& daemon_endpoint) : address_(Network::Utility::parseInternetAddressAndPort(daemon_endpoint, false /*v6only*/)), - io_handle_(Network::ioHandleForAddr(Network::Socket::Type::Datagram, address_)) {} + io_handle_(Network::ioHandleForAddr(Network::Socket::Type::Datagram, address_, {})) {} void DaemonBrokerImpl::send(const std::string& data) const { auto& logger = Logger::Registry::getLog(Logger::Id::tracing); diff --git a/source/extensions/tracers/xray/localized_sampling.cc b/source/extensions/tracers/xray/localized_sampling.cc index e56cfc52b393..a621daad1614 100644 --- a/source/extensions/tracers/xray/localized_sampling.cc +++ b/source/extensions/tracers/xray/localized_sampling.cc @@ -177,16 +177,16 @@ LocalizedSamplingManifest::LocalizedSamplingManifest(const std::string& rule_jso } bool LocalizedSamplingStrategy::shouldTrace(const SamplingRequest& sampling_request) { - if (!custom_manifest_.hasCustomRules()) { - return shouldTrace(default_manifest_.defaultRule()); + if (!manifest_.hasCustomRules()) { + return shouldTrace(manifest_.defaultRule()); } - for (auto&& rule : custom_manifest_.customRules()) { + for (auto&& rule : manifest_.customRules()) { if (rule.appliesTo(sampling_request)) { return shouldTrace(rule); } } - return shouldTrace(custom_manifest_.defaultRule()); + return shouldTrace(manifest_.defaultRule()); } bool LocalizedSamplingStrategy::shouldTrace(LocalizedSamplingRule& rule) { diff --git a/source/extensions/tracers/xray/localized_sampling.h b/source/extensions/tracers/xray/localized_sampling.h index dc9cde196047..c72bde3f9420 100644 --- a/source/extensions/tracers/xray/localized_sampling.h +++ b/source/extensions/tracers/xray/localized_sampling.h @@ -138,33 +138,21 @@ class LocalizedSamplingStrategy : public SamplingStrategy { public: LocalizedSamplingStrategy(const std::string& sampling_rules_json, Random::RandomGenerator& rng, TimeSource& time_source) - : SamplingStrategy(rng), default_manifest_(LocalizedSamplingManifest::createDefault()), - custom_manifest_(sampling_rules_json), time_source_(time_source), - use_default_(!custom_manifest_.hasCustomRules()) {} + : SamplingStrategy(rng), manifest_(sampling_rules_json), time_source_(time_source) {} /** - * Determines if an incoming request matches one of the sampling rules in the local manifests. + * Determines if an incoming request matches one of the sampling rules in the local manifest. * If a match is found, then the request might be traced based on the sampling percentages etc. * determined by the matching rule. */ bool shouldTrace(const SamplingRequest& sampling_request) override; - /** - * Determines whether default rules are in effect. Mainly for unit testing purposes. - */ - bool usingDefaultManifest() const { return use_default_; } - - /** - * @return the default manifest. Mainly for unit testing purposes. - */ - const LocalizedSamplingManifest& defaultManifest() const { return default_manifest_; } + const LocalizedSamplingManifest manifest() const { return manifest_; } private: bool shouldTrace(LocalizedSamplingRule& rule); - LocalizedSamplingManifest default_manifest_; - LocalizedSamplingManifest custom_manifest_; + LocalizedSamplingManifest manifest_; TimeSource& time_source_; - bool use_default_; }; } // namespace XRay diff --git a/source/extensions/transport_sockets/tcp_stats/BUILD b/source/extensions/transport_sockets/tcp_stats/BUILD new file mode 100644 index 000000000000..87d14cb75071 --- /dev/null +++ b/source/extensions/transport_sockets/tcp_stats/BUILD @@ -0,0 +1,39 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_extension", + "envoy_cc_library", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_extension_package() + +envoy_cc_library( + name = "tcp_stats_lib", + srcs = ["tcp_stats.cc"], + hdrs = ["tcp_stats.h"], + deps = [ + "//envoy/buffer:buffer_interface", + "//envoy/event:timer_interface", + "//envoy/network:transport_socket_interface", + "//source/common/common:assert_lib", + "//source/common/common:minimal_logger_lib", + "//source/common/protobuf:utility_lib", + "//source/extensions/transport_sockets/common:passthrough_lib", + "@envoy_api//envoy/extensions/transport_sockets/tcp_stats/v3:pkg_cc_proto", + ], +) + +envoy_cc_extension( + name = "config", + srcs = ["config.cc"], + hdrs = ["config.h"], + deps = [ + ":tcp_stats_lib", + "//envoy/registry", + "//envoy/server:transport_socket_config_interface", + "//source/common/config:utility_lib", + "@envoy_api//envoy/extensions/transport_sockets/tcp_stats/v3:pkg_cc_proto", + ], +) diff --git a/source/extensions/transport_sockets/tcp_stats/config.cc b/source/extensions/transport_sockets/tcp_stats/config.cc new file mode 100644 index 000000000000..a293dd5b977c --- /dev/null +++ b/source/extensions/transport_sockets/tcp_stats/config.cc @@ -0,0 +1,117 @@ +#include "source/extensions/transport_sockets/tcp_stats/config.h" + +#include "envoy/extensions/transport_sockets/tcp_stats/v3/tcp_stats.pb.validate.h" +#include "envoy/registry/registry.h" +#include "envoy/server/transport_socket_config.h" + +#include "source/common/config/utility.h" +#include "source/extensions/transport_sockets/tcp_stats/tcp_stats.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace TcpStats { + +TcpStatsSocketFactory::TcpStatsSocketFactory( + Server::Configuration::TransportSocketFactoryContext& context, + const envoy::extensions::transport_sockets::tcp_stats::v3::Config& config, + Network::TransportSocketFactoryPtr&& inner_factory) + : inner_factory_(std::move(inner_factory)) { +#if defined(__linux__) + config_ = std::make_shared(config, context.scope()); +#else + UNREFERENCED_PARAMETER(config); + UNREFERENCED_PARAMETER(context); + throw EnvoyException("envoy.transport_sockets.tcp_stats is not supported on this platform."); +#endif +} + +Network::TransportSocketPtr TcpStatsSocketFactory::createTransportSocket( + Network::TransportSocketOptionsConstSharedPtr options) const { +#if defined(__linux__) + auto inner_socket = inner_factory_->createTransportSocket(options); + if (inner_socket == nullptr) { + return nullptr; + } + return std::make_unique(config_, std::move(inner_socket)); +#else + UNREFERENCED_PARAMETER(options); + return nullptr; +#endif +} + +bool TcpStatsSocketFactory::implementsSecureTransport() const { + return inner_factory_->implementsSecureTransport(); +} + +bool TcpStatsSocketFactory::usesProxyProtocolOptions() const { + return inner_factory_->usesProxyProtocolOptions(); +} + +class TcpStatsConfigFactory : public virtual Server::Configuration::TransportSocketConfigFactory { +public: + std::string name() const override { return "envoy.transport_sockets.tcp_stats"; } + ProtobufTypes::MessagePtr createEmptyConfigProto() override { + return std::make_unique(); + } +}; + +class UpstreamTcpStatsConfigFactory + : public Server::Configuration::UpstreamTransportSocketConfigFactory, + public TcpStatsConfigFactory { +public: + Network::TransportSocketFactoryPtr createTransportSocketFactory( + const Protobuf::Message& config, + Server::Configuration::TransportSocketFactoryContext& context) override { + const auto& outer_config = MessageUtil::downcastAndValidate< + const envoy::extensions::transport_sockets::tcp_stats::v3::Config&>( + config, context.messageValidationVisitor()); + auto& inner_config_factory = Envoy::Config::Utility::getAndCheckFactory< + Server::Configuration::UpstreamTransportSocketConfigFactory>( + outer_config.transport_socket()); + ProtobufTypes::MessagePtr inner_factory_config = + Envoy::Config::Utility::translateToFactoryConfig(outer_config.transport_socket(), + context.messageValidationVisitor(), + inner_config_factory); + auto inner_transport_factory = + inner_config_factory.createTransportSocketFactory(*inner_factory_config, context); + return std::make_unique(context, outer_config, + std::move(inner_transport_factory)); + } +}; + +class DownstreamTcpStatsConfigFactory + : public Server::Configuration::DownstreamTransportSocketConfigFactory, + public TcpStatsConfigFactory { +public: + Network::TransportSocketFactoryPtr + createTransportSocketFactory(const Protobuf::Message& config, + Server::Configuration::TransportSocketFactoryContext& context, + const std::vector& server_names) override { + const auto& outer_config = MessageUtil::downcastAndValidate< + const envoy::extensions::transport_sockets::tcp_stats::v3::Config&>( + config, context.messageValidationVisitor()); + auto& inner_config_factory = Envoy::Config::Utility::getAndCheckFactory< + Server::Configuration::DownstreamTransportSocketConfigFactory>( + outer_config.transport_socket()); + ProtobufTypes::MessagePtr inner_factory_config = + Envoy::Config::Utility::translateToFactoryConfig(outer_config.transport_socket(), + context.messageValidationVisitor(), + inner_config_factory); + auto inner_transport_factory = inner_config_factory.createTransportSocketFactory( + *inner_factory_config, context, server_names); + return std::make_unique(context, outer_config, + std::move(inner_transport_factory)); + } +}; + +REGISTER_FACTORY(UpstreamTcpStatsConfigFactory, + Server::Configuration::UpstreamTransportSocketConfigFactory); + +REGISTER_FACTORY(DownstreamTcpStatsConfigFactory, + Server::Configuration::DownstreamTransportSocketConfigFactory); + +} // namespace TcpStats +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/transport_sockets/tcp_stats/config.h b/source/extensions/transport_sockets/tcp_stats/config.h new file mode 100644 index 000000000000..1b5fd20c1038 --- /dev/null +++ b/source/extensions/transport_sockets/tcp_stats/config.h @@ -0,0 +1,34 @@ +#pragma once + +#include "envoy/extensions/transport_sockets/tcp_stats/v3/tcp_stats.pb.h" +#include "envoy/server/transport_socket_config.h" + +#include "source/extensions/transport_sockets/tcp_stats/tcp_stats.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace TcpStats { + +class TcpStatsSocketFactory : public Network::TransportSocketFactory { +public: + TcpStatsSocketFactory(Server::Configuration::TransportSocketFactoryContext& context, + const envoy::extensions::transport_sockets::tcp_stats::v3::Config& config, + Network::TransportSocketFactoryPtr&& inner_factory); + + Network::TransportSocketPtr + createTransportSocket(Network::TransportSocketOptionsConstSharedPtr options) const override; + bool implementsSecureTransport() const override; + bool usesProxyProtocolOptions() const override; + +private: + Network::TransportSocketFactoryPtr inner_factory_; +#if defined(__linux__) + ConfigConstSharedPtr config_; +#endif +}; + +} // namespace TcpStats +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/transport_sockets/tcp_stats/tcp_stats.cc b/source/extensions/transport_sockets/tcp_stats/tcp_stats.cc new file mode 100644 index 000000000000..464e32be9531 --- /dev/null +++ b/source/extensions/transport_sockets/tcp_stats/tcp_stats.cc @@ -0,0 +1,158 @@ +#if defined(__linux__) + +// `struct tcp_info` is defined in two places: /usr/include/netinet/tcp.h (included from +// envoy/common/platform.h) and /usr/include/linux/tcp.h. The former version is older and doesn't +// contain all the fields needed. Including both headers results in a compilation error due to the +// duplicate (and different) definitions of `struct tcp_info`. To work around this, define +// `DO_NOT_INCLUDE_NETINET_TCP_H` to prevent inclusion of the wrong version. +#define DO_NOT_INCLUDE_NETINET_TCP_H 1 + +#include "source/extensions/transport_sockets/tcp_stats/tcp_stats.h" + +#include + +#include "envoy/buffer/buffer.h" +#include "envoy/network/connection.h" + +#include "source/common/common/assert.h" +#include "source/common/protobuf/utility.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace TcpStats { + +Config::Config(const envoy::extensions::transport_sockets::tcp_stats::v3::Config& config_proto, + Stats::Scope& scope) + : stats_(generateStats(scope)), + update_period_(PROTOBUF_GET_OPTIONAL_MS(config_proto, update_period)) {} + +TcpStats Config::generateStats(Stats::Scope& scope) { + const std::string prefix("tcp_stats"); + return TcpStats{ALL_TCP_STATS(POOL_COUNTER_PREFIX(scope, prefix), + POOL_GAUGE_PREFIX(scope, prefix), + POOL_HISTOGRAM_PREFIX(scope, prefix))}; +} + +TcpStatsSocket::TcpStatsSocket(ConfigConstSharedPtr config, + Network::TransportSocketPtr inner_socket) + : PassthroughSocket(std::move(inner_socket)), config_(std::move(config)) {} + +void TcpStatsSocket::setTransportSocketCallbacks(Network::TransportSocketCallbacks& callbacks) { + callbacks_ = &callbacks; + transport_socket_->setTransportSocketCallbacks(callbacks); +} + +void TcpStatsSocket::onConnected() { + if (config_->update_period_.has_value()) { + timer_ = callbacks_->connection().dispatcher().createTimer([this]() { + recordStats(); + timer_->enableTimer(config_->update_period_.value()); + }); + timer_->enableTimer(config_->update_period_.value()); + } + + transport_socket_->onConnected(); +} + +void TcpStatsSocket::closeSocket(Network::ConnectionEvent event) { + // Record final values. + recordStats(); + + // Ensure gauges are zero'd out at the end of a connection no matter what the OS told us. + if (last_cx_tx_unsent_bytes_ > 0) { + config_->stats_.cx_tx_unsent_bytes_.sub(last_cx_tx_unsent_bytes_); + } + if (last_cx_tx_unacked_segments_ > 0) { + config_->stats_.cx_tx_unacked_segments_.sub(last_cx_tx_unacked_segments_); + } + + if (timer_ != nullptr) { + timer_->disableTimer(); + } + + transport_socket_->closeSocket(event); +} + +absl::optional TcpStatsSocket::querySocketInfo() { + struct tcp_info info; + memset(&info, 0, sizeof(info)); + socklen_t optlen = sizeof(info); + const auto result = callbacks_->ioHandle().getOption(IPPROTO_TCP, TCP_INFO, &info, &optlen); + if ((result.return_value_ != 0) || (optlen < sizeof(info))) { + ENVOY_LOG(debug, "Failed getsockopt(IPPROTO_TCP, TCP_INFO): rc {} errno {} optlen {}", + result.return_value_, result.errno_, optlen); + return absl::nullopt; + } else { + return info; + } +} + +void TcpStatsSocket::recordStats() { + absl::optional tcp_info = querySocketInfo(); + if (!tcp_info.has_value()) { + return; + } + + auto update_counter = [](Stats::Counter& counter, auto& last_value, auto current_value) { + int64_t diff = static_cast(current_value) - static_cast(last_value); + ASSERT(diff >= 0); + if (diff > 0) { + counter.add(diff); + } + last_value = current_value; + }; + + auto update_gauge = [](Stats::Gauge& gauge, auto& last_value, auto current_value) { + static_assert(sizeof(last_value) == sizeof(current_value)); + int64_t diff = static_cast(current_value) - static_cast(last_value); + gauge.add(diff); + last_value = current_value; + }; + + // This is before the update to `cx_tx_data_segments_` and `cx_tx_retransmitted_segments_` because + // they use the same metrics, and `update_counter` will update `last_...`, so this needs to use + // those `last_...` values (and not update them) first. + // + // Don't record a value if the numerator is negative, or the denominator is zero or negative + // (prevent divide-by-zero). + if ((tcp_info->tcpi_data_segs_out > last_cx_tx_data_segments_) && + (tcp_info->tcpi_total_retrans >= last_cx_tx_retransmitted_segments_)) { + // uint32 * uint32 cannot overflow a uint64, so this can safely be done as integer math + // instead of floating point. + static_assert((sizeof(tcp_info->tcpi_total_retrans) == sizeof(uint32_t)) && + (Stats::Histogram::PercentScale < UINT32_MAX)); + + const uint32_t data_segs_out_diff = tcp_info->tcpi_data_segs_out - last_cx_tx_data_segments_; + const uint32_t retransmitted_segs_diff = + tcp_info->tcpi_total_retrans - last_cx_tx_retransmitted_segments_; + const uint64_t percent_retransmissions = + (static_cast(retransmitted_segs_diff) * + static_cast(Stats::Histogram::PercentScale)) / + static_cast(data_segs_out_diff); + config_->stats_.cx_tx_percent_retransmitted_segments_.recordValue(percent_retransmissions); + } + + update_counter(config_->stats_.cx_tx_segments_, last_cx_tx_segments_, tcp_info->tcpi_segs_out); + update_counter(config_->stats_.cx_rx_segments_, last_cx_rx_segments_, tcp_info->tcpi_segs_in); + update_counter(config_->stats_.cx_tx_data_segments_, last_cx_tx_data_segments_, + tcp_info->tcpi_data_segs_out); + update_counter(config_->stats_.cx_rx_data_segments_, last_cx_rx_data_segments_, + tcp_info->tcpi_data_segs_in); + update_counter(config_->stats_.cx_tx_retransmitted_segments_, last_cx_tx_retransmitted_segments_, + tcp_info->tcpi_total_retrans); + + update_gauge(config_->stats_.cx_tx_unsent_bytes_, last_cx_tx_unsent_bytes_, + tcp_info->tcpi_notsent_bytes); + update_gauge(config_->stats_.cx_tx_unacked_segments_, last_cx_tx_unacked_segments_, + tcp_info->tcpi_unacked); + + config_->stats_.cx_rtt_us_.recordValue(tcp_info->tcpi_rtt); + config_->stats_.cx_rtt_variance_us_.recordValue(tcp_info->tcpi_rttvar); +} + +} // namespace TcpStats +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy +#endif // defined(__linux__) diff --git a/source/extensions/transport_sockets/tcp_stats/tcp_stats.h b/source/extensions/transport_sockets/tcp_stats/tcp_stats.h new file mode 100644 index 000000000000..a5066575f8b7 --- /dev/null +++ b/source/extensions/transport_sockets/tcp_stats/tcp_stats.h @@ -0,0 +1,85 @@ +#pragma once + +#if defined(__linux__) + +#include "envoy/event/timer.h" +#include "envoy/extensions/transport_sockets/tcp_stats/v3/tcp_stats.pb.h" +#include "envoy/network/connection.h" +#include "envoy/network/filter.h" +#include "envoy/stats/scope.h" +#include "envoy/stats/stats_macros.h" + +#include "source/common/common/logger.h" +#include "source/extensions/transport_sockets/common/passthrough.h" + +// Defined in /usr/include/linux/tcp.h. +struct tcp_info; + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace TcpStats { + +#define ALL_TCP_STATS(COUNTER, GAUGE, HISTOGRAM) \ + COUNTER(cx_tx_segments) \ + COUNTER(cx_rx_segments) \ + COUNTER(cx_tx_data_segments) \ + COUNTER(cx_rx_data_segments) \ + COUNTER(cx_tx_retransmitted_segments) \ + GAUGE(cx_tx_unsent_bytes, Accumulate) \ + GAUGE(cx_tx_unacked_segments, Accumulate) \ + HISTOGRAM(cx_tx_percent_retransmitted_segments, Percent) \ + HISTOGRAM(cx_rtt_us, Microseconds) \ + HISTOGRAM(cx_rtt_variance_us, Microseconds) + +struct TcpStats { + ALL_TCP_STATS(GENERATE_COUNTER_STRUCT, GENERATE_GAUGE_STRUCT, GENERATE_HISTOGRAM_STRUCT) +}; + +class Config { +public: + Config(const envoy::extensions::transport_sockets::tcp_stats::v3::Config& config_proto, + Stats::Scope& scope); + + TcpStats stats_; + const absl::optional update_period_; + +private: + TcpStats generateStats(Stats::Scope& scope); +}; + +using ConfigConstSharedPtr = std::shared_ptr; + +class TcpStatsSocket : public TransportSockets::PassthroughSocket, + Logger::Loggable { +public: + TcpStatsSocket(ConfigConstSharedPtr config, Network::TransportSocketPtr inner_socket); + + // Network::TransportSocket + void setTransportSocketCallbacks(Network::TransportSocketCallbacks& callbacks) override; + void onConnected() override; + void closeSocket(Network::ConnectionEvent event) override; + +private: + absl::optional querySocketInfo(); + void recordStats(); + + const ConfigConstSharedPtr config_; + Network::TransportSocketCallbacks* callbacks_{}; + Event::TimerPtr timer_; + + uint32_t last_cx_tx_segments_{}; + uint32_t last_cx_rx_segments_{}; + uint32_t last_cx_tx_data_segments_{}; + uint32_t last_cx_rx_data_segments_{}; + uint32_t last_cx_tx_retransmitted_segments_{}; + uint32_t last_cx_tx_unsent_bytes_{}; + uint32_t last_cx_tx_unacked_segments_{}; +}; + +} // namespace TcpStats +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy + +#endif // defined(__linux__) diff --git a/source/extensions/transport_sockets/tls/cert_validator/BUILD b/source/extensions/transport_sockets/tls/cert_validator/BUILD index ce92df41e80c..93f745f8faa3 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/BUILD +++ b/source/extensions/transport_sockets/tls/cert_validator/BUILD @@ -13,12 +13,14 @@ envoy_cc_library( srcs = [ "default_validator.cc", "factory.cc", + "san_matcher.cc", "utility.cc", ], hdrs = [ "cert_validator.h", "default_validator.h", "factory.h", + "san_matcher.h", "utility.h", ], external_deps = [ @@ -35,9 +37,13 @@ envoy_cc_library( "//source/common/common:hex_lib", "//source/common/common:minimal_logger_lib", "//source/common/common:utility_lib", + "//source/common/config:utility_lib", "//source/common/stats:symbol_table_lib", "//source/common/stats:utility_lib", "//source/extensions/transport_sockets/tls:stats_lib", "//source/extensions/transport_sockets/tls:utility_lib", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", + "@envoy_api//envoy/type/matcher/v3:pkg_cc_proto", ], ) 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 691b5018189d..93bbe8b17a04 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc +++ b/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc @@ -18,6 +18,7 @@ #include "source/common/common/hex.h" #include "source/common/common/matchers.h" #include "source/common/common/utility.h" +#include "source/common/config/utility.h" #include "source/common/network/address_impl.h" #include "source/common/protobuf/utility.h" #include "source/common/runtime/runtime_features.h" @@ -100,7 +101,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,17 +139,18 @@ 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); } } const Envoy::Ssl::CertificateValidationContextConfig* cert_validation_config = config_; if (cert_validation_config != nullptr) { if (!cert_validation_config->subjectAltNameMatchers().empty()) { - for (const envoy::type::matcher::v3::StringMatcher& matcher : + for (const envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher& matcher : cert_validation_config->subjectAltNameMatchers()) { - subject_alt_name_matchers_.push_back(Matchers::StringMatcherImpl(matcher)); + subject_alt_name_matchers_.emplace_back(createStringSanMatcher(matcher)); } verify_mode = verify_mode_validation_context; } @@ -218,8 +222,8 @@ int DefaultCertValidator::doVerifyCertChain( // If `trusted_ca` exists, it is already verified in the code above. Thus, we just need to make // sure the verification for other validation context configurations doesn't fail (i.e. either - // `NotValidated` or `Validated`). If `trusted_ca` doesn't exist, we will need to make sure other - // configurations are verified and the verification succeed. + // `NotValidated` or `Validated`). If `trusted_ca` doesn't exist, we will need to make sure + // other configurations are verified and the verification succeed. int validation_status = verify_trusted_ca_ ? validated != Envoy::Ssl::ClientValidationStatus::Failed : validated == Envoy::Ssl::ClientValidationStatus::Validated; @@ -229,8 +233,7 @@ int DefaultCertValidator::doVerifyCertChain( Envoy::Ssl::ClientValidationStatus DefaultCertValidator::verifyCertificate( X509* cert, const std::vector& verify_san_list, - const std::vector>& - subject_alt_name_matchers) { + const std::vector& subject_alt_name_matchers) { Envoy::Ssl::ClientValidationStatus validated = Envoy::Ssl::ClientValidationStatus::NotValidated; if (!verify_san_list.empty()) { @@ -288,23 +291,15 @@ bool DefaultCertValidator::verifySubjectAltName(X509* cert, } bool DefaultCertValidator::matchSubjectAltName( - X509* cert, - const std::vector>& - subject_alt_name_matchers) { + X509* cert, const std::vector& subject_alt_name_matchers) { bssl::UniquePtr san_names( static_cast(X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr))); if (san_names == nullptr) { return false; } - for (const GENERAL_NAME* general_name : san_names.get()) { - const std::string san = Utility::generalNameAsString(general_name); - for (auto& config_san_matcher : subject_alt_name_matchers) { - // For DNS SAN, if the StringMatcher type is exact, we have to follow DNS matching semantics. - if (general_name->type == GEN_DNS && - config_san_matcher.matcher().match_pattern_case() == - envoy::type::matcher::v3::StringMatcher::MatchPatternCase::kExact - ? Utility::dnsNameMatch(config_san_matcher.matcher().exact(), absl::string_view(san)) - : config_san_matcher.match(san)) { + for (const auto& config_san_matcher : subject_alt_name_matchers) { + for (const GENERAL_NAME* general_name : san_names.get()) { + if (config_san_matcher->match(general_name)) { return true; } } diff --git a/source/extensions/transport_sockets/tls/cert_validator/default_validator.h b/source/extensions/transport_sockets/tls/cert_validator/default_validator.h index 0cfe2766d137..64f52bc38728 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/default_validator.h +++ b/source/extensions/transport_sockets/tls/cert_validator/default_validator.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "envoy/common/pure.h" @@ -18,6 +19,7 @@ #include "source/common/common/matchers.h" #include "source/common/stats/symbol_table_impl.h" #include "source/extensions/transport_sockets/tls/cert_validator/cert_validator.h" +#include "source/extensions/transport_sockets/tls/cert_validator/san_matcher.h" #include "source/extensions/transport_sockets/tls/stats.h" #include "absl/synchronization/mutex.h" @@ -53,10 +55,9 @@ class DefaultCertValidator : public CertValidator, Logger::Loggable& verify_san_list, - const std::vector>& - subject_alt_name_matchers); + Envoy::Ssl::ClientValidationStatus + verifyCertificate(X509* cert, const std::vector& verify_san_list, + const std::vector& subject_alt_name_matchers); /** * Verifies certificate hash for pinning. The hash is a hex-encoded SHA-256 of the DER-encoded @@ -94,10 +95,8 @@ class DefaultCertValidator : public CertValidator, Logger::Loggable>& - subject_alt_name_matchers); + static bool matchSubjectAltName(X509* cert, + const std::vector& subject_alt_name_matchers); private: const Envoy::Ssl::CertificateValidationContextConfig* config_; @@ -107,8 +106,7 @@ class DefaultCertValidator : public CertValidator, Logger::Loggable ca_cert_; std::string ca_file_path_; - std::vector> - subject_alt_name_matchers_; + std::vector subject_alt_name_matchers_; std::vector> verify_certificate_hash_list_; std::vector> verify_certificate_spki_list_; bool verify_trusted_ca_{false}; diff --git a/source/extensions/transport_sockets/tls/cert_validator/san_matcher.cc b/source/extensions/transport_sockets/tls/cert_validator/san_matcher.cc new file mode 100644 index 000000000000..4be1c64ec096 --- /dev/null +++ b/source/extensions/transport_sockets/tls/cert_validator/san_matcher.cc @@ -0,0 +1,53 @@ +#include "source/extensions/transport_sockets/tls/cert_validator/san_matcher.h" + +#include + +#include "envoy/config/core/v3/extension.pb.h" +#include "envoy/extensions/transport_sockets/tls/v3/common.pb.h" +#include "envoy/registry/registry.h" +#include "envoy/ssl/certificate_validation_context_config.h" + +#include "source/extensions/transport_sockets/tls/utility.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace Tls { + +bool StringSanMatcher::match(const GENERAL_NAME* general_name) const { + if (general_name->type != general_name_type_) { + return false; + } + // For DNS SAN, if the StringMatcher type is exact, we have to follow DNS matching semantics. + const std::string san = Utility::generalNameAsString(general_name); + return general_name->type == GEN_DNS && + matcher_.matcher().match_pattern_case() == + envoy::type::matcher::v3::StringMatcher::MatchPatternCase::kExact + ? Utility::dnsNameMatch(matcher_.matcher().exact(), absl::string_view(san)) + : matcher_.match(san); +} + +SanMatcherPtr createStringSanMatcher( + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher const& matcher) { + // Verify that a new san type has not been added. + static_assert(envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SanType_MAX == + 4); + + switch (matcher.san_type()) { + case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::DNS: + return SanMatcherPtr{std::make_unique(GEN_DNS, matcher.matcher())}; + case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::EMAIL: + return SanMatcherPtr{std::make_unique(GEN_EMAIL, matcher.matcher())}; + case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::URI: + return SanMatcherPtr{std::make_unique(GEN_URI, matcher.matcher())}; + case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::IP_ADDRESS: + return SanMatcherPtr{std::make_unique(GEN_IPADD, matcher.matcher())}; + default: + NOT_REACHED_GCOVR_EXCL_LINE; + } +} + +} // namespace Tls +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/transport_sockets/tls/cert_validator/san_matcher.h b/source/extensions/transport_sockets/tls/cert_validator/san_matcher.h new file mode 100644 index 000000000000..5c2141f4b570 --- /dev/null +++ b/source/extensions/transport_sockets/tls/cert_validator/san_matcher.h @@ -0,0 +1,51 @@ +#pragma once + +#include + +#include "envoy/config/core/v3/extension.pb.h" +#include "envoy/extensions/transport_sockets/tls/v3/common.pb.h" +#include "envoy/ssl/certificate_validation_context_config.h" +#include "envoy/type/matcher/v3/string.pb.h" + +#include "source/common/common/hash.h" +#include "source/common/common/matchers.h" +#include "source/common/protobuf/protobuf.h" +#include "source/extensions/transport_sockets/tls/utility.h" + +#include "openssl/x509v3.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace Tls { + +/** Interface to verify if there is a match in a list of subject alternative + * names. + */ +class SanMatcher { +public: + virtual bool match(GENERAL_NAME const*) const PURE; + virtual ~SanMatcher() = default; +}; + +using SanMatcherPtr = std::unique_ptr; + +class StringSanMatcher : public SanMatcher { +public: + bool match(const GENERAL_NAME* general_name) const override; + ~StringSanMatcher() override = default; + StringSanMatcher(int general_name_type, envoy::type::matcher::v3::StringMatcher matcher) + : general_name_type_(general_name_type), matcher_(matcher) {} + +private: + const int general_name_type_; + const Matchers::StringMatcherImpl matcher_; +}; + +SanMatcherPtr createStringSanMatcher( + const envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher& matcher); + +} // namespace Tls +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc index 63df8297c76d..c3b8d338fcae 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc +++ b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc @@ -1,5 +1,6 @@ #include "source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h" +#include "envoy/extensions/transport_sockets/tls/v3/common.pb.h" #include "envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.pb.h" #include "envoy/network/transport_socket.h" #include "envoy/registry/registry.h" @@ -37,7 +38,14 @@ SPIFFEValidator::SPIFFEValidator(const Envoy::Ssl::CertificateValidationContextC if (!config->subjectAltNameMatchers().empty()) { for (const auto& matcher : config->subjectAltNameMatchers()) { - subject_alt_name_matchers_.push_back(Matchers::StringMatcherImpl(matcher)); + if (matcher.san_type() == + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::URI) { + // Only match against URI SAN since SPIFFE specification does not restrict values in other + // SAN types. See the discussion: https://github.com/envoyproxy/envoy/issues/15392 + // TODO(pradeepcrao): Throw an exception when a non-URI matcher is encountered after the + // deprecated field match_subject_alt_names is removed + subject_alt_name_matchers_.emplace_back(createStringSanMatcher(matcher)); + } } } @@ -224,15 +232,10 @@ bool SPIFFEValidator::matchSubjectAltName(X509& leaf_cert) { ASSERT(san_names != nullptr, "san_names should have at least one name after SPIFFE cert validation"); - // Only match against URI SAN since SPIFFE specification does not restrict values in other SAN - // types. See the discussion: https://github.com/envoyproxy/envoy/issues/15392 for (const GENERAL_NAME* general_name : san_names.get()) { - if (general_name->type == GEN_URI) { - const std::string san = Utility::generalNameAsString(general_name); - for (const auto& config_san_matcher : subject_alt_name_matchers_) { - if (config_san_matcher.match(san)) { - return true; - } + for (const auto& config_san_matcher : subject_alt_name_matchers_) { + if (config_san_matcher->match(general_name)) { + return true; } } } diff --git a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h index b4cc068e908e..669d77aec7d0 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h +++ b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h @@ -17,6 +17,7 @@ #include "source/common/common/matchers.h" #include "source/common/stats/symbol_table_impl.h" #include "source/extensions/transport_sockets/tls/cert_validator/cert_validator.h" +#include "source/extensions/transport_sockets/tls/cert_validator/san_matcher.h" #include "source/extensions/transport_sockets/tls/stats.h" #include "openssl/ssl.h" @@ -67,8 +68,7 @@ class SPIFFEValidator : public CertValidator { bool allow_expired_certificate_{false}; std::vector> ca_certs_; std::string ca_file_name_; - std::vector> - subject_alt_name_matchers_{}; + std::vector subject_alt_name_matchers_{}; absl::flat_hash_map trust_bundle_stores_; SslStats& stats_; 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 de692e42fff4..3aa9974ff8b9 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 b591b4733f10..f5bfa73b0ee1 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/context_config_impl.cc b/source/extensions/transport_sockets/tls/context_config_impl.cc index aa5fa77be56c..6eea3e42e173 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 0afc83d5a67a..9ded6a83016f 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 a3b981b68eb6..0107a88ec6fa 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/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index 31ed7fd52f01..d1c1229814e7 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,13 @@ Network::Connection& SslSocket::connection() const { return callbacks_->connecti void SslSocket::onSuccess(SSL* ssl) { ctx_->logHandshake(ssl); + if (callbacks_->connection().streamInfo().upstreamInfo()) { + callbacks_->connection() + .streamInfo() + .upstreamInfo() + ->upstreamTiming() + .onUpstreamHandshakeComplete(callbacks_->connection().dispatcher().timeSource()); + } callbacks_->raiseEvent(Network::ConnectionEvent::Connected); } @@ -335,12 +343,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/source/server/BUILD b/source/server/BUILD index 77f8c3cc7bc1..85cb1facb30c 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"], @@ -388,6 +420,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", ], ) @@ -455,7 +488,9 @@ envoy_cc_library( ":lds_api_lib", ":transport_socket_config_lib", "//envoy/access_log:access_log_interface", + "//envoy/config:typed_metadata_interface", "//envoy/network:connection_interface", + "//envoy/network:listener_interface", "//envoy/server:api_listener_interface", "//envoy/server:filter_config_interface", "//envoy/server:listener_manager_interface", @@ -465,6 +500,7 @@ envoy_cc_library( "//source/common/common:basic_resource_lib", "//source/common/common:empty_string", "//source/common/config:utility_lib", + "//source/common/config:metadata_lib", "//source/common/http:conn_manager_lib", "//source/common/init:manager_lib", "//source/common/init:target_lib", @@ -500,6 +536,7 @@ envoy_cc_library( hdrs = ["filter_chain_manager_impl.h"], deps = [ ":filter_chain_factory_context_callback", + "//envoy/config:typed_metadata_interface", "//envoy/server:instance_interface", "//envoy/server:listener_manager_interface", "//envoy/server:transport_socket_config_interface", @@ -583,6 +620,7 @@ envoy_cc_library( "//source/common/common:cleanup_lib", "//source/common/common:logger_lib", "//source/common/common:mutex_tracer_lib", + "//source/common/common:perf_tracing_lib", "//source/common/common:utility_lib", "//source/common/config:grpc_mux_lib", "//source/common/config:new_grpc_mux_lib", diff --git a/source/server/active_internal_listener.cc b/source/server/active_internal_listener.cc new file mode 100644 index 000000000000..701d9376175e --- /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 000000000000..f455162a6c9c --- /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/active_tcp_listener.cc b/source/server/active_tcp_listener.cc index 3e0f850c9b7a..763a9170d4b2 100644 --- a/source/server/active_tcp_listener.cc +++ b/source/server/active_tcp_listener.cc @@ -17,7 +17,7 @@ ActiveTcpListener::ActiveTcpListener(Network::TcpConnectionHandler& parent, : OwnedActiveStreamListenerBase(parent, parent.dispatcher(), parent.dispatcher().createListener( config.listenSocketFactory().getListenSocket(worker_index), - *this, config.bindToPort()), + *this, config.bindToPort(), config.ignoreGlobalConnLimit()), config), tcp_conn_handler_(parent) { config.connectionBalancer().registerHandler(*this); diff --git a/source/server/admin/admin.cc b/source/server/admin/admin.cc index 74c17040b261..0039d7cf4567 100644 --- a/source/server/admin/admin.cc +++ b/source/server/admin/admin.cc @@ -109,7 +109,6 @@ const char AdminHtmlEnd[] = R"( )"; - } // namespace ConfigTracker& AdminImpl::getConfigTracker() { return config_tracker_; } @@ -144,7 +143,8 @@ void AdminImpl::startHttpListener(const std::list& } } -AdminImpl::AdminImpl(const std::string& profile_path, Server::Instance& server) +AdminImpl::AdminImpl(const std::string& profile_path, Server::Instance& server, + bool ignore_global_conn_limit) : server_(server), request_id_extension_(Extensions::RequestId::UUIDRequestIDExtension::defaultInstance( server_.api().randomGenerator())), @@ -221,7 +221,8 @@ AdminImpl::AdminImpl(const std::string& profile_path, Server::Instance& server) }, date_provider_(server.dispatcher().timeSource()), admin_filter_chain_(std::make_shared()), - local_reply_(LocalReply::Factory::createDefault()) {} + local_reply_(LocalReply::Factory::createDefault()), + ignore_global_conn_limit_(ignore_global_conn_limit) {} Http::ServerConnectionPtr AdminImpl::createCodec(Network::Connection& connection, const Buffer::Instance& data, diff --git a/source/server/admin/admin.h b/source/server/admin/admin.h index e99330c2c8fc..e255c27d3367 100644 --- a/source/server/admin/admin.h +++ b/source/server/admin/admin.h @@ -68,7 +68,8 @@ class AdminImpl : public Admin, public Http::ConnectionManagerConfig, Logger::Loggable { public: - AdminImpl(const std::string& profile_path, Server::Instance& server); + AdminImpl(const std::string& profile_path, Server::Instance& server, + bool ignore_global_conn_limit); Http::Code runCallback(absl::string_view path_and_query, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, @@ -230,7 +231,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; @@ -270,6 +270,9 @@ class AdminImpl : public Admin, struct NullThreadLocalOverloadState : public ThreadLocalOverloadState { NullThreadLocalOverloadState(Event::Dispatcher& dispatcher) : dispatcher_(dispatcher) {} const OverloadActionState& getState(const std::string&) override { return inactive_; } + bool tryAllocateResource(OverloadProactiveResourceName, int64_t) override { return false; } + bool tryDeallocateResource(OverloadProactiveResourceName, int64_t) override { return false; } + bool isResourceMonitorEnabled(OverloadProactiveResourceName) override { return false; } Event::Dispatcher& dispatcher_; const OverloadActionState inactive_ = OverloadActionState::inactive(); }; @@ -341,7 +344,7 @@ class AdminImpl : public Admin, AdminListener(AdminImpl& parent, Stats::ScopePtr&& listener_scope) : parent_(parent), name_("admin"), scope_(std::move(listener_scope)), stats_(Http::ConnectionManagerImpl::generateListenerStats("http.admin.", *scope_)), - init_manager_(nullptr) {} + init_manager_(nullptr), ignore_global_conn_limit_(parent.ignore_global_conn_limit_) {} // Network::ListenerConfig Network::FilterChainManager& filterChainManager() override { return parent_; } @@ -360,6 +363,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; } @@ -370,6 +376,7 @@ class AdminImpl : public Admin, } uint32_t tcpBacklogSize() const override { return ENVOY_TCP_BACKLOG_SIZE; } Init::Manager& initManager() override { return *init_manager_; } + bool ignoreGlobalConnLimit() const override { return ignore_global_conn_limit_; } AdminImpl& parent_; const std::string name_; @@ -381,6 +388,7 @@ class AdminImpl : public Admin, private: const std::vector empty_access_logs_; std::unique_ptr init_manager_; + const bool ignore_global_conn_limit_; }; using AdminListenerPtr = std::unique_ptr; @@ -453,6 +461,7 @@ class AdminImpl : public Admin, const LocalReply::LocalReplyPtr local_reply_; const std::vector detection_extensions_{}; const absl::optional scheme_{}; + const bool ignore_global_conn_limit_; }; } // namespace Server diff --git a/source/server/admin/prometheus_stats.cc b/source/server/admin/prometheus_stats.cc index a3d0c7657335..b1cf89936c9b 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/source/server/config_validation/dispatcher.cc b/source/server/config_validation/dispatcher.cc index 155b482709e2..36d83abe8f1d 100644 --- a/source/server/config_validation/dispatcher.cc +++ b/source/server/config_validation/dispatcher.cc @@ -16,7 +16,8 @@ Network::ClientConnectionPtr ValidationDispatcher::createClientConnection( } Network::ListenerPtr ValidationDispatcher::createListener(Network::SocketSharedPtr&&, - Network::TcpListenerCallbacks&, bool) { + Network::TcpListenerCallbacks&, bool, + bool) { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; } diff --git a/source/server/config_validation/dispatcher.h b/source/server/config_validation/dispatcher.h index e16829cdb0b6..0a280d0b98a5 100644 --- a/source/server/config_validation/dispatcher.h +++ b/source/server/config_validation/dispatcher.h @@ -24,7 +24,7 @@ class ValidationDispatcher : public DispatcherImpl { Network::Address::InstanceConstSharedPtr, Network::TransportSocketPtr&&, const Network::ConnectionSocket::OptionsSharedPtr& options) override; Network::ListenerPtr createListener(Network::SocketSharedPtr&&, Network::TcpListenerCallbacks&, - bool bind_to_port) override; + bool bind_to_port, bool ignore_global_conn_limit) override; }; } // namespace Event diff --git a/source/server/config_validation/server.cc b/source/server/config_validation/server.cc index 57eba26cabcb..7dcf1596703c 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/config_validation/server.h b/source/server/config_validation/server.h index bcde799b276e..6df0b4c67e72 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -16,7 +16,7 @@ #include "source/common/common/assert.h" #include "source/common/common/random_generator.h" #include "source/common/grpc/common.h" -#include "source/common/network/dns_resolver/dns_factory.h" +#include "source/common/network/dns_resolver/dns_factory_util.h" #include "source/common/protobuf/message_validator_impl.h" #include "source/common/quic/quic_stat_names.h" #include "source/common/router/context_impl.h" @@ -125,6 +125,7 @@ class ValidationInstance final : Logger::Loggable, void setDefaultTracingConfig(const envoy::config::trace::v3::Tracing& tracing_config) override { http_context_.setDefaultTracingConfig(tracing_config); } + void setSinkPredicates(std::unique_ptr&&) override {} // Server::ListenerComponentFactory LdsApiPtr createLdsApi(const envoy::config::core::v3::ConfigSource& lds_config, @@ -149,11 +150,10 @@ class ValidationInstance final : Logger::Loggable, Configuration::ListenerFactoryContext& context) override { return ProdListenerComponentFactory::createUdpListenerFilterFactoryList_(filters, context); } - Network::SocketSharedPtr createListenSocket(Network::Address::InstanceConstSharedPtr, - Network::Socket::Type, - const Network::Socket::OptionsSharedPtr&, - ListenerComponentFactory::BindType, - uint32_t) override { + Network::SocketSharedPtr + createListenSocket(Network::Address::InstanceConstSharedPtr, Network::Socket::Type, + const Network::Socket::OptionsSharedPtr&, ListenerComponentFactory::BindType, + const Network::SocketCreationOptions&, uint32_t) override { // Returned sockets are not currently used so we can return nothing here safely vs. a // validation mock. // TODO(mattklein123): The fact that this returns nullptr makes the production code more diff --git a/source/server/configuration_impl.cc b/source/server/configuration_impl.cc index a4556846df54..d5abc1439b7a 100644 --- a/source/server/configuration_impl.cc +++ b/source/server/configuration_impl.cc @@ -206,6 +206,7 @@ InitialImpl::InitialImpl(const envoy::config::bootstrap::v3::Bootstrap& bootstra admin_.socket_options_, Network::SocketOptionFactory::buildLiteralOptions(admin.socket_options())); } + admin_.ignore_global_conn_limit_ = admin.ignore_global_conn_limit(); if (!bootstrap.flags_path().empty()) { flags_path_ = bootstrap.flags_path(); diff --git a/source/server/configuration_impl.h b/source/server/configuration_impl.h index 7efa5e22fda5..792a106e26f5 100644 --- a/source/server/configuration_impl.h +++ b/source/server/configuration_impl.h @@ -192,11 +192,13 @@ class InitialImpl : public Initial { Network::Address::InstanceConstSharedPtr address() override { return address_; } Network::Socket::OptionsSharedPtr socketOptions() override { return socket_options_; } std::list accessLogs() const override { return access_logs_; } + bool ignoreGlobalConnLimit() const override { return ignore_global_conn_limit_; } std::string profile_path_; std::list access_logs_; Network::Address::InstanceConstSharedPtr address_; Network::Socket::OptionsSharedPtr socket_options_; + bool ignore_global_conn_limit_; }; AdminImpl admin_; diff --git a/source/server/connection_handler_impl.cc b/source/server/connection_handler_impl.cc index 4bcdcd9cc16a..24789e5feb5a 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 f10424b7c1a2..23a39659c55d 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/filter_chain_manager_impl.cc b/source/server/filter_chain_manager_impl.cc index 26dee2401e9c..bff85725036f 100644 --- a/source/server/filter_chain_manager_impl.cc +++ b/source/server/filter_chain_manager_impl.cc @@ -52,6 +52,11 @@ PerFilterChainFactoryContextImpl::listenerMetadata() const { return parent_context_.listenerMetadata(); } +const Envoy::Config::TypedMetadata& +PerFilterChainFactoryContextImpl::listenerTypedMetadata() const { + return parent_context_.listenerTypedMetadata(); +} + envoy::config::core::v3::TrafficDirection PerFilterChainFactoryContextImpl::direction() const { return parent_context_.direction(); } @@ -791,6 +796,10 @@ FactoryContextImpl::getTransportSocketFactoryContext() const { const envoy::config::core::v3::Metadata& FactoryContextImpl::listenerMetadata() const { return config_.metadata(); } +const Envoy::Config::TypedMetadata& FactoryContextImpl::listenerTypedMetadata() const { + // TODO(nareddyt): Needs an implementation for this context. Currently not used. + NOT_IMPLEMENTED_GCOVR_EXCL_LINE; +} envoy::config::core::v3::TrafficDirection FactoryContextImpl::direction() const { return config_.traffic_direction(); } diff --git a/source/server/filter_chain_manager_impl.h b/source/server/filter_chain_manager_impl.h index 4e9cb2576797..14485d60108f 100644 --- a/source/server/filter_chain_manager_impl.h +++ b/source/server/filter_chain_manager_impl.h @@ -5,6 +5,7 @@ #include #include "envoy/config/listener/v3/listener_components.pb.h" +#include "envoy/config/typed_metadata.h" #include "envoy/network/drain_decision.h" #include "envoy/server/filter_config.h" #include "envoy/server/instance.h" @@ -72,6 +73,7 @@ class PerFilterChainFactoryContextImpl : public Configuration::FilterChainFactor ThreadLocal::SlotAllocator& threadLocal() override; Admin& admin() override; const envoy::config::core::v3::Metadata& listenerMetadata() const override; + const Envoy::Config::TypedMetadata& listenerTypedMetadata() const override; envoy::config::core::v3::TrafficDirection direction() const override; TimeSource& timeSource() override; ProtobufMessage::ValidationVisitor& messageValidationVisitor() override; @@ -170,6 +172,7 @@ class FactoryContextImpl : public Configuration::FactoryContext { Configuration::ServerFactoryContext& getServerFactoryContext() const override; Configuration::TransportSocketFactoryContext& getTransportSocketFactoryContext() const override; const envoy::config::core::v3::Metadata& listenerMetadata() const override; + const Envoy::Config::TypedMetadata& listenerTypedMetadata() const override; envoy::config::core::v3::TrafficDirection direction() const override; Network::DrainDecision& drainDecision() override; Stats::Scope& listenerScope() override; diff --git a/source/server/hot_restarting_parent.cc b/source/server/hot_restarting_parent.cc index 14c012b70cbc..fab51c4fc7b5 100644 --- a/source/server/hot_restarting_parent.cc +++ b/source/server/hot_restarting_parent.cc @@ -128,26 +128,26 @@ HotRestartingParent::Internal::getListenSocketsForChild(const HotRestartMessage: // magnitude of memory usage that they are meant to avoid, since this map holds full-string // names. The problem can be solved by splitting the export up over many chunks. void HotRestartingParent::Internal::exportStatsToChild(HotRestartMessage::Reply::Stats* stats) { - for (const auto& gauge : server_->stats().gauges()) { - if (gauge->used()) { - const std::string name = gauge->name(); - (*stats->mutable_gauges())[name] = gauge->value(); - recordDynamics(stats, name, gauge->statName()); + server_->stats().forEachSinkedGauge(nullptr, [this, stats](Stats::Gauge& gauge) mutable { + if (gauge.used()) { + const std::string name = gauge.name(); + (*stats->mutable_gauges())[name] = gauge.value(); + recordDynamics(stats, name, gauge.statName()); } - } + }); - for (const auto& counter : server_->stats().counters()) { - if (counter->used()) { + server_->stats().forEachSinkedCounter(nullptr, [this, stats](Stats::Counter& counter) mutable { + if (counter.used()) { // The hot restart parent is expected to have stopped its normal stat exporting (and so // latching) by the time it begins exporting to the hot restart child. - uint64_t latched_value = counter->latch(); + uint64_t latched_value = counter.latch(); if (latched_value > 0) { - const std::string name = counter->name(); + const std::string name = counter.name(); (*stats->mutable_counter_deltas())[name] = latched_value; - recordDynamics(stats, name, counter->statName()); + recordDynamics(stats, name, counter.statName()); } } - } + }); stats->set_memory_allocated(Memory::Stats::totalCurrentlyAllocated()); stats->set_num_connections(server_->listenerManager().numConnections()); } diff --git a/source/server/listener_impl.cc b/source/server/listener_impl.cc index df2ba6182f54..e1d1788e9d16 100644 --- a/source/server/listener_impl.cc +++ b/source/server/listener_impl.cc @@ -72,21 +72,27 @@ ListenSocketFactoryImpl::ListenSocketFactoryImpl( ListenerComponentFactory& factory, Network::Address::InstanceConstSharedPtr address, Network::Socket::Type socket_type, const Network::Socket::OptionsSharedPtr& options, const std::string& listener_name, uint32_t tcp_backlog_size, - ListenerComponentFactory::BindType bind_type, uint32_t num_sockets) + ListenerComponentFactory::BindType bind_type, + const Network::SocketCreationOptions& creation_options, uint32_t num_sockets) : factory_(factory), local_address_(address), socket_type_(socket_type), options_(options), - listener_name_(listener_name), tcp_backlog_size_(tcp_backlog_size), bind_type_(bind_type) { + listener_name_(listener_name), tcp_backlog_size_(tcp_backlog_size), bind_type_(bind_type), + socket_creation_options_(creation_options) { if (local_address_->type() == Network::Address::Type::Ip) { if (socket_type == Network::Socket::Type::Datagram) { 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; } } @@ -114,7 +120,8 @@ ListenSocketFactoryImpl::ListenSocketFactoryImpl(const ListenSocketFactoryImpl& socket_type_(factory_to_clone.socket_type_), options_(factory_to_clone.options_), listener_name_(factory_to_clone.listener_name_), tcp_backlog_size_(factory_to_clone.tcp_backlog_size_), - bind_type_(factory_to_clone.bind_type_) { + bind_type_(factory_to_clone.bind_type_), + socket_creation_options_(factory_to_clone.socket_creation_options_) { for (auto& socket : factory_to_clone.sockets_) { // In the cloning case we always duplicate() the socket. This makes sure that during listener // update/drain we don't lose any incoming connections when using reuse_port. Specifically on @@ -137,8 +144,8 @@ Network::SocketSharedPtr ListenSocketFactoryImpl::createListenSocketAndApplyOpti // Socket might be nullptr when doing server validation. // TODO(mattklein123): See the comment in the validation code. Make that code not return nullptr // so this code can be simpler. - Network::SocketSharedPtr socket = - factory.createListenSocket(local_address_, socket_type, options_, bind_type_, worker_index); + Network::SocketSharedPtr socket = factory.createListenSocket( + local_address_, socket_type, options_, bind_type_, socket_creation_options_, worker_index); // Binding is done by now. ENVOY_LOG(debug, "Create listen socket for listener {} on address {}", listener_name_, @@ -207,8 +214,8 @@ void ListenSocketFactoryImpl::doFinalPreWorkerInit() { ListenerFactoryContextBaseImpl::ListenerFactoryContextBaseImpl( Envoy::Server::Instance& server, ProtobufMessage::ValidationVisitor& validation_visitor, const envoy::config::listener::v3::Listener& config, DrainManagerPtr drain_manager) - : server_(server), metadata_(config.metadata()), direction_(config.traffic_direction()), - global_scope_(server.stats().createScope("")), + : server_(server), metadata_(config.metadata()), typed_metadata_(config.metadata()), + direction_(config.traffic_direction()), global_scope_(server.stats().createScope("")), listener_scope_(server_.stats().createScope( fmt::format("listener.{}.", !config.stat_prefix().empty() @@ -249,6 +256,9 @@ Admin& ListenerFactoryContextBaseImpl::admin() { return server_.admin(); } const envoy::config::core::v3::Metadata& ListenerFactoryContextBaseImpl::listenerMetadata() const { return metadata_; }; +const Envoy::Config::TypedMetadata& ListenerFactoryContextBaseImpl::listenerTypedMetadata() const { + return typed_metadata_; +} envoy::config::core::v3::TrafficDirection ListenerFactoryContextBaseImpl::direction() const { return direction_; }; @@ -287,7 +297,7 @@ ListenerImpl::ListenerImpl(const envoy::config::listener::v3::Listener& config, const std::string& name, bool added_via_api, bool workers_started, uint64_t hash, uint32_t concurrency) : parent_(parent), address_(Network::Address::resolveProtoAddress(config.address())), - bind_to_port_(shouldBindToPort(config)), + bind_to_port_(shouldBindToPort(config)), mptcp_enabled_(config.enable_mptcp()), hand_off_restored_destination_connections_( PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, use_original_dst, false)), per_connection_buffer_limit_bytes_( @@ -299,6 +309,7 @@ ListenerImpl::ListenerImpl(const envoy::config::listener::v3::Listener& config, validation_visitor_( added_via_api_ ? parent_.server_.messageValidationContext().dynamicValidationVisitor() : parent_.server_.messageValidationContext().staticValidationVisitor()), + ignore_global_conn_limit_(config.ignore_global_conn_limit()), listener_init_target_(fmt::format("Listener-init-target {}", name), [this]() { dynamic_init_manager_->initialize(local_init_watcher_); }), dynamic_init_manager_(std::make_unique( @@ -348,6 +359,7 @@ ListenerImpl::ListenerImpl(const envoy::config::listener::v3::Listener& config, buildAccessLog(); auto socket_type = Network::Utility::protobufAddressSocketType(config.address()); + validateConfig(socket_type); // buildUdpListenerFactory() must come before buildListenSocketOptions() because the UDP // listener factory can provide additional options. buildUdpListenerFactory(socket_type, concurrency); @@ -359,6 +371,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. @@ -375,6 +388,7 @@ ListenerImpl::ListenerImpl(ListenerImpl& origin, const std::string& name, bool added_via_api, bool workers_started, uint64_t hash) : parent_(parent), address_(origin.address_), bind_to_port_(shouldBindToPort(config)), + mptcp_enabled_(config.enable_mptcp()), hand_off_restored_destination_connections_( PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, use_original_dst, false)), per_connection_buffer_limit_bytes_( @@ -386,6 +400,7 @@ ListenerImpl::ListenerImpl(ListenerImpl& origin, validation_visitor_( added_via_api_ ? parent_.server_.messageValidationContext().dynamicValidationVisitor() : parent_.server_.messageValidationContext().staticValidationVisitor()), + ignore_global_conn_limit_(config.ignore_global_conn_limit()), // listener_init_target_ is not used during in place update because we expect server started. listener_init_target_("", nullptr), dynamic_init_manager_(std::make_unique( @@ -410,11 +425,12 @@ ListenerImpl::ListenerImpl(ListenerImpl& origin, quic_stat_names_(parent_.quicStatNames()) { buildAccessLog(); auto socket_type = Network::Utility::protobufAddressSocketType(config.address()); + validateConfig(socket_type); buildListenSocketOptions(socket_type); createListenerFilterFactories(socket_type); validateFilterChains(socket_type); buildFilterChains(); - + buildInternalListener(); if (socket_type == Network::Socket::Type::Stream) { // Apply the options below only for TCP. buildSocketOptions(); @@ -424,6 +440,24 @@ ListenerImpl::ListenerImpl(ListenerImpl& origin, } } +void ListenerImpl::validateConfig(Network::Socket::Type socket_type) { + if (mptcp_enabled_) { + if (socket_type != Network::Socket::Type::Stream) { + throw EnvoyException( + fmt::format("listener {}: enable_mptcp can only be used with TCP listeners", name_)); + } + if (address_->type() != Network::Address::Type::Ip) { + throw EnvoyException( + fmt::format("listener {}: enable_mptcp can only be used with IP addresses", name_)); + } + if (!Api::OsSysCallsSingleton::get().supportsMptcp()) { + throw EnvoyException(fmt::format( + "listener {}: enable_mptcp is set but MPTCP is not supported by the operating system", + name_)); + } + } +} + void ListenerImpl::buildAccessLog() { for (const auto& access_log : config_.access_log()) { AccessLog::InstanceSharedPtr current_access_log = @@ -432,6 +466,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) { @@ -680,6 +746,9 @@ Admin& PerListenerFactoryContextImpl::admin() { return listener_factory_context_ const envoy::config::core::v3::Metadata& PerListenerFactoryContextImpl::listenerMetadata() const { return listener_factory_context_base_->listenerMetadata(); }; +const Envoy::Config::TypedMetadata& PerListenerFactoryContextImpl::listenerTypedMetadata() const { + return listener_factory_context_base_->listenerTypedMetadata(); +} envoy::config::core::v3::TrafficDirection PerListenerFactoryContextImpl::direction() const { return listener_factory_context_base_->direction(); }; @@ -784,8 +853,8 @@ bool ListenerImpl::supportUpdateFilterChain(const envoy::config::listener::v3::L } // Full listener update currently rejects tcp listener having 0 filter chain. - // In place filter chain update could survive under zero filter chain but we should keep the same - // behavior for now. This also guards the below filter chain access. + // In place filter chain update could survive under zero filter chain but we should keep the + // same behavior for now. This also guards the below filter chain access. if (config.filter_chains_size() == 0) { return false; } diff --git a/source/server/listener_impl.h b/source/server/listener_impl.h index aac102089f78..765631127c8e 100644 --- a/source/server/listener_impl.h +++ b/source/server/listener_impl.h @@ -5,8 +5,10 @@ #include "envoy/access_log/access_log.h" #include "envoy/config/core/v3/base.pb.h" #include "envoy/config/listener/v3/listener.pb.h" +#include "envoy/config/typed_metadata.h" #include "envoy/network/drain_decision.h" #include "envoy/network/filter.h" +#include "envoy/network/listener.h" #include "envoy/server/drain_manager.h" #include "envoy/server/filter_config.h" #include "envoy/server/instance.h" @@ -15,6 +17,7 @@ #include "source/common/common/basic_resource_impl.h" #include "source/common/common/logger.h" +#include "source/common/config/metadata.h" #include "source/common/init/manager_impl.h" #include "source/common/init/target_impl.h" #include "source/common/quic/quic_stat_names.h" @@ -43,7 +46,9 @@ class ListenSocketFactoryImpl : public Network::ListenSocketFactory, Network::Socket::Type socket_type, const Network::Socket::OptionsSharedPtr& options, const std::string& listener_name, uint32_t tcp_backlog_size, - ListenerComponentFactory::BindType bind_type, uint32_t num_sockets); + ListenerComponentFactory::BindType bind_type, + const Network::SocketCreationOptions& creation_options, + uint32_t num_sockets); // Network::ListenSocketFactory Network::Socket::Type socketType() const override { return socket_type_; } @@ -77,6 +82,7 @@ class ListenSocketFactoryImpl : public Network::ListenSocketFactory, const std::string listener_name_; const uint32_t tcp_backlog_size_; ListenerComponentFactory::BindType bind_type_; + const Network::SocketCreationOptions socket_creation_options_; // One socket for each worker, pre-created before the workers fetch the sockets. There are // 3 different cases: // 1) All are null when doing config validation. @@ -121,6 +127,7 @@ class ListenerFactoryContextBaseImpl final : public Configuration::FactoryContex ThreadLocal::Instance& threadLocal() override; Admin& admin() override; const envoy::config::core::v3::Metadata& listenerMetadata() const override; + const Envoy::Config::TypedMetadata& listenerTypedMetadata() const override; envoy::config::core::v3::TrafficDirection direction() const override; TimeSource& timeSource() override; ProtobufMessage::ValidationContext& messageValidationContext() override; @@ -146,6 +153,8 @@ class ListenerFactoryContextBaseImpl final : public Configuration::FactoryContex private: Envoy::Server::Instance& server_; const envoy::config::core::v3::Metadata metadata_; + const Envoy::Config::TypedMetadataImpl + typed_metadata_; envoy::config::core::v3::TrafficDirection direction_; Stats::ScopePtr global_scope_; Stats::ScopePtr listener_scope_; // Stats with listener named scope. @@ -195,6 +204,7 @@ class PerListenerFactoryContextImpl : public Configuration::ListenerFactoryConte ThreadLocal::Instance& threadLocal() override; Admin& admin() override; const envoy::config::core::v3::Metadata& listenerMetadata() const override; + const Envoy::Config::TypedMetadata& listenerTypedMetadata() const override; envoy::config::core::v3::TrafficDirection direction() const override; TimeSource& timeSource() override; ProtobufMessage::ValidationContext& messageValidationContext() override; @@ -296,6 +306,7 @@ class ListenerImpl final : public Network::ListenerConfig, Network::FilterChainFactory& filterChainFactory() override { return *this; } Network::ListenSocketFactory& listenSocketFactory() override { return *socket_factory_; } bool bindToPort() override { return bind_to_port_; } + bool mptcpEnabled() { return mptcp_enabled_; } bool handOffRestoredDestinationConnections() const override { return hand_off_restored_destination_connections_; } @@ -315,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 { @@ -322,6 +337,7 @@ class ListenerImpl final : public Network::ListenerConfig, } uint32_t tcpBacklogSize() const override { return tcp_backlog_size_; } Init::Manager& initManager() override; + bool ignoreGlobalConnLimit() const override { return ignore_global_conn_limit_; } envoy::config::core::v3::TrafficDirection direction() const override { return config().traffic_direction(); } @@ -361,6 +377,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(). @@ -370,6 +393,8 @@ 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); void createListenerFilterFactories(Network::Socket::Type socket_type); @@ -389,6 +414,7 @@ class ListenerImpl final : public Network::ListenerConfig, Network::ListenSocketFactoryPtr socket_factory_; const bool bind_to_port_; + const bool mptcp_enabled_; const bool hand_off_restored_destination_connections_; const uint32_t per_connection_buffer_limit_bytes_; const uint64_t listener_tag_; @@ -398,6 +424,7 @@ class ListenerImpl final : public Network::ListenerConfig, const uint64_t hash_; const uint32_t tcp_backlog_size_; ProtobufMessage::ValidationVisitor& validation_visitor_; + const bool ignore_global_conn_limit_; // A target is added to Server's InitManager if workers_started_ is false. Init::TargetImpl listener_init_target_; @@ -415,6 +442,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 c1e27ee9db29..839e25385069 100644 --- a/source/server/listener_manager_impl.cc +++ b/source/server/listener_manager_impl.cc @@ -168,9 +168,8 @@ Network::ListenerFilterMatcherSharedPtr ProdListenerComponentFactory::createList Network::SocketSharedPtr ProdListenerComponentFactory::createListenSocket( Network::Address::InstanceConstSharedPtr address, Network::Socket::Type socket_type, - const Network::Socket::OptionsSharedPtr& options, BindType bind_type, uint32_t worker_index) { - ASSERT(address->type() == Network::Address::Type::Ip || - address->type() == Network::Address::Type::Pipe); + const Network::Socket::OptionsSharedPtr& options, BindType bind_type, + const Network::SocketCreationOptions& creation_options, uint32_t worker_index) { ASSERT(socket_type == Network::Socket::Type::Stream || socket_type == Network::Socket::Type::Datagram); @@ -191,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) @@ -212,11 +216,11 @@ Network::SocketSharedPtr ProdListenerComponentFactory::createListenSocket( } if (socket_type == Network::Socket::Type::Stream) { - return std::make_shared(address, options, - bind_type != BindType::NoBind); + return std::make_shared( + address, options, bind_type != BindType::NoBind, creation_options); } else { - return std::make_shared(address, options, - bind_type != BindType::NoBind); + return std::make_shared( + address, options, bind_type != BindType::NoBind, creation_options); } } @@ -328,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. @@ -510,7 +515,7 @@ void ListenerManagerImpl::drainListener(ListenerImplPtr&& listener) { stopListener(*draining_it->listener_, [this, listener_tag]() { for (auto& listener : draining_listeners_) { if (listener.listener_->listenerTag() == listener_tag) { - listener.listener_->listenSocketFactory().closeAllSockets(); + maybeCloseSocketsForListener(*listener.listener_); } } }); @@ -861,7 +866,7 @@ void ListenerManagerImpl::stopListeners(StopListenersType stop_listeners_type) { stats_.listener_stopped_.inc(); for (auto& listener : active_listeners_) { if (listener->listenerTag() == listener_tag) { - listener->listenSocketFactory().closeAllSockets(); + maybeCloseSocketsForListener(*listener); } } }); @@ -956,13 +961,13 @@ Network::DrainableFilterChainSharedPtr ListenerFilterChainFactoryBuilder::buildF void ListenerManagerImpl::setNewOrDrainingSocketFactory( const std::string& name, const envoy::config::core::v3::Address& proto_address, ListenerImpl& listener) { - // Typically we catch address issues when we try to bind to the same address multiple times. - // However, for listeners that do not bind we must check to make sure we are not duplicating. This - // is an edge case and nothing will explicitly break, but there is no possibility that two - // listeners that do not bind will ever be used. Only the first one will be used when searched for - // by address. Thus we block it. - if (!listener.bindToPort() && (hasListenerWithCompatibleAddress(warming_listeners_, listener) || - hasListenerWithCompatibleAddress(active_listeners_, listener))) { + // For listeners that do not bind or listeners that do not bind to port 0 we must check to make + // sure we are not duplicating the address. This avoids ambiguity about which non-binding + // listener is used or even worse for the binding to port != 0 and reuse port case multiple + // different listeners receiving connections destined for the same port. + if ((!listener.bindToPort() || listener.config().address().socket_address().port_value() != 0) && + (hasListenerWithCompatibleAddress(warming_listeners_, listener) || + hasListenerWithCompatibleAddress(active_listeners_, listener))) { const std::string message = fmt::format("error adding listener: '{}' has duplicate address '{}' as existing listener", name, listener.address()->asString()); @@ -1017,9 +1022,11 @@ Network::ListenSocketFactoryPtr ListenerManagerImpl::createListenSocketFactory( : ListenerComponentFactory::BindType::NoReusePort; } TRY_ASSERT_MAIN_THREAD { + Network::SocketCreationOptions creation_options; + creation_options.mptcp_enabled_ = listener.mptcpEnabled(); return std::make_unique( factory_, listener.address(), socket_type, listener.listenSocketOptions(), listener.name(), - listener.tcpBacklogSize(), bind_type, server_.options().concurrency()); + listener.tcpBacklogSize(), bind_type, creation_options, server_.options().concurrency()); } END_TRY catch (const EnvoyException& e) { @@ -1030,6 +1037,17 @@ Network::ListenSocketFactoryPtr ListenerManagerImpl::createListenSocketFactory( } } +void ListenerManagerImpl::maybeCloseSocketsForListener(ListenerImpl& listener) { + if (!listener.udpListenerConfig().has_value() || + listener.udpListenerConfig()->listenerFactory().isTransportConnectionless()) { + // Close the listen sockets right away to avoid leaving TCP connections in accept queue + // already waiting for long timeout. However, connection-oriented UDP listeners shouldn't + // close the socket because they need to receive packets for existing connections via the + // listen sockets. + listener.listenSocketFactory().closeAllSockets(); + } +} + ApiListenerOptRef ListenerManagerImpl::apiListener() { return api_listener_ ? ApiListenerOptRef(std::ref(*api_listener_)) : absl::nullopt; } diff --git a/source/server/listener_manager_impl.h b/source/server/listener_manager_impl.h index 3401b71ad43a..5711a988bd0e 100644 --- a/source/server/listener_manager_impl.h +++ b/source/server/listener_manager_impl.h @@ -91,10 +91,10 @@ class ProdListenerComponentFactory : public ListenerComponentFactory, return createUdpListenerFilterFactoryList_(filters, context); } - Network::SocketSharedPtr createListenSocket(Network::Address::InstanceConstSharedPtr address, - Network::Socket::Type socket_type, - const Network::Socket::OptionsSharedPtr& options, - BindType bind_type, uint32_t worker_index) override; + Network::SocketSharedPtr 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) override; DrainManagerPtr createDrainManager(envoy::config::listener::v3::Listener::DrainType drain_type) override; @@ -299,6 +299,8 @@ class ListenerManagerImpl : public ListenerManager, Logger::Loggable 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 697dee2440f1..3c4426ade7ae 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/overload_manager_impl.cc b/source/server/overload_manager_impl.cc index 1e999e718ce9..4a274c145e1b 100644 --- a/source/server/overload_manager_impl.cc +++ b/source/server/overload_manager_impl.cc @@ -25,9 +25,13 @@ namespace Server { */ class ThreadLocalOverloadStateImpl : public ThreadLocalOverloadState { public: - explicit ThreadLocalOverloadStateImpl(const NamedOverloadActionSymbolTable& action_symbol_table) + explicit ThreadLocalOverloadStateImpl( + const NamedOverloadActionSymbolTable& action_symbol_table, + std::shared_ptr>& + proactive_resources) : action_symbol_table_(action_symbol_table), - actions_(action_symbol_table.size(), OverloadActionState(UnitFloat::min())) {} + actions_(action_symbol_table.size(), OverloadActionState(UnitFloat::min())), + proactive_resources_(proactive_resources) {} const OverloadActionState& getState(const std::string& action) override { if (const auto symbol = action_symbol_table_.lookup(action); symbol != absl::nullopt) { @@ -40,10 +44,44 @@ class ThreadLocalOverloadStateImpl : public ThreadLocalOverloadState { actions_[action.index()] = state; } + bool tryAllocateResource(OverloadProactiveResourceName resource_name, + int64_t increment) override { + const auto proactive_resource = proactive_resources_->find(resource_name); + if (proactive_resource != proactive_resources_->end()) { + return proactive_resource->second.tryAllocateResource(increment); + } else { + ENVOY_LOG_MISC(warn, " {Failed to allocate unknown proactive resource }"); + // Resource monitor is not configured. + return false; + } + } + + bool tryDeallocateResource(OverloadProactiveResourceName resource_name, + int64_t decrement) override { + const auto proactive_resource = proactive_resources_->find(resource_name); + if (proactive_resource != proactive_resources_->end()) { + if (proactive_resource->second.tryDeallocateResource(decrement)) { + return true; + } else { + return false; + } + } else { + ENVOY_LOG_MISC(warn, " {Failed to deallocate unknown proactive resource }"); + return false; + } + } + + bool isResourceMonitorEnabled(OverloadProactiveResourceName resource_name) override { + const auto proactive_resource = proactive_resources_->find(resource_name); + return proactive_resource != proactive_resources_->end(); + } + private: static const OverloadActionState always_inactive_; const NamedOverloadActionSymbolTable& action_symbol_table_; std::vector actions_; + std::shared_ptr> + proactive_resources_; }; const OverloadActionState ThreadLocalOverloadStateImpl::always_inactive_{UnitFloat::min()}; @@ -268,19 +306,47 @@ OverloadManagerImpl::OverloadManagerImpl(Event::Dispatcher& dispatcher, Stats::S Api::Api& api, const Server::Options& options) : started_(false), dispatcher_(dispatcher), tls_(slot_allocator), refresh_interval_( - std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(config, refresh_interval, 1000))) { + std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(config, refresh_interval, 1000))), + proactive_resources_( + std::make_unique< + absl::node_hash_map>()) { Configuration::ResourceMonitorFactoryContextImpl context(dispatcher, options, api, validation_visitor); + // We should hide impl details from users, for them there should be no distinction between + // proactive and regular resource monitors in configuration API. But internally we will maintain + // two distinct collections of proactive and regular resources. Proactive resources are not + // subject to periodic flushes and can be recalculated/updated on demand by invoking + // `tryAllocateResource/tryDeallocateResource` via thread local overload state. for (const auto& resource : config.resource_monitors()) { const auto& name = resource.name(); - ENVOY_LOG(debug, "Adding resource monitor for {}", name); - auto& factory = - Config::Utility::getAndCheckFactory(resource); - auto config = Config::Utility::translateToFactoryConfig(resource, validation_visitor, factory); - auto monitor = factory.createResourceMonitor(*config, context); - - auto result = resources_.try_emplace(name, name, std::move(monitor), *this, stats_scope); - if (!result.second) { + // Check if it is a proactive resource. + auto proactive_resource_it = + OverloadProactiveResources::get().proactive_action_name_to_resource_.find(name); + ENVOY_LOG(debug, "Evaluating resource {}", name); + bool result = false; + if (proactive_resource_it != + OverloadProactiveResources::get().proactive_action_name_to_resource_.end()) { + ENVOY_LOG(debug, "Adding proactive resource monitor for {}", name); + auto& factory = + Config::Utility::getAndCheckFactory( + resource); + auto config = + Config::Utility::translateToFactoryConfig(resource, validation_visitor, factory); + auto monitor = factory.createProactiveResourceMonitor(*config, context); + result = + proactive_resources_ + ->try_emplace(proactive_resource_it->second, name, std::move(monitor), stats_scope) + .second; + } else { + ENVOY_LOG(debug, "Adding resource monitor for {}", name); + auto& factory = + Config::Utility::getAndCheckFactory(resource); + auto config = + Config::Utility::translateToFactoryConfig(resource, validation_visitor, factory); + auto monitor = factory.createResourceMonitor(*config, context); + result = resources_.try_emplace(name, name, std::move(monitor), *this, stats_scope).second; + } + if (!result) { throw EnvoyException(absl::StrCat("Duplicate resource monitor ", name)); } } @@ -315,12 +381,15 @@ OverloadManagerImpl::OverloadManagerImpl(Event::Dispatcher& dispatcher, Stats::S for (const auto& trigger : action.triggers()) { const std::string& resource = trigger.name(); + auto proactive_resource_it = + OverloadProactiveResources::get().proactive_action_name_to_resource_.find(resource); - if (resources_.find(resource) == resources_.end()) { + if (resources_.find(resource) == resources_.end() && + proactive_resource_it == + OverloadProactiveResources::get().proactive_action_name_to_resource_.end()) { throw EnvoyException( fmt::format("Unknown trigger resource {} for overload action {}", resource, name)); } - resource_to_actions_.insert(std::make_pair(resource, symbol)); } } @@ -331,7 +400,8 @@ void OverloadManagerImpl::start() { started_ = true; tls_.set([this](Event::Dispatcher&) { - return std::make_shared(action_symbol_table_); + return std::make_shared(action_symbol_table_, + proactive_resources_); }); if (resources_.empty()) { @@ -364,6 +434,8 @@ void OverloadManagerImpl::stop() { // Clear the resource map to block on any pending updates. resources_.clear(); + + // TODO(nezdolik): wrap proactive monitors into atomic? and clear it here } bool OverloadManagerImpl::registerForAction(const std::string& action, diff --git a/source/server/overload_manager_impl.h b/source/server/overload_manager_impl.h index 784308b1f4b8..b7e4d3e12fb0 100644 --- a/source/server/overload_manager_impl.h +++ b/source/server/overload_manager_impl.h @@ -132,12 +132,12 @@ class OverloadManagerImpl : Logger::Loggable, public OverloadM private: using FlushEpochId = uint64_t; - class Resource : public ResourceMonitor::Callbacks { + class Resource : public ResourceUpdateCallbacks { public: Resource(const std::string& name, ResourceMonitorPtr monitor, OverloadManagerImpl& manager, Stats::Scope& stats_scope); - // ResourceMonitor::Callbacks + // ResourceMonitor::ResourceUpdateCallbacks void onSuccess(const ResourceUsage& usage) override; void onFailure(const EnvoyException& error) override; @@ -173,6 +173,9 @@ class OverloadManagerImpl : Logger::Loggable, public OverloadM const std::chrono::milliseconds refresh_interval_; Event::TimerPtr timer_; absl::node_hash_map resources_; + std::shared_ptr> + proactive_resources_; + absl::node_hash_map actions_; Event::ScaledTimerTypeMapConstSharedPtr timer_minimums_; diff --git a/source/server/server.cc b/source/server/server.cc index 561a6162f933..86a61ecc4250 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -20,6 +20,7 @@ #include "envoy/server/bootstrap_extension_config.h" #include "envoy/server/instance.h" #include "envoy/server/options.h" +#include "envoy/stats/histogram.h" #include "envoy/stats/stats.h" #include "envoy/upstream/cluster_manager.h" @@ -38,7 +39,7 @@ #include "source/common/local_info/local_info_impl.h" #include "source/common/memory/stats.h" #include "source/common/network/address_impl.h" -#include "source/common/network/dns_resolver/dns_factory.h" +#include "source/common/network/dns_resolver/dns_factory_util.h" #include "source/common/network/socket_interface.h" #include "source/common/network/socket_interface_impl.h" #include "source/common/network/tcp_listener_impl.h" @@ -149,6 +150,16 @@ InstanceImpl::~InstanceImpl() { listener_manager_.reset(); ENVOY_LOG(debug, "destroyed listener manager"); dispatcher_->shutdown(); + +#ifdef ENVOY_PERFETTO + if (tracing_session_ != nullptr) { + // Flush the trace data. + perfetto::TrackEvent::Flush(); + // Disable tracing and block until tracing has stopped. + tracing_session_->StopBlocking(); + close(tracing_fd_); + } +#endif } Upstream::ClusterManager& InstanceImpl::clusterManager() { @@ -168,7 +179,7 @@ void InstanceImpl::failHealthcheck(bool fail) { } MetricSnapshotImpl::MetricSnapshotImpl(Stats::Store& store, TimeSource& time_source) { - store.forEachCounter( + store.forEachSinkedCounter( [this](std::size_t size) mutable { snapped_counters_.reserve(size); counters_.reserve(size); @@ -178,7 +189,7 @@ MetricSnapshotImpl::MetricSnapshotImpl(Stats::Store& store, TimeSource& time_sou counters_.push_back({counter.latch(), counter}); }); - store.forEachGauge( + store.forEachSinkedGauge( [this](std::size_t size) mutable { snapped_gauges_.reserve(size); gauges_.reserve(size); @@ -195,7 +206,7 @@ MetricSnapshotImpl::MetricSnapshotImpl(Stats::Store& store, TimeSource& time_sou histograms_.push_back(*histogram); } - store.forEachTextReadout( + store.forEachSinkedTextReadout( [this](std::size_t size) mutable { snapped_text_readouts_.reserve(size); text_readouts_.reserve(size); @@ -379,6 +390,36 @@ void InstanceImpl::initialize(Network::Address::InstanceConstSharedPtr local_add messageValidationContext().staticValidationVisitor(), *api_); bootstrap_config_update_time_ = time_source_.systemTime(); +#ifdef ENVOY_PERFETTO + perfetto::TracingInitArgs args; + // Include in-process events only. + args.backends = perfetto::kInProcessBackend; + perfetto::Tracing::Initialize(args); + perfetto::TrackEvent::Register(); + + // Prepare a configuration for a new "Perfetto" tracing session. + perfetto::TraceConfig cfg; + // TODO(rojkov): make the tracer configurable with either "Perfetto"'s native + // message or custom one embedded into Bootstrap. + cfg.add_buffers()->set_size_kb(1024); + auto* ds_cfg = cfg.add_data_sources()->mutable_config(); + ds_cfg->set_name("track_event"); + + const std::string pftrace_path = + PROTOBUF_GET_STRING_OR_DEFAULT(bootstrap_, perf_tracing_file_path, "envoy.pftrace"); + // Instantiate a new tracing session. + tracing_session_ = perfetto::Tracing::NewTrace(); + tracing_fd_ = open(pftrace_path.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0600); + if (tracing_fd_ == -1) { + throw EnvoyException( + fmt::format("unable to open tracing file {}: {}", pftrace_path, errorDetails(errno))); + } + // Configure the tracing session. + tracing_session_->Setup(cfg, tracing_fd_); + // Enable tracing and block until tracing has started. + tracing_session_->StartBlocking(); +#endif + // Immediate after the bootstrap has been loaded, override the header prefix, if configured to // do so. This must be set before any other code block references the HeaderValues ConstSingleton. if (!bootstrap_.header_prefix().empty()) { @@ -397,7 +438,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_)); @@ -487,7 +528,8 @@ void InstanceImpl::initialize(Network::Address::InstanceConstSharedPtr local_add // This is needed so that we don't read the value until runtime is fully initialized. enable_reuse_port_default_ = ReusePortDefault::Runtime; } - admin_ = std::make_unique(initial_config.admin().profilePath(), *this); + admin_ = std::make_unique(initial_config.admin().profilePath(), *this, + initial_config.admin().ignoreGlobalConnLimit()); loadServerFlags(initial_config.flagsPath()); @@ -920,7 +962,7 @@ InstanceImpl::registerCallback(Stage stage, StageCallbackWithCompletion callback } void InstanceImpl::notifyCallbacksForStage(Stage stage, Event::PostCb completion_cb) { - ASSERT(Thread::MainThread::isMainOrTestThread()); + ASSERT_IS_MAIN_OR_TEST_THREAD(); const auto it = stage_callbacks_.find(stage); if (it != stage_callbacks_.end()) { for (const StageCallback& callback : it->second) { diff --git a/source/server/server.h b/source/server/server.h index 19ac1b5ce169..29f4944f74d4 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -27,6 +27,7 @@ #include "source/common/common/assert.h" #include "source/common/common/cleanup.h" #include "source/common/common/logger_delegates.h" +#include "source/common/common/perf_tracing.h" #include "source/common/grpc/async_client_manager_impl.h" #include "source/common/grpc/context_impl.h" #include "source/common/http/context_impl.h" @@ -293,6 +294,10 @@ class InstanceImpl final : Logger::Loggable, Quic::QuicStatNames& quicStatNames() { return quic_stat_names_; } + void setSinkPredicates(std::unique_ptr&& sink_predicates) override { + stats_store_.setSinkPredicates(std::move(sink_predicates)); + } + // ServerLifecycleNotifier ServerLifecycleNotifier::HandlePtr registerCallback(Stage stage, StageCallback callback) override; ServerLifecycleNotifier::HandlePtr @@ -399,6 +404,11 @@ class InstanceImpl final : Logger::Loggable, LifecycleCallbackHandle(std::list& callbacks, T& callback) : RaiiListElement(callbacks, callback) {} }; + +#ifdef ENVOY_PERFETTO + std::unique_ptr tracing_session_{}; + os_fd_t tracing_fd_{INVALID_HANDLE}; +#endif }; // Local implementation of Stats::MetricSnapshot used to flush metrics to sinks. We could diff --git a/test/benchmark/main.cc b/test/benchmark/main.cc index 3af910496320..6ab63fb6a51d 100644 --- a/test/benchmark/main.cc +++ b/test/benchmark/main.cc @@ -43,7 +43,6 @@ int main(int argc, char** argv) { } TestEnvironment::initializeTestMain(argv[0]); - Thread::TestThread test_thread; // Suppressing non-error messages in benchmark tests. This hides warning // messages that appear when using a runtime feature when there isn't an initialized diff --git a/test/common/access_log/BUILD b/test/common/access_log/BUILD index 5a1908dfb08a..97af03fc5e15 100644 --- a/test/common/access_log/BUILD +++ b/test/common/access_log/BUILD @@ -11,12 +11,17 @@ envoy_package() envoy_cc_test( name = "access_log_impl_test", srcs = ["access_log_impl_test.cc"], + copts = select({ + "//bazel:windows_x86_64": [], # TODO: fix the windows ANTLR build + "//conditions:default": [ + "-DUSE_CEL_PARSER", + ], + }), deps = [ "//source/common/access_log:access_log_lib", "//source/common/stream_info:utility_lib", "//source/extensions/access_loggers/file:config", - "//source/extensions/access_loggers/grpc:http_config", - "//source/extensions/access_loggers/grpc:tcp_config", + "//source/extensions/access_loggers/filters/cel:config", "//source/extensions/access_loggers/stream:config", "//test/common/stream_info:test_util", "//test/common/upstream:utility_lib", diff --git a/test/common/access_log/access_log_impl_test.cc b/test/common/access_log/access_log_impl_test.cc index 07b03106064b..48f8dcf72aba 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"); @@ -102,9 +106,9 @@ name: accesslog EXPECT_CALL(*file_, write(_)); 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_.upstreamInfo()->setUpstreamHost( + Upstream::makeTestHostDescription(cluster, "tcp://10.0.0.5:1234", simTime())); + 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"); @@ -213,8 +217,8 @@ name: accesslog TEST_F(AccessLogImplTest, UpstreamHost) { auto cluster = std::make_shared>(); - stream_info_.upstream_host_ = - Upstream::makeTestHostDescription(cluster, "tcp://10.0.0.5:1234", simTime()); + stream_info_.upstreamInfo()->setUpstreamHost( + Upstream::makeTestHostDescription(cluster, "tcp://10.0.0.5:1234", simTime())); const std::string yaml = R"EOF( name: accesslog @@ -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(); @@ -1592,6 +1598,72 @@ name: accesslog } } +#if defined(USE_CEL_PARSER) +TEST_F(AccessLogImplTest, CelExtensionFilter) { + const std::string yaml = R"EOF( +name: accesslog +filter: + extension_filter: + name: cel_extension_filter + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: "(request.headers['log'] == 'true') && (response.code >= 400)" +typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + )EOF"; + + InstanceSharedPtr logger = AccessLogFactory::fromProto(parseAccessLogFromV3Yaml(yaml), context_); + + request_headers_.addCopy("log", "true"); + stream_info_.response_code_ = 404; + EXPECT_CALL(*file_, write(_)); + logger->log(&request_headers_, &response_headers_, &response_trailers_, stream_info_); + + request_headers_.remove("log"); + EXPECT_CALL(*file_, write(_)).Times(0); + logger->log(&request_headers_, &response_headers_, &response_trailers_, stream_info_); +} + +TEST_F(AccessLogImplTest, CelExtensionFilterExpressionError) { + const std::string yaml = R"EOF( +name: accesslog +filter: + extension_filter: + name: cel_extension_filter + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: "foo['test']" +typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + )EOF"; + + InstanceSharedPtr logger = AccessLogFactory::fromProto(parseAccessLogFromV3Yaml(yaml), context_); + + EXPECT_CALL(*file_, write(_)).Times(0); + logger->log(&request_headers_, &response_headers_, &response_trailers_, stream_info_); +} + +TEST_F(AccessLogImplTest, CelExtensionFilterExpressionUnparsable) { + const std::string yaml = R"EOF( +name: accesslog +filter: + extension_filter: + name: cel_extension_filter + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: "(+++" +typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + )EOF"; + + EXPECT_THROW_WITH_REGEX(AccessLogFactory::fromProto(parseAccessLogFromV3Yaml(yaml), context_), + EnvoyException, "Not able to parse filter expression: .*"); +} +#endif // USE_CEL_PARSER + // Test that the deprecated extension names are disabled by default. // TODO(zuercher): remove when envoy.deprecated_features.allow_deprecated_extension_names is removed TEST_F(AccessLogImplTest, DEPRECATED_FEATURE_TEST(DeprecatedExtensionFilterName)) { diff --git a/test/common/api/BUILD b/test/common/api/BUILD new file mode 100644 index 000000000000..d8d514ba3a41 --- /dev/null +++ b/test/common/api/BUILD @@ -0,0 +1,17 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_test", + "envoy_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_test( + name = "os_sys_calls_test", + srcs = ["os_sys_calls_test.cc"], + deps = [ + "//source/common/api:os_sys_calls_lib", + ], +) diff --git a/test/common/api/os_sys_calls_test.cc b/test/common/api/os_sys_calls_test.cc new file mode 100644 index 000000000000..5748b257ac79 --- /dev/null +++ b/test/common/api/os_sys_calls_test.cc @@ -0,0 +1,34 @@ +#include "source/common/api/os_sys_calls_impl.h" + +#include "gtest/gtest.h" + +namespace Envoy { + +TEST(OsSyscallsTest, SetAlternateGetifaddrs) { + auto& os_syscalls = Api::OsSysCallsSingleton::get(); + const bool pre_alternate_support = os_syscalls.supportsGetifaddrs(); + Api::InterfaceAddressVector interfaces{}; +#if defined(WIN32) || (defined(__ANDROID_API__) && __ANDROID_API__ < 24) + EXPECT_FALSE(pre_alternate_support); + EXPECT_DEATH(os_syscalls.getifaddrs(interfaces), "not implemented"); +#else + EXPECT_TRUE(pre_alternate_support); + const auto pre_alternate_rc = os_syscalls.getifaddrs(interfaces); + EXPECT_EQ(0, pre_alternate_rc.return_value_); + EXPECT_FALSE(interfaces.empty()); +#endif + + os_syscalls.setAlternateGetifaddrs( + [](Api::InterfaceAddressVector& interfaces) -> Api::SysCallIntResult { + interfaces.emplace_back("made_up_if", 0, nullptr); + return {0, 0}; + }); + interfaces.clear(); + + const bool post_alternate_support = os_syscalls.supportsGetifaddrs(); + EXPECT_TRUE(post_alternate_support); + EXPECT_EQ(0, os_syscalls.getifaddrs(interfaces).return_value_); + EXPECT_EQ(1, interfaces.size()); + EXPECT_EQ("made_up_if", interfaces.front().interface_name_); +} +} // namespace Envoy diff --git a/test/common/buffer/BUILD b/test/common/buffer/BUILD index 43cfdcf59608..1e17b41e4e8e 100644 --- a/test/common/buffer/BUILD +++ b/test/common/buffer/BUILD @@ -112,6 +112,8 @@ envoy_cc_benchmark_binary( ], deps = [ "//source/common/buffer:buffer_lib", + "//source/common/buffer:watermark_buffer_lib", + "@envoy_api//envoy/config/overload/v3:pkg_cc_proto", ], ) diff --git a/test/common/buffer/buffer_speed_test.cc b/test/common/buffer/buffer_speed_test.cc index 458a92098aa0..d9ea2df1efb9 100644 --- a/test/common/buffer/buffer_speed_test.cc +++ b/test/common/buffer/buffer_speed_test.cc @@ -1,4 +1,8 @@ +#include "envoy/config/overload/v3/overload.pb.h" +#include "envoy/http/stream_reset_handler.h" + #include "source/common/buffer/buffer_impl.h" +#include "source/common/buffer/watermark_buffer.h" #include "source/common/common/assert.h" #include "absl/strings/string_view.h" @@ -8,6 +12,11 @@ namespace Envoy { static constexpr uint64_t MaxBufferLength = 1024 * 1024; +class FakeStreamResetHandler : public Http::StreamResetHandler { +public: + void resetStream(Http::StreamResetReason reason) override { UNREFERENCED_PARAMETER(reason); } +}; + // The fragment needs to be heap allocated in order to survive past the processing done in the inner // loop in the benchmarks below. Do not attempt to release the actual contents of the buffer. void deleteFragment(const void*, size_t, const Buffer::BufferFragmentImpl* self) { delete self; } @@ -24,6 +33,82 @@ static void bufferCreateEmpty(benchmark::State& state) { } BENCHMARK(bufferCreateEmpty); +// Test add performance of OwnedImpl vs WatermarkBuffer +static void bufferVsWatermarkBuffer(benchmark::State& state) { + const uint64_t length = state.range(0); + const uint64_t high_watermark = state.range(1); + const uint64_t step = state.range(2); + const bool use_watermark_buffer = (state.range(3) != 0); + const std::string data(step, 'a'); + + for (auto _ : state) { + UNREFERENCED_PARAMETER(_); + std::unique_ptr buffer; + if (use_watermark_buffer) { + buffer = std::make_unique([]() {}, []() {}, []() {}); + buffer->setWatermarks(high_watermark); + } else { + buffer = std::make_unique(); + } + + for (uint64_t idx = 0; idx < length; idx += step) { + buffer->add(data); + } + } +} +BENCHMARK(bufferVsWatermarkBuffer) + ->Args({1024, 0, 1, 0}) + ->Args({1024, 0, 1, 1}) + ->Args({1024, 1, 1, 1}) + ->Args({1024, 1024, 1, 1}) + ->Args({64 * 1024, 0, 1, 0}) + ->Args({64 * 1024, 0, 1, 1}) + ->Args({64 * 1024, 1, 1, 1}) + ->Args({64 * 1024, 64 * 1024, 1, 1}) + ->Args({64 * 1024, 0, 32, 0}) + ->Args({64 * 1024, 64 * 1024, 32, 1}) + ->Args({1024 * 1024, 0, 1, 0}) + ->Args({1024 * 1024, 0, 1, 1}) + ->Args({1024 * 1024, 1, 1, 1}) + ->Args({1024 * 1024, 1024 * 1024, 1, 1}) + ->Args({1024 * 1024, 0, 32, 0}) + ->Args({1024 * 1024, 1024 * 1024, 32, 1}); + +// Measure performance impact of enabling accounts. +static void bufferAccountUse(benchmark::State& state) { + const std::string data(state.range(0), 'a'); + uint64_t iters = state.range(1); + const bool enable_accounts = (state.range(2) != 0); + + auto config = envoy::config::overload::v3::BufferFactoryConfig(); + config.set_minimum_account_to_track_power_of_two(2); + Buffer::WatermarkBufferFactory buffer_factory(config); + FakeStreamResetHandler reset_handler; + auto account = buffer_factory.createAccount(reset_handler); + RELEASE_ASSERT(account != nullptr, ""); + + for (auto _ : state) { + UNREFERENCED_PARAMETER(_); + Buffer::OwnedImpl buffer; + if (enable_accounts) { + buffer.bindAccount(account); + } + for (uint64_t idx = 0; idx < iters; ++idx) { + buffer.add(data); + } + } + account->clearDownstream(); +} +BENCHMARK(bufferAccountUse) + ->Args({1, 1024 * 1024, 0}) + ->Args({1, 1024 * 1024, 1}) + ->Args({1024, 1024, 0}) + ->Args({1024, 1024, 1}) + ->Args({4 * 1024, 1024, 0}) + ->Args({4 * 1024, 1024, 1}) + ->Args({16 * 1024, 1024, 0}) + ->Args({16 * 1024, 1024, 1}); + // Test the creation of an OwnedImpl with varying amounts of content. static void bufferCreate(benchmark::State& state) { const std::string data(state.range(0), 'a'); diff --git a/test/common/buffer/owned_impl_test.cc b/test/common/buffer/owned_impl_test.cc index 854be0f4dd5e..7a4adc7d3058 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/common/logger_test.cc b/test/common/common/logger_test.cc index ba0578abaecf..faeb939f2d21 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,15 +176,77 @@ 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"); } +struct TlsLogSink : SinkDelegate { + TlsLogSink(DelegatingLogSinkSharedPtr log_sink) : SinkDelegate(log_sink) { setTlsDelegate(); } + ~TlsLogSink() override { restoreTlsDelegate(); } + + 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, ()); +}; + +// 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(_, _)); + ENVOY_LOG_MISC(info, "hello tls"); + + // Calls on other threads should use the global sink. + std::thread([&]() { + 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(_, _)); + ENVOY_LOG_MISC(info, "hello tls"); + + // All the logging functions should be delegated to the TLS override. + EXPECT_CALL(tls_sink, flush()); + Registry::getSink()->flush(); + + EXPECT_CALL(tls_sink, logWithStableName(_, _, _, _)); + Registry::getSink()->logWithStableName("foo", "level", "bar", "msg"); + } + + // 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(_, _)); + 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/common/common/utility_test.cc b/test/common/common/utility_test.cc index 344f45aba0f0..274ab220dd38 100644 --- a/test/common/common/utility_test.cc +++ b/test/common/common/utility_test.cc @@ -127,6 +127,35 @@ TEST(StringUtil, atoull) { EXPECT_EQ(18446744073709551615U, out); } +TEST(StringUtil, hasEmptySpace) { + EXPECT_FALSE(StringUtil::hasEmptySpace("1234567890_-+=][|\"&*^%$#@!")); + EXPECT_TRUE(StringUtil::hasEmptySpace("1233 789")); + EXPECT_TRUE(StringUtil::hasEmptySpace("1233\t789")); + EXPECT_TRUE(StringUtil::hasEmptySpace("1233\f789")); + EXPECT_TRUE(StringUtil::hasEmptySpace("1233\v789")); + EXPECT_TRUE(StringUtil::hasEmptySpace("1233\n789")); + EXPECT_TRUE(StringUtil::hasEmptySpace("1233\r789")); + + EXPECT_TRUE(StringUtil::hasEmptySpace("1233 \t\f789")); + EXPECT_TRUE(StringUtil::hasEmptySpace("1233\v\n\r789")); + EXPECT_TRUE(StringUtil::hasEmptySpace("1233\f\v\n789")); +} + +TEST(StringUtil, replaceAllEmptySpace) { + EXPECT_EQ("1234567890_-+=][|\"&*^%$#@!", + StringUtil::replaceAllEmptySpace("1234567890_-+=][|\"&*^%$#@!")); + EXPECT_EQ("1233_789", StringUtil::replaceAllEmptySpace("1233 789")); + EXPECT_EQ("1233_789", StringUtil::replaceAllEmptySpace("1233\t789")); + EXPECT_EQ("1233_789", StringUtil::replaceAllEmptySpace("1233\f789")); + EXPECT_EQ("1233_789", StringUtil::replaceAllEmptySpace("1233\v789")); + EXPECT_EQ("1233_789", StringUtil::replaceAllEmptySpace("1233\n789")); + EXPECT_EQ("1233_789", StringUtil::replaceAllEmptySpace("1233\r789")); + + EXPECT_EQ("1233___789", StringUtil::replaceAllEmptySpace("1233 \t\f789")); + EXPECT_EQ("1233___789", StringUtil::replaceAllEmptySpace("1233\v\n\r789")); + EXPECT_EQ("1233___789", StringUtil::replaceAllEmptySpace("1233\f\v\n789")); +} + TEST(DateUtil, All) { EXPECT_FALSE(DateUtil::timePointValid(SystemTime())); DangerousDeprecatedTestTime test_time; @@ -214,12 +243,12 @@ TEST(InputConstMemoryStream, All) { } TEST(StringUtil, WhitespaceChars) { - EXPECT_NE(nullptr, strchr(StringUtil::WhitespaceChars, ' ')); - EXPECT_NE(nullptr, strchr(StringUtil::WhitespaceChars, '\t')); - EXPECT_NE(nullptr, strchr(StringUtil::WhitespaceChars, '\f')); - EXPECT_NE(nullptr, strchr(StringUtil::WhitespaceChars, '\v')); - EXPECT_NE(nullptr, strchr(StringUtil::WhitespaceChars, '\n')); - EXPECT_NE(nullptr, strchr(StringUtil::WhitespaceChars, '\r')); + EXPECT_NE(nullptr, strchr(StringUtil::WhitespaceChars.data(), ' ')); + EXPECT_NE(nullptr, strchr(StringUtil::WhitespaceChars.data(), '\t')); + EXPECT_NE(nullptr, strchr(StringUtil::WhitespaceChars.data(), '\f')); + EXPECT_NE(nullptr, strchr(StringUtil::WhitespaceChars.data(), '\v')); + EXPECT_NE(nullptr, strchr(StringUtil::WhitespaceChars.data(), '\n')); + EXPECT_NE(nullptr, strchr(StringUtil::WhitespaceChars.data(), '\r')); } TEST(StringUtil, itoa) { diff --git a/test/common/config/subscription_factory_impl_test.cc b/test/common/config/subscription_factory_impl_test.cc index 0aa9a84786d2..c27f407b1a7a 100644 --- a/test/common/config/subscription_factory_impl_test.cc +++ b/test/common/config/subscription_factory_impl_test.cc @@ -353,11 +353,16 @@ TEST_P(SubscriptionFactoryTestUnifiedOrLegacyMux, GrpcCollectionSubscriptionUnsu Upstream::ClusterManager::ClusterSet primary_clusters; primary_clusters.insert("static_cluster"); EXPECT_CALL(cm_, primaryClusters()).WillOnce(ReturnRef(primary_clusters)); + std::string expected_config_text = R"pb(api_type: GRPC)pb"; + envoy::config::core::v3::ApiConfigSource expected_config_proto; + Protobuf::TextFormat::ParseFromString(expected_config_text, &expected_config_proto); EXPECT_THROW_WITH_REGEX( collectionSubscriptionFromUrl( "xdstp://foo/envoy.config.endpoint.v3.ClusterLoadAssignment/bar", config) ->start({}), - EnvoyException, "Unknown xdstp:// transport API type in api_type: GRPC"); + EnvoyException, + fmt::format("Unknown xdstp:// transport API type in {}", + expected_config_proto.DebugString())); } TEST_P(SubscriptionFactoryTestUnifiedOrLegacyMux, diff --git a/test/common/config/utility_test.cc b/test/common/config/utility_test.cc index f822f37e8cc8..c55a580295db 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/conn_pool/conn_pool_base_test.cc b/test/common/conn_pool/conn_pool_base_test.cc index 90ec7ceb6456..ba0f4a39f883 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/common/event/file_event_impl_test.cc b/test/common/event/file_event_impl_test.cc index 6bcd002d3c39..d3a842ada010 100644 --- a/test/common/event/file_event_impl_test.cc +++ b/test/common/event/file_event_impl_test.cc @@ -88,8 +88,6 @@ TEST_P(FileEventImplActivateTest, Activate) { EXPECT_CALL(read_event, ready()); ReadyWatcher write_event; EXPECT_CALL(write_event, ready()); - ReadyWatcher closed_event; - EXPECT_CALL(closed_event, ready()); const FileTriggerType trigger = Event::PlatformDefaultTriggerType; @@ -103,14 +101,10 @@ TEST_P(FileEventImplActivateTest, Activate) { if (events & FileReadyType::Write) { write_event.ready(); } - - if (events & FileReadyType::Closed) { - closed_event.ready(); - } }, - trigger, FileReadyType::Read | FileReadyType::Write | FileReadyType::Closed); + trigger, FileReadyType::Read | FileReadyType::Write); - file_event->activate(FileReadyType::Read | FileReadyType::Write | FileReadyType::Closed); + file_event->activate(FileReadyType::Read | FileReadyType::Write); dispatcher->run(Event::Dispatcher::RunType::NonBlock); os_sys_calls_.close(fd); @@ -125,7 +119,6 @@ TEST_P(FileEventImplActivateTest, ActivateChaining) { ReadyWatcher fd_event; ReadyWatcher read_event; ReadyWatcher write_event; - ReadyWatcher closed_event; ReadyWatcher prepare_watcher; evwatch_prepare_new(&static_cast(dispatcher.get())->base(), onWatcherReady, @@ -140,19 +133,13 @@ TEST_P(FileEventImplActivateTest, ActivateChaining) { if (events & FileReadyType::Read) { read_event.ready(); file_event->activate(FileReadyType::Write); - file_event->activate(FileReadyType::Closed); } if (events & FileReadyType::Write) { write_event.ready(); - file_event->activate(FileReadyType::Closed); - } - - if (events & FileReadyType::Closed) { - closed_event.ready(); } }, - trigger, FileReadyType::Read | FileReadyType::Write | FileReadyType::Closed); + trigger, FileReadyType::Read | FileReadyType::Write); testing::InSequence s; // First loop iteration: handle scheduled read event and the real write event produced by poll. @@ -166,13 +153,10 @@ TEST_P(FileEventImplActivateTest, ActivateChaining) { EXPECT_CALL(prepare_watcher, ready()); EXPECT_CALL(fd_event, ready()); EXPECT_CALL(write_event, ready()); - EXPECT_CALL(closed_event, ready()); - // Third loop iteration: handle close event scheduled while handling write. - EXPECT_CALL(prepare_watcher, ready()); - EXPECT_CALL(fd_event, ready()); - EXPECT_CALL(closed_event, ready()); - // Fourth loop iteration: poll returned no new real events. - EXPECT_CALL(prepare_watcher, ready()); + if constexpr (Event::PlatformDefaultTriggerType != Event::FileTriggerType::EmulatedEdge) { + // Third loop iteration: poll returned no new real events. + EXPECT_CALL(prepare_watcher, ready()); + } file_event->activate(FileReadyType::Read); dispatcher->run(Event::Dispatcher::RunType::NonBlock); @@ -189,7 +173,6 @@ TEST_P(FileEventImplActivateTest, SetEnableCancelsActivate) { ReadyWatcher fd_event; ReadyWatcher read_event; ReadyWatcher write_event; - ReadyWatcher closed_event; ReadyWatcher prepare_watcher; evwatch_prepare_new(&static_cast(dispatcher.get())->base(), onWatcherReady, @@ -210,12 +193,8 @@ TEST_P(FileEventImplActivateTest, SetEnableCancelsActivate) { if (events & FileReadyType::Write) { write_event.ready(); } - - if (events & FileReadyType::Closed) { - closed_event.ready(); - } }, - trigger, FileReadyType::Read | FileReadyType::Write | FileReadyType::Closed); + trigger, FileReadyType::Read | FileReadyType::Write); testing::InSequence s; // First loop iteration: handle scheduled read event and the real write event produced by poll. diff --git a/test/common/filter/config_discovery_impl_test.cc b/test/common/filter/config_discovery_impl_test.cc index 7eebb2b237a0..4db6a0010866 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_{}; }; diff --git a/test/common/formatter/substitution_formatter_fuzz_test.cc b/test/common/formatter/substitution_formatter_fuzz_test.cc index f92a34885023..6a758272a717 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 7c919bfc4c10..13f33f25e53b 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/formatter/substitution_formatter_test.cc b/test/common/formatter/substitution_formatter_test.cc index e2b9217e21ef..fb0e627534e7 100644 --- a/test/common/formatter/substitution_formatter_test.cc +++ b/test/common/formatter/substitution_formatter_test.cc @@ -253,102 +253,95 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { Http::TestResponseHeaderMapImpl response_headers; Http::TestResponseTrailerMapImpl response_trailers; std::string body; + MockTimeSystem time_system; + auto& upstream_timing = stream_info.upstream_info_->upstreamTiming(); { StreamInfoFormatter request_duration_format("REQUEST_DURATION"); - absl::optional dur = std::chrono::nanoseconds(5000000); - EXPECT_CALL(stream_info, lastDownstreamRxByteReceived()).WillRepeatedly(Return(dur)); - EXPECT_EQ("5", request_duration_format.format(request_headers, response_headers, - response_trailers, stream_info, body)); + EXPECT_EQ(absl::nullopt, request_duration_format.format(request_headers, response_headers, + response_trailers, stream_info, body)); EXPECT_THAT(request_duration_format.formatValue(request_headers, response_headers, response_trailers, stream_info, body), - ProtoEq(ValueUtil::numberValue(5.0))); + ProtoEq(ValueUtil::nullValue())); } { StreamInfoFormatter request_duration_format("REQUEST_DURATION"); - absl::optional dur; - EXPECT_CALL(stream_info, lastDownstreamRxByteReceived()).WillRepeatedly(Return(dur)); - EXPECT_EQ(absl::nullopt, request_duration_format.format(request_headers, response_headers, - response_trailers, stream_info, body)); + EXPECT_CALL(time_system, monotonicTime) + .WillOnce(Return(MonotonicTime(std::chrono::nanoseconds(5000000)))); + stream_info.downstream_timing_.onLastDownstreamRxByteReceived(time_system); + EXPECT_EQ("5", request_duration_format.format(request_headers, response_headers, + response_trailers, stream_info, body)); EXPECT_THAT(request_duration_format.formatValue(request_headers, response_headers, response_trailers, stream_info, body), - ProtoEq(ValueUtil::nullValue())); + ProtoEq(ValueUtil::numberValue(5.0))); } { StreamInfoFormatter request_tx_duration_format("REQUEST_TX_DURATION"); - absl::optional dur = std::chrono::nanoseconds(15000000); - EXPECT_CALL(stream_info, lastUpstreamTxByteSent()).WillRepeatedly(Return(dur)); - EXPECT_EQ("15", request_tx_duration_format.format(request_headers, response_headers, - response_trailers, stream_info, body)); + EXPECT_EQ(absl::nullopt, + request_tx_duration_format.format(request_headers, response_headers, + response_trailers, stream_info, body)); EXPECT_THAT(request_tx_duration_format.formatValue(request_headers, response_headers, response_trailers, stream_info, body), - ProtoEq(ValueUtil::numberValue(15.0))); + ProtoEq(ValueUtil::nullValue())); } { StreamInfoFormatter request_tx_duration_format("REQUEST_TX_DURATION"); - absl::optional dur; - EXPECT_CALL(stream_info, lastUpstreamTxByteSent()).WillRepeatedly(Return(dur)); - EXPECT_EQ(absl::nullopt, - request_tx_duration_format.format(request_headers, response_headers, - response_trailers, stream_info, body)); + EXPECT_CALL(time_system, monotonicTime) + .WillOnce(Return(MonotonicTime(std::chrono::nanoseconds(15000000)))); + upstream_timing.onLastUpstreamTxByteSent(time_system); + EXPECT_EQ("15", request_tx_duration_format.format(request_headers, response_headers, + response_trailers, stream_info, body)); EXPECT_THAT(request_tx_duration_format.formatValue(request_headers, response_headers, response_trailers, stream_info, body), - ProtoEq(ValueUtil::nullValue())); + ProtoEq(ValueUtil::numberValue(15.0))); } { StreamInfoFormatter response_duration_format("RESPONSE_DURATION"); - absl::optional dur = std::chrono::nanoseconds(10000000); - EXPECT_CALL(stream_info, firstUpstreamRxByteReceived()).WillRepeatedly(Return(dur)); - EXPECT_EQ("10", response_duration_format.format(request_headers, response_headers, - response_trailers, stream_info, body)); + EXPECT_EQ(absl::nullopt, response_duration_format.format(request_headers, response_headers, + response_trailers, stream_info, body)); EXPECT_THAT(response_duration_format.formatValue(request_headers, response_headers, response_trailers, stream_info, body), - ProtoEq(ValueUtil::numberValue(10.0))); + ProtoEq(ValueUtil::nullValue())); } { StreamInfoFormatter response_duration_format("RESPONSE_DURATION"); - absl::optional dur; - EXPECT_CALL(stream_info, firstUpstreamRxByteReceived()).WillRepeatedly(Return(dur)); - EXPECT_EQ(absl::nullopt, response_duration_format.format(request_headers, response_headers, - response_trailers, stream_info, body)); + EXPECT_CALL(time_system, monotonicTime) + .WillOnce(Return(MonotonicTime(std::chrono::nanoseconds(10000000)))); + upstream_timing.onFirstUpstreamRxByteReceived(time_system); + EXPECT_EQ("10", response_duration_format.format(request_headers, response_headers, + response_trailers, stream_info, body)); EXPECT_THAT(response_duration_format.formatValue(request_headers, response_headers, response_trailers, stream_info, body), - ProtoEq(ValueUtil::nullValue())); + ProtoEq(ValueUtil::numberValue(10.0))); } { StreamInfoFormatter ttlb_duration_format("RESPONSE_TX_DURATION"); - absl::optional dur_upstream = std::chrono::nanoseconds(10000000); - EXPECT_CALL(stream_info, firstUpstreamRxByteReceived()).WillRepeatedly(Return(dur_upstream)); - absl::optional dur_downstream = std::chrono::nanoseconds(25000000); - EXPECT_CALL(stream_info, lastDownstreamTxByteSent()).WillRepeatedly(Return(dur_downstream)); - - EXPECT_EQ("15", ttlb_duration_format.format(request_headers, response_headers, - response_trailers, stream_info, body)); + EXPECT_EQ(absl::nullopt, ttlb_duration_format.format(request_headers, response_headers, + response_trailers, stream_info, body)); EXPECT_THAT(ttlb_duration_format.formatValue(request_headers, response_headers, response_trailers, stream_info, body), - ProtoEq(ValueUtil::numberValue(15.0))); + ProtoEq(ValueUtil::nullValue())); } { StreamInfoFormatter ttlb_duration_format("RESPONSE_TX_DURATION"); - absl::optional dur_upstream; - EXPECT_CALL(stream_info, firstUpstreamRxByteReceived()).WillRepeatedly(Return(dur_upstream)); - absl::optional dur_downstream; - EXPECT_CALL(stream_info, lastDownstreamTxByteSent()).WillRepeatedly(Return(dur_downstream)); + EXPECT_CALL(time_system, monotonicTime) + .WillOnce(Return(MonotonicTime(std::chrono::nanoseconds(25000000)))); + stream_info.downstream_timing_.onLastDownstreamTxByteSent(time_system); - EXPECT_EQ(absl::nullopt, ttlb_duration_format.format(request_headers, response_headers, - response_trailers, stream_info, body)); + EXPECT_EQ("15", ttlb_duration_format.format(request_headers, response_headers, + response_trailers, stream_info, body)); EXPECT_THAT(ttlb_duration_format.formatValue(request_headers, response_headers, response_trailers, stream_info, body), - ProtoEq(ValueUtil::nullValue())); + ProtoEq(ValueUtil::numberValue(15.0))); } { @@ -361,6 +354,28 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::numberValue(1.0))); } + { + StreamInfoFormatter attempt_count_format("UPSTREAM_REQUEST_ATTEMPT_COUNT"); + absl::optional attempt_count{3}; + EXPECT_CALL(stream_info, attemptCount()).WillRepeatedly(Return(attempt_count)); + EXPECT_EQ("3", attempt_count_format.format(request_headers, response_headers, response_trailers, + stream_info, body)); + EXPECT_THAT(attempt_count_format.formatValue(request_headers, response_headers, + response_trailers, stream_info, body), + ProtoEq(ValueUtil::numberValue(3.0))); + } + + { + StreamInfoFormatter attempt_count_format("UPSTREAM_REQUEST_ATTEMPT_COUNT"); + absl::optional attempt_count; + EXPECT_CALL(stream_info, attemptCount()).WillRepeatedly(Return(attempt_count)); + EXPECT_EQ("0", attempt_count_format.format(request_headers, response_headers, response_trailers, + stream_info, body)); + EXPECT_THAT(attempt_count_format.formatValue(request_headers, response_headers, + response_trailers, stream_info, body), + ProtoEq(ValueUtil::numberValue(0.0))); + } + { StreamInfo::BytesMeterSharedPtr upstream_bytes_meter{ std::make_shared()}; @@ -514,8 +529,6 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { const std::string observable_cluster_name = "observability_name"; auto cluster_info_mock = std::make_shared(); absl::optional cluster_info = cluster_info_mock; - // Make sure that cluster info is obtained without calling upstreamHost. - EXPECT_CALL(stream_info, upstreamHost()).Times(0); EXPECT_CALL(stream_info, upstreamClusterInfo()).WillRepeatedly(Return(cluster_info)); EXPECT_CALL(*cluster_info_mock, observabilityName()) .WillRepeatedly(ReturnRef(observable_cluster_name)); @@ -534,8 +547,6 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { const std::string upstream_cluster_name = "cluster_name"; auto cluster_info_mock = std::make_shared(); absl::optional cluster_info = cluster_info_mock; - // Make sure that cluster info is obtained without calling upstreamHost. - EXPECT_CALL(stream_info, upstreamHost()).Times(0); EXPECT_CALL(stream_info, upstreamClusterInfo()).WillRepeatedly(Return(cluster_info)); EXPECT_CALL(*cluster_info_mock, name()).WillRepeatedly(ReturnRef(upstream_cluster_name)); EXPECT_EQ("cluster_name", upstream_format.format(request_headers, response_headers, @@ -548,8 +559,6 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { { StreamInfoFormatter upstream_format("UPSTREAM_CLUSTER"); absl::optional cluster_info = nullptr; - // Make sure that cluster info is obtained without calling upstreamHost. - EXPECT_CALL(stream_info, upstreamHost()).Times(0); EXPECT_CALL(stream_info, upstreamClusterInfo()).WillRepeatedly(Return(cluster_info)); EXPECT_EQ(absl::nullopt, upstream_format.format(request_headers, response_headers, response_trailers, stream_info, body)); @@ -560,7 +569,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { { StreamInfoFormatter upstream_format("UPSTREAM_HOST"); - EXPECT_CALL(stream_info, upstreamHost()).WillRepeatedly(Return(nullptr)); + stream_info.upstreamInfo()->setUpstreamHost(nullptr); 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, @@ -720,8 +729,57 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { stream_info, body), ProtoEq(ValueUtil::nullValue())); } + { + StreamInfoFormatter upstream_format("UPSTREAM_TRANSPORT_FAILURE_REASON"); + std::string upstream_transport_failure_reason = "SSL error"; + stream_info.upstreamInfo()->setUpstreamTransportFailureReason( + 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; + stream_info.upstreamInfo()->setUpstreamTransportFailureReason( + 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; + + { + NiceMock stream_info; + StreamInfoFormatter upstream_format("VIRTUAL_CLUSTER_NAME"); + std::string virtual_cluster_name = "authN"; + stream_info.setVirtualClusterName(virtual_cluster_name); + EXPECT_EQ("authN", upstream_format.format(request_headers, response_headers, response_trailers, + stream_info, body)); + } { + NiceMock stream_info; + StreamInfoFormatter upstream_format("VIRTUAL_CLUSTER_NAME"); + EXPECT_EQ(absl::nullopt, upstream_format.format(request_headers, response_headers, + response_trailers, stream_info, 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 +793,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 +803,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 +816,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 +826,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 +839,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 +849,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 +862,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 +872,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 +886,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 +899,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 +909,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 +922,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 +934,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 +944,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 +957,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 +969,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 +979,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 +990,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 +1002,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 +1012,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 +1025,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 +1037,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 +1048,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 +1062,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 +1076,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 +1086,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 +1100,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 +1114,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 +1124,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 +1138,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 +1151,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 +1161,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 +1173,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 +1185,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 +1195,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 +1207,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 +1219,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 +1229,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 +1243,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 +1257,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 +1266,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 +1713,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 +1720,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 +1731,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 +1744,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 +1757,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 +1772,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 +1779,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 +1790,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 +1804,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 +1817,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(); @@ -2563,10 +2645,12 @@ TEST(SubstitutionFormatterTest, StructFormatterTypedTest) { Http::TestRequestHeaderMapImpl request_headers; Http::TestResponseHeaderMapImpl response_headers; Http::TestResponseTrailerMapImpl response_trailers; - StreamInfo::MockStreamInfo stream_info; + NiceMock stream_info; std::string body; - EXPECT_CALL(Const(stream_info), lastDownstreamRxByteReceived()) - .WillRepeatedly(Return(std::chrono::nanoseconds(5000000))); + MockTimeSystem time_system; + EXPECT_CALL(time_system, monotonicTime) + .WillOnce(Return(MonotonicTime(std::chrono::nanoseconds(5000000)))); + stream_info.downstream_timing_.onLastDownstreamRxByteReceived(time_system); ProtobufWkt::Value list; list.mutable_list_value()->add_values()->set_bool_value(true); @@ -2602,7 +2686,7 @@ TEST(SubstitutionFormatterTest, StructFormatterTypedTest) { } TEST(SubstitutionFormatterTest, JsonFormatterTest) { - StreamInfo::MockStreamInfo stream_info; + NiceMock stream_info; Http::TestRequestHeaderMapImpl request_header; Http::TestResponseHeaderMapImpl response_header; Http::TestResponseTrailerMapImpl response_trailer; @@ -2612,8 +2696,10 @@ TEST(SubstitutionFormatterTest, JsonFormatterTest) { populateMetadataTestData(metadata); absl::optional protocol = Http::Protocol::Http11; EXPECT_CALL(stream_info, protocol()).WillRepeatedly(Return(protocol)); - EXPECT_CALL(Const(stream_info), lastDownstreamRxByteReceived()) - .WillRepeatedly(Return(std::chrono::nanoseconds(5000000))); + MockTimeSystem time_system; + EXPECT_CALL(time_system, monotonicTime) + .WillOnce(Return(MonotonicTime(std::chrono::nanoseconds(5000000)))); + stream_info.downstream_timing_.onLastDownstreamRxByteReceived(time_system); ProtobufWkt::Struct key_mapping; TestUtility::loadFromYaml(R"EOF( @@ -2639,13 +2725,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 +2746,7 @@ TEST(SubstitutionFormatterTest, CompositeFormatterSuccess) { } { + NiceMock stream_info; const std::string format = "{}*JUST PLAIN string]"; FormatterImpl formatter(format, false); @@ -2668,6 +2755,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 +2766,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 +2781,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 +2801,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 +2818,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 +2839,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 +2860,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 +2876,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 +2889,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 +2902,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 +2921,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/grpc/BUILD b/test/common/grpc/BUILD index c4e37301fa70..2a7c3681ae72 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", @@ -186,3 +187,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/async_client_impl_test.cc b/test/common/grpc/async_client_impl_test.cc index f30efdd16d95..c6f2b3400668 100644 --- a/test/common/grpc/async_client_impl_test.cc +++ b/test/common/grpc/async_client_impl_test.cc @@ -175,11 +175,12 @@ TEST_F(EnvoyAsyncClientImplTest, RequestHttpStartFail) { Tracing::MockSpan active_span; Tracing::MockSpan* child_span{new Tracing::MockSpan()}; - EXPECT_CALL(active_span, spawnChild_(_, "async test_cluster egress", _)) + EXPECT_CALL(active_span, spawnChild_(_, "async helloworld.Greeter.SayHello egress", _)) .WillOnce(Return(child_span)); EXPECT_CALL(*child_span, setTag(Eq(Tracing::Tags::get().Component), Eq(Tracing::Tags::get().Proxy))); EXPECT_CALL(*child_span, setTag(Eq(Tracing::Tags::get().UpstreamCluster), Eq("test_cluster"))); + EXPECT_CALL(*child_span, setTag(Eq(Tracing::Tags::get().UpstreamAddress), Eq("test_cluster"))); EXPECT_CALL(*child_span, setTag(Eq(Tracing::Tags::get().GrpcStatusCode), Eq("14"))); EXPECT_CALL(*child_span, setTag(Eq(Tracing::Tags::get().Error), Eq(Tracing::Tags::get().True))); EXPECT_CALL(*child_span, finishSpan()); @@ -242,11 +243,12 @@ TEST_F(EnvoyAsyncClientImplTest, RequestHttpSendHeadersFail) { Tracing::MockSpan active_span; Tracing::MockSpan* child_span{new Tracing::MockSpan()}; - EXPECT_CALL(active_span, spawnChild_(_, "async test_cluster egress", _)) + EXPECT_CALL(active_span, spawnChild_(_, "async helloworld.Greeter.SayHello egress", _)) .WillOnce(Return(child_span)); EXPECT_CALL(*child_span, setTag(Eq(Tracing::Tags::get().Component), Eq(Tracing::Tags::get().Proxy))); EXPECT_CALL(*child_span, setTag(Eq(Tracing::Tags::get().UpstreamCluster), Eq("test_cluster"))); + EXPECT_CALL(*child_span, setTag(Eq(Tracing::Tags::get().UpstreamAddress), Eq("test_cluster"))); EXPECT_CALL(*child_span, injectContext(_)); EXPECT_CALL(*child_span, setTag(Eq(Tracing::Tags::get().GrpcStatusCode), Eq("13"))); EXPECT_CALL(*child_span, setTag(Eq(Tracing::Tags::get().Error), Eq(Tracing::Tags::get().True))); diff --git a/test/common/grpc/async_client_manager_impl_test.cc b/test/common/grpc/async_client_manager_impl_test.cc index c0fe6baddae8..a6c1440d17c3 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() 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 000000000000..fc025f6532a0 --- /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 diff --git a/test/common/grpc/google_async_client_impl_test.cc b/test/common/grpc/google_async_client_impl_test.cc index e0eb023f3ef1..70fa4bf3e6f5 100644 --- a/test/common/grpc/google_async_client_impl_test.cc +++ b/test/common/grpc/google_async_client_impl_test.cc @@ -160,11 +160,12 @@ TEST_F(EnvoyGoogleAsyncClientImplTest, RequestHttpStartFail) { Tracing::MockSpan active_span; Tracing::MockSpan* child_span{new Tracing::MockSpan()}; - EXPECT_CALL(active_span, spawnChild_(_, "async test_cluster egress", _)) + EXPECT_CALL(active_span, spawnChild_(_, "async helloworld.Greeter.SayHello egress", _)) .WillOnce(Return(child_span)); EXPECT_CALL(*child_span, setTag(Eq(Tracing::Tags::get().Component), Eq(Tracing::Tags::get().Proxy))); EXPECT_CALL(*child_span, setTag(Eq(Tracing::Tags::get().UpstreamCluster), Eq("test_cluster"))); + EXPECT_CALL(*child_span, setTag(Eq(Tracing::Tags::get().UpstreamAddress), Eq("fake_address"))); EXPECT_CALL(*child_span, setTag(Eq(Tracing::Tags::get().GrpcStatusCode), Eq("14"))); EXPECT_CALL(*child_span, setTag(Eq(Tracing::Tags::get().Error), Eq(Tracing::Tags::get().True))); EXPECT_CALL(*child_span, finishSpan()); diff --git a/test/common/grpc/grpc_client_integration.h b/test/common/grpc/grpc_client_integration.h index 372e0abd1d38..57b7dcbdc28f 100644 --- a/test/common/grpc/grpc_client_integration.h +++ b/test/common/grpc/grpc_client_integration.h @@ -41,7 +41,7 @@ class BaseGrpcClientIntegrationParamTest { break; } default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } } }; diff --git a/test/common/grpc/grpc_client_integration_test_harness.h b/test/common/grpc/grpc_client_integration_test_harness.h index 852bfb3f2425..99808db5ab8b 100644 --- a/test/common/grpc/grpc_client_integration_test_harness.h +++ b/test/common/grpc/grpc_client_integration_test_harness.h @@ -47,10 +47,13 @@ using testing::_; using testing::AtLeast; +using testing::AtMost; using testing::Eq; using testing::Invoke; using testing::InvokeWithoutArgs; +using testing::IsEmpty; using testing::NiceMock; +using testing::Not; using testing::Return; using testing::ReturnRef; @@ -339,7 +342,7 @@ class GrpcClientIntegrationTest : public GrpcClientIntegrationParamTest { stats_scope_, createGoogleGrpcConfig(), *api_, google_grpc_stat_names_); #else - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); #endif } @@ -373,10 +376,13 @@ class GrpcClientIntegrationTest : public GrpcClientIntegrationParamTest { request_msg.set_name(HELLO_REQUEST); Tracing::MockSpan active_span; - EXPECT_CALL(active_span, spawnChild_(_, "async fake_cluster egress", _)) + EXPECT_CALL(active_span, spawnChild_(_, "async helloworld.Greeter.SayHello egress", _)) .WillOnce(Return(request->child_span_)); EXPECT_CALL(*request->child_span_, setTag(Eq(Tracing::Tags::get().UpstreamCluster), Eq("fake_cluster"))); + EXPECT_CALL(*request->child_span_, + setTag(Eq(Tracing::Tags::get().UpstreamAddress), Not(IsEmpty()))) + .Times(AtMost(1)); EXPECT_CALL(*request->child_span_, setTag(Eq(Tracing::Tags::get().Component), Eq(Tracing::Tags::get().Proxy))); EXPECT_CALL(*request->child_span_, injectContext(_)); diff --git a/test/common/http/async_client_impl_test.cc b/test/common/http/async_client_impl_test.cc index 28d78009c2fd..cceea46b4e82 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_client_test.cc b/test/common/http/codec_client_test.cc index 87fecd8ebe18..b3c873786e3a 100644 --- a/test/common/http/codec_client_test.cc +++ b/test/common/http/codec_client_test.cc @@ -291,7 +291,8 @@ class CodecNetworkTest : public Event::TestUsingSimulatedTime, Network::ClientConnectionPtr client_connection = dispatcher_->createClientConnection( socket->connectionInfoProvider().localAddress(), source_address_, Network::Test::createRawBufferSocket(), nullptr); - upstream_listener_ = dispatcher_->createListener(std::move(socket), listener_callbacks_, true); + upstream_listener_ = + dispatcher_->createListener(std::move(socket), listener_callbacks_, true, false); client_connection_ = client_connection.get(); client_connection_->addConnectionCallbacks(client_callbacks_); diff --git a/test/common/http/codec_impl_fuzz_test.cc b/test/common/http/codec_impl_fuzz_test.cc index 61e8bbd736ce..cd456d0bb588 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; } @@ -683,6 +683,10 @@ void codecFuzz(const test::common::http::CodecImplFuzzTestCase& input, HttpVersi dynamic_cast(*client).goAway(); dynamic_cast(*server).goAway(); } + + // Run deletion as would happen on the dispatchers to avoid inversion of + // lifetimes of dispatcher and connection. + server_connection.dispatcher_.to_delete_.clear(); } } // namespace diff --git a/test/common/http/conn_manager_impl_fuzz_test.cc b/test/common/http/conn_manager_impl_fuzz_test.cc index 35aa7dd8ad97..4177b065e607 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 05a3a0d59d05..152f799b4ac8 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/conn_manager_impl_test_2.cc b/test/common/http/conn_manager_impl_test_2.cc index 44bd0db57dde..5f396b9e1f52 100644 --- a/test/common/http/conn_manager_impl_test_2.cc +++ b/test/common/http/conn_manager_impl_test_2.cc @@ -150,7 +150,7 @@ TEST_F(HttpConnectionManagerImplTest, DownstreamProtocolError) { return codecProtocolError("protocol error"); })); - EXPECT_CALL(response_encoder_.stream_, removeCallbacks(_)); + EXPECT_CALL(response_encoder_.stream_, removeCallbacks(_)).Times(2); EXPECT_CALL(filter_factory_, createFilterChain(_)).Times(0); // A protocol exception should result in reset of the streams followed by a remote or local close @@ -232,7 +232,7 @@ TEST_F(HttpConnectionManagerImplTest, FrameFloodError) { return bufferFloodError("too many outbound frames"); })); - EXPECT_CALL(response_encoder_.stream_, removeCallbacks(_)); + EXPECT_CALL(response_encoder_.stream_, removeCallbacks(_)).Times(2); EXPECT_CALL(filter_factory_, createFilterChain(_)).Times(0); // FrameFloodException should result in reset of the streams followed by abortive close. @@ -349,6 +349,35 @@ TEST_F(HttpConnectionManagerImplTest, ConnectionDurationNoCodec) { EXPECT_EQ(1U, stats_.named_.downstream_cx_max_duration_reached_.value()); } +// Regression test for https://github.com/envoyproxy/envoy/issues/19045 +TEST_F(HttpConnectionManagerImplTest, MaxRequests) { + max_requests_per_connection_ = 1; + codec_->protocol_ = Protocol::Http2; + setup(false, ""); + + Event::MockTimer* drain_timer = setUpTimer(); + EXPECT_CALL(*drain_timer, enableTimer(_, _)); + + EXPECT_CALL(*codec_, dispatch(_)).WillRepeatedly(Invoke([&](Buffer::Instance&) -> Http::Status { + conn_manager_->newStream(response_encoder_); + return Http::okStatus(); + })); + + EXPECT_CALL(*codec_, goAway()); + EXPECT_CALL(*codec_, shutdownNotice()); + EXPECT_CALL(*drain_timer, disableTimer()); + + // Kick off two requests. + Buffer::OwnedImpl fake_input("hello"); + conn_manager_->onData(fake_input, false); + conn_manager_->onData(fake_input, false); + drain_timer->invokeCallback(); + + EXPECT_EQ(2U, stats_.named_.downstream_cx_max_requests_reached_.value()); + + conn_manager_->onEvent(Network::ConnectionEvent::RemoteClose); +} + TEST_F(HttpConnectionManagerImplTest, ConnectionDuration) { max_connection_duration_ = (std::chrono::milliseconds(10)); Event::MockTimer* connection_duration_timer = setUpTimer(); @@ -1104,7 +1133,7 @@ TEST_F(HttpConnectionManagerImplTest, UpstreamWatermarkCallbacks) { EXPECT_EQ(1U, stats_.named_.downstream_flow_control_resumed_reading_total_.value()); // Backup upstream once again. - EXPECT_CALL(response_encoder_, getStream()).WillOnce(ReturnRef(stream_)); + EXPECT_CALL(response_encoder_, getStream()).WillRepeatedly(ReturnRef(stream_)); EXPECT_CALL(stream_, readDisable(true)); ASSERT(decoder_filters_[0]->callbacks_ != nullptr); decoder_filters_[0]->callbacks_->onDecoderFilterAboveWriteBufferHighWatermark(); @@ -1334,7 +1363,7 @@ TEST_F(HttpConnectionManagerImplTest, HitFilterWatermarkLimits) { })); expectOnDestroy(); - EXPECT_CALL(stream_, removeCallbacks(_)); + EXPECT_CALL(stream_, removeCallbacks(_)).Times(2); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::LocalClose); } @@ -1364,45 +1393,47 @@ TEST_F(HttpConnectionManagerImplTest, HitRequestBufferLimits) { // Return 413 from an intermediate filter and make sure we don't continue the filter chain. TEST_F(HttpConnectionManagerImplTest, HitRequestBufferLimitsIntermediateFilter) { - InSequence s; - initial_buffer_limit_ = 10; - setup(false, ""); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder_->decodeHeaders(std::move(headers), false); + { + InSequence s; + initial_buffer_limit_ = 10; + setup(false, ""); - Buffer::OwnedImpl fake_data("hello"); - decoder_->decodeData(fake_data, false); + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{ + new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; + decoder_->decodeHeaders(std::move(headers), false); - Buffer::OwnedImpl fake_data2("world world"); - decoder_->decodeData(fake_data2, true); - return Http::okStatus(); - })); + Buffer::OwnedImpl fake_data("hello"); + decoder_->decodeData(fake_data, false); - setUpBufferLimits(); - setupFilterChain(2, 1); + Buffer::OwnedImpl fake_data2("world world"); + decoder_->decodeData(fake_data2, true); + return Http::okStatus(); + })); - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - EXPECT_CALL(*decoder_filters_[0], decodeData(_, false)) - .WillOnce(Return(FilterDataStatus::StopIterationAndBuffer)); - EXPECT_CALL(*decoder_filters_[0], decodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*decoder_filters_[0], decodeComplete()); - Http::TestResponseHeaderMapImpl response_headers{ - {":status", "413"}, {"content-length", "17"}, {"content-type", "text/plain"}}; - EXPECT_CALL(*encoder_filters_[0], encodeHeaders(HeaderMapEqualRef(&response_headers), false)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) - .WillOnce(Return(FilterDataStatus::StopIterationAndWatermark)); - EXPECT_CALL(*encoder_filters_[0], encodeComplete()); + setUpBufferLimits(); + setupFilterChain(2, 1); - // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + EXPECT_CALL(*decoder_filters_[0], decodeData(_, false)) + .WillOnce(Return(FilterDataStatus::StopIterationAndBuffer)); + EXPECT_CALL(*decoder_filters_[0], decodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*decoder_filters_[0], decodeComplete()); + Http::TestResponseHeaderMapImpl response_headers{ + {":status", "413"}, {"content-length", "17"}, {"content-type", "text/plain"}}; + EXPECT_CALL(*encoder_filters_[0], encodeHeaders(HeaderMapEqualRef(&response_headers), false)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) + .WillOnce(Return(FilterDataStatus::StopIterationAndWatermark)); + EXPECT_CALL(*encoder_filters_[0], encodeComplete()); + + // Kick off the incoming data. + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); + } doRemoteClose(false); } @@ -1469,17 +1500,20 @@ TEST_F(HttpConnectionManagerImplTest, HitResponseBufferLimitsAfterHeaders) { const std::string data = "A long enough string to go over watermarks"; Buffer::OwnedImpl fake_response(data); InSequence s; - EXPECT_CALL(stream_, removeCallbacks(_)); - expectOnDestroy(false); EXPECT_CALL(*encoder_filters_[1], encodeData(_, false)) .WillOnce(Return(FilterDataStatus::StopIterationAndBuffer)); EXPECT_CALL(stream_, resetStream(_)); - filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::LocalClose); EXPECT_LOG_CONTAINS( "debug", "Resetting stream due to response_payload_too_large. Prior headers have already been sent", decoder_filters_[0]->callbacks_->encodeData(fake_response, false);); EXPECT_EQ(1U, stats_.named_.rs_too_large_.value()); + + // Clean up connection + EXPECT_CALL(stream_, removeCallbacks(_)); + expectOnDestroy(false); + EXPECT_CALL(stream_, removeCallbacks(_)); + filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::LocalClose); } TEST_F(HttpConnectionManagerImplTest, FilterHeadReply) { @@ -2848,5 +2882,127 @@ TEST_F(HttpConnectionManagerImplTest, RequestRejectedViaIPDetection) { EXPECT_EQ(1U, stats_.named_.downstream_rq_rejected_via_ip_detection_.value()); } +TEST_F(HttpConnectionManagerImplTest, DisconnectDuringEncodeHeader) { + setup(false, "envoy-server-test"); + setupFilterChain(1, 0); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + startRequest(/*end_stream=*/true); + + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) + .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_NE(nullptr, headers.Server()); + EXPECT_EQ("envoy-server-test", headers.getServerValue()); + conn_manager_->onEvent(Network::ConnectionEvent::LocalClose); + })); + EXPECT_CALL(*decoder_filters_[0], onStreamComplete()); + EXPECT_CALL(*decoder_filters_[0], onDestroy()); + + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + decoder_filters_[0]->callbacks_->streamInfo().setResponseCodeDetails(""); + decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); +} + +TEST_F(HttpConnectionManagerImplTest, DisconnectDuringEncodeBody) { + setup(false, "envoy-server-test"); + setupFilterChain(1, 0); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + startRequest(/*end_stream=*/true); + + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) + .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_NE(nullptr, headers.Server()); + EXPECT_EQ("envoy-server-test", headers.getServerValue()); + })); + EXPECT_CALL(response_encoder_, encodeData(_, true)) + .WillOnce(Invoke([&](Buffer::Instance&, bool) -> void { + conn_manager_->onEvent(Network::ConnectionEvent::LocalClose); + })); + EXPECT_CALL(*decoder_filters_[0], onStreamComplete()); + EXPECT_CALL(*decoder_filters_[0], onDestroy()); + + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + decoder_filters_[0]->callbacks_->streamInfo().setResponseCodeDetails(""); + decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), false, "details"); + Buffer::OwnedImpl response_body("response"); + decoder_filters_[0]->callbacks_->encodeData(response_body, true); +} + +TEST_F(HttpConnectionManagerImplTest, DisconnectDuringEncodeTrailer) { + setup(false, "envoy-server-test"); + setupFilterChain(1, 0); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + startRequest(/*end_stream=*/true); + + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) + .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_NE(nullptr, headers.Server()); + EXPECT_EQ("envoy-server-test", headers.getServerValue()); + })); + EXPECT_CALL(response_encoder_, encodeData(_, false)); + EXPECT_CALL(response_encoder_, encodeTrailers(_)) + .WillOnce(Invoke([&](const Http::ResponseTrailerMap&) -> void { + conn_manager_->onEvent(Network::ConnectionEvent::LocalClose); + })); + EXPECT_CALL(*decoder_filters_[0], onStreamComplete()); + EXPECT_CALL(*decoder_filters_[0], onDestroy()); + + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + decoder_filters_[0]->callbacks_->streamInfo().setResponseCodeDetails(""); + decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), false, "details"); + Buffer::OwnedImpl response_body("response"); + decoder_filters_[0]->callbacks_->encodeData(response_body, false); + decoder_filters_[0]->callbacks_->encodeTrailers( + ResponseTrailerMapPtr{new TestResponseTrailerMapImpl{{"some", "trailer"}}}); +} + +TEST_F(HttpConnectionManagerImplTest, DirectLocalReplyCausesDisconnect) { + initial_buffer_limit_ = 10; + setup(false, ""); + setUpEncoderAndDecoder(false, false); + sendRequestHeadersAndData(); + + // Start the response without processing the request headers through all + // filters. + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + decoder_filters_[0]->callbacks_->streamInfo().setResponseCodeDetails(""); + decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), false, "details"); + + // Now overload the buffer with response data. The filter returns + // StopIterationAndBuffer, which will trigger an early response. + + expectOnDestroy(); + Buffer::OwnedImpl fake_response("A long enough string to go over watermarks"); + // Fake response starts doing through the filter. + EXPECT_CALL(*encoder_filters_[1], encodeData(_, false)) + .WillOnce(Return(FilterDataStatus::StopIterationAndBuffer)); + std::string response_body; + // The 500 goes directly to the encoder. + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) + .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> FilterHeadersStatus { + // Make sure this is a 500 + EXPECT_EQ("500", headers.getStatusValue()); + // Make sure Envoy standard sanitization has been applied. + EXPECT_TRUE(headers.Date() != nullptr); + EXPECT_EQ("response_payload_too_large", + decoder_filters_[0]->callbacks_->streamInfo().responseCodeDetails().value()); + return FilterHeadersStatus::Continue; + })); + EXPECT_CALL(response_encoder_, encodeData(_, true)) + .WillOnce(Invoke([&](Buffer::Instance&, bool) -> void { + conn_manager_->onEvent(Network::ConnectionEvent::LocalClose); + })); + decoder_filters_[0]->callbacks_->encodeData(fake_response, false); + + EXPECT_EQ(1U, stats_.named_.rs_too_large_.value()); +} + } // namespace Http } // namespace Envoy diff --git a/test/common/http/conn_manager_impl_test_base.cc b/test/common/http/conn_manager_impl_test_base.cc index 0ecfafee44fd..14ba94dd1a03 100644 --- a/test/common/http/conn_manager_impl_test_base.cc +++ b/test/common/http/conn_manager_impl_test_base.cc @@ -264,7 +264,9 @@ void HttpConnectionManagerImplTest::expectOnDestroy(bool deferred) { } void HttpConnectionManagerImplTest::doRemoteClose(bool deferred) { - EXPECT_CALL(stream_, removeCallbacks(_)); + // We will call removeCallbacks twice. + // Once in resetAllStreams, and once in doDeferredStreamDestroy. + EXPECT_CALL(stream_, removeCallbacks(_)).Times(2); expectOnDestroy(deferred); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); } diff --git a/test/common/http/conn_manager_impl_test_base.h b/test/common/http/conn_manager_impl_test_base.h index 6225b61d854d..f3a2c0eda8b2 100644 --- a/test/common/http/conn_manager_impl_test_base.h +++ b/test/common/http/conn_manager_impl_test_base.h @@ -153,7 +153,7 @@ class HttpConnectionManagerImplTest : public testing::Test, public ConnectionMan originalIpDetectionExtensions() const override { return ip_detection_extensions_; } - uint64_t maxRequestsPerConnection() const override { return 0; } + uint64_t maxRequestsPerConnection() const override { return max_requests_per_connection_; } Envoy::Event::SimulatedTimeSystem test_time_; NiceMock route_config_provider_; @@ -184,6 +184,7 @@ class HttpConnectionManagerImplTest : public testing::Test, public ConnectionMan absl::optional user_agent_; uint32_t max_request_headers_kb_{Http::DEFAULT_MAX_REQUEST_HEADERS_KB}; uint32_t max_request_headers_count_{Http::DEFAULT_MAX_HEADERS_COUNT}; + uint64_t max_requests_per_connection_{}; absl::optional idle_timeout_; absl::optional max_connection_duration_; std::chrono::milliseconds stream_idle_timeout_{}; diff --git a/test/common/http/conn_pool_grid_test.cc b/test/common/http/conn_pool_grid_test.cc index 69528416002d..73ea421692e9 100644 --- a/test/common/http/conn_pool_grid_test.cc +++ b/test/common/http/conn_pool_grid_test.cc @@ -714,13 +714,32 @@ TEST_F(ConnectivityGridTest, ConnectionCloseDuringCreation) { ASSERT_TRUE(optional_it1.has_value()); EXPECT_EQ("HTTP/3", (**optional_it1)->protocolDescription()); + const bool supports_getifaddrs = Api::OsSysCallsSingleton::get().supportsGetifaddrs(); + Api::InterfaceAddressVector interfaces{}; + if (supports_getifaddrs) { + ASSERT_EQ(0, Api::OsSysCallsSingleton::get().getifaddrs(interfaces).return_value_); + } + Api::MockOsSysCalls os_sys_calls; TestThreadsafeSingletonInjector os_calls(&os_sys_calls); + EXPECT_CALL(os_sys_calls, supportsGetifaddrs()).WillOnce(Return(supports_getifaddrs)); + if (supports_getifaddrs) { + EXPECT_CALL(os_sys_calls, getifaddrs(_)) + .WillOnce( + Invoke([&](Api::InterfaceAddressVector& interface_vector) -> Api::SysCallIntResult { + interface_vector.insert(interface_vector.begin(), interfaces.begin(), + interfaces.end()); + return {0, 0}; + })); + } EXPECT_CALL(os_sys_calls, socket(_, _, _)).WillOnce(Return(Api::SysCallSocketResult{1, 0})); #if defined(__APPLE__) || defined(WIN32) EXPECT_CALL(os_sys_calls, setsocketblocking(1, false)) .WillOnce(Return(Api::SysCallIntResult{1, 0})); #endif + EXPECT_CALL(os_sys_calls, setsockopt_(_, _, _, _, _)) + .Times(testing::AtLeast(0u)) + .WillRepeatedly(Return(0)); EXPECT_CALL(os_sys_calls, bind(_, _, _)).WillOnce(Return(Api::SysCallIntResult{1, 0})); EXPECT_CALL(os_sys_calls, setsockopt_(_, _, _, _, _)).WillRepeatedly(Return(0)); EXPECT_CALL(os_sys_calls, sendmsg(_, _, _)).WillOnce(Return(Api::SysCallSizeResult{-1, 101})); diff --git a/test/common/http/header_map_impl_test.cc b/test/common/http/header_map_impl_test.cc index ce4bb8a7ccd1..1b95f4b49ad1 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/http1/codec_impl_test.cc b/test/common/http/http1/codec_impl_test.cc index 86e8be81686f..2b1bdf8d0f11 100644 --- a/test/common/http/http1/codec_impl_test.cc +++ b/test/common/http/http1/codec_impl_test.cc @@ -86,6 +86,12 @@ class Http1ServerConnectionImplTest : public Http1CodecTestBase { max_request_headers_count_, headers_with_underscores_action_); } + ~Http1ServerConnectionImplTest() override { + // Run deletion as would happen on the dispatchers to avoid inversion of + // lifetimes of dispatcher and connection. + connection_.dispatcher_.to_delete_.clear(); + } + NiceMock connection_; NiceMock callbacks_; NiceMock codec_settings_; @@ -1582,7 +1588,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 +2492,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 +2501,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); @@ -2508,6 +2514,50 @@ TEST_F(Http1ClientConnectionImplTest, ContinueHeaders) { EXPECT_TRUE(status.ok()); } +// 102 response followed by 200 results in a [decode1xxHeaders, decodeHeaders] sequence. +TEST_F(Http1ClientConnectionImplTest, ProcessingHeaders) { + 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 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(); @@ -2517,13 +2567,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); @@ -2536,9 +2586,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 +2595,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/BUILD b/test/common/http/http2/BUILD index 075f7607ea42..b9c22bdacdf0 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 50d9b50802b3..62424f61c81c 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 { @@ -55,18 +57,6 @@ class Http2CodecImplTestFixture { static bool slowContainsStreamId(int id, ConnectionImpl& connection) { return connection.slowContainsStreamId(id); } - // The Http::Connection::dispatch method does not throw (any more). However unit tests in this - // file use codecs for sending test data through mock network connections to the codec under test. - // It is infeasible to plumb error codes returned by the dispatch() method of the codecs under - // test, through mock connections and sending codec. As a result error returned by the dispatch - // method of the codec under test invoked by the ConnectionWrapper is thrown as an exception. Note - // that exception goes only through the mock network connection and sending codec, i.e. it is - // thrown only through the test harness code. Specific exception types are to distinguish error - // codes returned when processing requests or responses. - // TODO(yanavlasov): modify the code to verify test expectations at the point of calling codec - // under test through the ON_CALL expectations in the - // setupDefaultConnectionMocks() method. This will make the exceptions below - // unnecessary. struct ClientCodecError : public std::runtime_error { ClientCodecError(Http::Status&& status) : std::runtime_error(std::string(status.message())), status_(std::move(status)) {} @@ -82,32 +72,24 @@ class Http2CodecImplTestFixture { }; struct ConnectionWrapper { - Http::Status dispatch(const Buffer::Instance& data, ConnectionImpl& connection) { - connection_ = &connection; - Http::Status status = Http::okStatus(); - buffer_.add(data); - return dispatchBufferedData(); - } + explicit ConnectionWrapper(ConnectionImpl* connection) : connection_(connection) {} - Http::Status dispatchBufferedData() { - Http::Status status = Http::okStatus(); - if (!dispatching_) { - while (buffer_.length() > 0) { - dispatching_ = true; - status = connection_->dispatch(buffer_); - if (!status.ok()) { - // Exit early if we hit an error status. - return status; - } - dispatching_ = false; - } + // Drives dispatch, which involves processing incoming bytes and sending pending frames. + void driveDispatch() { + while (canDispatch()) { + status_ = connection_->dispatch(buffer_); } - return status; + } + + // Returns true if there are bytes to send or receive, and the connection is in good state. + bool canDispatch() const { + return (buffer_.length() > 0 || connection_->wantsToWrite()) && status_.ok(); } bool dispatching_{}; Buffer::OwnedImpl buffer_; ConnectionImpl* connection_{}; + Http::Status status_; }; enum SettingsTupleIndex { @@ -118,8 +100,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); @@ -139,19 +123,34 @@ class Http2CodecImplTestFixture { EXPECT_EQ(0, TestUtility::findGauge(server_stats_store_, "http2.pending_send_bytes")->value()); } + + // Ensure that tests driveToCompletion(). Some tests set `expect_buffered_data_on_teardown_` to + // indicate that they purposefully leave buffered data. + if (expect_buffered_data_on_teardown_) { + EXPECT_TRUE(client_wrapper_->buffer_.length() > 0 || server_wrapper_->buffer_.length() > 0); + } else { + EXPECT_EQ(client_wrapper_->buffer_.length(), 0); + EXPECT_EQ(server_wrapper_->buffer_.length(), 0); + } } 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( client_connection_, client_callbacks_, client_stats_store_, client_http2_options_, random_, max_request_headers_kb_, max_response_headers_count_, ProdNghttp2SessionFactory::get()); + client_wrapper_ = std::make_unique(client_.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_); + server_wrapper_ = std::make_unique(server_.get()); request_encoder_ = &client_->newStream(response_decoder_); setupDefaultConnectionMocks(); + driveToCompletion(); EXPECT_CALL(server_callbacks_, newStream(_, _)) .WillRepeatedly(Invoke([&](ResponseEncoder& encoder, bool) -> RequestDecoder& { @@ -168,18 +167,11 @@ class Http2CodecImplTestFixture { if (corrupt_metadata_frame_) { corruptMetadataFramePayload(data); } - auto status = server_wrapper_.dispatch(data, *server_); - if (!status.ok()) { - throw ServerCodecError(std::move(status)); - } + server_wrapper_->buffer_.add(data); })); ON_CALL(server_connection_, write(_, _)) - .WillByDefault(Invoke([&](Buffer::Instance& data, bool) -> void { - auto status = client_wrapper_.dispatch(data, *client_); - if (!status.ok()) { - throw ClientCodecError(std::move(status)); - } - })); + .WillByDefault(Invoke( + [&](Buffer::Instance& data, bool) -> void { client_wrapper_->buffer_.add(data); })); } void http2OptionsFromTuple(envoy::config::core::v3::Http2ProtocolOptions& options, @@ -236,8 +228,99 @@ 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)); + } + } + + void driveClient() { client_wrapper_->driveDispatch(); } + void driveServer() { server_wrapper_->driveDispatch(); } + + void driveToCompletion() { + while (client_wrapper_->canDispatch() || server_wrapper_->canDispatch()) { + driveClient(); + driveServer(); + } + } + + 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_; @@ -245,14 +328,14 @@ class Http2CodecImplTestFixture { NiceMock client_connection_; MockConnectionCallbacks client_callbacks_; std::unique_ptr client_; - ConnectionWrapper client_wrapper_; + std::unique_ptr client_wrapper_; Stats::TestUtil::TestStore server_stats_store_; envoy::config::core::v3::Http2ProtocolOptions server_http2_options_; NiceMock random_; NiceMock server_connection_; MockServerConnectionCallbacks server_callbacks_; std::unique_ptr server_; - ConnectionWrapper server_wrapper_; + std::unique_ptr server_wrapper_; MockResponseDecoder response_decoder_; RequestEncoder* request_encoder_; MockRequestDecoder request_decoder_; @@ -260,6 +343,7 @@ class Http2CodecImplTestFixture { MockStreamCallbacks server_stream_callbacks_; // Corrupt a metadata frame payload. bool corrupt_metadata_frame_ = false; + bool expect_buffered_data_on_teardown_ = false; uint32_t max_request_headers_kb_ = Http::DEFAULT_MAX_REQUEST_HEADERS_KB; uint32_t max_request_headers_count_ = Http::DEFAULT_MAX_HEADERS_COUNT; @@ -281,7 +365,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() { @@ -291,13 +376,18 @@ class Http2CodecImplTest : public ::testing::TestWithParamencodeHeaders(request_headers, false).ok()); + driveToCompletion(); nghttp2_priority_spec spec = {0, 10, 0}; // HTTP/2 codec adds 1 to the number of active streams when computing PRIORITY frames limit constexpr uint32_t max_allowed = 2 * CommonUtility::OptionsLimits::DEFAULT_MAX_INBOUND_PRIORITY_FRAMES_PER_STREAM; for (uint32_t i = 0; i < max_allowed + 1; ++i) { - EXPECT_EQ(0, nghttp2_submit_priority(client_->session(), 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)); + } } } @@ -308,6 +398,7 @@ class Http2CodecImplTest : public ::testing::TestWithParamencodeHeaders(request_headers, true).ok()); + driveToCompletion(); // Send one DATA frame back EXPECT_CALL(response_decoder_, decodeHeaders_(_, false)); @@ -316,6 +407,7 @@ class Http2CodecImplTest : public ::testing::TestWithParamencodeHeaders(response_headers, false); Buffer::OwnedImpl data("0"); EXPECT_NO_THROW(response_encoder_->encodeData(data, false)); + driveToCompletion(); // See the limit formula in the // `Envoy::Http::Http2::ProtocolConstraints::checkInboundFrameLimits()' method. @@ -324,7 +416,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)); + } } } @@ -335,6 +431,7 @@ class Http2CodecImplTest : public ::testing::TestWithParamencodeHeaders(request_headers, false).ok()); + driveToCompletion(); // HTTP/2 codec does not send empty DATA frames with no END_STREAM flag. // To make this work, send raw bytes representing empty DATA frames bypassing client codec. @@ -355,6 +452,7 @@ TEST_P(Http2CodecImplTest, ShutdownNotice) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); EXPECT_CALL(client_callbacks_, onGoAway(_)); server_->shutdownNotice(); @@ -363,6 +461,7 @@ TEST_P(Http2CodecImplTest, ShutdownNotice) { TestResponseHeaderMapImpl response_headers{{":status", "200"}}; EXPECT_CALL(response_decoder_, decodeHeaders_(_, true)); response_encoder_->encodeHeaders(response_headers, true); + driveToCompletion(); } TEST_P(Http2CodecImplTest, ProtocolErrorForTest) { @@ -373,6 +472,7 @@ TEST_P(Http2CodecImplTest, ProtocolErrorForTest) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); EXPECT_CALL(client_callbacks_, onGoAway(Http::GoAwayErrorCode::Other)); @@ -381,9 +481,10 @@ TEST_P(Http2CodecImplTest, ProtocolErrorForTest) { ServerConnectionImpl* raw_server = dynamic_cast(server_.get()); ASSERT(raw_server != nullptr); EXPECT_EQ(StatusCode::CodecProtocolError, getStatusCode(raw_server->protocolErrorForTest())); + driveToCompletion(); } -// 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(); @@ -391,37 +492,48 @@ TEST_P(Http2CodecImplTest, ContinueHeaders) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); 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); + driveToCompletion(); TestResponseHeaderMapImpl response_headers{{":status", "200"}}; EXPECT_CALL(response_decoder_, decodeHeaders_(_, true)); response_encoder_->encodeHeaders(response_headers, true); + driveToCompletion(); }; // nghttp2 rejects trailers with :status. TEST_P(Http2CodecImplTest, TrailerStatus) { + expect_buffered_data_on_teardown_ = true; initialize(); TestRequestHeaderMapImpl request_headers; HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); + EXPECT_TRUE(Http2CodecImplTestFixture::slowContainsStreamId(1, *client_)); 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); + driveToCompletion(); TestResponseHeaderMapImpl response_headers{{":status", "200"}}; EXPECT_CALL(response_decoder_, decodeHeaders_(_, false)); response_encoder_->encodeHeaders(response_headers, false); + driveToCompletion(); // nghttp2 doesn't allow :status in trailers - EXPECT_THROW(response_encoder_->encode100ContinueHeaders(continue_headers), ClientCodecError); + response_encoder_->encode1xxHeaders(continue_headers); + driveToCompletion(); + EXPECT_FALSE(client_wrapper_->status_.ok()); + EXPECT_TRUE(isCodecProtocolError(client_wrapper_->status_)); EXPECT_EQ(1, client_stats_store_.counter("http2.rx_messaging_error").value()); }; @@ -433,58 +545,74 @@ TEST_P(Http2CodecImplTest, MultipleContinueHeaders) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); 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); + driveToCompletion(); + EXPECT_CALL(response_decoder_, decode1xxHeaders_(_)); + response_encoder_->encode1xxHeaders(continue_headers); + driveToCompletion(); TestResponseHeaderMapImpl response_headers{{":status", "200"}}; EXPECT_CALL(response_decoder_, decodeHeaders_(_, true)); response_encoder_->encodeHeaders(response_headers, true); + driveToCompletion(); }; -// 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; HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); - TestResponseHeaderMapImpl other_headers{{":status", "102"}}; + TestResponseHeaderMapImpl other_headers{{":status", "104"}}; EXPECT_CALL(response_decoder_, decodeHeaders_(_, false)); response_encoder_->encodeHeaders(other_headers, false); + driveToCompletion(); }; // nghttp2 treats 101 inside an HTTP/2 stream as an invalid HTTP header field. TEST_P(Http2CodecImplTest, Invalid101SwitchingProtocols) { + expect_buffered_data_on_teardown_ = true; initialize(); TestRequestHeaderMapImpl request_headers; HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); TestResponseHeaderMapImpl upgrade_headers{{":status", "101"}}; EXPECT_CALL(response_decoder_, decodeHeaders_(_, _)).Times(0); - EXPECT_THROW(response_encoder_->encodeHeaders(upgrade_headers, false), ClientCodecError); + response_encoder_->encodeHeaders(upgrade_headers, false); + driveToCompletion(); + EXPECT_FALSE(client_wrapper_->status_.ok()); + EXPECT_TRUE(isCodecProtocolError(client_wrapper_->status_)); EXPECT_EQ(1, client_stats_store_.counter("http2.rx_messaging_error").value()); } TEST_P(Http2CodecImplTest, InvalidContinueWithFin) { + expect_buffered_data_on_teardown_ = true; initialize(); TestRequestHeaderMapImpl request_headers; HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); TestResponseHeaderMapImpl continue_headers{{":status", "100"}}; - EXPECT_THROW(response_encoder_->encodeHeaders(continue_headers, true), ClientCodecError); + response_encoder_->encodeHeaders(continue_headers, true); + driveToCompletion(); + EXPECT_FALSE(client_wrapper_->status_.ok()); + EXPECT_TRUE(isCodecProtocolError(client_wrapper_->status_)); EXPECT_EQ(1, client_stats_store_.counter("http2.rx_messaging_error").value()); } @@ -499,20 +627,13 @@ TEST_P(Http2CodecImplTest, InvalidContinueWithFinAllowed) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); - // Buffer client data to avoid mock recursion causing lifetime issues. - ON_CALL(server_connection_, write(_, _)) - .WillByDefault( - Invoke([&](Buffer::Instance& data, bool) -> void { client_wrapper_.buffer_.add(data); })); - + EXPECT_CALL(request_callbacks, onResetStream(StreamResetReason::ProtocolError, _)); TestResponseHeaderMapImpl continue_headers{{":status", "100"}}; response_encoder_->encodeHeaders(continue_headers, true); - - // Flush pending data. - EXPECT_CALL(request_callbacks, onResetStream(StreamResetReason::ProtocolError, _)); - setupDefaultConnectionMocks(); - auto status = client_wrapper_.dispatch(Buffer::OwnedImpl(), *client_); - EXPECT_TRUE(status.ok()); + driveToCompletion(); + EXPECT_TRUE(client_wrapper_->status_.ok()); EXPECT_EQ(1, client_stats_store_.counter("http2.rx_messaging_error").value()); expectDetailsRequest("http2.violation.of.messaging.rule"); @@ -525,6 +646,7 @@ TEST_P(Http2CodecImplTest, CodecHasCorrectStreamErrorIfFalse) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); EXPECT_FALSE(response_encoder_->streamErrorOnInvalidHttpMessage()); } @@ -537,23 +659,30 @@ TEST_P(Http2CodecImplTest, CodecHasCorrectStreamErrorIfTrue) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); EXPECT_TRUE(response_encoder_->streamErrorOnInvalidHttpMessage()); } TEST_P(Http2CodecImplTest, InvalidRepeatContinue) { + expect_buffered_data_on_teardown_ = true; initialize(); TestRequestHeaderMapImpl request_headers; HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); 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); + driveToCompletion(); - EXPECT_THROW(response_encoder_->encodeHeaders(continue_headers, true), ClientCodecError); + response_encoder_->encodeHeaders(continue_headers, true); + driveToCompletion(); + EXPECT_FALSE(client_wrapper_->status_.ok()); + EXPECT_TRUE(isCodecProtocolError(client_wrapper_->status_)); EXPECT_EQ(1, client_stats_store_.counter("http2.rx_messaging_error").value()); }; @@ -568,35 +697,31 @@ TEST_P(Http2CodecImplTest, InvalidRepeatContinueAllowed) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); TestResponseHeaderMapImpl continue_headers{{":status", "100"}}; - EXPECT_CALL(response_decoder_, decode100ContinueHeaders_(_)); - response_encoder_->encode100ContinueHeaders(continue_headers); - - // Buffer client data to avoid mock recursion causing lifetime issues. - ON_CALL(server_connection_, write(_, _)) - .WillByDefault( - Invoke([&](Buffer::Instance& data, bool) -> void { client_wrapper_.buffer_.add(data); })); - - response_encoder_->encodeHeaders(continue_headers, true); + EXPECT_CALL(response_decoder_, decode1xxHeaders_(_)); + response_encoder_->encode1xxHeaders(continue_headers); + driveToCompletion(); - // Flush pending data. EXPECT_CALL(request_callbacks, onResetStream(StreamResetReason::ProtocolError, _)); - setupDefaultConnectionMocks(); - auto status = client_wrapper_.dispatch(Buffer::OwnedImpl(), *client_); - EXPECT_TRUE(status.ok()); + response_encoder_->encodeHeaders(continue_headers, true); + driveToCompletion(); + EXPECT_TRUE(client_wrapper_->status_.ok()); EXPECT_EQ(1, client_stats_store_.counter("http2.rx_messaging_error").value()); expectDetailsRequest("http2.violation.of.messaging.rule"); }; TEST_P(Http2CodecImplTest, Invalid204WithContentLength) { + expect_buffered_data_on_teardown_ = true; initialize(); TestRequestHeaderMapImpl request_headers; HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); TestResponseHeaderMapImpl response_headers{{":status", "204"}, {"content-length", "3"}}; // What follows is a hack to get headers that should span into continuation frames. The default @@ -607,11 +732,14 @@ TEST_P(Http2CodecImplTest, Invalid204WithContentLength) { response_headers.addCopy(std::to_string(i), std::to_string(i)); } + response_encoder_->encodeHeaders(response_headers, false); EXPECT_LOG_CONTAINS( "debug", "Invalid HTTP header field was received: frame type: 1, stream: 1, name: [content-length], " "value: [3]", - EXPECT_THROW(response_encoder_->encodeHeaders(response_headers, false), ClientCodecError)); + driveToCompletion()); + EXPECT_FALSE(client_wrapper_->status_.ok()); + EXPECT_TRUE(isCodecProtocolError(client_wrapper_->status_)); EXPECT_EQ(1, client_stats_store_.counter("http2.rx_messaging_error").value()); }; @@ -626,11 +754,7 @@ TEST_P(Http2CodecImplTest, Invalid204WithContentLengthAllowed) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); - - // Buffer client data to avoid mock recursion causing lifetime issues. - ON_CALL(server_connection_, write(_, _)) - .WillByDefault( - Invoke([&](Buffer::Instance& data, bool) -> void { client_wrapper_.buffer_.add(data); })); + driveToCompletion(); TestResponseHeaderMapImpl response_headers{{":status", "204"}, {"content-length", "3"}}; // What follows is a hack to get headers that should span into continuation frames. The default @@ -641,14 +765,11 @@ TEST_P(Http2CodecImplTest, Invalid204WithContentLengthAllowed) { response_headers.addCopy(std::to_string(i), std::to_string(i)); } - response_encoder_->encodeHeaders(response_headers, false); - - // Flush pending data. EXPECT_CALL(request_callbacks, onResetStream(StreamResetReason::ProtocolError, _)); EXPECT_CALL(server_stream_callbacks_, onResetStream(StreamResetReason::RemoteReset, _)); - setupDefaultConnectionMocks(); - auto status = client_wrapper_.dispatch(Buffer::OwnedImpl(), *client_); - EXPECT_TRUE(status.ok()); + response_encoder_->encodeHeaders(response_headers, false); + driveToCompletion(); + EXPECT_TRUE(client_wrapper_->status_.ok()); EXPECT_EQ(1, client_stats_store_.counter("http2.rx_messaging_error").value()); expectDetailsRequest("http2.invalid.header.field"); @@ -661,6 +782,7 @@ TEST_P(Http2CodecImplTest, RefusedStreamReset) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); MockStreamCallbacks callbacks; request_encoder_->getStream().addCallbacks(callbacks); @@ -668,12 +790,14 @@ TEST_P(Http2CodecImplTest, RefusedStreamReset) { onResetStream(StreamResetReason::LocalRefusedStreamReset, _)); EXPECT_CALL(callbacks, onResetStream(StreamResetReason::RemoteRefusedStreamReset, _)); response_encoder_->getStream().resetStream(StreamResetReason::LocalRefusedStreamReset); + driveToCompletion(); } TEST_P(Http2CodecImplTest, InvalidHeadersFrame) { initialize(); const auto status = request_encoder_->encodeHeaders(TestRequestHeaderMapImpl{}, true); + driveToCompletion(); EXPECT_FALSE(status.ok()); EXPECT_THAT(status.message(), testing::HasSubstr("missing required")); @@ -686,26 +810,30 @@ TEST_P(Http2CodecImplTest, TrailingHeaders) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); EXPECT_CALL(request_decoder_, decodeData(_, false)); Buffer::OwnedImpl hello("hello"); request_encoder_->encodeData(hello, false); + driveToCompletion(); EXPECT_CALL(request_decoder_, decodeTrailers_(_)); request_encoder_->encodeTrailers(TestRequestTrailerMapImpl{{"trailing", "header"}}); + driveToCompletion(); TestResponseHeaderMapImpl response_headers{{":status", "200"}}; EXPECT_CALL(response_decoder_, decodeHeaders_(_, false)); response_encoder_->encodeHeaders(response_headers, false); + driveToCompletion(); EXPECT_CALL(response_decoder_, decodeData(_, false)); Buffer::OwnedImpl world("world"); response_encoder_->encodeData(world, false); + driveToCompletion(); EXPECT_CALL(response_decoder_, decodeTrailers_(_)); response_encoder_->encodeTrailers(TestResponseTrailerMapImpl{{"trailing", "header"}}); + driveToCompletion(); } // When having empty trailers, codec submits empty buffer and end_stream instead. TEST_P(Http2CodecImplTest, IgnoreTrailingEmptyHeaders) { - TestScopedRuntime scoped_runtime; - initialize(); Buffer::OwnedImpl empty_buffer; @@ -714,30 +842,31 @@ TEST_P(Http2CodecImplTest, IgnoreTrailingEmptyHeaders) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); EXPECT_CALL(request_decoder_, decodeData(_, false)); Buffer::OwnedImpl hello("hello"); request_encoder_->encodeData(hello, false); + driveToCompletion(); EXPECT_CALL(request_decoder_, decodeData(BufferEqual(&empty_buffer), true)); request_encoder_->encodeTrailers(TestRequestTrailerMapImpl{}); + driveToCompletion(); TestResponseHeaderMapImpl response_headers{{":status", "200"}}; EXPECT_CALL(response_decoder_, decodeHeaders_(_, false)); response_encoder_->encodeHeaders(response_headers, false); + driveToCompletion(); EXPECT_CALL(response_decoder_, decodeData(_, false)); Buffer::OwnedImpl world("world"); response_encoder_->encodeData(world, false); + driveToCompletion(); EXPECT_CALL(response_decoder_, decodeData(BufferEqual(&empty_buffer), true)); response_encoder_->encodeTrailers(TestResponseTrailerMapImpl{}); + driveToCompletion(); } TEST_P(Http2CodecImplTest, TrailingHeadersLargeClientBody) { initialize(); - // Buffer server data so we can make sure we don't get any window updates. - ON_CALL(client_connection_, write(_, _)) - .WillByDefault( - Invoke([&](Buffer::Instance& data, bool) -> void { server_wrapper_.buffer_.add(data); })); - TestRequestHeaderMapImpl request_headers; HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); @@ -746,21 +875,25 @@ TEST_P(Http2CodecImplTest, TrailingHeadersLargeClientBody) { Buffer::OwnedImpl body(std::string(1024 * 1024, 'a')); request_encoder_->encodeData(body, false); request_encoder_->encodeTrailers(TestRequestTrailerMapImpl{{"trailing", "header"}}); + // Only drive the client so we can make sure we don't get any window updates. + driveClient(); // Flush pending data. - setupDefaultConnectionMocks(); EXPECT_CALL(request_decoder_, decodeTrailers_(_)); - auto status = server_wrapper_.dispatch(Buffer::OwnedImpl(), *server_); - EXPECT_TRUE(status.ok()); + driveToCompletion(); + EXPECT_TRUE(server_wrapper_->status_.ok()); TestResponseHeaderMapImpl response_headers{{":status", "200"}}; EXPECT_CALL(response_decoder_, decodeHeaders_(_, false)); response_encoder_->encodeHeaders(response_headers, false); + driveToCompletion(); EXPECT_CALL(response_decoder_, decodeData(_, false)); Buffer::OwnedImpl world("world"); response_encoder_->encodeData(world, false); + driveToCompletion(); EXPECT_CALL(response_decoder_, decodeTrailers_(_)); response_encoder_->encodeTrailers(TestResponseTrailerMapImpl{{"trailing", "header"}}); + driveToCompletion(); } TEST_P(Http2CodecImplTest, SmallMetadataVecTest) { @@ -772,6 +905,7 @@ TEST_P(Http2CodecImplTest, SmallMetadataVecTest) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); MetadataMapVector metadata_map_vector; const int size = 10; @@ -788,9 +922,11 @@ TEST_P(Http2CodecImplTest, SmallMetadataVecTest) { EXPECT_CALL(request_decoder_, decodeMetadata_(_)).Times(size); request_encoder_->encodeMetadata(metadata_map_vector); + driveToCompletion(); EXPECT_CALL(response_decoder_, decodeMetadata_(_)).Times(size); response_encoder_->encodeMetadata(metadata_map_vector); + driveToCompletion(); } TEST_P(Http2CodecImplTest, LargeMetadataVecTest) { @@ -802,6 +938,7 @@ TEST_P(Http2CodecImplTest, LargeMetadataVecTest) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); MetadataMapVector metadata_map_vector; const int size = 10; @@ -815,13 +952,16 @@ TEST_P(Http2CodecImplTest, LargeMetadataVecTest) { EXPECT_CALL(request_decoder_, decodeMetadata_(_)).Times(size); request_encoder_->encodeMetadata(metadata_map_vector); + driveToCompletion(); EXPECT_CALL(response_decoder_, decodeMetadata_(_)).Times(size); response_encoder_->encodeMetadata(metadata_map_vector); + driveToCompletion(); } TEST_P(Http2CodecImplTest, BadMetadataVecReceivedTest) { allow_metadata_ = true; + expect_buffered_data_on_teardown_ = true; initialize(); // Generates a valid stream_id by sending a request header. @@ -829,6 +969,7 @@ TEST_P(Http2CodecImplTest, BadMetadataVecReceivedTest) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); MetadataMap metadata_map = { {"header_key1", "header_value1"}, @@ -841,8 +982,12 @@ TEST_P(Http2CodecImplTest, BadMetadataVecReceivedTest) { metadata_map_vector.push_back(std::move(metadata_map_ptr)); corrupt_metadata_frame_ = true; - EXPECT_THROW_WITH_MESSAGE(request_encoder_->encodeMetadata(metadata_map_vector), ServerCodecError, - "The user callback function failed"); + request_encoder_->encodeMetadata(metadata_map_vector); + driveToCompletion(); + // The error is detected by the server codec. + EXPECT_FALSE(server_wrapper_->status_.ok()); + EXPECT_TRUE(isCodecProtocolError(server_wrapper_->status_)); + EXPECT_EQ(server_wrapper_->status_.message(), "The user callback function failed"); } // Encode response metadata while dispatching request data from the client, so @@ -872,6 +1017,7 @@ TEST_P(Http2CodecImplTest, EncodeMetadataWhileDispatchingTest) { })); EXPECT_CALL(response_decoder_, decodeMetadata_(_)).Times(size); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); } // Verifies that metadata is not decoded after the stream ended. @@ -884,6 +1030,7 @@ TEST_P(Http2CodecImplTest, NoMetadataEndStreamTest) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); const MetadataMap metadata_map = {{"header_key1", "header_value1"}}; MetadataMapPtr metadata_map_ptr = std::make_unique(metadata_map); @@ -893,10 +1040,12 @@ TEST_P(Http2CodecImplTest, NoMetadataEndStreamTest) { // The metadata decoding will not be called after the stream has ended. EXPECT_CALL(request_decoder_, decodeMetadata_(_)).Times(0); request_encoder_->encodeMetadata(metadata_map_vector); + driveToCompletion(); } // Validate the keepalive PINGs are sent and received correctly. TEST_P(Http2CodecImplTest, ConnectionKeepalive) { + expect_buffered_data_on_teardown_ = true; constexpr uint32_t interval_ms = 100; constexpr uint32_t timeout_ms = 200; client_http2_options_.mutable_connection_keepalive()->mutable_interval()->set_nanos(interval_ms * @@ -916,8 +1065,14 @@ TEST_P(Http2CodecImplTest, ConnectionKeepalive) { EXPECT_CALL(*timeout_timer, disableTimer()); // This indicates that an ACK was received. EXPECT_CALL(*send_timer, enableTimer(std::chrono::milliseconds(interval_ms), _)); send_timer->invokeCallback(); + driveToCompletion(); + // Re-enable the timeout timer by sending a PING without allowing it to be received and acked. // Test that a timeout closes the connection. + EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(timeout_ms), _)); + EXPECT_CALL(*timeout_timer, disableTimer()).Times(0); // This indicates that no ACK was received. + send_timer->invokeCallback(); + driveClient(); EXPECT_CALL(client_connection_, close(Network::ConnectionCloseType::NoFlush)); timeout_timer->invokeCallback(); } @@ -945,18 +1100,11 @@ TEST_P(Http2CodecImplTest, ConnectionKeepaliveJitter) { initialize(); ASSERT_TRUE(send_timer->enabled()); - // Don't allow synchronous dispatch, it results in receiving the ping under - // the stack of sending it. for (uint64_t i = 0; i < 250; i++) { - client_wrapper_.dispatching_ = true; - EXPECT_CALL(random_, random()).WillOnce(Return(i)); ASSERT_TRUE(send_timer->enabled()); send_timer->invokeCallback(); - - client_wrapper_.dispatching_ = false; - client_wrapper_.dispatchBufferedData().IgnoreError(); - client_wrapper_.dispatching_ = true; + driveToCompletion(); } EXPECT_EQ(min_observed.count(), min_expected.count()); @@ -980,6 +1128,7 @@ TEST_P(Http2CodecImplTest, IdlePing) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); // Advance time past 1s. This time the ping should be sent, and the timeout // alarm enabled. @@ -988,6 +1137,7 @@ TEST_P(Http2CodecImplTest, IdlePing) { EXPECT_CALL(*timeout_timer, enableTimer(_, _)).Times(0); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder2->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); } TEST_P(Http2CodecImplTest, DumpsStreamlessConnectionWithoutAllocatingMemory) { @@ -1020,7 +1170,7 @@ TEST_P(Http2CodecImplTest, DumpsStreamlessConnectionWithoutAllocatingMemory) { "consecutive_inbound_frames_with_empty_payload_: 0, " "max_consecutive_inbound_frames_with_empty_payload_: 1, opened_streams_: 0, " "inbound_priority_frames_: 0, max_inbound_priority_frames_per_stream_: 100, " - "inbound_window_update_frames_: 0, outbound_data_frames_: 0, " + "inbound_window_update_frames_: 1, outbound_data_frames_: 0, " "max_inbound_window_update_frames_per_data_frame_sent_: 10\n" " Number of active streams: 0, current_stream_id_: null Dumping 0 Active Streams:\n" " current_slice_: null\n")); @@ -1037,10 +1187,12 @@ TEST_P(Http2CodecImplTest, ShouldDumpActiveStreamsWithoutAllocatingMemory) { HttpTestUtility::addDefaultHeaders(expected_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(HeaderMapEqual(&expected_headers), false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); TestResponseHeaderMapImpl response_headers{{":status", "200"}}; EXPECT_CALL(response_decoder_, decodeHeaders_(_, true)); response_encoder_->encodeHeaders(response_headers, true); + driveToCompletion(); // Dump server { @@ -1107,6 +1259,7 @@ TEST_P(Http2CodecImplTest, ShouldDumpCurrentSliceWithoutAllocatingMemory) { HttpTestUtility::addDefaultHeaders(expected_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(HeaderMapEqual(&expected_headers), false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); // Send data payload, dump buffer as decoding data EXPECT_CALL(request_decoder_, decodeData(_, false)).WillOnce(Invoke([&](Buffer::Instance&, bool) { @@ -1118,6 +1271,7 @@ TEST_P(Http2CodecImplTest, ShouldDumpCurrentSliceWithoutAllocatingMemory) { })); Buffer::OwnedImpl hello("hello envoy"); request_encoder_->encodeData(hello, false); + driveToCompletion(); // Check contents for the current slice information { @@ -1147,6 +1301,7 @@ TEST_P(Http2CodecImplTest, ClientConnectionShouldDumpCorrespondingRequestWithout HttpTestUtility::addDefaultHeaders(expected_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(HeaderMapEqual(&expected_headers), false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); // Prepare for state dump. EXPECT_CALL(upstream_to_downstream, dumpState(_, _)); @@ -1162,6 +1317,7 @@ TEST_P(Http2CodecImplTest, ClientConnectionShouldDumpCorrespondingRequestWithout TestResponseHeaderMapImpl response_headers{{":status", "200"}}; response_encoder_->encodeHeaders(response_headers, false); + driveToCompletion(); // Check contents for the corresponding downstream request. EXPECT_THAT( @@ -1188,12 +1344,10 @@ TEST_P(Http2CodecImplDeferredResetTest, NoDeferredResetForClientStreams) { // call to resetStream goes down the regular reset path for client streams; the pending outbound // header and data for the reset stream are discarded immediately. EXPECT_CALL(request_decoder_, decodeData(_, _)).Times(0); - ON_CALL(client_connection_, write(_, _)) - .WillByDefault( - Invoke([&](Buffer::Instance& data, bool) -> void { server_wrapper_.buffer_.add(data); })); TestRequestHeaderMapImpl request_headers; HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveClient(); // Dispatch server. We expect to see some data. EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)).WillOnce(InvokeWithoutArgs([&]() -> void { @@ -1209,11 +1363,10 @@ TEST_P(Http2CodecImplDeferredResetTest, NoDeferredResetForClientStreams) { request_encoder_->getStream().resetStream(StreamResetReason::LocalReset); })); - setupDefaultConnectionMocks(); - EXPECT_NE(0, server_wrapper_.buffer_.length()); - auto status = server_wrapper_.dispatch(Buffer::OwnedImpl(), *server_); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(0, server_wrapper_.buffer_.length()); + EXPECT_NE(0, server_wrapper_->buffer_.length()); + driveToCompletion(); + EXPECT_TRUE(server_wrapper_->status_.ok()); + EXPECT_EQ(0, server_wrapper_->buffer_.length()); } TEST_P(Http2CodecImplDeferredResetTest, DeferredResetServerIfLocalEndStreamBeforeReset) { @@ -1228,9 +1381,6 @@ TEST_P(Http2CodecImplDeferredResetTest, DeferredResetServerIfLocalEndStreamBefor // order to delay sendPendingFrames processing in those calls until the end of dispatch. The // delayed sendPendingFrames processing allows us to verify that resetStream calls go down the // deferred reset path if there are pending data frames with local end_stream set. - ON_CALL(server_connection_, write(_, _)) - .WillByDefault(Invoke( - [&](Buffer::Instance& data, bool) -> void { client_wrapper_.buffer_.add(data); })); TestResponseHeaderMapImpl response_headers{{":status", "200"}}; response_encoder_->encodeHeaders(response_headers, false); Buffer::OwnedImpl body(std::string(32 * 1024, 'a')); @@ -1243,6 +1393,10 @@ TEST_P(Http2CodecImplDeferredResetTest, DeferredResetServerIfLocalEndStreamBefor response_encoder_->getStream().resetStream(StreamResetReason::LocalReset); })); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + // Drive the client once to send the headers to the server, and drive the server once to encode + // the HEADERS, DATA, and RST_STREAM as described above. + driveClient(); + driveServer(); MockStreamCallbacks client_stream_callbacks; request_encoder_->getStream().addCallbacks(client_stream_callbacks); @@ -1250,9 +1404,8 @@ TEST_P(Http2CodecImplDeferredResetTest, DeferredResetServerIfLocalEndStreamBefor EXPECT_CALL(response_decoder_, decodeData(_, false)).Times(AnyNumber()); EXPECT_CALL(response_decoder_, decodeData(_, true)); EXPECT_CALL(client_stream_callbacks, onResetStream(StreamResetReason::RemoteReset, _)); - setupDefaultConnectionMocks(); - auto status = client_wrapper_.dispatch(Buffer::OwnedImpl(), *client_); - EXPECT_TRUE(status.ok()); + driveToCompletion(); + EXPECT_TRUE(client_wrapper_->status_.ok()); } TEST_P(Http2CodecImplDeferredResetTest, LargeDataDeferredResetServerIfLocalEndStreamBeforeReset) { @@ -1267,9 +1420,6 @@ TEST_P(Http2CodecImplDeferredResetTest, LargeDataDeferredResetServerIfLocalEndSt // order to delay sendPendingFrames processing in those calls until the end of dispatch. The // delayed sendPendingFrames processing allows us to verify that resetStream calls go down the // deferred reset path if there are pending data frames with local end_stream set. - ON_CALL(server_connection_, write(_, _)) - .WillByDefault(Invoke( - [&](Buffer::Instance& data, bool) -> void { client_wrapper_.buffer_.add(data); })); TestResponseHeaderMapImpl response_headers{{":status", "200"}}; response_encoder_->encodeHeaders(response_headers, false); Buffer::OwnedImpl body(std::string(1024 * 1024, 'a')); @@ -1282,15 +1432,18 @@ TEST_P(Http2CodecImplDeferredResetTest, LargeDataDeferredResetServerIfLocalEndSt response_encoder_->getStream().resetStream(StreamResetReason::LocalReset); })); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + // Drive the client once to send the headers to the server, and drive the server once to encode + // the HEADERS, DATA, and RST_STREAM as described above. + driveClient(); + driveServer(); MockStreamCallbacks client_stream_callbacks; request_encoder_->getStream().addCallbacks(client_stream_callbacks); EXPECT_CALL(response_decoder_, decodeHeaders_(_, false)); EXPECT_CALL(response_decoder_, decodeData(_, false)).Times(AnyNumber()); EXPECT_CALL(client_stream_callbacks, onResetStream(StreamResetReason::RemoteReset, _)); - setupDefaultConnectionMocks(); - auto status = client_wrapper_.dispatch(Buffer::OwnedImpl(), *client_); - EXPECT_TRUE(status.ok()); + driveToCompletion(); + EXPECT_TRUE(client_wrapper_->status_.ok()); } TEST_P(Http2CodecImplDeferredResetTest, NoDeferredResetServerIfResetBeforeLocalEndStream) { @@ -1305,9 +1458,6 @@ TEST_P(Http2CodecImplDeferredResetTest, NoDeferredResetServerIfResetBeforeLocalE // order to delay sendPendingFrames processing in those calls until the end of dispatch. The // call to resetStream goes down the regular reset path since local end_stream is not set; the // pending outbound header and data for the reset stream are discarded immediately. - ON_CALL(server_connection_, write(_, _)) - .WillByDefault(Invoke( - [&](Buffer::Instance& data, bool) -> void { client_wrapper_.buffer_.add(data); })); TestResponseHeaderMapImpl response_headers{{":status", "200"}}; response_encoder_->encodeHeaders(response_headers, false); Buffer::OwnedImpl body(std::string(1024 * 1024, 'a')); @@ -1317,15 +1467,18 @@ TEST_P(Http2CodecImplDeferredResetTest, NoDeferredResetServerIfResetBeforeLocalE response_encoder_->getStream().resetStream(StreamResetReason::LocalReset); })); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + // Drive the client once to send the headers to the server, and drive the server once to encode + // the HEADERS, DATA, and RST_STREAM as described above. + driveClient(); + driveServer(); MockStreamCallbacks client_stream_callbacks; request_encoder_->getStream().addCallbacks(client_stream_callbacks); EXPECT_CALL(response_decoder_, decodeHeaders_(_, _)).Times(0); EXPECT_CALL(response_decoder_, decodeData(_, _)).Times(0); EXPECT_CALL(client_stream_callbacks, onResetStream(StreamResetReason::RemoteReset, _)); - setupDefaultConnectionMocks(); - auto status = client_wrapper_.dispatch(Buffer::OwnedImpl(), *client_); - EXPECT_TRUE(status.ok()); + driveToCompletion(); + EXPECT_TRUE(client_wrapper_->status_.ok()); } class Http2CodecImplFlowControlTest : public Http2CodecImplTest {}; @@ -1346,6 +1499,7 @@ TEST_P(Http2CodecImplFlowControlTest, TestFlowControlInPendingSendData) { HttpTestUtility::addDefaultHeaders(expected_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(HeaderMapEqual(&expected_headers), false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); // Force the server stream to be read disabled. This will cause it to stop sending window // updates to the client. @@ -1353,8 +1507,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); @@ -1366,16 +1519,18 @@ TEST_P(Http2CodecImplFlowControlTest, TestFlowControlInPendingSendData) { EXPECT_CALL(request_decoder_, decodeData(_, false)).Times(AnyNumber()); Buffer::OwnedImpl long_data(std::string(initial_stream_window, 'a')); request_encoder_->encodeData(long_data, false); + driveToCompletion(); // 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. Buffer::OwnedImpl more_long_data(std::string(initial_stream_window, 'a')); request_encoder_->encodeData(more_long_data, false); + driveToCompletion(); EXPECT_EQ(initial_stream_window, client_->getStream(1)->pending_send_data_->length()); EXPECT_EQ(initial_stream_window, TestUtility::findGauge(client_stats_store_, "http2.pending_send_bytes")->value()); @@ -1385,6 +1540,7 @@ TEST_P(Http2CodecImplFlowControlTest, TestFlowControlInPendingSendData) { EXPECT_CALL(callbacks, onAboveWriteBufferHighWatermark()); Buffer::OwnedImpl last_byte("!"); request_encoder_->encodeData(last_byte, false); + driveToCompletion(); EXPECT_EQ(initial_stream_window + 1, client_->getStream(1)->pending_send_data_->length()); EXPECT_EQ(initial_stream_window + 1, TestUtility::findGauge(client_stats_store_, "http2.pending_send_bytes")->value()); @@ -1407,6 +1563,7 @@ TEST_P(Http2CodecImplFlowControlTest, TestFlowControlInPendingSendData) { })); EXPECT_CALL(request_decoder2, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder2->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); // Add the stream callbacks belatedly. On creation the stream should have // been noticed that the connection was backed up. Any new subscriber to @@ -1430,14 +1587,13 @@ TEST_P(Http2CodecImplFlowControlTest, TestFlowControlInPendingSendData) { EXPECT_CALL(callbacks2, onBelowWriteBufferLowWatermark()).Times(0); EXPECT_CALL(callbacks3, onBelowWriteBufferLowWatermark()); server_->getStream(1)->readDisable(false); + driveToCompletion(); EXPECT_EQ(0, client_->getStream(1)->pending_send_data_->length()); 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 @@ -1453,14 +1609,14 @@ TEST_P(Http2CodecImplFlowControlTest, EarlyResetRestoresWindow) { HttpTestUtility::addDefaultHeaders(expected_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(HeaderMapEqual(&expected_headers), false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); // 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_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); @@ -1470,13 +1626,14 @@ TEST_P(Http2CodecImplFlowControlTest, EarlyResetRestoresWindow) { // The one giant write will cause the buffer to go over the limit, then drain and go back under // the limit. request_encoder_->encodeData(long_data, false); + driveToCompletion(); // 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, _)); @@ -1496,9 +1653,10 @@ TEST_P(Http2CodecImplFlowControlTest, EarlyResetRestoresWindow) { server_->onUnderlyingConnectionBelowWriteBufferLowWatermark(); })); response_encoder_->getStream().resetStream(StreamResetReason::LocalRefusedStreamReset); + driveToCompletion(); // 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. @@ -1512,6 +1670,7 @@ TEST_P(Http2CodecImplFlowControlTest, FlowControlPendingRecvData) { HttpTestUtility::addDefaultHeaders(expected_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(HeaderMapEqual(&expected_headers), false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); // Set artificially small watermarks to make the recv buffer easy to overrun. In production, // the recv buffer can be overrun by a client which negotiates a larger @@ -1522,6 +1681,7 @@ TEST_P(Http2CodecImplFlowControlTest, FlowControlPendingRecvData) { EXPECT_CALL(request_decoder_, decodeData(_, false)); Buffer::OwnedImpl data(std::string(40, 'a')); request_encoder_->encodeData(data, false); + driveToCompletion(); } // Verify that we create and disable the stream flush timer when trailers follow a stream that @@ -1534,28 +1694,34 @@ TEST_P(Http2CodecImplFlowControlTest, TrailingHeadersLargeServerBody) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); - ON_CALL(client_connection_, write(_, _)) - .WillByDefault( - Invoke([&](Buffer::Instance& data, bool) -> void { server_wrapper_.buffer_.add(data); })); TestResponseHeaderMapImpl response_headers{{":status", "200"}}; EXPECT_CALL(response_decoder_, decodeHeaders_(_, false)); response_encoder_->encodeHeaders(response_headers, false); + driveToCompletion(); EXPECT_CALL(server_stream_callbacks_, onAboveWriteBufferHighWatermark()); EXPECT_CALL(response_decoder_, decodeData(_, false)).Times(AtLeast(1)); - auto flush_timer = new NiceMock(&server_connection_.dispatcher_); - EXPECT_CALL(*flush_timer, enableTimer(std::chrono::milliseconds(30000), _)); Buffer::OwnedImpl body(std::string(1024 * 1024, 'a')); response_encoder_->encodeData(body, false); + // Drive the server once to send some encoded data, and drive the client once to receive part of + // that data. Do not drive further in order to avoid sending a WINDOW_UPDATE from client to + // server, intentionally exhausting the window. + driveServer(); + driveClient(); + auto flush_timer = new NiceMock(&server_connection_.dispatcher_); + EXPECT_CALL(*flush_timer, enableTimer(std::chrono::milliseconds(30000), _)); response_encoder_->encodeTrailers(TestResponseTrailerMapImpl{{"trailing", "header"}}); - // Send window updates from the client. - setupDefaultConnectionMocks(); + // Now drive the response to completion, allowing a WINDOW_UPDATE from client to server. The + // client decodes more data, the server is finally able to send trailers (disabling the flush + // timer), and the client decodes any remaining data and trailers. EXPECT_CALL(response_decoder_, decodeData(_, false)).Times(AtLeast(1)); - EXPECT_CALL(response_decoder_, decodeTrailers_(_)); EXPECT_CALL(*flush_timer, disableTimer()); - auto status = server_wrapper_.dispatch(Buffer::OwnedImpl(), *server_); - EXPECT_TRUE(status.ok()); + EXPECT_CALL(response_decoder_, decodeData(_, false)).Times(AnyNumber()); + EXPECT_CALL(response_decoder_, decodeTrailers_(_)); + driveToCompletion(); + EXPECT_TRUE(server_wrapper_->status_.ok()); EXPECT_EQ(0, server_stats_store_.counter("http2.tx_flush_timeout").value()); } @@ -1571,19 +1737,23 @@ TEST_P(Http2CodecImplFlowControlTest, TrailingHeadersLargeServerBodyFlushTimeout HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); - ON_CALL(client_connection_, write(_, _)) - .WillByDefault( - Invoke([&](Buffer::Instance& data, bool) -> void { server_wrapper_.buffer_.add(data); })); TestResponseHeaderMapImpl response_headers{{":status", "200"}}; EXPECT_CALL(response_decoder_, decodeHeaders_(_, false)); response_encoder_->encodeHeaders(response_headers, false); + driveToCompletion(); EXPECT_CALL(server_stream_callbacks_, onAboveWriteBufferHighWatermark()); EXPECT_CALL(response_decoder_, decodeData(_, false)).Times(AtLeast(1)); auto flush_timer = new NiceMock(&server_connection_.dispatcher_); EXPECT_CALL(*flush_timer, enableTimer(std::chrono::milliseconds(30000), _)); Buffer::OwnedImpl body(std::string(1024 * 1024, 'a')); response_encoder_->encodeData(body, false); + // Drive the server once to send some encoded data, and drive the client once to receive part of + // that data. Do not drive further in order to avoid sending a WINDOW_UPDATE from client to + // server, intentionally exhausting the window. + driveServer(); + driveClient(); response_encoder_->encodeTrailers(TestResponseTrailerMapImpl{{"trailing", "header"}}); // Invoke a stream flush timeout. Make sure we don't get a reset locally for higher layers but @@ -1591,6 +1761,7 @@ TEST_P(Http2CodecImplFlowControlTest, TrailingHeadersLargeServerBodyFlushTimeout EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)).Times(0); EXPECT_CALL(client_stream_callbacks, onResetStream(StreamResetReason::RemoteReset, _)); flush_timer->invokeCallback(); + driveToCompletion(); EXPECT_EQ(1, server_stats_store_.counter("http2.tx_flush_timeout").value()); } @@ -1606,24 +1777,31 @@ TEST_P(Http2CodecImplFlowControlTest, LargeServerBodyFlushTimeout) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); - ON_CALL(client_connection_, write(_, _)) - .WillByDefault( - Invoke([&](Buffer::Instance& data, bool) -> void { server_wrapper_.buffer_.add(data); })); TestResponseHeaderMapImpl response_headers{{":status", "200"}}; EXPECT_CALL(response_decoder_, decodeHeaders_(_, false)); response_encoder_->encodeHeaders(response_headers, false); - EXPECT_CALL(response_decoder_, decodeData(_, false)).Times(AtLeast(1)); + driveToCompletion(); + + // The server enables the flush timer under encodeData(). The client then decodes some data. auto flush_timer = new NiceMock(&server_connection_.dispatcher_); EXPECT_CALL(*flush_timer, enableTimer(std::chrono::milliseconds(30000), _)); + EXPECT_CALL(response_decoder_, decodeData(_, false)).Times(AtLeast(1)); Buffer::OwnedImpl body(std::string(1024 * 1024, 'a')); response_encoder_->encodeData(body, true); + // Drive the server once to send some encoded data, and drive the client once to receive part of + // that data. Do not drive further in order to avoid sending a WINDOW_UPDATE from client to + // server, intentionally exhausting the window. + driveServer(); + driveClient(); // Invoke a stream flush timeout. Make sure we don't get a reset locally for higher layers but // we do get a reset on the client. EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)).Times(0); EXPECT_CALL(client_stream_callbacks, onResetStream(StreamResetReason::RemoteReset, _)); flush_timer->invokeCallback(); + driveToCompletion(); EXPECT_EQ(1, server_stats_store_.counter("http2.tx_flush_timeout").value()); } @@ -1639,26 +1817,32 @@ TEST_P(Http2CodecImplFlowControlTest, LargeServerBodyFlushTimeoutAfterGoaway) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); - ON_CALL(client_connection_, write(_, _)) - .WillByDefault( - Invoke([&](Buffer::Instance& data, bool) -> void { server_wrapper_.buffer_.add(data); })); TestResponseHeaderMapImpl response_headers{{":status", "200"}}; EXPECT_CALL(response_decoder_, decodeHeaders_(_, false)); response_encoder_->encodeHeaders(response_headers, false); - EXPECT_CALL(response_decoder_, decodeData(_, false)).Times(AtLeast(1)); + driveToCompletion(); + + // The server enables the flush timer under encodeData(). The client then decodes some data. auto flush_timer = new NiceMock(&server_connection_.dispatcher_); EXPECT_CALL(*flush_timer, enableTimer(std::chrono::milliseconds(30000), _)); + EXPECT_CALL(response_decoder_, decodeData(_, false)).Times(AtLeast(1)); Buffer::OwnedImpl body(std::string(1024 * 1024, 'a')); response_encoder_->encodeData(body, true); + // Drive the server once to send some encoded data, and drive the client once to receive part of + // that data. Do not drive further in order to avoid sending a WINDOW_UPDATE from client to + // server, intentionally exhausting the window. + driveServer(); + driveClient(); // Force a protocol error. - Buffer::OwnedImpl garbage_data("this should cause a protocol error"); - EXPECT_CALL(client_callbacks_, onGoAway(_)); + server_wrapper_->buffer_.add("this should cause a protocol error"); EXPECT_CALL(*flush_timer, disableTimer()); EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)).Times(0); - auto status = server_wrapper_.dispatch(garbage_data, *server_); - EXPECT_FALSE(status.ok()); + EXPECT_CALL(client_callbacks_, onGoAway(_)); + driveToCompletion(); + EXPECT_FALSE(server_wrapper_->status_.ok()); EXPECT_EQ(0, server_stats_store_.counter("http2.tx_flush_timeout").value()); } @@ -1673,6 +1857,7 @@ TEST_P(Http2CodecImplFlowControlTest, WindowUpdateOnReadResumingFlood) { HttpTestUtility::addDefaultHeaders(expected_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(HeaderMapEqual(&expected_headers), false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); int frame_count = 0; Buffer::OwnedImpl buffer; @@ -1682,15 +1867,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); @@ -1698,27 +1879,34 @@ 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')); request_encoder_->encodeData(long_data, false); + driveToCompletion(); EXPECT_EQ(initial_stream_window / 2, server_->getStream(1)->unconsumed_bytes_); // pre-fill downstream outbound frame queue TestResponseHeaderMapImpl response_headers{{":status", "200"}}; response_encoder_->encodeHeaders(response_headers, false); + driveToCompletion(); // Account for the single HEADERS frame above and pre-fill outbound queue with 1 byte DATA frames for (uint32_t i = 0; i < CommonUtility::OptionsLimits::DEFAULT_MAX_OUTBOUND_FRAMES - 2; ++i) { Buffer::OwnedImpl data("0"); EXPECT_NO_THROW(response_encoder_->encodeData(data, false)); } + driveToCompletion(); EXPECT_FALSE(violation_callback->enabled_); // Now unblock the server's stream. This will cause the bytes to be consumed, 2 flow control // updates to be sent, and overflow outbound frame queue. server_->getStream(1)->readDisable(false); + driveToCompletion(); EXPECT_TRUE(violation_callback->enabled_); EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); @@ -1738,6 +1926,7 @@ TEST_P(Http2CodecImplFlowControlTest, RstStreamOnPendingFlushTimeoutFlood) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); int frame_count = 0; Buffer::OwnedImpl buffer; @@ -1752,11 +1941,13 @@ TEST_P(Http2CodecImplFlowControlTest, RstStreamOnPendingFlushTimeoutFlood) { TestResponseHeaderMapImpl response_headers{{":status", "200"}}; response_encoder_->encodeHeaders(response_headers, false); + driveToCompletion(); // Account for the single HEADERS frame above and pre-fill outbound queue with 6 byte DATA frames for (uint32_t i = 0; i < CommonUtility::OptionsLimits::DEFAULT_MAX_OUTBOUND_FRAMES - 2; ++i) { Buffer::OwnedImpl data(std::string(6, '0')); EXPECT_NO_THROW(response_encoder_->encodeData(data, false)); } + driveToCompletion(); // client stream windows should have 5535 bytes left and the next frame should overflow it. // nghttp2 sends 1 DATA frame for the remainder of the client window and it should make @@ -1765,12 +1956,14 @@ TEST_P(Http2CodecImplFlowControlTest, RstStreamOnPendingFlushTimeoutFlood) { EXPECT_CALL(*flush_timer, enableTimer(std::chrono::milliseconds(30000), _)); Buffer::OwnedImpl large_body(std::string(6 * 1024, '1')); response_encoder_->encodeData(large_body, true); + driveToCompletion(); EXPECT_FALSE(violation_callback->enabled_); EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)); // Pending flush timeout causes RST_STREAM to be sent and overflow the outbound frame queue. flush_timer->invokeCallback(); + driveToCompletion(); EXPECT_TRUE(violation_callback->enabled_); EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); @@ -1790,6 +1983,7 @@ TEST_P(Http2CodecImplTest, WatermarkUnderEndStream) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); // The 'true' on encodeData will set local_end_stream_ on the client but not // the server. Verify that client watermark callbacks will not be called, but @@ -1807,6 +2001,7 @@ TEST_P(Http2CodecImplTest, WatermarkUnderEndStream) { })); Buffer::OwnedImpl hello("hello"); request_encoder_->encodeData(hello, true); + driveToCompletion(); // The 'true' on encodeData will set local_end_stream_ on the server. Verify // that neither client nor server watermark callbacks will be called again. @@ -1823,6 +2018,7 @@ TEST_P(Http2CodecImplTest, WatermarkUnderEndStream) { server_->onUnderlyingConnectionBelowWriteBufferLowWatermark(); })); response_encoder_->encodeHeaders(response_headers, true); + driveToCompletion(); } class Http2CodecImplStreamLimitTest : public Http2CodecImplTest {}; @@ -1837,12 +2033,15 @@ TEST_P(Http2CodecImplStreamLimitTest, MaxClientStreams) { 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()); + client_wrapper_ = std::make_unique(client_.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_); + server_wrapper_ = std::make_unique(server_.get()); + setupDefaultConnectionMocks(); + driveToCompletion(); for (int i = 0; i < 101; ++i) { request_encoder_ = &client_->newStream(response_decoder_); - setupDefaultConnectionMocks(); EXPECT_CALL(server_callbacks_, newStream(_, _)) .WillOnce(Invoke([&](ResponseEncoder& encoder, bool) -> RequestDecoder& { response_encoder_ = &encoder; @@ -1854,97 +2053,64 @@ TEST_P(Http2CodecImplStreamLimitTest, MaxClientStreams) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); } } 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()); + driveToCompletion(); // 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(); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); EXPECT_EQ(1, server_stats_store_.counter("http2.stream_refused_errors").value()); EXPECT_EQ(1, server_stats_store_.counter("http2.tx_reset").value()); EXPECT_EQ(1, TestUtility::findGauge(client_stats_store_, "http2.streams_active")->value()); EXPECT_EQ(1, TestUtility::findGauge(server_stats_store_, "http2.streams_active")->value()); + // The server codec should not fail since the error is "consumed". + EXPECT_TRUE(server_wrapper_->status_.ok()); } TEST_P(Http2CodecImplStreamLimitTest, LazyDecreaseMaxConcurrentStreamsIgnoreError) { - TestScopedRuntime scoped_runtime; + expect_buffered_data_on_teardown_ = true; + 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()); + driveToCompletion(); // 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(); - EXPECT_THROW_WITH_MESSAGE(request_encoder_->encodeHeaders(request_headers, true).IgnoreError(), - ServerCodecError, "The user callback function failed"); + EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); + // The server codec should fail since there are no available streams. + EXPECT_FALSE(server_wrapper_->status_.ok()); + EXPECT_TRUE(isCodecProtocolError(server_wrapper_->status_)); + EXPECT_EQ(server_wrapper_->status_.message(), "The user callback function failed"); EXPECT_EQ(0, server_stats_store_.counter("http2.stream_refused_errors").value()); EXPECT_EQ(0, server_stats_store_.counter("http2.tx_reset").value()); // Not verifying the http2.streams_active server/client gauges here as the - // EXPECT_THROW_WITH_MESSAGE above doesn't let us fully capture the behavior of the real system. + // test dispatch function doesn't let us fully capture the behavior of the real system. // In the real world, the status returned from dispatch would trigger a connection close which // would result in the active stream gauges to go down to 0. } @@ -1959,12 +2125,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 \ @@ -1978,11 +2144,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( \ @@ -2002,10 +2168,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) { { @@ -2058,8 +2224,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; @@ -2103,15 +2270,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 @@ -2124,6 +2292,7 @@ TEST_P(Http2CustomSettingsTest, UserDefinedSettings) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, _)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); uint32_t hpack_table_size = ::testing::get(getSettingsTuple()); if (hpack_table_size != NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) { @@ -2163,6 +2332,7 @@ TEST_P(Http2CodecImplTest, LargeRequestHeadersInvokeResetStream) { request_headers.addCopy("big", long_string); EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); } // Large request headers are accepted when max limit configured. @@ -2178,6 +2348,7 @@ TEST_P(Http2CodecImplTest, LargeRequestHeadersAccepted) { EXPECT_CALL(request_decoder_, decodeHeaders_(_, _)); EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)).Times(0); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); } // Tests request headers with name containing underscore are dropped when the option is set to drop @@ -2192,6 +2363,7 @@ TEST_P(Http2CodecImplTest, HeaderNameWithUnderscoreAreDropped) { request_headers.addCopy("bad_header", "something"); EXPECT_CALL(request_decoder_, decodeHeaders_(HeaderMapEqual(&expected_headers), _)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); EXPECT_EQ(1, server_stats_store_.counter("http2.dropped_headers_with_underscores").value()); } @@ -2206,6 +2378,7 @@ TEST_P(Http2CodecImplTest, HeaderNameWithUnderscoreAreRejectedByDefault) { request_headers.addCopy("bad_header", "something"); EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); EXPECT_EQ( 1, server_stats_store_.counter("http2.requests_rejected_with_underscores_in_headers").value()); @@ -2224,6 +2397,7 @@ TEST_P(Http2CodecImplTest, HeaderNameWithUnderscoreAllowed) { EXPECT_CALL(request_decoder_, decodeHeaders_(HeaderMapEqual(&expected_headers), _)); EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)).Times(0); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); EXPECT_EQ(0, server_stats_store_.counter("http2.dropped_headers_with_underscores").value()); } @@ -2244,6 +2418,7 @@ TEST_P(Http2CodecImplTest, LargeMethodRequestEncode) { EXPECT_CALL(request_decoder_, decodeHeaders_(HeaderMapEqual(&request_headers), false)); EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)).Times(0); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); } // Tests stream reset when the number of request headers exceeds the default maximum of 100. @@ -2257,6 +2432,7 @@ TEST_P(Http2CodecImplTest, ManyRequestHeadersInvokeResetStream) { } EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); } // Tests that max number of request headers is configurable. @@ -2272,6 +2448,7 @@ TEST_P(Http2CodecImplTest, ManyRequestHeadersAccepted) { EXPECT_CALL(request_decoder_, decodeHeaders_(_, _)); EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)).Times(0); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); } // Tests that max number of response headers is configurable. @@ -2283,6 +2460,7 @@ TEST_P(Http2CodecImplTest, ManyResponseHeadersAccepted) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); TestResponseHeaderMapImpl response_headers{{":status", "200"}, {"compression", "test"}}; for (int i = 0; i < 105; i++) { @@ -2290,6 +2468,7 @@ TEST_P(Http2CodecImplTest, ManyResponseHeadersAccepted) { } EXPECT_CALL(response_decoder_, decodeHeaders_(_, true)); response_encoder_->encodeHeaders(response_headers, true); + driveToCompletion(); } TEST_P(Http2CodecImplTest, LargeRequestHeadersAtLimitAccepted) { @@ -2313,6 +2492,7 @@ TEST_P(Http2CodecImplTest, LargeRequestHeadersAtLimitAccepted) { EXPECT_CALL(request_decoder_, decodeHeaders_(_, _)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); } TEST_P(Http2CodecImplTest, LargeRequestHeadersOverDefaultCodecLibraryLimit) { @@ -2327,6 +2507,7 @@ TEST_P(Http2CodecImplTest, LargeRequestHeadersOverDefaultCodecLibraryLimit) { EXPECT_CALL(request_decoder_, decodeHeaders_(_, _)); EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)).Times(0); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); } TEST_P(Http2CodecImplTest, LargeRequestHeadersExceedPerHeaderLimit) { @@ -2347,6 +2528,7 @@ TEST_P(Http2CodecImplTest, LargeRequestHeadersExceedPerHeaderLimit) { server_->shutdownNotice(); server_->goAway(); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); } TEST_P(Http2CodecImplTest, ManyLargeRequestHeadersUnderPerHeaderLimit) { @@ -2363,6 +2545,7 @@ TEST_P(Http2CodecImplTest, ManyLargeRequestHeadersUnderPerHeaderLimit) { EXPECT_CALL(request_decoder_, decodeHeaders_(_, _)); EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)).Times(0); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); } TEST_P(Http2CodecImplTest, LargeRequestHeadersAtMaxConfigurable) { @@ -2380,6 +2563,7 @@ TEST_P(Http2CodecImplTest, LargeRequestHeadersAtMaxConfigurable) { EXPECT_CALL(request_decoder_, decodeHeaders_(_, _)); EXPECT_CALL(server_stream_callbacks_, onResetStream(_, _)).Times(0); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); } // Note this is Http2CodecImplTestAll not Http2CodecImplTest, to test @@ -2391,26 +2575,26 @@ TEST_P(Http2CodecImplTestAll, TestCodecHeaderCompression) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok()); + driveToCompletion(); TestResponseHeaderMapImpl response_headers{{":status", "200"}, {"compression", "test"}}; EXPECT_CALL(response_decoder_, decodeHeaders_(_, true)); response_encoder_->encodeHeaders(response_headers, true); + driveToCompletion(); // 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_)); } } @@ -2422,11 +2606,12 @@ TEST_P(Http2CodecImplTest, PingFlood) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); // 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; @@ -2437,8 +2622,11 @@ TEST_P(Http2CodecImplTest, PingFlood) { buffer.move(frame); })); - EXPECT_THROW_WITH_MESSAGE(client_->sendPendingFrames().IgnoreError(), ServerCodecError, - "Too many control frames in the outbound queue."); + driveToCompletion(); + // The PING flood is detected by the server codec. + EXPECT_FALSE(server_wrapper_->status_.ok()); + EXPECT_TRUE(isBufferFloodError(server_wrapper_->status_)); + EXPECT_EQ(server_wrapper_->status_.message(), "Too many control frames in the outbound queue."); EXPECT_EQ(1, server_stats_store_.counter("http2.outbound_control_flood").value()); } @@ -2451,16 +2639,17 @@ TEST_P(Http2CodecImplTest, PingFloodMitigationDisabled) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); // 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(_, _)) .Times(CommonUtility::OptionsLimits::DEFAULT_MAX_OUTBOUND_CONTROL_FRAMES + 1); - EXPECT_NO_THROW(client_->sendPendingFrames().IgnoreError()); + EXPECT_NO_THROW(driveToCompletion()); } // Verify that outbound control frame counter decreases when send buffer is drained @@ -2476,9 +2665,10 @@ TEST_P(Http2CodecImplTest, PingFloodCounterReset) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); 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; @@ -2490,7 +2680,7 @@ TEST_P(Http2CodecImplTest, PingFloodCounterReset) { })); // We should be 1 frame under the control frame flood mitigation threshold. - EXPECT_NO_THROW(client_->sendPendingFrames().IgnoreError()); + EXPECT_NO_THROW(driveToCompletion()); EXPECT_EQ(ack_count, kMaxOutboundControlFrames); // Drain floor(kMaxOutboundFrames / 2) slices from the send buffer @@ -2498,17 +2688,20 @@ 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. - EXPECT_NO_THROW(client_->sendPendingFrames().IgnoreError()); + EXPECT_NO_THROW(driveToCompletion()); 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)); - EXPECT_THROW_WITH_MESSAGE(client_->sendPendingFrames().IgnoreError(), ServerCodecError, - "Too many control frames in the outbound queue."); + submitPing(client_, 0); + driveToCompletion(); + // The server codec should fail when it gets 1 PING too many. + EXPECT_FALSE(server_wrapper_->status_.ok()); + EXPECT_TRUE(isBufferFloodError(server_wrapper_->status_)); + EXPECT_EQ(server_wrapper_->status_.message(), "Too many control frames in the outbound queue."); } // Verify that codec detects flood of outbound HEADER frames @@ -2519,6 +2712,7 @@ TEST_P(Http2CodecImplTest, ResponseHeadersFlood) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); int frame_count = 0; Buffer::OwnedImpl buffer; @@ -2534,6 +2728,7 @@ TEST_P(Http2CodecImplTest, ResponseHeadersFlood) { for (uint32_t i = 0; i < CommonUtility::OptionsLimits::DEFAULT_MAX_OUTBOUND_FRAMES + 1; ++i) { EXPECT_NO_THROW(response_encoder_->encodeHeaders(response_headers, false)); } + EXPECT_NO_THROW(driveToCompletion()); EXPECT_TRUE(violation_callback->enabled_); EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); @@ -2551,6 +2746,7 @@ TEST_P(Http2CodecImplTest, ResponseDataFlood) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); int frame_count = 0; Buffer::OwnedImpl buffer; @@ -2570,6 +2766,7 @@ TEST_P(Http2CodecImplTest, ResponseDataFlood) { Buffer::OwnedImpl data("0"); EXPECT_NO_THROW(response_encoder_->encodeData(data, false)); } + EXPECT_NO_THROW(driveToCompletion()); EXPECT_TRUE(violation_callback->enabled_); EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); @@ -2588,6 +2785,7 @@ TEST_P(Http2CodecImplTest, ResponseDataFloodMitigationDisabled) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); // +2 is to account for HEADERS and PING ACK, that is used to trigger mitigation EXPECT_CALL(server_connection_, write(_, _)) @@ -2602,10 +2800,11 @@ TEST_P(Http2CodecImplTest, ResponseDataFloodMitigationDisabled) { Buffer::OwnedImpl data("0"); EXPECT_NO_THROW(response_encoder_->encodeData(data, false)); } + EXPECT_NO_THROW(driveToCompletion()); // 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)); - EXPECT_NO_THROW(client_->sendPendingFrames().IgnoreError()); + submitPing(client_, 0); + EXPECT_NO_THROW(driveToCompletion()); } // Verify that outbound frame counter decreases when send buffer is drained @@ -2618,6 +2817,7 @@ TEST_P(Http2CodecImplTest, ResponseDataFloodCounterReset) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); int frame_count = 0; Buffer::OwnedImpl buffer; @@ -2634,6 +2834,7 @@ TEST_P(Http2CodecImplTest, ResponseDataFloodCounterReset) { Buffer::OwnedImpl data("0"); EXPECT_NO_THROW(response_encoder_->encodeData(data, false)); } + EXPECT_NO_THROW(driveToCompletion()); EXPECT_EQ(frame_count, kMaxOutboundFrames); // Drain kMaxOutboundFrames / 2 slices from the send buffer @@ -2646,6 +2847,7 @@ TEST_P(Http2CodecImplTest, ResponseDataFloodCounterReset) { Buffer::OwnedImpl data("0"); EXPECT_NO_THROW(response_encoder_->encodeData(data, false)); } + EXPECT_NO_THROW(driveToCompletion()); EXPECT_TRUE(violation_callback->enabled_); EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); @@ -2660,6 +2862,7 @@ TEST_P(Http2CodecImplTest, PingStacksWithDataFlood) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); int frame_count = 0; Buffer::OwnedImpl buffer; @@ -2676,10 +2879,14 @@ TEST_P(Http2CodecImplTest, PingStacksWithDataFlood) { Buffer::OwnedImpl data("0"); EXPECT_NO_THROW(response_encoder_->encodeData(data, false)); } + EXPECT_NO_THROW(driveToCompletion()); // Send one PING frame above the outbound queue size limit - EXPECT_EQ(0, nghttp2_submit_ping(client_->session(), NGHTTP2_FLAG_NONE, nullptr)); - EXPECT_THROW_WITH_MESSAGE(client_->sendPendingFrames().IgnoreError(), ServerCodecError, - "Too many frames in the outbound queue."); + submitPing(client_, 0); + driveToCompletion(); + // The server codec should fail when it gets 1 frame too many. + EXPECT_FALSE(server_wrapper_->status_.ok()); + EXPECT_TRUE(isBufferFloodError(server_wrapper_->status_)); + EXPECT_EQ(server_wrapper_->status_.message(), "Too many frames in the outbound queue."); EXPECT_EQ(1, server_stats_store_.counter("http2.outbound_flood").value()); } @@ -2692,6 +2899,7 @@ TEST_P(Http2CodecImplTest, ResponseTrailersFlood) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); int frame_count = 0; Buffer::OwnedImpl buffer; @@ -2711,9 +2919,11 @@ TEST_P(Http2CodecImplTest, ResponseTrailersFlood) { Buffer::OwnedImpl data("0"); EXPECT_NO_THROW(response_encoder_->encodeData(data, false)); } + EXPECT_NO_THROW(driveToCompletion()); EXPECT_FALSE(violation_callback->enabled_); EXPECT_NO_THROW(response_encoder_->encodeTrailers(TestResponseTrailerMapImpl{{"foo", "bar"}})); + EXPECT_NO_THROW(driveToCompletion()); EXPECT_TRUE(violation_callback->enabled_); EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); @@ -2732,6 +2942,7 @@ TEST_P(Http2CodecImplTest, MetadataFlood) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); int frame_count = 0; Buffer::OwnedImpl buffer; @@ -2751,6 +2962,7 @@ TEST_P(Http2CodecImplTest, MetadataFlood) { Buffer::OwnedImpl data("0"); EXPECT_NO_THROW(response_encoder_->encodeData(data, false)); } + EXPECT_NO_THROW(driveToCompletion()); EXPECT_FALSE(violation_callback->enabled_); @@ -2763,6 +2975,7 @@ TEST_P(Http2CodecImplTest, MetadataFlood) { metadata_map_vector.push_back(std::move(metadata_map_ptr)); response_encoder_->encodeMetadata(metadata_map_vector); + driveToCompletion(); EXPECT_TRUE(violation_callback->enabled_); EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); @@ -2773,35 +2986,46 @@ TEST_P(Http2CodecImplTest, MetadataFlood) { } TEST_P(Http2CodecImplTest, PriorityFlood) { + expect_buffered_data_on_teardown_ = true; priorityFlood(); - EXPECT_THROW_WITH_MESSAGE(client_->sendPendingFrames().IgnoreError(), ServerCodecError, - "Too many PRIORITY frames"); + driveToCompletion(); + // The PRIORITY flood is detected by the server codec. + EXPECT_FALSE(server_wrapper_->status_.ok()); + EXPECT_TRUE(isBufferFloodError(server_wrapper_->status_)); + EXPECT_EQ(server_wrapper_->status_.message(), "Too many PRIORITY frames"); } TEST_P(Http2CodecImplTest, PriorityFloodOverride) { max_inbound_priority_frames_per_stream_ = 2147483647; priorityFlood(); - EXPECT_NO_THROW(client_->sendPendingFrames().IgnoreError()); + EXPECT_NO_THROW(driveToCompletion()); } TEST_P(Http2CodecImplTest, WindowUpdateFlood) { + expect_buffered_data_on_teardown_ = true; windowUpdateFlood(); - EXPECT_THROW_WITH_MESSAGE(client_->sendPendingFrames().IgnoreError(), ServerCodecError, - "Too many WINDOW_UPDATE frames"); + driveToCompletion(); + // The server codec should fail when it gets 1 WINDOW_UPDATE frame too many. + EXPECT_FALSE(server_wrapper_->status_.ok()); + EXPECT_TRUE(isBufferFloodError(server_wrapper_->status_)); + EXPECT_EQ(server_wrapper_->status_.message(), "Too many WINDOW_UPDATE frames"); } TEST_P(Http2CodecImplTest, WindowUpdateFloodOverride) { max_inbound_window_update_frames_per_data_frame_sent_ = 2147483647; windowUpdateFlood(); - EXPECT_NO_THROW(client_->sendPendingFrames().IgnoreError()); + EXPECT_NO_THROW(driveToCompletion()); } TEST_P(Http2CodecImplTest, EmptyDataFlood) { + expect_buffered_data_on_teardown_ = true; Buffer::OwnedImpl data; emptyDataFlood(data); + server_wrapper_->buffer_.add(std::move(data)); EXPECT_CALL(request_decoder_, decodeData(_, false)); - auto status = server_wrapper_.dispatch(data, *server_); + driveToCompletion(); + const Http::Status& status = server_wrapper_->status_; EXPECT_FALSE(status.ok()); EXPECT_TRUE(isInboundFramesWithEmptyPayloadError(status)); EXPECT_EQ("Too many consecutive frames with an empty payload", status.message()); @@ -2811,12 +3035,13 @@ TEST_P(Http2CodecImplTest, EmptyDataFloodOverride) { max_consecutive_inbound_frames_with_empty_payload_ = 2147483647; Buffer::OwnedImpl data; emptyDataFlood(data); + server_wrapper_->buffer_.add(std::move(data)); EXPECT_CALL(request_decoder_, decodeData(_, false)) .Times( CommonUtility::OptionsLimits::DEFAULT_MAX_CONSECUTIVE_INBOUND_FRAMES_WITH_EMPTY_PAYLOAD + 1); - auto status = server_wrapper_.dispatch(data, *server_); - EXPECT_TRUE(status.ok()); + driveToCompletion(); + EXPECT_TRUE(server_wrapper_->status_.ok()); } // Verify that codec detects flood of outbound frames caused by goAway() method @@ -2827,6 +3052,7 @@ TEST_P(Http2CodecImplTest, GoAwayCausesOutboundFlood) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); int frame_count = 0; Buffer::OwnedImpl buffer; @@ -2846,10 +3072,12 @@ TEST_P(Http2CodecImplTest, GoAwayCausesOutboundFlood) { Buffer::OwnedImpl data("0"); EXPECT_NO_THROW(response_encoder_->encodeData(data, false)); } + EXPECT_NO_THROW(driveToCompletion()); EXPECT_FALSE(violation_callback->enabled_); server_->goAway(); + driveToCompletion(); EXPECT_TRUE(violation_callback->enabled_); EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); @@ -2867,6 +3095,7 @@ TEST_P(Http2CodecImplTest, ShudowNoticeCausesOutboundFlood) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); int frame_count = 0; Buffer::OwnedImpl buffer; @@ -2886,10 +3115,12 @@ TEST_P(Http2CodecImplTest, ShudowNoticeCausesOutboundFlood) { Buffer::OwnedImpl data("0"); EXPECT_NO_THROW(response_encoder_->encodeData(data, false)); } + EXPECT_NO_THROW(driveToCompletion()); EXPECT_FALSE(violation_callback->enabled_); server_->shutdownNotice(); + driveToCompletion(); EXPECT_TRUE(violation_callback->enabled_); EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); @@ -2920,6 +3151,7 @@ TEST_P(Http2CodecImplTest, KeepAliveCausesOutboundFlood) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); int frame_count = 0; Buffer::OwnedImpl buffer; @@ -2939,6 +3171,7 @@ TEST_P(Http2CodecImplTest, KeepAliveCausesOutboundFlood) { Buffer::OwnedImpl data("0"); EXPECT_NO_THROW(response_encoder_->encodeData(data, false)); } + EXPECT_NO_THROW(driveToCompletion()); EXPECT_FALSE(violation_callback->enabled_); @@ -2962,6 +3195,7 @@ TEST_P(Http2CodecImplTest, ResetStreamCausesOutboundFlood) { HttpTestUtility::addDefaultHeaders(request_headers); EXPECT_CALL(request_decoder_, decodeHeaders_(_, false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); int frame_count = 0; Buffer::OwnedImpl buffer; @@ -2981,6 +3215,7 @@ TEST_P(Http2CodecImplTest, ResetStreamCausesOutboundFlood) { Buffer::OwnedImpl data("0"); EXPECT_NO_THROW(response_encoder_->encodeData(data, false)); } + EXPECT_NO_THROW(driveToCompletion()); EXPECT_FALSE(violation_callback->enabled_); EXPECT_CALL(server_stream_callbacks_, onResetStream(StreamResetReason::RemoteReset, _)); @@ -3014,10 +3249,12 @@ TEST_P(Http2CodecImplTest, ConnectTest) { expected_headers.setReferenceKey(Headers::get().Protocol, "bytestream"); EXPECT_CALL(request_decoder_, decodeHeaders_(HeaderMapEqual(&expected_headers), false)); EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, false).ok()); + driveToCompletion(); EXPECT_CALL(callbacks, onResetStream(StreamResetReason::ConnectError, _)); EXPECT_CALL(server_stream_callbacks_, onResetStream(StreamResetReason::ConnectError, _)); response_encoder_->getStream().resetStream(StreamResetReason::ConnectError); + driveToCompletion(); } class TestNghttp2SessionFactory; @@ -3037,23 +3274,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 { @@ -3063,8 +3310,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( @@ -3074,7 +3321,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_, @@ -3091,8 +3338,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_; @@ -3105,23 +3378,24 @@ 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_); client_ = std::make_unique( client_connection_, client_callbacks_, client_stats_store_, client_http2_options_, random_, max_request_headers_kb_, max_response_headers_count_, http2_session_factory_); + client_wrapper_ = std::make_unique(client_.get()); + // SETTINGS are required as part of the preface. + submitSettings(client_, {}); 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_); - ON_CALL(client_connection_, write(_, _)) - .WillByDefault(Invoke([&](Buffer::Instance& data, bool) -> void { - ASSERT_TRUE(server_wrapper_.dispatch(data, *server_).ok()); - })); - ON_CALL(server_connection_, write(_, _)) - .WillByDefault(Invoke([&](Buffer::Instance& data, bool) -> void { - ASSERT_TRUE(client_wrapper_.dispatch(data, *client_).ok()); - })); + server_wrapper_ = std::make_unique(server_.get()); + setupDefaultConnectionMocks(); + driveToCompletion(); } private: @@ -3137,12 +3411,12 @@ TEST_F(Http2CodecMetadataTest, UnknownStreamId) { MetadataMap metadata_map = {{"key", "value"}}; 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); // 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)); + driveToCompletion(); EXPECT_TRUE(client_->submitMetadata(metadata_vector, 1000)); + driveToCompletion(); } } // namespace Http2 diff --git a/test/common/http/http2/codec_impl_test_util.h b/test/common/http/http2/codec_impl_test_util.h index 2188b011aa8a..88e9d71f890f 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/conn_pool_test.cc b/test/common/http/http2/conn_pool_test.cc index 23c86c527b22..5b917ea935fb 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) { @@ -1638,6 +1669,47 @@ TEST_F(Http2ConnPoolImplTest, MaybePreconnect) { closeAllClients(); } +TEST_F(Http2ConnPoolImplTest, TestUnusedCapacity) { + cluster_->http2_options_.mutable_max_concurrent_streams()->set_value(8); + cluster_->max_requests_per_connection_ = 6; + + expectClientsCreate(1); + ActiveTestRequest r1(*this, 0, false); + // Initially, capacity is based on remaining streams and capped at 6. + CHECK_STATE(0 /*active*/, 1 /*pending*/, 6 /*capacity*/); + expectClientConnect(0, r1); + // Now the stream is active, remaining concurrency capacity is 5. + CHECK_STATE(1 /*active*/, 0 /*pending*/, 5 /*capacity*/); + + // With two more streams, remaining unused capacity is 3. + ActiveTestRequest r2(*this, 0, true); + ActiveTestRequest r3(*this, 0, true); + CHECK_STATE(3 /*active*/, 0 /*pending*/, 3 /*capacity*/); + + // Settings frame results in 1 unused capacity. + NiceMock settings; + settings.max_concurrent_streams_ = 4; + test_clients_[0].codec_client_->onSettings(settings); + CHECK_STATE(3 /*active*/, 0 /*pending*/, 1 /*capacity*/); + + // Closing a stream, unused capacity returns to 2. + completeRequest(r1); + CHECK_STATE(2 /*active*/, 0 /*pending*/, 2 /*capacity*/); + + // Closing another, unused capacity returns to 3 (3 remaining stream). + completeRequest(r2); + CHECK_STATE(1 /*active*/, 0 /*pending*/, 3 /*capacity*/); + + // Closing the last stream, unused capacity remains at 3, as there is only 3 remaining streams. + completeRequest(r3); + CHECK_STATE(0 /*active*/, 0 /*pending*/, 3 /*capacity*/); + + // Clean up with an outstanding stream. + pool_->drainConnections(Envoy::ConnectionPool::DrainBehavior::DrainExistingConnections); + closeAllClients(); + CHECK_STATE(0 /*active*/, 0 /*pending*/, 0 /*capacity*/); +} + TEST_F(Http2ConnPoolImplTest, TestStateWithMultiplexing) { cluster_->http2_options_.mutable_max_concurrent_streams()->set_value(2); cluster_->max_requests_per_connection_ = 4; @@ -1647,22 +1719,22 @@ TEST_F(Http2ConnPoolImplTest, TestStateWithMultiplexing) { // Initially, capacity is based on concurrency and capped at 2. CHECK_STATE(0 /*active*/, 1 /*pending*/, 2 /*capacity*/); expectClientConnect(0, r1); - // Now the stream is active, remaining concurrency capacity is 1 + // Now the stream is active, remaining concurrency capacity is 1. CHECK_STATE(1 /*active*/, 0 /*pending*/, 1 /*capacity*/); // With one more stream, remaining concurrency capacity is 0. ActiveTestRequest r2(*this, 0, true); CHECK_STATE(2 /*active*/, 0 /*pending*/, 0 /*capacity*/); - // If one stream closes, concurrency capacity goes to 1 (2 remaining streams) + // If one stream closes, concurrency capacity goes to 1 (2 remaining streams). completeRequest(r1); CHECK_STATE(1 /*active*/, 0 /*pending*/, 1 /*capacity*/); - // Assigning a new stream, concurrency capacity returns to 0 (1 remaining stream); + // Assigning a new stream, concurrency capacity returns to 0 (1 remaining stream). ActiveTestRequest r3(*this, 0, true); CHECK_STATE(2 /*active*/, 0 /*pending*/, 0 /*capacity*/); - // Closing a stream, capacity returns to 1 (both concurrency and remaining streams) + // Closing a stream, capacity returns to 1 (both concurrency and remaining streams). completeRequest(r2); CHECK_STATE(1 /*active*/, 0 /*pending*/, 1 /*capacity*/); diff --git a/test/common/http/http2/http2_frame.cc b/test/common/http/http2/http2_frame.cc index 2f6dfeacb878..68569c0daf2e 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 fbb3f5a96ae1..dca17c480434 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 { 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 000000000000..1d073b80a7be --- /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/common/http/http3/conn_pool_test.cc b/test/common/http/http3/conn_pool_test.cc index ba4c3124cb38..b60676533c1d 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(); diff --git a/test/common/http/inline_cookie_test.cc b/test/common/http/inline_cookie_test.cc index 5d8269f81975..b2d08a06682a 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/http/match_wrapper/config_test.cc b/test/common/http/match_wrapper/config_test.cc index 8287d9e5fd2d..79fca551a413 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/common/http/utility_test.cc b/test/common/http/utility_test.cc index e602f2108df0..c1263240d26f 100644 --- a/test/common/http/utility_test.cc +++ b/test/common/http/utility_test.cc @@ -364,6 +364,92 @@ TEST(HttpUtility, appendVia) { } } +TEST(HttpUtility, updateAuthority) { + { + TestRequestHeaderMapImpl headers; + Utility::updateAuthority(headers, "dns.name", true); + EXPECT_EQ("dns.name", headers.get_(":authority")); + EXPECT_EQ("", headers.get_("x-forwarded-host")); + } + + { + TestRequestHeaderMapImpl headers; + Utility::updateAuthority(headers, "dns.name", false); + EXPECT_EQ("dns.name", headers.get_(":authority")); + EXPECT_EQ("", headers.get_("x-forwarded-host")); + } + + { + TestRequestHeaderMapImpl headers; + Utility::updateAuthority(headers, "", true); + EXPECT_EQ("", headers.get_(":authority")); + EXPECT_EQ("", headers.get_("x-forwarded-host")); + } + + { + TestRequestHeaderMapImpl headers; + Utility::updateAuthority(headers, "", false); + EXPECT_EQ("", headers.get_(":authority")); + EXPECT_EQ("", headers.get_("x-forwarded-host")); + } + + { + TestRequestHeaderMapImpl headers{{":authority", "host.com"}}; + Utility::updateAuthority(headers, "dns.name", true); + EXPECT_EQ("dns.name", headers.get_(":authority")); + EXPECT_EQ("host.com", headers.get_("x-forwarded-host")); + } + + { + TestRequestHeaderMapImpl headers{{":authority", "host.com"}}; + Utility::updateAuthority(headers, "dns.name", false); + EXPECT_EQ("dns.name", headers.get_(":authority")); + EXPECT_EQ("", headers.get_("x-forwarded-host")); + } + + { + TestRequestHeaderMapImpl headers{{":authority", "host.com"}}; + Utility::updateAuthority(headers, "", true); + EXPECT_EQ("", headers.get_(":authority")); + EXPECT_EQ("host.com", headers.get_("x-forwarded-host")); + } + + { + TestRequestHeaderMapImpl headers{{":authority", "host.com"}}; + Utility::updateAuthority(headers, "", false); + EXPECT_EQ("", headers.get_(":authority")); + EXPECT_EQ("", headers.get_("x-forwarded-host")); + } + + { + TestRequestHeaderMapImpl headers{{":authority", "dns.name"}, {"x-forwarded-host", "host.com"}}; + Utility::updateAuthority(headers, "newhost.com", true); + EXPECT_EQ("newhost.com", headers.get_(":authority")); + EXPECT_EQ("host.com,dns.name", headers.get_("x-forwarded-host")); + } + + { + TestRequestHeaderMapImpl headers{{":authority", "dns.name"}, {"x-forwarded-host", "host.com"}}; + Utility::updateAuthority(headers, "newhost.com", false); + EXPECT_EQ("newhost.com", headers.get_(":authority")); + EXPECT_EQ("host.com", headers.get_("x-forwarded-host")); + } + + { + TestRequestHeaderMapImpl headers{{"x-forwarded-host", "host.com"}}; + Utility::updateAuthority(headers, "dns.name", true); + EXPECT_EQ("dns.name", headers.get_(":authority")); + EXPECT_EQ("host.com", headers.get_("x-forwarded-host")); + } + + { + TestRequestHeaderMapImpl headers{{"x-forwarded-host", "host.com"}}; + Utility::updateAuthority(headers, "dns.name", false); + EXPECT_EQ("dns.name", headers.get_(":authority")); + EXPECT_EQ("host.com", headers.get_("x-forwarded-host")); + } +} + TEST(HttpUtility, createSslRedirectPath) { { TestRequestHeaderMapImpl headers{{":authority", "www.lyft.com"}, {":path", "/hello"}}; diff --git a/test/common/json/BUILD b/test/common/json/BUILD index b77f0b53d88a..e68b7f30bad6 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 7e2f40b89c6f..925afbef6413 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/network/BUILD b/test/common/network/BUILD index 228068e269aa..34fcb5c4a853 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/address_impl_test.cc b/test/common/network/address_impl_test.cc index 76f7e9e4c42f..c82c87bff634 100644 --- a/test/common/network/address_impl_test.cc +++ b/test/common/network/address_impl_test.cc @@ -49,7 +49,7 @@ void testSocketBindAndConnect(Network::Address::IpVersion ip_version, bool v6onl ASSERT_NE(addr_port->ip(), nullptr); // Create a socket on which we'll listen for connections from clients. - SocketImpl sock(Socket::Type::Stream, addr_port, nullptr); + SocketImpl sock(Socket::Type::Stream, addr_port, nullptr, {}); EXPECT_TRUE(sock.ioHandle().isOpen()) << addr_port->asString(); // Check that IPv6 sockets accept IPv6 connections only. @@ -74,7 +74,7 @@ void testSocketBindAndConnect(Network::Address::IpVersion ip_version, bool v6onl auto client_connect = [](Address::InstanceConstSharedPtr addr_port) { // Create a client socket and connect to the server. - SocketImpl client_sock(Socket::Type::Stream, addr_port, nullptr); + SocketImpl client_sock(Socket::Type::Stream, addr_port, nullptr, {}); EXPECT_TRUE(client_sock.ioHandle().isOpen()) << addr_port->asString(); @@ -348,7 +348,7 @@ TEST(PipeInstanceTest, BasicPermission) { const mode_t mode = 0777; PipeInstance pipe(path, mode); InstanceConstSharedPtr address = std::make_shared(pipe); - SocketImpl sock(Socket::Type::Stream, address, nullptr); + SocketImpl sock(Socket::Type::Stream, address, nullptr, {}); EXPECT_TRUE(sock.ioHandle().isOpen()) << pipe.asString(); @@ -376,7 +376,7 @@ TEST(PipeInstanceTest, PermissionFail) { const mode_t mode = 0777; PipeInstance pipe(path, mode); InstanceConstSharedPtr address = std::make_shared(pipe); - SocketImpl sock(Socket::Type::Stream, address, nullptr); + SocketImpl sock(Socket::Type::Stream, address, nullptr, {}); EXPECT_TRUE(sock.ioHandle().isOpen()) << pipe.asString(); @@ -448,7 +448,7 @@ TEST(PipeInstanceTest, UnlinksExistingFile) { const auto bind_uds_socket = [](const std::string& path) { PipeInstance pipe(path); InstanceConstSharedPtr address = std::make_shared(pipe); - SocketImpl sock(Socket::Type::Stream, address, nullptr); + SocketImpl sock(Socket::Type::Stream, address, nullptr, {}); EXPECT_TRUE(sock.ioHandle().isOpen()) << pipe.asString(); diff --git a/test/common/network/connection_impl_test.cc b/test/common/network/connection_impl_test.cc index b666ded243ef..88b8f79e9e6b 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()); @@ -111,7 +118,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,13 +138,20 @@ 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"); } socket_ = std::make_shared( Network::Test::getCanonicalLoopbackAddress(GetParam())); - listener_ = dispatcher_->createListener(socket_, listener_callbacks_, true); + listener_ = dispatcher_->createListener(socket_, listener_callbacks_, true, false); client_connection_ = std::make_unique( *dispatcher_, socket_->connectionInfoProvider().localAddress(), source_address_, Network::Test::createRawBufferSocket(), socket_options_); @@ -232,11 +246,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 +260,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 +416,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 +441,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 +461,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 +482,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 +694,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), @@ -1352,7 +1370,7 @@ TEST_P(ConnectionImplTest, BindFailureTest) { dispatcher_ = api_->allocateDispatcher("test_thread"); socket_ = std::make_shared( Network::Test::getCanonicalLoopbackAddress(GetParam())); - listener_ = dispatcher_->createListener(socket_, listener_callbacks_, true); + listener_ = dispatcher_->createListener(socket_, listener_callbacks_, true, false); client_connection_ = dispatcher_->createClientConnection( socket_->connectionInfoProvider().localAddress(), source_address_, @@ -1636,7 +1654,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 +1685,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 +1746,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 +1838,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 +1874,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 +1914,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 +1961,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 +2026,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); @@ -2823,7 +2841,7 @@ class ReadBufferLimitTest : public ConnectionImplTest { dispatcher_ = api_->allocateDispatcher("test_thread"); socket_ = std::make_shared( Network::Test::getCanonicalLoopbackAddress(GetParam())); - listener_ = dispatcher_->createListener(socket_, listener_callbacks_, true); + listener_ = dispatcher_->createListener(socket_, listener_callbacks_, true, false); client_connection_ = dispatcher_->createClientConnection(socket_->connectionInfoProvider().localAddress(), @@ -2961,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/dns_resolver/BUILD b/test/common/network/dns_resolver/BUILD index 87f8ba300554..a357f6f20a83 100644 --- a/test/common/network/dns_resolver/BUILD +++ b/test/common/network/dns_resolver/BUILD @@ -16,7 +16,7 @@ envoy_cc_test( "--runtime-feature-disable-for-tests=envoy.restart_features.use_apple_api_for_dns_lookups", ], deps = [ - "//source/common/network/dns_resolver:dns_factory_lib", + "//source/common/network/dns_resolver:dns_factory_util_lib", "//source/extensions/network/dns_resolver/cares:config", "//test/mocks/network:network_mocks", ], diff --git a/test/common/network/dns_resolver/dns_factory_test.cc b/test/common/network/dns_resolver/dns_factory_test.cc index 45d959b87b80..813b64a52971 100644 --- a/test/common/network/dns_resolver/dns_factory_test.cc +++ b/test/common/network/dns_resolver/dns_factory_test.cc @@ -1,5 +1,5 @@ #include "source/common/network/address_impl.h" -#include "source/common/network/dns_resolver/dns_factory.h" +#include "source/common/network/dns_resolver/dns_factory_util.h" #include "test/mocks/network/mocks.h" diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index faa25b383a5b..2588af571f16 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 diff --git a/test/common/network/listen_socket_impl_test.cc b/test/common/network/listen_socket_impl_test.cc index 4ca83f960449..c6b8c5fd5e1a 100644 --- a/test/common/network/listen_socket_impl_test.cc +++ b/test/common/network/listen_socket_impl_test.cc @@ -24,15 +24,25 @@ 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); - auto conn_socket_ = ConnectionSocketImpl(Socket::Type::Stream, loopback_addr, loopback_addr); + auto conn_socket_ = ConnectionSocketImpl(Socket::Type::Stream, loopback_addr, loopback_addr, {}); conn_socket_.setRequestedServerName(serverName); 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>; @@ -119,7 +129,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 +183,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 @@ -221,8 +231,8 @@ TEST_P(ListenSocketImplTestTcp, SupportedIpFamilyVirtualSocketIsCreatedWithNoBsd StackedScopedInjectableLoader new_interface(std::move(mock_interface)); { - EXPECT_CALL(*mock_interface_ptr, socket(_, _)).Times(0); - EXPECT_CALL(*mock_interface_ptr, socket(_, _, _, _)).Times(0); + EXPECT_CALL(*mock_interface_ptr, socket(_, _, _)).Times(0); + EXPECT_CALL(*mock_interface_ptr, socket(_, _, _, _, _)).Times(0); TcpListenSocket virtual_listener_socket(any_address, nullptr, /*bind_to_port*/ false); } @@ -236,8 +246,8 @@ TEST_P(ListenSocketImplTestTcp, DeathAtUnSupportedIpFamilyListenSocket) { : Utility::getIpv4AnyAddress(); StackedScopedInjectableLoader new_interface(std::move(mock_interface)); { - EXPECT_CALL(*mock_interface_ptr, socket(_, _)).Times(0); - EXPECT_CALL(*mock_interface_ptr, socket(_, _, _, _)).Times(0); + EXPECT_CALL(*mock_interface_ptr, socket(_, _, _)).Times(0); + EXPECT_CALL(*mock_interface_ptr, socket(_, _, _, _, _)).Times(0); EXPECT_DEATH( { TcpListenSocket virtual_listener_socket(the_other_address, nullptr, diff --git a/test/common/network/listener_impl_test.cc b/test/common/network/listener_impl_test.cc index b1f0aac6462d..2e12e648be71 100644 --- a/test/common/network/listener_impl_test.cc +++ b/test/common/network/listener_impl_test.cc @@ -35,7 +35,8 @@ static void errorCallbackTest(Address::IpVersion version) { auto socket = std::make_shared( Network::Test::getCanonicalLoopbackAddress(version)); Network::MockTcpListenerCallbacks listener_callbacks; - Network::ListenerPtr listener = dispatcher->createListener(socket, listener_callbacks, true); + Network::ListenerPtr listener = + dispatcher->createListener(socket, listener_callbacks, true, false); Network::ClientConnectionPtr client_connection = dispatcher->createClientConnection( socket->connectionInfoProvider().localAddress(), Network::Address::InstanceConstSharedPtr(), @@ -66,8 +67,10 @@ TEST_P(ListenerImplDeathTest, ErrorCallback) { class TestTcpListenerImpl : public TcpListenerImpl { public: TestTcpListenerImpl(Event::DispatcherImpl& dispatcher, Random::RandomGenerator& random_generator, - SocketSharedPtr socket, TcpListenerCallbacks& cb, bool bind_to_port) - : TcpListenerImpl(dispatcher, random_generator, std::move(socket), cb, bind_to_port) {} + SocketSharedPtr socket, TcpListenerCallbacks& cb, bool bind_to_port, + bool ignore_global_conn_limit) + : TcpListenerImpl(dispatcher, random_generator, std::move(socket), cb, bind_to_port, + ignore_global_conn_limit) {} MOCK_METHOD(Address::InstanceConstSharedPtr, getLocalAddress, (os_fd_t fd)); }; @@ -85,10 +88,10 @@ TEST_P(TcpListenerImplTest, UseActualDst) { Random::MockRandomGenerator random_generator; // Do not redirect since use_original_dst is false. Network::TestTcpListenerImpl listener(dispatcherImpl(), random_generator, socket, - listener_callbacks1, true); + listener_callbacks1, true, false); Network::MockTcpListenerCallbacks listener_callbacks2; Network::TestTcpListenerImpl listenerDst(dispatcherImpl(), random_generator, socketDst, - listener_callbacks2, false); + listener_callbacks2, false, false); Network::ClientConnectionPtr client_connection = dispatcher_->createClientConnection( socket->connectionInfoProvider().localAddress(), Network::Address::InstanceConstSharedPtr(), @@ -122,7 +125,8 @@ TEST_P(TcpListenerImplTest, GlobalConnectionLimitEnforcement) { auto socket = std::make_shared( Network::Test::getCanonicalLoopbackAddress(version_)); Network::MockTcpListenerCallbacks listener_callbacks; - Network::ListenerPtr listener = dispatcher_->createListener(socket, listener_callbacks, true); + Network::ListenerPtr listener = + dispatcher_->createListener(socket, listener_callbacks, true, false); std::vector client_connections; std::vector server_connections; @@ -181,6 +185,54 @@ TEST_P(TcpListenerImplTest, GlobalConnectionLimitEnforcement) { {{"overload.global_downstream_max_connections", ""}}); } +TEST_P(TcpListenerImplTest, GlobalConnectionLimitListenerOptOut) { + // Required to manipulate runtime values when there is no test server. + TestScopedRuntime scoped_runtime; + + Runtime::LoaderSingleton::getExisting()->mergeValues( + {{"overload.global_downstream_max_connections", "1"}}); + auto socket = std::make_shared( + Network::Test::getCanonicalLoopbackAddress(version_)); + Network::MockTcpListenerCallbacks listener_callbacks; + Network::ListenerPtr listener = + dispatcher_->createListener(socket, listener_callbacks, true, true); + + std::vector client_connections; + std::vector server_connections; + StreamInfo::StreamInfoImpl stream_info(dispatcher_->timeSource(), nullptr); + EXPECT_CALL(listener_callbacks, onAccept_(_)) + .WillRepeatedly(Invoke([&](Network::ConnectionSocketPtr& accepted_socket) -> void { + server_connections.emplace_back(dispatcher_->createServerConnection( + std::move(accepted_socket), Network::Test::createRawBufferSocket(), stream_info)); + dispatcher_->exit(); + })); + + auto initiate_connections = [&](const int count) { + for (int i = 0; i < count; ++i) { + client_connections.emplace_back( + dispatcher_->createClientConnection(socket->connectionInfoProvider().localAddress(), + Network::Address::InstanceConstSharedPtr(), + Network::Test::createRawBufferSocket(), nullptr)); + client_connections.back()->connect(); + } + }; + + initiate_connections(2); + EXPECT_CALL(listener_callbacks, onReject(TcpListenerCallbacks::RejectCause::GlobalCxLimit)) + .Times(0); + dispatcher_->run(Event::Dispatcher::RunType::Block); + + for (const auto& conn : client_connections) { + conn->close(ConnectionCloseType::NoFlush); + } + for (const auto& conn : server_connections) { + conn->close(ConnectionCloseType::NoFlush); + } + + // We expect any server-side connections that get created to populate 'server_connections'. + EXPECT_EQ(2, server_connections.size()); +} + TEST_P(TcpListenerImplTest, WildcardListenerUseActualDst) { auto socket = std::make_shared( Network::Test::getCanonicalLoopbackAddress(version_)); @@ -188,7 +240,7 @@ TEST_P(TcpListenerImplTest, WildcardListenerUseActualDst) { Random::MockRandomGenerator random_generator; // Do not redirect since use_original_dst is false. Network::TestTcpListenerImpl listener(dispatcherImpl(), random_generator, socket, - listener_callbacks, true); + listener_callbacks, true, false); auto local_dst_address = Network::Utility::getAddressWithPort( *Network::Test::getCanonicalLoopbackAddress(version_), @@ -232,7 +284,7 @@ TEST_P(TcpListenerImplTest, WildcardListenerIpv4Compat) { // Do not redirect since use_original_dst is false. Network::TestTcpListenerImpl listener(dispatcherImpl(), random_generator, socket, - listener_callbacks, true); + listener_callbacks, true, false); auto listener_address = Network::Utility::getAddressWithPort( *Network::Test::getCanonicalLoopbackAddress(version_), @@ -271,8 +323,8 @@ TEST_P(TcpListenerImplTest, DisableAndEnableListener) { MockTcpListenerCallbacks listener_callbacks; MockConnectionCallbacks connection_callbacks; Random::MockRandomGenerator random_generator; - TestTcpListenerImpl listener(dispatcherImpl(), random_generator, socket, listener_callbacks, - true); + TestTcpListenerImpl listener(dispatcherImpl(), random_generator, socket, listener_callbacks, true, + false); // When listener is disabled, the timer should fire before any connection is accepted. listener.disable(); @@ -312,8 +364,8 @@ TEST_P(TcpListenerImplTest, SetListenerRejectFractionZero) { MockTcpListenerCallbacks listener_callbacks; MockConnectionCallbacks connection_callbacks; Random::MockRandomGenerator random_generator; - TestTcpListenerImpl listener(dispatcherImpl(), random_generator, socket, listener_callbacks, - true); + TestTcpListenerImpl listener(dispatcherImpl(), random_generator, socket, listener_callbacks, true, + false); listener.setRejectFraction(UnitFloat(0)); @@ -343,8 +395,8 @@ TEST_P(TcpListenerImplTest, SetListenerRejectFractionIntermediate) { MockTcpListenerCallbacks listener_callbacks; MockConnectionCallbacks connection_callbacks; Random::MockRandomGenerator random_generator; - TestTcpListenerImpl listener(dispatcherImpl(), random_generator, socket, listener_callbacks, - true); + TestTcpListenerImpl listener(dispatcherImpl(), random_generator, socket, listener_callbacks, true, + false); listener.setRejectFraction(UnitFloat(0.5f)); @@ -406,8 +458,8 @@ TEST_P(TcpListenerImplTest, SetListenerRejectFractionAll) { MockTcpListenerCallbacks listener_callbacks; MockConnectionCallbacks connection_callbacks; Random::MockRandomGenerator random_generator; - TestTcpListenerImpl listener(dispatcherImpl(), random_generator, socket, listener_callbacks, - true); + TestTcpListenerImpl listener(dispatcherImpl(), random_generator, socket, listener_callbacks, true, + false); listener.setRejectFraction(UnitFloat(1)); diff --git a/test/common/protobuf/utility_test.cc b/test/common/protobuf/utility_test.cc index c89d4f78c500..0e5fa91e94e5 100644 --- a/test/common/protobuf/utility_test.cc +++ b/test/common/protobuf/utility_test.cc @@ -37,6 +37,7 @@ #include "test/test_common/utility.h" #include "absl/container/node_hash_set.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "udpa/type/v1/typed_struct.pb.h" #include "xds/type/v3/typed_struct.pb.h" @@ -45,7 +46,15 @@ using namespace std::chrono_literals; namespace Envoy { -using testing::HasSubstr; +using ::testing::HasSubstr; + +bool checkProtoEquality(const ProtobufWkt::Value& proto1, std::string text_proto2) { + ProtobufWkt::Value proto2; + if (!Protobuf::TextFormat::ParseFromString(text_proto2, &proto2)) { + return false; + } + return Envoy::Protobuf::util::MessageDifferencer::Equals(proto1, proto2); +} class RuntimeStatsHelper : public TestScopedRuntime { public: @@ -179,9 +188,11 @@ TEST_F(ProtobufUtilityTest, RepeatedPtrUtilDebugString) { Protobuf::RepeatedPtrField repeated; EXPECT_EQ("[]", RepeatedPtrUtil::debugString(repeated)); repeated.Add()->set_value(10); - EXPECT_EQ("[value: 10\n]", RepeatedPtrUtil::debugString(repeated)); + EXPECT_THAT(RepeatedPtrUtil::debugString(repeated), + testing::ContainsRegex("\\[value:\\s*10\n\\]")); repeated.Add()->set_value(20); - EXPECT_EQ("[value: 10\n, value: 20\n]", RepeatedPtrUtil::debugString(repeated)); + EXPECT_THAT(RepeatedPtrUtil::debugString(repeated), + testing::ContainsRegex("\\[value:\\s*10\n, value:\\s*20\n\\]")); } // Validated exception thrown when downcastAndValidate observes a PGV failures. @@ -1228,25 +1239,28 @@ TEST_F(ProtobufUtilityTest, MessageUtilLoadYamlDouble) { } TEST_F(ProtobufUtilityTest, ValueUtilLoadFromYamlScalar) { - EXPECT_EQ(ValueUtil::loadFromYaml("null").ShortDebugString(), "null_value: NULL_VALUE"); - EXPECT_EQ(ValueUtil::loadFromYaml("true").ShortDebugString(), "bool_value: true"); - EXPECT_EQ(ValueUtil::loadFromYaml("1").ShortDebugString(), "number_value: 1"); - EXPECT_EQ(ValueUtil::loadFromYaml("9223372036854775807").ShortDebugString(), - "string_value: \"9223372036854775807\""); - EXPECT_EQ(ValueUtil::loadFromYaml("\"foo\"").ShortDebugString(), "string_value: \"foo\""); - EXPECT_EQ(ValueUtil::loadFromYaml("foo").ShortDebugString(), "string_value: \"foo\""); + EXPECT_TRUE(checkProtoEquality(ValueUtil::loadFromYaml("null"), "null_value: NULL_VALUE")); + EXPECT_TRUE(checkProtoEquality(ValueUtil::loadFromYaml("true"), "bool_value: true")); + EXPECT_TRUE(checkProtoEquality(ValueUtil::loadFromYaml("1"), "number_value: 1")); + EXPECT_TRUE(checkProtoEquality(ValueUtil::loadFromYaml("9223372036854775807"), + "string_value: \"9223372036854775807\"")); + EXPECT_TRUE(checkProtoEquality(ValueUtil::loadFromYaml("\"foo\""), "string_value: \"foo\"")); + EXPECT_TRUE(checkProtoEquality(ValueUtil::loadFromYaml("foo"), "string_value: \"foo\"")); } TEST_F(ProtobufUtilityTest, ValueUtilLoadFromYamlObject) { - EXPECT_EQ(ValueUtil::loadFromYaml("[foo, bar]").ShortDebugString(), - "list_value { values { string_value: \"foo\" } values { string_value: \"bar\" } }"); - EXPECT_EQ(ValueUtil::loadFromYaml("foo: bar").ShortDebugString(), - "struct_value { fields { key: \"foo\" value { string_value: \"bar\" } } }"); + EXPECT_TRUE(checkProtoEquality( + ValueUtil::loadFromYaml("[foo, bar]"), + "list_value { values { string_value: \"foo\" } values { string_value: \"bar\" } }")); + EXPECT_TRUE(checkProtoEquality( + ValueUtil::loadFromYaml("foo: bar"), + "struct_value { fields { key: \"foo\" value { string_value: \"bar\" } } }")); } TEST_F(ProtobufUtilityTest, ValueUtilLoadFromYamlObjectWithIgnoredEntries) { - EXPECT_EQ(ValueUtil::loadFromYaml("!ignore foo: bar\nbaz: qux").ShortDebugString(), - "struct_value { fields { key: \"baz\" value { string_value: \"qux\" } } }"); + EXPECT_TRUE(checkProtoEquality( + ValueUtil::loadFromYaml("!ignore foo: bar\nbaz: qux"), + "struct_value { fields { key: \"baz\" value { string_value: \"qux\" } } }")); } TEST(LoadFromYamlExceptionTest, BadConversion) { @@ -1463,9 +1477,13 @@ TEST_F(ProtobufUtilityTest, JsonConvertFail) { ProtobufWkt::Duration source_duration; source_duration.set_seconds(-281474976710656); ProtobufWkt::Struct dest_struct; + std::string expected_duration_text = R"pb(seconds: -281474976710656)pb"; + ProtobufWkt::Duration expected_duration_proto; + Protobuf::TextFormat::ParseFromString(expected_duration_text, &expected_duration_proto); EXPECT_THROW_WITH_REGEX(TestUtility::jsonConvert(source_duration, dest_struct), EnvoyException, - "Unable to convert protobuf message to JSON string.*" - "seconds exceeds limit for field: seconds: -281474976710656\n"); + fmt::format("Unable to convert protobuf message to JSON string.*" + "seconds exceeds limit for field: {}", + expected_duration_proto.DebugString())); } // Regression test for https://github.com/envoyproxy/envoy/issues/3665. @@ -1486,14 +1504,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 +1520,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) { diff --git a/test/common/quic/BUILD b/test/common/quic/BUILD index 4c16057ecbba..f549b51982ae 100644 --- a/test/common/quic/BUILD +++ b/test/common/quic/BUILD @@ -55,24 +55,28 @@ envoy_cc_test( ) envoy_cc_test( - name = "quic_stat_names_test", - srcs = ["quic_stat_names_test.cc"], + name = "quic_filter_manager_connection_impl_test", + srcs = ["quic_filter_manager_connection_impl_test.cc"], tags = ["nofips"], deps = [ - "//source/common/quic:quic_stat_names_lib", - "//source/common/stats:stats_lib", - "//test/mocks/stats:stats_mocks", + "//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 = "envoy_quic_session_cache_test", - srcs = ["envoy_quic_session_cache_test.cc"], - external_deps = ["quiche_quic_platform"], + name = "quic_stat_names_test", + srcs = ["quic_stat_names_test.cc"], tags = ["nofips"], deps = [ - "//source/common/quic:envoy_quic_session_cache_lib", + "//source/common/quic:quic_stat_names_lib", + "//source/common/stats:stats_lib", + "//test/mocks/stats:stats_mocks", + "//test/test_common:utility_lib", ], ) diff --git a/test/common/quic/active_quic_listener_test.cc b/test/common/quic/active_quic_listener_test.cc index c1632e55cec7..45cddb815629 100644 --- a/test/common/quic/active_quic_listener_test.cc +++ b/test/common/quic/active_quic_listener_test.cc @@ -200,8 +200,9 @@ class ActiveQuicListenerTest : public testing::TestWithParam(Network::Socket::Type::Datagram, - local_address_, nullptr)); + client_sockets_.push_back( + std::make_unique(Network::Socket::Type::Datagram, local_address_, + nullptr, Network::SocketCreationOptions{})); Buffer::OwnedImpl payload = generateChloPacketToSend(quic_version_, quic_config_, connection_id); Buffer::RawSliceVector slice = payload.getRawSlices(); diff --git a/test/common/quic/envoy_quic_client_session_test.cc b/test/common/quic/envoy_quic_client_session_test.cc index 56ee37b1a29c..0900cc4d9990 100644 --- a/test/common/quic/envoy_quic_client_session_test.cc +++ b/test/common/quic/envoy_quic_client_session_test.cc @@ -65,13 +65,12 @@ class TestEnvoyQuicClientConnection : public EnvoyQuicClientConnection { using EnvoyQuicClientConnection::connectionStats; }; -class EnvoyQuicClientSessionTest : public testing::Test { +class EnvoyQuicClientSessionTest : public testing::TestWithParam { public: EnvoyQuicClientSessionTest() : api_(Api::createApiForTest(time_system_)), dispatcher_(api_->allocateDispatcher("test_thread")), connection_helper_(*dispatcher_), - alarm_factory_(*dispatcher_, *connection_helper_.GetClock()), - quic_version_([]() { return quic::CurrentSupportedHttp3Versions(); }()), + alarm_factory_(*dispatcher_, *connection_helper_.GetClock()), quic_version_({GetParam()}), peer_addr_(Network::Utility::getAddressWithPort(*Network::Utility::getIpv6LoopbackAddress(), 12345)), self_addr_(Network::Utility::getAddressWithPort(*Network::Utility::getIpv6LoopbackAddress(), @@ -161,7 +160,10 @@ class EnvoyQuicClientSessionTest : public testing::Test { QuicHttpClientConnectionImpl http_connection_; }; -TEST_F(EnvoyQuicClientSessionTest, NewStream) { +INSTANTIATE_TEST_SUITE_P(EnvoyQuicClientSessionTests, EnvoyQuicClientSessionTest, + testing::ValuesIn(quic::CurrentSupportedHttp3Versions())); + +TEST_P(EnvoyQuicClientSessionTest, NewStream) { Http::MockResponseDecoder response_decoder; Http::MockStreamCallbacks stream_callbacks; EnvoyQuicClientStream& stream = sendGetRequest(response_decoder, stream_callbacks); @@ -178,7 +180,7 @@ TEST_F(EnvoyQuicClientSessionTest, NewStream) { stream.OnStreamHeaderList(/*fin=*/true, headers.uncompressed_header_bytes(), headers); } -TEST_F(EnvoyQuicClientSessionTest, PacketLimits) { +TEST_P(EnvoyQuicClientSessionTest, PacketLimits) { // We always allow for reading packets, even if there's no stream. EXPECT_EQ(0, envoy_quic_session_.GetNumActiveStreams()); EXPECT_EQ(16, envoy_quic_session_.numPacketsExpectedPerEventLoop()); @@ -217,7 +219,7 @@ TEST_F(EnvoyQuicClientSessionTest, PacketLimits) { envoy_quic_session_.close(Network::ConnectionCloseType::NoFlush); } -TEST_F(EnvoyQuicClientSessionTest, OnResetFrame) { +TEST_P(EnvoyQuicClientSessionTest, OnResetFrame) { Http::MockResponseDecoder response_decoder; Http::MockStreamCallbacks stream_callbacks; EnvoyQuicClientStream& stream = sendGetRequest(response_decoder, stream_callbacks); @@ -235,7 +237,7 @@ TEST_F(EnvoyQuicClientSessionTest, OnResetFrame) { ->value()); } -TEST_F(EnvoyQuicClientSessionTest, SendResetFrame) { +TEST_P(EnvoyQuicClientSessionTest, SendResetFrame) { Http::MockResponseDecoder response_decoder; Http::MockStreamCallbacks stream_callbacks; EnvoyQuicClientStream& stream = sendGetRequest(response_decoder, stream_callbacks); @@ -252,7 +254,7 @@ TEST_F(EnvoyQuicClientSessionTest, SendResetFrame) { ->value()); } -TEST_F(EnvoyQuicClientSessionTest, OnGoAwayFrame) { +TEST_P(EnvoyQuicClientSessionTest, OnGoAwayFrame) { Http::MockResponseDecoder response_decoder; Http::MockStreamCallbacks stream_callbacks; @@ -260,7 +262,7 @@ TEST_F(EnvoyQuicClientSessionTest, OnGoAwayFrame) { envoy_quic_session_.OnHttp3GoAway(4u); } -TEST_F(EnvoyQuicClientSessionTest, ConnectionClose) { +TEST_P(EnvoyQuicClientSessionTest, ConnectionClose) { std::string error_details("dummy details"); quic::QuicErrorCode error(quic::QUIC_INVALID_FRAME_DATA); quic::QuicConnectionCloseFrame frame(quic_version_[0].transport_version, error, @@ -278,7 +280,7 @@ TEST_F(EnvoyQuicClientSessionTest, ConnectionClose) { ->value()); } -TEST_F(EnvoyQuicClientSessionTest, ConnectionCloseWithActiveStream) { +TEST_P(EnvoyQuicClientSessionTest, ConnectionCloseWithActiveStream) { Http::MockResponseDecoder response_decoder; Http::MockStreamCallbacks stream_callbacks; EnvoyQuicClientStream& stream = sendGetRequest(response_decoder, stream_callbacks); @@ -294,7 +296,7 @@ TEST_F(EnvoyQuicClientSessionTest, ConnectionCloseWithActiveStream) { ->value()); } -TEST_F(EnvoyQuicClientSessionTest, HandshakeTimesOutWithActiveStream) { +TEST_P(EnvoyQuicClientSessionTest, HandshakeTimesOutWithActiveStream) { Http::MockResponseDecoder response_decoder; Http::MockStreamCallbacks stream_callbacks; EnvoyQuicClientStream& stream = sendGetRequest(response_decoder, stream_callbacks); @@ -311,7 +313,7 @@ TEST_F(EnvoyQuicClientSessionTest, HandshakeTimesOutWithActiveStream) { ->value()); } -TEST_F(EnvoyQuicClientSessionTest, ConnectionClosePopulatesQuicVersionStats) { +TEST_P(EnvoyQuicClientSessionTest, ConnectionClosePopulatesQuicVersionStats) { std::string error_details("dummy details"); quic::QuicErrorCode error(quic::QUIC_INVALID_FRAME_DATA); quic::QuicConnectionCloseFrame frame(quic_version_[0].transport_version, error, @@ -322,10 +324,23 @@ TEST_F(EnvoyQuicClientSessionTest, ConnectionClosePopulatesQuicVersionStats) { EXPECT_EQ(absl::StrCat(quic::QuicErrorCodeToString(error), " with details: ", error_details), envoy_quic_session_.transportFailureReason()); EXPECT_EQ(Network::Connection::State::Closed, envoy_quic_session_.state()); - EXPECT_EQ(1U, TestUtility::findCounter(store_, "http3.quic_version_rfc_v1")->value()); + std::string quic_version_stat_name; + switch (GetParam().transport_version) { + case quic::QUIC_VERSION_IETF_DRAFT_29: + quic_version_stat_name = "h3_29"; + break; + case quic::QUIC_VERSION_IETF_RFC_V1: + quic_version_stat_name = "rfc_v1"; + break; + default: + break; + } + EXPECT_EQ(1U, TestUtility::findCounter( + store_, absl::StrCat("http3.quic_version_", quic_version_stat_name)) + ->value()); } -TEST_F(EnvoyQuicClientSessionTest, IncomingUnidirectionalReadStream) { +TEST_P(EnvoyQuicClientSessionTest, IncomingUnidirectionalReadStream) { quic::QuicStreamId stream_id = 1u; quic::QuicStreamFrame stream_frame(stream_id, false, 0, "aaa"); envoy_quic_session_.OnStreamFrame(stream_frame); diff --git a/test/common/quic/envoy_quic_client_stream_test.cc b/test/common/quic/envoy_quic_client_stream_test.cc index 854e72166cee..7bead0f2972d 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()); @@ -206,14 +206,14 @@ TEST_F(EnvoyQuicClientStreamTest, PostRequestAnd100Continue) { })); 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/common/quic/envoy_quic_dispatcher_test.cc b/test/common/quic/envoy_quic_dispatcher_test.cc index 7e7e4fe0e49f..3481ebc731fa 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_proof_source_test.cc b/test/common/quic/envoy_quic_proof_source_test.cc index b481768eb91f..3c49ad23f35e 100644 --- a/test/common/quic/envoy_quic_proof_source_test.cc +++ b/test/common/quic/envoy_quic_proof_source_test.cc @@ -58,7 +58,8 @@ class SignatureVerifier { ON_CALL(cert_validation_ctx_config_, certificateRevocationListPath()) .WillByDefault(ReturnRef(path_string)); const std::vector empty_string_list; - const std::vector san_matchers; + const std::vector + san_matchers; ON_CALL(cert_validation_ctx_config_, subjectAltNameMatchers()) .WillByDefault(ReturnRef(san_matchers)); ON_CALL(cert_validation_ctx_config_, verifyCertificateHashList()) diff --git a/test/common/quic/envoy_quic_proof_verifier_test.cc b/test/common/quic/envoy_quic_proof_verifier_test.cc index dea9daa038f5..06b3405b7d2a 100644 --- a/test/common/quic/envoy_quic_proof_verifier_test.cc +++ b/test/common/quic/envoy_quic_proof_verifier_test.cc @@ -75,7 +75,8 @@ class EnvoyQuicProofVerifierTest : public testing::Test { const std::string path_string_{"some_path"}; const std::string alpn_{"h2,http/1.1"}; const std::string sig_algs_{"rsa_pss_rsae_sha256"}; - const std::vector san_matchers_; + const std::vector + san_matchers_; const std::string empty_string_; const std::vector empty_string_list_; const std::string cert_chain_{quic::test::kTestCertificateChainPem}; diff --git a/test/common/quic/envoy_quic_server_stream_test.cc b/test/common/quic/envoy_quic_server_stream_test.cc index ee01d965e3fd..d34d569bdb92 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/test/common/quic/envoy_quic_session_cache_test.cc b/test/common/quic/envoy_quic_session_cache_test.cc deleted file mode 100644 index 5490b0d491af..000000000000 --- a/test/common/quic/envoy_quic_session_cache_test.cc +++ /dev/null @@ -1,342 +0,0 @@ -#include "source/common/quic/envoy_quic_session_cache.h" - -#include "absl/strings/escaping.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -namespace Envoy { -namespace Quic { -namespace { - -constexpr uint32_t Timeout = 1000; -const quic::QuicVersionLabel FakeVersionLabel = 0x01234567; -const quic::QuicVersionLabel FakeVersionLabel2 = 0x89ABCDEF; -const uint64_t FakeIdleTimeoutMilliseconds = 12012; -const uint8_t FakeStatelessResetTokenData[16] = {0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, - 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F}; -const uint64_t FakeMaxPacketSize = 9001; -const uint64_t FakeInitialMaxData = 101; -const bool FakeDisableMigration = true; -const auto CustomParameter1 = static_cast(0xffcd); -const char* CustomParameter1Value = "foo"; -const auto CustomParameter2 = static_cast(0xff34); -const char* CustomParameter2Value = "bar"; - -std::vector createFakeStatelessResetToken() { - return std::vector(FakeStatelessResetTokenData, - FakeStatelessResetTokenData + sizeof(FakeStatelessResetTokenData)); -} - -// Make a TransportParameters that has a few fields set to help test comparison. -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->max_idle_timeout_ms.set_value(FakeIdleTimeoutMilliseconds); - params->stateless_reset_token = createFakeStatelessResetToken(); - params->max_udp_payload_size.set_value(FakeMaxPacketSize); - params->initial_max_data.set_value(FakeInitialMaxData); - params->disable_active_migration = FakeDisableMigration; - params->custom_parameters[CustomParameter1] = CustomParameter1Value; - params->custom_parameters[CustomParameter2] = CustomParameter2Value; - return params; -} - -// Generated with SSL_SESSION_to_bytes. -static constexpr char CachedSession[] = - "3082068702010102020304040213010420b9c2a657e565db0babd09e192a9fc4d768fbd706" - "9f03f9278a4a0be62392e55b0420d87ed2ab8cafc986fd2e288bd2d654cd57c3a2bed1d532" - "20726e55fed39d021ea10602045ed16771a205020302a300a382025f3082025b30820143a0" - "03020102020104300d06092a864886f70d01010b0500302c3110300e060355040a13074163" - "6d6520436f311830160603550403130f496e7465726d656469617465204341301e170d3133" - "303130313130303030305a170d3233313233313130303030305a302d3110300e060355040a" - "130741636d6520436f3119301706035504031310746573742e6578616d706c652e636f6d30" - "59301306072a8648ce3d020106082a8648ce3d030107034200040526220e77278300d06bc0" - "86aff4f999a828a2ed5cc75adc2972794befe885aa3a9b843de321b36b0a795289cebff1a5" - "428bad5e34665ce5e36daad08fb3ffd8a3523050300e0603551d0f0101ff04040302078030" - "130603551d25040c300a06082b06010505070301300c0603551d130101ff04023000301b06" - "03551d11041430128210746573742e6578616d706c652e636f6d300d06092a864886f70d01" - "010b050003820101008c1f1e380831b6437a8b9284d28d4ead38d9503a9fc936db89048aa2" - "edd6ec2fb830d962ef7a4f384e679504f4d5520f3272e0b9e702b110aff31711578fa5aeb1" - "11e9d184c994b0f97e7b17d1995f3f477f25bc1258398ec0ec729caed55d594a009f48093a" - "17f33a7f3bb6e420cc3499838398a421d93c7132efa8bee5ed2645cbc55179c400da006feb" - "761badd356cac3bd7a0e6b22a511106a355ec62a4c0ac2541d2996adb4a918c866d10c3e31" - "62039a91d4ce600b276740d833380b37f66866d261bf6efa8855e7ae6c7d12a8a864cd9a1f" - "4663e07714b0204e51bbc189a2d04c2a5043202379ff1c8cbf30cbb44fde4ee9a1c0c976dc" - "4943df2c132ca4020400aa7f047d494e534543555245003072020101020203040402130104" - "000420d87ed2ab8cafc986fd2e288bd2d654cd57c3a2bed1d53220726e55fed39d021ea106" - "02045ed16771a205020302a300a4020400b20302011db5060404bd909308b807020500ffff" - "ffffb9050203093a80ba07040568332d3238bb030101ffbc03040100b20302011db3820307" - "30820303308201eba003020102020102300d06092a864886f70d01010b050030243110300e" - "060355040a130741636d6520436f3110300e06035504031307526f6f74204341301e170d31" - "33303130313130303030305a170d3233313233313130303030305a302c3110300e06035504" - "0a130741636d6520436f311830160603550403130f496e7465726d65646961746520434130" - "820122300d06092a864886f70d01010105000382010f003082010a0282010100cd3550e70a" - "6880e52bf0012b93110c50f723e1d8d2ed489aea3b649f82fae4ad2396a8a19b31d1d64ab2" - "79f1c18003184154a5303a82bd57109cfd5d34fd19d3211bcb06e76640e1278998822dd72e" - "0d5c059a740d45de325e784e81b4c86097f08b2a8ce057f6b9db5a53641d27e09347d993ee" - "acf67be7d297b1a6853775ffaaf78fae924e300b5654fd32f99d3cd82e95f56417ff26d265" - "e2b1786c835d67a4d8ae896b6eb34b35a5b1033c209779ed0bf8de25a13a507040ae9e0475" - "a26a2f15845b08c3e0554e47dbbc7925b02e580dbcaaa6f2eecde6b8028c5b00b33d44d0a6" - "bfb3e72e9d4670de45d1bd79bdc0f2470b71286091c29873152db4b1f30203010001a33830" - "36300e0603551d0f0101ff04040302020430130603551d25040c300a06082b060105050703" - "01300f0603551d130101ff040530030101ff300d06092a864886f70d01010b050003820101" - "00bc4f8234860558dd404a626403819bfc759029d625a002143e75ebdb2898d1befdd326c3" - "4b14dc3507d732bb29af7e6af31552db53052a2be0d950efee5e0f699304231611ed8bf73a" - "6f216a904c6c2f1a2186d1ed08a8005a7914394d71e7d4b643c808f86365c5fecad8b52934" - "2d3b3f03447126d278d75b1dab3ed53f23e36e9b3d695f28727916e5ee56ce22d387c81f05" - "919b2a37bd4981eb67d9f57b7072285dbbb61f48b6b14768c069a092aad5a094cf295dafd2" - "3ca008f89a5f5ab37a56e5f68df45091c7cb85574677127087a2887ba3baa6d4fc436c6e40" - "40885e81621d38974f0c7f0d792418c5adebb10e92a165f8d79b169617ff575c0d4a85b506" - "0404bd909308b603010100b70402020403b807020500ffffffffb9050203093a80ba070405" - "68332d3238bb030101ff"; - -class FakeTimeSource : public TimeSource { -public: - // From TimeSource. - SystemTime systemTime() override { return fake_time_; } - MonotonicTime monotonicTime() override { - // EnvoyQuicSessionCache does not use monotonic time, return empty value. - ADD_FAILURE() << "Unexpected call to monotonicTime"; - return MonotonicTime(); - } - - void advance(int seconds) { fake_time_ += std::chrono::seconds(seconds); } - -private: - SystemTime fake_time_{}; -}; - -} // namespace - -class EnvoyQuicSessionCacheTest : public ::testing::Test { -public: - EnvoyQuicSessionCacheTest() - : ssl_ctx_(SSL_CTX_new(TLS_method())), cache_(time_source_), - params_(makeFakeTransportParams()) {} - -protected: - bssl::UniquePtr makeSession(uint32_t timeout = Timeout) { - std::string cached_session = - absl::HexStringToBytes(absl::string_view(CachedSession, sizeof(CachedSession))); - SSL_SESSION* session = - SSL_SESSION_from_bytes(reinterpret_cast(cached_session.data()), - cached_session.size(), ssl_ctx_.get()); - SSL_SESSION_set_time(session, std::chrono::system_clock::to_time_t(time_source_.systemTime())); - SSL_SESSION_set_timeout(session, timeout); - return bssl::UniquePtr(session); - } - - bssl::UniquePtr ssl_ctx_; - FakeTimeSource time_source_; - EnvoyQuicSessionCache cache_; - std::unique_ptr params_; -}; - -// Tests that simple insertion and lookup work correctly. -TEST_F(EnvoyQuicSessionCacheTest, SingleSession) { - bssl::UniquePtr session = makeSession(); - quic::QuicServerId id1("a.com", 443); - - std::unique_ptr params2 = makeFakeTransportParams(); - bssl::UniquePtr session2 = makeSession(); - SSL_SESSION* unowned2 = session2.get(); - quic::QuicServerId id2("b.com", 443); - - EXPECT_EQ(nullptr, cache_.Lookup(id1, ssl_ctx_.get())); - EXPECT_EQ(nullptr, cache_.Lookup(id2, ssl_ctx_.get())); - EXPECT_EQ(0u, cache_.size()); - - cache_.Insert(id1, std::move(session), *params_, nullptr); - EXPECT_EQ(1u, cache_.size()); - std::unique_ptr resumption_state = cache_.Lookup(id1, ssl_ctx_.get()); - ASSERT_NE(resumption_state, nullptr); - ASSERT_NE(resumption_state->transport_params, nullptr); - EXPECT_EQ(*params_, *(resumption_state->transport_params)); - EXPECT_EQ(nullptr, cache_.Lookup(id2, ssl_ctx_.get())); - // No session is available for id1, even though the entry exists. - EXPECT_EQ(1u, cache_.size()); - EXPECT_EQ(nullptr, cache_.Lookup(id1, ssl_ctx_.get())); - // Lookup() will trigger a deletion of invalid entry. - EXPECT_EQ(0u, cache_.size()); - - bssl::UniquePtr session3 = makeSession(); - SSL_SESSION* unowned3 = session3.get(); - quic::QuicServerId id3("c.com", 443); - cache_.Insert(id3, std::move(session3), *params_, nullptr); - cache_.Insert(id2, std::move(session2), *params2, nullptr); - EXPECT_EQ(2u, cache_.size()); - std::unique_ptr resumption_state2 = cache_.Lookup(id2, ssl_ctx_.get()); - ASSERT_NE(resumption_state2, nullptr); - EXPECT_EQ(unowned2, resumption_state2->tls_session.get()); - std::unique_ptr resumption_state3 = cache_.Lookup(id3, ssl_ctx_.get()); - ASSERT_NE(resumption_state3, nullptr); - EXPECT_EQ(unowned3, resumption_state3->tls_session.get()); - - // Verify that the cache is cleared after Lookups. - EXPECT_EQ(nullptr, cache_.Lookup(id1, ssl_ctx_.get())); - EXPECT_EQ(nullptr, cache_.Lookup(id2, ssl_ctx_.get())); - EXPECT_EQ(nullptr, cache_.Lookup(id3, ssl_ctx_.get())); - EXPECT_EQ(0u, cache_.size()); -} - -TEST_F(EnvoyQuicSessionCacheTest, MultipleSessions) { - bssl::UniquePtr session = makeSession(); - quic::QuicServerId id1("a.com", 443); - bssl::UniquePtr session2 = makeSession(); - SSL_SESSION* unowned2 = session2.get(); - bssl::UniquePtr session3 = makeSession(); - SSL_SESSION* unowned3 = session3.get(); - - cache_.Insert(id1, std::move(session), *params_, nullptr); - cache_.Insert(id1, std::move(session2), *params_, nullptr); - cache_.Insert(id1, std::move(session3), *params_, nullptr); - // The latest session is popped first. - std::unique_ptr resumption_state1 = cache_.Lookup(id1, ssl_ctx_.get()); - ASSERT_NE(resumption_state1, nullptr); - EXPECT_EQ(unowned3, resumption_state1->tls_session.get()); - std::unique_ptr resumption_state2 = cache_.Lookup(id1, ssl_ctx_.get()); - ASSERT_NE(resumption_state2, nullptr); - EXPECT_EQ(unowned2, resumption_state2->tls_session.get()); - // Only two sessions are cache. - EXPECT_EQ(nullptr, cache_.Lookup(id1, ssl_ctx_.get())); -} - -// Test that when a different TransportParameter is inserted for -// the same server id, the existing entry is removed. -TEST_F(EnvoyQuicSessionCacheTest, DifferentTransportParams) { - bssl::UniquePtr session = makeSession(); - quic::QuicServerId id1("a.com", 443); - bssl::UniquePtr session2 = makeSession(); - bssl::UniquePtr session3 = makeSession(); - SSL_SESSION* unowned3 = session3.get(); - - cache_.Insert(id1, std::move(session), *params_, nullptr); - cache_.Insert(id1, std::move(session2), *params_, nullptr); - // tweak the transport parameters a little bit. - params_->perspective = quic::Perspective::IS_SERVER; - cache_.Insert(id1, std::move(session3), *params_, nullptr); - std::unique_ptr resumption_state = cache_.Lookup(id1, ssl_ctx_.get()); - ASSERT_NE(resumption_state, nullptr); - EXPECT_EQ(unowned3, resumption_state->tls_session.get()); - EXPECT_EQ(*params_.get(), *resumption_state->transport_params); - EXPECT_EQ(nullptr, cache_.Lookup(id1, ssl_ctx_.get())); -} - -TEST_F(EnvoyQuicSessionCacheTest, DifferentApplicationState) { - bssl::UniquePtr session = makeSession(); - quic::QuicServerId id1("a.com", 443); - bssl::UniquePtr session2 = makeSession(); - bssl::UniquePtr session3 = makeSession(); - SSL_SESSION* unowned3 = session3.get(); - quic::ApplicationState state; - state.push_back('a'); - - cache_.Insert(id1, std::move(session), *params_, &state); - cache_.Insert(id1, std::move(session2), *params_, &state); - cache_.Insert(id1, std::move(session3), *params_, nullptr); - std::unique_ptr resumption_state = cache_.Lookup(id1, ssl_ctx_.get()); - ASSERT_NE(resumption_state, nullptr); - EXPECT_EQ(unowned3, resumption_state->tls_session.get()); - EXPECT_EQ(nullptr, resumption_state->application_state); - EXPECT_EQ(nullptr, cache_.Lookup(id1, ssl_ctx_.get())); -} - -TEST_F(EnvoyQuicSessionCacheTest, BothStatesDifferent) { - bssl::UniquePtr session = makeSession(); - quic::QuicServerId id1("a.com", 443); - bssl::UniquePtr session2 = makeSession(); - bssl::UniquePtr session3 = makeSession(); - SSL_SESSION* unowned3 = session3.get(); - quic::ApplicationState state; - state.push_back('a'); - - cache_.Insert(id1, std::move(session), *params_, &state); - cache_.Insert(id1, std::move(session2), *params_, &state); - params_->perspective = quic::Perspective::IS_SERVER; - cache_.Insert(id1, std::move(session3), *params_, nullptr); - std::unique_ptr resumption_state = cache_.Lookup(id1, ssl_ctx_.get()); - ASSERT_NE(resumption_state, nullptr); - EXPECT_EQ(unowned3, resumption_state->tls_session.get()); - EXPECT_EQ(*params_, *resumption_state->transport_params); - EXPECT_EQ(nullptr, resumption_state->application_state); - EXPECT_EQ(nullptr, cache_.Lookup(id1, ssl_ctx_.get())); -} - -// When the size limit is exceeded, the oldest entry should be erased. -TEST_F(EnvoyQuicSessionCacheTest, SizeLimit) { - constexpr size_t size_limit = 1024; - std::array unowned_sessions; - for (size_t i = 0; i <= size_limit; i++) { - time_source_.advance(1); - bssl::UniquePtr session = makeSession(/*timeout=*/10000); - unowned_sessions[i] = session.get(); - quic::QuicServerId id(absl::StrCat("domain", i, ".example.com"), 443); - cache_.Insert(id, std::move(session), *params_, nullptr); - } - EXPECT_EQ(cache_.size(), size_limit); - // First entry has been removed. - quic::QuicServerId id0("domain0.example.com", 443); - EXPECT_EQ(nullptr, cache_.Lookup(id0, ssl_ctx_.get())); - // All other entries all present. - for (size_t i = 1; i <= size_limit; i++) { - quic::QuicServerId id(absl::StrCat("domain", i, ".example.com"), 443); - std::unique_ptr resumption_state = cache_.Lookup(id, ssl_ctx_.get()); - ASSERT_NE(resumption_state, nullptr) << i; - EXPECT_EQ(resumption_state->tls_session.get(), unowned_sessions[i]); - } -} - -TEST_F(EnvoyQuicSessionCacheTest, ClearEarlyData) { - SSL_CTX_set_early_data_enabled(ssl_ctx_.get(), 1); - bssl::UniquePtr session = makeSession(); - quic::QuicServerId id1("a.com", 443); - bssl::UniquePtr session2 = makeSession(); - - EXPECT_TRUE(SSL_SESSION_early_data_capable(session.get())); - EXPECT_TRUE(SSL_SESSION_early_data_capable(session2.get())); - - cache_.Insert(id1, std::move(session), *params_, nullptr); - cache_.Insert(id1, std::move(session2), *params_, nullptr); - - cache_.ClearEarlyData(id1); - - std::unique_ptr resumption_state = cache_.Lookup(id1, ssl_ctx_.get()); - ASSERT_NE(resumption_state, nullptr); - EXPECT_FALSE(SSL_SESSION_early_data_capable(resumption_state->tls_session.get())); - resumption_state = cache_.Lookup(id1, ssl_ctx_.get()); - EXPECT_FALSE(SSL_SESSION_early_data_capable(resumption_state->tls_session.get())); - EXPECT_EQ(nullptr, cache_.Lookup(id1, ssl_ctx_.get())); -} - -// Expired session isn't considered valid and nullptr will be returned upon -// Lookup. -TEST_F(EnvoyQuicSessionCacheTest, Expiration) { - bssl::UniquePtr session = makeSession(); - quic::QuicServerId id1("a.com", 443); - - bssl::UniquePtr session2 = makeSession(3 * Timeout); - SSL_SESSION* unowned2 = session2.get(); - quic::QuicServerId id2("b.com", 443); - - cache_.Insert(id1, std::move(session), *params_, nullptr); - cache_.Insert(id2, std::move(session2), *params_, nullptr); - - EXPECT_EQ(2u, cache_.size()); - // Expire the session. - time_source_.advance(Timeout * 2); - // The entry has not been removed yet. - EXPECT_EQ(2u, cache_.size()); - - EXPECT_EQ(nullptr, cache_.Lookup(id1, ssl_ctx_.get())); - EXPECT_EQ(1u, cache_.size()); - std::unique_ptr resumption_state = cache_.Lookup(id2, ssl_ctx_.get()); - ASSERT_NE(resumption_state, nullptr); - EXPECT_EQ(unowned2, resumption_state->tls_session.get()); - EXPECT_EQ(1u, cache_.size()); -} - -} // namespace Quic -} // namespace Envoy diff --git a/test/common/quic/envoy_quic_utils_test.cc b/test/common/quic/envoy_quic_utils_test.cc index d8d290975f08..99794c49700d 100644 --- a/test/common/quic/envoy_quic_utils_test.cc +++ b/test/common/quic/envoy_quic_utils_test.cc @@ -171,5 +171,26 @@ TEST(EnvoyQuicUtilsTest, deduceSignatureAlgorithmFromNullPublicKey) { EXPECT_EQ("Invalid leaf cert, bad public key", error); } +TEST(EnvoyQuicUtilsTest, ConvertQuicConfig) { + envoy::config::core::v3::QuicProtocolOptions config; + quic::QuicConfig quic_config; + + // Test defaults. + convertQuicConfig(config, quic_config); + EXPECT_EQ(100, quic_config.GetMaxBidirectionalStreamsToSend()); + EXPECT_EQ(100, quic_config.GetMaxUnidirectionalStreamsToSend()); + EXPECT_EQ(16777216, quic_config.GetInitialMaxStreamDataBytesIncomingBidirectionalToSend()); + EXPECT_EQ(25165824, quic_config.GetInitialSessionFlowControlWindowToSend()); + + // Test converting values. + config.mutable_max_concurrent_streams()->set_value(2); + config.mutable_initial_stream_window_size()->set_value(3); + config.mutable_initial_connection_window_size()->set_value(50); + convertQuicConfig(config, quic_config); + EXPECT_EQ(2, quic_config.GetMaxBidirectionalStreamsToSend()); + EXPECT_EQ(2, quic_config.GetMaxUnidirectionalStreamsToSend()); + EXPECT_EQ(3, quic_config.GetInitialMaxStreamDataBytesIncomingBidirectionalToSend()); +} + } // namespace Quic } // namespace Envoy 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 000000000000..aa6352b06b29 --- /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 diff --git a/test/common/quic/test_utils.h b/test/common/quic/test_utils.h index 68ea85e78234..76cde969e24d 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/common/router/BUILD b/test/common/router/BUILD index 596a8a86e7a3..93864dfe0e67 100644 --- a/test/common/router/BUILD +++ b/test/common/router/BUILD @@ -25,6 +25,7 @@ envoy_cc_test_library( srcs = ["config_impl_test.cc"], deps = [ ":route_fuzz_proto_cc_proto", + "//envoy/common:hashable_interface", "//source/common/config:metadata_lib", "//source/common/http:header_map_lib", "//source/common/http:headers_lib", diff --git a/test/common/router/config_impl_speed_test.cc b/test/common/router/config_impl_speed_test.cc index 8361844e8027..8ef1fadf2eed 100644 --- a/test/common/router/config_impl_speed_test.cc +++ b/test/common/router/config_impl_speed_test.cc @@ -69,7 +69,7 @@ static RouteConfiguration genRouteConfig(benchmark::State& state, break; } default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } } diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index 30e4ec1d4fe4..3a4ae6e8a19a 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -6,6 +6,7 @@ #include #include +#include "envoy/common/hashable.h" #include "envoy/config/route/v3/route.pb.h" #include "envoy/config/route/v3/route.pb.validate.h" #include "envoy/config/route/v3/route_components.pb.h" @@ -648,11 +649,13 @@ TEST_F(RouteMatcherTest, TestRoutes) { route: cluster: ats host_rewrite_header: x-rewrite-host + append_x_forwarded_host: true - match: path: "/do-not-rewrite-host-with-header-value" route: cluster: ats host_rewrite_header: x-rewrite-host + append_x_forwarded_host: true - match: path: "/rewrite-host-with-path-regex/envoyproxy.io" route: @@ -662,6 +665,7 @@ TEST_F(RouteMatcherTest, TestRoutes) { google_re2: {} regex: "^/.+/(.+)$" substitution: \1 + append_x_forwarded_host: true - match: prefix: "/" route: @@ -935,6 +939,9 @@ TEST_F(RouteMatcherTest, TestRoutes) { EXPECT_FALSE(route->currentUrlPathAfterRewrite(headers).has_value()); route->finalizeRequestHeaders(headers, stream_info, true); EXPECT_EQ("new_host", headers.get_(Http::Headers::get().Host)); + // Config setting append_x_forwarded_host is false (by default). Expect empty x-forwarded-host + // header value. + EXPECT_EQ("", headers.get_(Http::Headers::get().ForwardedHost)); } // Rewrites host using supplied header. @@ -945,6 +952,7 @@ TEST_F(RouteMatcherTest, TestRoutes) { EXPECT_FALSE(route->currentUrlPathAfterRewrite(headers).has_value()); route->finalizeRequestHeaders(headers, stream_info, true); EXPECT_EQ("rewrote", headers.get_(Http::Headers::get().Host)); + EXPECT_EQ("api.lyft.com", headers.get_(Http::Headers::get().ForwardedHost)); } // Does not rewrite host because of missing header. @@ -955,6 +963,7 @@ TEST_F(RouteMatcherTest, TestRoutes) { EXPECT_FALSE(route->currentUrlPathAfterRewrite(headers).has_value()); route->finalizeRequestHeaders(headers, stream_info, true); EXPECT_EQ("api.lyft.com", headers.get_(Http::Headers::get().Host)); + EXPECT_EQ("", headers.get_(Http::Headers::get().ForwardedHost)); } // Rewrites host using path. @@ -965,6 +974,7 @@ TEST_F(RouteMatcherTest, TestRoutes) { EXPECT_FALSE(route->currentUrlPathAfterRewrite(headers).has_value()); route->finalizeRequestHeaders(headers, stream_info, true); EXPECT_EQ("envoyproxy.io", headers.get_(Http::Headers::get().Host)); + EXPECT_EQ("api.lyft.com", headers.get_(Http::Headers::get().ForwardedHost)); } // Rewrites host using path, removes query parameters @@ -975,6 +985,7 @@ TEST_F(RouteMatcherTest, TestRoutes) { EXPECT_FALSE(route->currentUrlPathAfterRewrite(headers).has_value()); route->finalizeRequestHeaders(headers, stream_info, true); EXPECT_EQ("envoyproxy.io", headers.get_(Http::Headers::get().Host)); + EXPECT_EQ("api.lyft.com", headers.get_(Http::Headers::get().ForwardedHost)); } // Case sensitive rewrite matching test. @@ -2803,7 +2814,7 @@ class RouterMatcherFilterStateHashPolicyTest : public RouterMatcherHashPolicyTes StreamInfo::FilterState::LifeSpan::FilterChain); } class NonHashable : public StreamInfo::FilterState::Object {}; - class HashableObj : public StreamInfo::FilterState::Object, public Http::Hashable { + class HashableObj : public StreamInfo::FilterState::Object, public Hashable { absl::optional hash() const override { return 12345; }; }; diff --git a/test/common/router/header_formatter_test.cc b/test/common/router/header_formatter_test.cc index 02a466020d29..f1cf0960dd3e 100644 --- a/test/common/router/header_formatter_test.cc +++ b/test/common/router/header_formatter_test.cc @@ -106,7 +106,7 @@ TEST_F(StreamInfoHeaderFormatterTest, TestformatWithUpstreamRemoteAddressVariabl testFormatting("UPSTREAM_REMOTE_ADDRESS", "10.0.0.1:443"); NiceMock stream_info; - stream_info.host_.reset(); + stream_info.upstreamInfo()->setUpstreamHost(nullptr); testFormatting(stream_info, "UPSTREAM_REMOTE_ADDRESS", ""); } @@ -141,6 +141,28 @@ 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, TestFormatWithVirtualClusterNameVariable) { + NiceMock stream_info; + // Validate for empty VC + testFormatting(stream_info, "VIRTUAL_CLUSTER_NAME", ""); + + // Validate for a valid VC + const std::string virtual_cluster_name = "authN"; + stream_info.setVirtualClusterName(virtual_cluster_name); + testFormatting(stream_info, "VIRTUAL_CLUSTER_NAME", virtual_cluster_name); +} + TEST_F(StreamInfoHeaderFormatterTest, TestFormatWithDownstreamPeerUriSanVariableSingleSan) { NiceMock stream_info; auto connection_info = std::make_shared>(); @@ -581,7 +603,7 @@ TEST_F(StreamInfoHeaderFormatterTest, TestFormatWithUpstreamMetadataVariable) { EXPECT_EQ(nested_struct.fields().at("list_key").kind_case(), ProtobufWkt::Value::kListValue); EXPECT_EQ(nested_struct.fields().at("struct_key").kind_case(), ProtobufWkt::Value::kStructValue); - ON_CALL(stream_info, upstreamHost()).WillByDefault(Return(host)); + stream_info.upstreamInfo()->setUpstreamHost(host); ON_CALL(*host, metadata()).WillByDefault(Return(metadata)); // Top-level value. @@ -655,7 +677,7 @@ TEST_F(StreamInfoHeaderFormatterTest, ValidateLimitsOnUserDefinedHeaders) { TEST_F(StreamInfoHeaderFormatterTest, TestFormatWithUpstreamMetadataVariableMissingHost) { NiceMock stream_info; std::shared_ptr> host; - ON_CALL(stream_info, upstreamHost()).WillByDefault(Return(host)); + stream_info.upstreamInfo()->setUpstreamHost(host); testFormatting(stream_info, "UPSTREAM_METADATA([\"namespace\", \"key\"])", ""); } @@ -733,11 +755,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 +771,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 +812,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( @@ -831,6 +857,8 @@ 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"}, {}}, + {"%VIRTUAL_CLUSTER_NAME%", {"authN"}, {}}, {"%PER_REQUEST_STATE(testing)%", {"test_value"}, {}}, {"%REQ(x-request-id)%", {"123"}, {}}, {"%START_TIME%", {"2018-04-03T23:06:09.123Z"}, {}}, @@ -868,14 +896,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 " @@ -925,12 +956,16 @@ TEST(HeaderParserTest, TestParseInternal) { }; NiceMock stream_info; + const std::string requested_server_name = "foo.bar"; + stream_info.downstream_connection_info_provider_->setRequestedServerName(requested_server_name); + const std::string virtual_cluster_name = "authN"; + stream_info.setVirtualClusterName(virtual_cluster_name); absl::optional protocol = Envoy::Http::Protocol::Http11; ON_CALL(stream_info, protocol()).WillByDefault(ReturnPointee(&protocol)); std::shared_ptr> host( new NiceMock()); - ON_CALL(stream_info, upstreamHost()).WillByDefault(Return(host)); + stream_info.upstreamInfo()->setUpstreamHost(host); Http::TestRequestHeaderMapImpl request_headers; request_headers.addCopy(Http::LowerCaseString(std::string("x-request-id")), 123); @@ -1106,7 +1141,7 @@ match: { prefix: "/new_endpoint" } new NiceMock()); NiceMock stream_info; auto metadata = std::make_shared(); - ON_CALL(stream_info, upstreamHost()).WillByDefault(Return(host)); + stream_info.upstreamInfo()->setUpstreamHost(host); ON_CALL(*host, metadata()).WillByDefault(Return(metadata)); req_header_parser->evaluateHeaders(header_map, stream_info); EXPECT_FALSE(header_map.has("x-key")); @@ -1181,7 +1216,7 @@ request_headers_to_remove: ["x-nope"] std::shared_ptr> host( new NiceMock()); - ON_CALL(stream_info, upstreamHost()).WillByDefault(Return(host)); + stream_info.upstreamInfo()->setUpstreamHost(host); // Metadata with percent signs in the key. auto metadata = std::make_shared( diff --git a/test/common/router/header_parser_fuzz_test.cc b/test/common/router/header_parser_fuzz_test.cc index 528846cf0691..6a514081ccef 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/router/rds_impl_test.cc b/test/common/router/rds_impl_test.cc index 05aba26966cc..62eb723c1268 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/common/router/router_2_test.cc b/test/common/router/router_2_test.cc index 4d37b31004f2..104f16285a71 100644 --- a/test/common/router/router_2_test.cc +++ b/test/common/router/router_2_test.cc @@ -280,10 +280,6 @@ TEST_F(WatermarkTest, RetryRequestNotComplete) { })); EXPECT_CALL(callbacks_.stream_info_, setResponseFlag(StreamInfo::ResponseFlag::UpstreamRemoteReset)); - EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_)) - .WillRepeatedly(Invoke([&](const Upstream::HostDescriptionConstSharedPtr& host) -> void { - EXPECT_EQ(host_address_, host->address()); - })); Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); @@ -301,7 +297,7 @@ TEST_F(WatermarkTest, RetryRequestNotComplete) { EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectFailed, _)); encoder1.stream_.resetStream(Http::StreamResetReason::RemoteReset); - EXPECT_EQ(callbacks_.details(), "upstream_reset_before_response_started{remote reset}"); + EXPECT_EQ(callbacks_.details(), "upstream_reset_before_response_started{remote_reset}"); } class RouterTestChildSpan : public RouterTestBase { @@ -728,10 +724,6 @@ TEST_F(RouterTestSupressGRPCStatsEnabled, ExcludeTimeoutHttpStats) { upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); - EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_)) - .WillOnce(Invoke([&](const Upstream::HostDescriptionConstSharedPtr host) -> void { - EXPECT_EQ(host_address_, host->address()); - })); expectResponseTimerCreate(); @@ -793,10 +785,6 @@ TEST_F(RouterTestSupressGRPCStatsDisabled, IncludeHttpTimeoutStats) { upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); - EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_)) - .WillOnce(Invoke([&](const Upstream::HostDescriptionConstSharedPtr host) -> void { - EXPECT_EQ(host_address_, host->address()); - })); expectResponseTimerCreate(); diff --git a/test/common/router/router_test.cc b/test/common/router/router_test.cc index ef956636f488..a7c17701573c 100644 --- a/test/common/router/router_test.cc +++ b/test/common/router/router_test.cc @@ -27,6 +27,7 @@ #include "source/common/router/debug_config.h" #include "source/common/router/router.h" #include "source/common/stream_info/uint32_accessor_impl.h" +#include "source/common/stream_info/utility.h" #include "source/common/tracing/http_tracer_impl.h" #include "source/common/upstream/upstream_impl.h" @@ -322,7 +323,7 @@ TEST_F(RouterTest, MissingRequiredHeaders) { callbacks_, sendLocalReply(Http::Code::ServiceUnavailable, testing::Eq("missing required header: :method"), _, _, - "filter_removed_required_request_headers{missing required header: :method}")) + "filter_removed_required_request_headers{missing_required_header:_:method}")) .WillOnce(testing::InvokeWithoutArgs([] {})); router_.decodeHeaders(headers, true); router_.onDestroy(); @@ -361,10 +362,6 @@ TEST_F(RouterTest, PoolFailureWithPriority) { EXPECT_CALL(callbacks_, encodeData(_, true)); EXPECT_CALL(callbacks_.stream_info_, setResponseFlag(StreamInfo::ResponseFlag::UpstreamConnectionFailure)); - EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_)) - .WillOnce(Invoke([&](const Upstream::HostDescriptionConstSharedPtr host) -> void { - EXPECT_EQ(host_address_, host->address()); - })); Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); @@ -374,7 +371,7 @@ TEST_F(RouterTest, PoolFailureWithPriority) { EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_EQ(callbacks_.details(), - "upstream_reset_before_response_started{connection failure,tls version mismatch}"); + "upstream_reset_before_response_started{connection_failure,tls_version_mismatch}"); } TEST_F(RouterTest, PoolFailureDueToConnectTimeout) { @@ -396,10 +393,6 @@ TEST_F(RouterTest, PoolFailureDueToConnectTimeout) { EXPECT_CALL(callbacks_, encodeData(_, true)); EXPECT_CALL(callbacks_.stream_info_, setResponseFlag(StreamInfo::ResponseFlag::UpstreamConnectionFailure)); - EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_)) - .WillOnce(Invoke([&](const Upstream::HostDescriptionConstSharedPtr host) -> void { - EXPECT_EQ(host_address_, host->address()); - })); Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); @@ -409,7 +402,7 @@ TEST_F(RouterTest, PoolFailureDueToConnectTimeout) { EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_EQ(callbacks_.details(), - "upstream_reset_before_response_started{connection failure,connect_timeout}"); + "upstream_reset_before_response_started{connection_failure,connect_timeout}"); } TEST_F(RouterTest, Http1Upstream) { @@ -1006,10 +999,6 @@ TEST_F(RouterTest, EnvoyAttemptCountInResponsePresentWithLocalReply) { EXPECT_CALL(callbacks_, encodeData(_, true)); EXPECT_CALL(callbacks_.stream_info_, setResponseFlag(StreamInfo::ResponseFlag::UpstreamConnectionFailure)); - EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_)) - .WillOnce(Invoke([&](const Upstream::HostDescriptionConstSharedPtr host) -> void { - EXPECT_EQ(host_address_, host->address()); - })); Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); @@ -1018,7 +1007,7 @@ TEST_F(RouterTest, EnvoyAttemptCountInResponsePresentWithLocalReply) { EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); - EXPECT_EQ(callbacks_.details(), "upstream_reset_before_response_started{connection failure}"); + EXPECT_EQ(callbacks_.details(), "upstream_reset_before_response_started{connection_failure}"); EXPECT_EQ(1U, callbacks_.stream_info_.attemptCount().value()); } @@ -1265,10 +1254,6 @@ TEST_F(RouterTest, UpstreamTimeout) { upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); - EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_)) - .WillOnce(Invoke([&](const Upstream::HostDescriptionConstSharedPtr host) -> void { - EXPECT_EQ(host_address_, host->address()); - })); expectResponseTimerCreate(); @@ -1902,10 +1887,6 @@ TEST_F(RouterTest, UpstreamTimeoutWithAltResponse) { upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); - EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_)) - .WillOnce(Invoke([&](const Upstream::HostDescriptionConstSharedPtr host) -> void { - EXPECT_EQ(host_address_, host->address()); - })); expectResponseTimerCreate(); @@ -1974,11 +1955,6 @@ TEST_F(RouterTest, UpstreamPerTryIdleTimeout) { Buffer::OwnedImpl data; router_.decodeData(data, true); - EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_)) - .WillOnce(Invoke([&](const Upstream::HostDescriptionConstSharedPtr host) -> void { - EXPECT_EQ(host_address_, host->address()); - })); - per_try_idle_timeout_ = new Event::MockTimer(&callbacks_.dispatcher_); EXPECT_CALL(*per_try_idle_timeout_, enableTimer(std::chrono::milliseconds(3000), _)); EXPECT_EQ(0U, @@ -2041,11 +2017,6 @@ TEST_F(RouterTest, UpstreamPerTryIdleTimeoutSuccess) { Buffer::OwnedImpl data; router_.decodeData(data, true); - EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_)) - .WillOnce(Invoke([&](const Upstream::HostDescriptionConstSharedPtr host) -> void { - EXPECT_EQ(host_address_, host->address()); - })); - per_try_idle_timeout_ = new Event::MockTimer(&callbacks_.dispatcher_); EXPECT_CALL(*per_try_idle_timeout_, enableTimer(std::chrono::milliseconds(3000), _)); EXPECT_EQ(0U, @@ -2083,11 +2054,6 @@ TEST_F(RouterTest, UpstreamPerTryTimeout) { upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); - EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_)) - .WillOnce(Invoke([&](const Upstream::HostDescriptionConstSharedPtr host) -> void { - EXPECT_EQ(host_address_, host->address()); - })); - Http::TestRequestHeaderMapImpl headers{{"x-envoy-internal", "true"}, {"x-envoy-upstream-rq-per-try-timeout-ms", "5"}}; HttpTestUtility::addDefaultHeaders(headers); @@ -2149,11 +2115,6 @@ TEST_F(RouterTest, UpstreamPerTryTimeoutDelayedPoolReady) { // Per try timeout starts when onPoolReady is called. expectPerTryTimerCreate(); - EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_)) - .WillOnce(Invoke([&](const Upstream::HostDescriptionConstSharedPtr host) -> void { - EXPECT_EQ(host_address_, host->address()); - })); - EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); pool_callbacks->onPoolReady(encoder, cm_.thread_local_cluster_.conn_pool_.host_, @@ -2198,11 +2159,6 @@ TEST_F(RouterTest, UpstreamPerTryTimeoutExcludesNewStream) { response_timeout_ = new Event::MockTimer(&callbacks_.dispatcher_); EXPECT_CALL(*response_timeout_, enableTimer(_, _)); - EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_)) - .WillOnce(Invoke([&](const Upstream::HostDescriptionConstSharedPtr host) -> void { - EXPECT_EQ(host_address_, host->address()); - })); - Http::TestRequestHeaderMapImpl headers{{"x-envoy-internal", "true"}, {"x-envoy-upstream-rq-per-try-timeout-ms", "5"}}; HttpTestUtility::addDefaultHeaders(headers); @@ -2548,7 +2504,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutThirdRequestSucceeds) { response_decoder3->decodeHeaders(std::move(response_headers2), true); EXPECT_TRUE(verifyHostUpstreamStats(1, 1)); - EXPECT_EQ(333U, callbacks_.stream_info_.upstream_connection_id_); + EXPECT_EQ(333U, callbacks_.stream_info_.upstreamInfo()->upstreamConnectionId()); // TODO: Verify hedge stats here once they are implemented. } @@ -3430,10 +3386,6 @@ TEST_F(RouterTest, RetryNoneHealthy) { })); expectResponseTimerCreate(); - EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_)) - .WillOnce(Invoke([&](const Upstream::HostDescriptionConstSharedPtr host) -> void { - EXPECT_EQ(host_address_, host->address()); - })); Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); @@ -3749,7 +3701,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 +3723,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 +3755,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 +3779,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()); @@ -5054,7 +5009,8 @@ TEST_F(RouterTest, PropagatesUpstreamFilterState) { bool filter_state_verified = false; router_.config().upstream_logs_.push_back( std::make_shared([&](const auto& stream_info) { - filter_state_verified = stream_info.upstreamFilterState()->hasDataWithName("upstream data"); + filter_state_verified = + stream_info.upstreamInfo()->upstreamFilterState()->hasDataWithName("upstream data"); })); upstream_stream_info_.filterState()->setData( @@ -5082,7 +5038,8 @@ TEST_F(RouterTest, PropagatesUpstreamFilterState) { EXPECT_TRUE(verifyHostUpstreamStats(1, 0)); EXPECT_TRUE(filter_state_verified); - EXPECT_TRUE(callbacks_.streamInfo().upstreamFilterState()->hasDataWithName("upstream data")); + EXPECT_TRUE(callbacks_.streamInfo().upstreamInfo()->upstreamFilterState()->hasDataWithName( + "upstream data")); } TEST_F(RouterTest, UpstreamSSLConnection) { @@ -5113,12 +5070,14 @@ TEST_F(RouterTest, UpstreamSSLConnection) { Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) response_decoder->decodeHeaders(std::move(response_headers), true); EXPECT_TRUE(verifyHostUpstreamStats(1, 0)); - ASSERT_NE(nullptr, callbacks_.streamInfo().upstreamSslConnection()); - EXPECT_EQ(session_id, callbacks_.streamInfo().upstreamSslConnection()->sessionId()); - EXPECT_FALSE(callbacks_.streamInfo().upstreamConnectionId().has_value()); + ASSERT_NE(nullptr, callbacks_.streamInfo().upstreamInfo()->upstreamSslConnection()); + EXPECT_EQ(session_id, + callbacks_.streamInfo().upstreamInfo()->upstreamSslConnection()->sessionId()); + EXPECT_FALSE(callbacks_.streamInfo().upstreamInfo()->upstreamConnectionId().has_value()); } // Verify that upstream timing information is set into the StreamInfo after the upstream @@ -5139,10 +5098,7 @@ TEST_F(RouterTest, UpstreamTimingSingleRequest) { StreamInfo::StreamInfoImpl stream_info(test_time_.timeSystem(), nullptr); ON_CALL(callbacks_, streamInfo()).WillByDefault(ReturnRef(stream_info)); - EXPECT_FALSE(stream_info.firstUpstreamTxByteSent().has_value()); - EXPECT_FALSE(stream_info.lastUpstreamTxByteSent().has_value()); - EXPECT_FALSE(stream_info.firstUpstreamRxByteReceived().has_value()); - EXPECT_FALSE(stream_info.lastUpstreamRxByteReceived().has_value()); + EXPECT_EQ(nullptr, stream_info.upstreamInfo()); Http::TestRequestHeaderMapImpl headers{}; HttpTestUtility::addDefaultHeaders(headers); @@ -5156,30 +5112,32 @@ TEST_F(RouterTest, UpstreamTimingSingleRequest) { Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) response_decoder->decodeHeaders(std::move(response_headers), false); test_time_.advanceTimeWait(std::chrono::milliseconds(43)); - // Confirm we still have no upstream timing data. It won't be set until after the - // stream has ended. - EXPECT_FALSE(stream_info.firstUpstreamTxByteSent().has_value()); - EXPECT_FALSE(stream_info.lastUpstreamTxByteSent().has_value()); - EXPECT_FALSE(stream_info.firstUpstreamRxByteReceived().has_value()); - EXPECT_FALSE(stream_info.lastUpstreamRxByteReceived().has_value()); + // Upstream timing data is now available live. + ASSERT_NE(nullptr, stream_info.upstreamInfo()); + auto& upstream_timing = stream_info.upstreamInfo()->upstreamTiming(); + EXPECT_TRUE(upstream_timing.first_upstream_tx_byte_sent_.has_value()); + EXPECT_TRUE(upstream_timing.last_upstream_tx_byte_sent_.has_value()); + EXPECT_TRUE(upstream_timing.first_upstream_rx_byte_received_.has_value()); + EXPECT_FALSE(upstream_timing.last_upstream_rx_byte_received_.has_value()); response_decoder->decodeData(data, true); - // Now these should be set. - EXPECT_TRUE(stream_info.firstUpstreamTxByteSent().has_value()); - EXPECT_TRUE(stream_info.lastUpstreamTxByteSent().has_value()); - EXPECT_TRUE(stream_info.firstUpstreamRxByteReceived().has_value()); - EXPECT_TRUE(stream_info.lastUpstreamRxByteReceived().has_value()); + // Now all these should be set. + EXPECT_TRUE(upstream_timing.first_upstream_tx_byte_sent_.has_value()); + EXPECT_TRUE(upstream_timing.last_upstream_tx_byte_sent_.has_value()); + EXPECT_TRUE(upstream_timing.first_upstream_rx_byte_received_.has_value()); + EXPECT_TRUE(upstream_timing.last_upstream_rx_byte_received_.has_value()); // Timings should match our sleep() calls. - EXPECT_EQ(stream_info.lastUpstreamRxByteReceived().value() - - stream_info.firstUpstreamRxByteReceived().value(), + EXPECT_EQ(upstream_timing.last_upstream_rx_byte_received_.value() - + upstream_timing.first_upstream_rx_byte_received_.value(), std::chrono::milliseconds(43)); - EXPECT_EQ(stream_info.lastUpstreamTxByteSent().value() - - stream_info.firstUpstreamTxByteSent().value(), + EXPECT_EQ(upstream_timing.last_upstream_tx_byte_sent_.value() - + upstream_timing.first_upstream_tx_byte_sent_.value(), std::chrono::milliseconds(32)); } @@ -5227,14 +5185,10 @@ TEST_F(RouterTest, UpstreamTimingRetry) { return nullptr; })); - // Check that upstream timing is not set when a retry will occur. Http::ResponseHeaderMapPtr bad_response_headers( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) response_decoder->decodeHeaders(std::move(bad_response_headers), true); - EXPECT_FALSE(stream_info.firstUpstreamTxByteSent().has_value()); - EXPECT_FALSE(stream_info.lastUpstreamTxByteSent().has_value()); - EXPECT_FALSE(stream_info.firstUpstreamRxByteReceived().has_value()); - EXPECT_FALSE(stream_info.lastUpstreamRxByteReceived().has_value()); router_.retry_state_->callback_(); EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _)).WillOnce(Return(RetryStatus::No)); @@ -5248,25 +5202,26 @@ TEST_F(RouterTest, UpstreamTimingRetry) { response_decoder->decodeData(data, true); - EXPECT_TRUE(stream_info.firstUpstreamTxByteSent().has_value()); - EXPECT_TRUE(stream_info.lastUpstreamTxByteSent().has_value()); - EXPECT_TRUE(stream_info.firstUpstreamRxByteReceived().has_value()); - EXPECT_TRUE(stream_info.lastUpstreamRxByteReceived().has_value()); + auto& upstream_timing = stream_info.upstreamInfo()->upstreamTiming(); + EXPECT_TRUE(upstream_timing.first_upstream_tx_byte_sent_.has_value()); + EXPECT_TRUE(upstream_timing.last_upstream_tx_byte_sent_.has_value()); + EXPECT_TRUE(upstream_timing.first_upstream_rx_byte_received_.has_value()); + EXPECT_TRUE(upstream_timing.last_upstream_rx_byte_received_.has_value()); - EXPECT_EQ(stream_info.lastUpstreamRxByteReceived().value() - - stream_info.firstUpstreamRxByteReceived().value(), + EXPECT_EQ(upstream_timing.last_upstream_rx_byte_received_.value() - + upstream_timing.first_upstream_rx_byte_received_.value(), std::chrono::milliseconds(153)); // Time spent in upstream tx is 0 because we're using simulated time and // don't have a good way to insert a "sleep" there, but values being present // and equal to the time the retry was sent is good enough of a test. - EXPECT_EQ(stream_info.lastUpstreamTxByteSent().value() - - stream_info.firstUpstreamTxByteSent().value(), + StreamInfo::TimingUtility timing(stream_info); + EXPECT_EQ(timing.lastUpstreamTxByteSent().value() - timing.firstUpstreamTxByteSent().value(), std::chrono::milliseconds(0)); - EXPECT_EQ(stream_info.lastUpstreamTxByteSent().value() + + EXPECT_EQ(timing.lastUpstreamTxByteSent().value() + stream_info.startTimeMonotonic().time_since_epoch(), retry_time.time_since_epoch()); - EXPECT_EQ(stream_info.firstUpstreamTxByteSent().value() + + EXPECT_EQ(timing.firstUpstreamTxByteSent().value() + stream_info.startTimeMonotonic().time_since_epoch(), retry_time.time_since_epoch()); } @@ -5296,7 +5251,8 @@ TEST_F(RouterTest, UpstreamTimingTimeout) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-rq-timeout-ms", "50"}}; HttpTestUtility::addDefaultHeaders(headers); router_.decodeHeaders(headers, false); - EXPECT_FALSE(stream_info.lastUpstreamRxByteReceived().has_value()); + auto& upstream_timing = stream_info.upstreamInfo()->upstreamTiming(); + EXPECT_FALSE(upstream_timing.last_upstream_rx_byte_received_.has_value()); test_time_.advanceTimeWait(std::chrono::milliseconds(13)); Buffer::OwnedImpl data; @@ -5308,19 +5264,21 @@ TEST_F(RouterTest, UpstreamTimingTimeout) { Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) response_decoder->decodeHeaders(std::move(response_headers), false); test_time_.advanceTimeWait(std::chrono::milliseconds(99)); response_timeout_->invokeCallback(); - EXPECT_TRUE(stream_info.firstUpstreamTxByteSent().has_value()); - EXPECT_TRUE(stream_info.lastUpstreamTxByteSent().has_value()); - EXPECT_TRUE(stream_info.firstUpstreamRxByteReceived().has_value()); - EXPECT_FALSE(stream_info.lastUpstreamRxByteReceived() - .has_value()); // False because no end_stream was seen. - EXPECT_EQ(stream_info.firstUpstreamTxByteSent().value(), std::chrono::milliseconds(10)); - EXPECT_EQ(stream_info.lastUpstreamTxByteSent().value(), std::chrono::milliseconds(23)); - EXPECT_EQ(stream_info.firstUpstreamRxByteReceived().value(), std::chrono::milliseconds(56)); + EXPECT_TRUE(upstream_timing.first_upstream_tx_byte_sent_.has_value()); + EXPECT_TRUE(upstream_timing.last_upstream_tx_byte_sent_.has_value()); + EXPECT_TRUE(upstream_timing.first_upstream_rx_byte_received_.has_value()); + // False because no end_stream was seen. + EXPECT_FALSE(upstream_timing.last_upstream_rx_byte_received_.has_value()); + StreamInfo::TimingUtility timing(stream_info); + EXPECT_EQ(timing.firstUpstreamTxByteSent().value(), std::chrono::milliseconds(10)); + EXPECT_EQ(timing.lastUpstreamTxByteSent().value(), std::chrono::milliseconds(23)); + EXPECT_EQ(timing.firstUpstreamRxByteReceived().value(), std::chrono::milliseconds(56)); } TEST(RouterFilterUtilityTest, FinalHedgingParamsHedgeOnPerTryTimeout) { @@ -5909,6 +5867,9 @@ TEST_F(RouterTest, CanaryStatusTrue) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-alt-stat-name", "alt_stat"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); + const absl::optional virtual_cluster_name = + absl::optional("fake_virtual_cluster"); + EXPECT_CALL(callbacks_.stream_info_, setVirtualClusterName(virtual_cluster_name)); router_.decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -5946,6 +5907,9 @@ TEST_F(RouterTest, CanaryStatusFalse) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-alt-stat-name", "alt_stat"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); + const absl::optional virtual_cluster_name = + absl::optional("fake_virtual_cluster"); + EXPECT_CALL(callbacks_.stream_info_, setVirtualClusterName(virtual_cluster_name)); router_.decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -5974,6 +5938,7 @@ TEST_F(RouterTest, AutoHostRewriteEnabled) { Http::TestRequestHeaderMapImpl outgoing_headers; HttpTestUtility::addDefaultHeaders(outgoing_headers); outgoing_headers.setHost(cm_.thread_local_cluster_.conn_pool_.host_->hostname_); + outgoing_headers.setForwardedHost(req_host); EXPECT_CALL(callbacks_.route_->route_entry_, timeout()) .WillOnce(Return(std::chrono::milliseconds(0))); @@ -5994,11 +5959,8 @@ TEST_F(RouterTest, AutoHostRewriteEnabled) { return Http::okStatus(); })); - EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_)) - .WillOnce(Invoke([&](const Upstream::HostDescriptionConstSharedPtr host) -> void { - EXPECT_EQ(host_address_, host->address()); - })); EXPECT_CALL(callbacks_.route_->route_entry_, autoHostRewrite()).WillOnce(Return(true)); + EXPECT_CALL(callbacks_.route_->route_entry_, appendXfh()).WillOnce(Return(true)); router_.decodeHeaders(incoming_headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -6033,10 +5995,6 @@ TEST_F(RouterTest, AutoHostRewriteDisabled) { return Http::okStatus(); })); - EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_)) - .WillOnce(Invoke([&](const Upstream::HostDescriptionConstSharedPtr host) -> void { - EXPECT_EQ(host_address_, host->address()); - })); EXPECT_CALL(callbacks_.route_->route_entry_, autoHostRewrite()).WillOnce(Return(false)); router_.decodeHeaders(incoming_headers, true); EXPECT_EQ(1U, @@ -6363,5 +6321,277 @@ TEST_F(RouterTest, RequestResponseSize) { testRequestResponseSize(false); } // when there are trailers in both the request/response. TEST_F(RouterTest, RequestResponseSizeWithTrailers) { testRequestResponseSize(true); } +TEST_F(RouterTest, ExpectedUpstreamTimeoutUpdatedDuringRetries) { + auto retry_options_predicate = std::make_shared(); + callbacks_.route_->route_entry_.retry_policy_.retry_options_predicates_.emplace_back( + retry_options_predicate); + + setIncludeAttemptCountInRequest(true); + + NiceMock encoder1; + Http::ResponseDecoder* response_decoder = nullptr; + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) + .WillOnce(Invoke( + [&](Http::ResponseDecoder& decoder, + Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { + response_decoder = &decoder; + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); + return nullptr; + })); + expectResponseTimerCreate(); + + Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, + {"x-envoy-internal", "true"}, + {"x-envoy-upstream-rq-timeout-ms", "200"}}; + HttpTestUtility::addDefaultHeaders(headers); + router_.decodeHeaders(headers, true); + EXPECT_EQ(1U, + callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); + + test_time_.advanceTimeWait(std::chrono::milliseconds(50)); + + // Initial request has 1 attempt. + EXPECT_EQ(1, atoi(std::string(headers.getEnvoyAttemptCountValue()).c_str())); + EXPECT_EQ(200, atoi(std::string(headers.getEnvoyExpectedRequestTimeoutMsValue()).c_str())); + + // 5xx response. + router_.retry_state_->expectHeadersRetry(); + Upstream::RetryOptionsPredicate::UpdateOptionsReturn update_options_return{ + std::make_shared()}; + EXPECT_CALL(*retry_options_predicate, updateOptions(_)).WillOnce(Return(update_options_return)); + Http::ResponseHeaderMapPtr response_headers1( + new Http::TestResponseHeaderMapImpl{{":status", "503"}}); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(503)); + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + response_decoder->decodeHeaders(std::move(response_headers1), true); + EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); + + // Verify retry options predicate return values have been updated. + EXPECT_EQ(update_options_return.new_upstream_socket_options_.value(), + router_.upstreamSocketOptions()); + + // We expect the 5xx response to kick off a new request. + EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); + NiceMock encoder2; + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) + .WillOnce(Invoke( + [&](Http::ResponseDecoder& decoder, + Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { + response_decoder = &decoder; + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); + return nullptr; + })); + router_.retry_state_->callback_(); + EXPECT_EQ(2U, + callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); + + // The retry should cause the header to increase to 2. + EXPECT_EQ(2, atoi(std::string(headers.getEnvoyAttemptCountValue()).c_str())); + // We already used 50ms of our 200ms timeout before the retry was triggered + EXPECT_EQ(150, atoi(std::string(headers.getEnvoyExpectedRequestTimeoutMsValue()).c_str())); + + // Normal response. + EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->health_checker_, setUnhealthy(_)) + .Times(0); + Http::ResponseHeaderMapPtr response_headers2( + new Http::TestResponseHeaderMapImpl{{":status", "200"}}); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); + response_decoder->decodeHeaders(std::move(response_headers2), true); + EXPECT_TRUE(verifyHostUpstreamStats(1, 1)); + EXPECT_EQ(2, callbacks_.stream_info_.attemptCount().value()); +} + +TEST_F(RouterTest, ExpectedUpstreamTimeoutNotUpdatedDuringRetriesWhenRuntimeGuardDisabled) { + TestScopedRuntime scoped_runtime; + Runtime::LoaderSingleton::getExisting()->mergeValues( + {{"envoy.reloadable_features.update_expected_rq_timeout_on_retry", "false"}}); + + auto retry_options_predicate = std::make_shared(); + callbacks_.route_->route_entry_.retry_policy_.retry_options_predicates_.emplace_back( + retry_options_predicate); + + setIncludeAttemptCountInRequest(true); + + NiceMock encoder1; + Http::ResponseDecoder* response_decoder = nullptr; + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) + .WillOnce(Invoke( + [&](Http::ResponseDecoder& decoder, + Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { + response_decoder = &decoder; + callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); + return nullptr; + })); + expectResponseTimerCreate(); + + Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, + {"x-envoy-internal", "true"}, + {"x-envoy-upstream-rq-timeout-ms", "200"}}; + HttpTestUtility::addDefaultHeaders(headers); + router_.decodeHeaders(headers, true); + EXPECT_EQ(1U, + callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); + + test_time_.advanceTimeWait(std::chrono::milliseconds(50)); + + // Initial request has 1 attempt. + EXPECT_EQ(1, atoi(std::string(headers.getEnvoyAttemptCountValue()).c_str())); + EXPECT_EQ(200, atoi(std::string(headers.getEnvoyExpectedRequestTimeoutMsValue()).c_str())); + + // 5xx response. + router_.retry_state_->expectHeadersRetry(); + Upstream::RetryOptionsPredicate::UpdateOptionsReturn update_options_return{ + std::make_shared()}; + EXPECT_CALL(*retry_options_predicate, updateOptions(_)).WillOnce(Return(update_options_return)); + Http::ResponseHeaderMapPtr response_headers1( + new Http::TestResponseHeaderMapImpl{{":status", "503"}}); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(503)); + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + response_decoder->decodeHeaders(std::move(response_headers1), true); + EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); + + // Verify retry options predicate return values have been updated. + EXPECT_EQ(update_options_return.new_upstream_socket_options_.value(), + router_.upstreamSocketOptions()); + + // We expect the 5xx response to kick off a new request. + EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); + NiceMock encoder2; + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _)) + .WillOnce(Invoke( + [&](Http::ResponseDecoder& decoder, + Http::ConnectionPool::Callbacks& callbacks) -> Http::ConnectionPool::Cancellable* { + response_decoder = &decoder; + callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, + upstream_stream_info_, Http::Protocol::Http10); + return nullptr; + })); + router_.retry_state_->callback_(); + EXPECT_EQ(2U, + callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); + + // The retry should cause the header to increase to 2. + EXPECT_EQ(2, atoi(std::string(headers.getEnvoyAttemptCountValue()).c_str())); + // We already used 50ms of our 200ms timeout before the retry was triggered, + // but with the guard disabled this should not change the header. + EXPECT_EQ(200, atoi(std::string(headers.getEnvoyExpectedRequestTimeoutMsValue()).c_str())); + + // Normal response. + EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->health_checker_, setUnhealthy(_)) + .Times(0); + Http::ResponseHeaderMapPtr response_headers2( + new Http::TestResponseHeaderMapImpl{{":status", "200"}}); + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, + putHttpResponseCode(200)); + response_decoder->decodeHeaders(std::move(response_headers2), true); + EXPECT_TRUE(verifyHostUpstreamStats(1, 1)); + EXPECT_EQ(2, callbacks_.stream_info_.attemptCount().value()); +} + +TEST(RouterFilterUtilityTest, SetTimeoutHeaders) { + { + NiceMock route; + Http::TestRequestHeaderMapImpl headers; + FilterUtility::TimeoutData timeout; + timeout.global_timeout_ = std::chrono::milliseconds(200); + timeout.per_try_timeout_ = std::chrono::milliseconds(0); + + FilterUtility::setTimeoutHeaders(0, timeout, route, headers, true, false, false); + EXPECT_EQ("200", + headers.get_( + "x-envoy-expected-rq-timeout-ms")); // No per try configured, use global timeout + } + { + NiceMock route; + Http::TestRequestHeaderMapImpl headers; + FilterUtility::TimeoutData timeout; + timeout.global_timeout_ = std::chrono::milliseconds(200); + timeout.per_try_timeout_ = std::chrono::milliseconds(0); + + FilterUtility::setTimeoutHeaders(150, timeout, route, headers, true, false, false); + EXPECT_EQ("50", headers.get_("x-envoy-expected-rq-timeout-ms")); // Remains of global timeout + } + { + NiceMock route; + Http::TestRequestHeaderMapImpl headers; + FilterUtility::TimeoutData timeout; + timeout.global_timeout_ = std::chrono::milliseconds(200); + timeout.per_try_timeout_ = std::chrono::milliseconds(150); + + FilterUtility::setTimeoutHeaders(0, timeout, route, headers, true, false, false); + EXPECT_EQ("150", headers.get_("x-envoy-expected-rq-timeout-ms")); // Per try timeout + } + { + NiceMock route; + Http::TestRequestHeaderMapImpl headers; + FilterUtility::TimeoutData timeout; + timeout.global_timeout_ = std::chrono::milliseconds(200); + timeout.per_try_timeout_ = std::chrono::milliseconds(150); + + FilterUtility::setTimeoutHeaders(25, timeout, route, headers, true, false, false); + EXPECT_EQ("150", headers.get_("x-envoy-expected-rq-timeout-ms")); // Per try timeout + } + { + NiceMock route; + Http::TestRequestHeaderMapImpl headers; + FilterUtility::TimeoutData timeout; + timeout.global_timeout_ = std::chrono::milliseconds(200); + timeout.per_try_timeout_ = std::chrono::milliseconds(150); + + FilterUtility::setTimeoutHeaders(150, timeout, route, headers, true, false, false); + EXPECT_EQ("50", headers.get_("x-envoy-expected-rq-timeout-ms")); // Remains of global timeout + } + { + NiceMock route; + Http::TestRequestHeaderMapImpl headers; + FilterUtility::TimeoutData timeout; + timeout.global_timeout_ = std::chrono::milliseconds(200); + timeout.per_try_timeout_ = std::chrono::milliseconds(0); + + FilterUtility::setTimeoutHeaders(300, timeout, route, headers, true, false, false); + EXPECT_EQ("1", headers.get_("x-envoy-expected-rq-timeout-ms")); // Over time + } + { + NiceMock route; + Http::TestRequestHeaderMapImpl headers; + FilterUtility::TimeoutData timeout; + timeout.global_timeout_ = std::chrono::milliseconds(200); + timeout.per_try_timeout_ = std::chrono::milliseconds(150); + + FilterUtility::setTimeoutHeaders(0, timeout, route, headers, true, false, true); + EXPECT_EQ("200", headers.get_("x-envoy-expected-rq-timeout-ms")); // Global timeout as hedged + } + { + NiceMock route; + Http::TestRequestHeaderMapImpl headers; + FilterUtility::TimeoutData timeout; + timeout.global_timeout_ = std::chrono::milliseconds(200); + timeout.per_try_timeout_ = std::chrono::milliseconds(150); + + FilterUtility::setTimeoutHeaders(25, timeout, route, headers, true, false, true); + EXPECT_EQ("175", headers.get_( + "x-envoy-expected-rq-timeout-ms")); // Remains of global timeout as hedged + } + { + NiceMock route; + Http::TestRequestHeaderMapImpl headers; + FilterUtility::TimeoutData timeout; + timeout.global_timeout_ = std::chrono::milliseconds(200); + timeout.per_try_timeout_ = std::chrono::milliseconds(150); + + FilterUtility::setTimeoutHeaders(150, timeout, route, headers, true, false, true); + EXPECT_EQ("50", headers.get_( + "x-envoy-expected-rq-timeout-ms")); // Remains of global timeout as hedged + } +} + } // namespace Router } // namespace Envoy diff --git a/test/common/secret/sds_api_test.cc b/test/common/secret/sds_api_test.cc index a0cea30a2f8a..f91734a588b9 100644 --- a/test/common/secret/sds_api_test.cc +++ b/test/common/secret/sds_api_test.cc @@ -665,7 +665,10 @@ TEST_F(SdsApiTest, DefaultCertificateValidationContextTest) { dynamic_cvc->set_allow_expired_certificate(false); dynamic_cvc->mutable_trusted_ca()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem")); - dynamic_cvc->add_match_subject_alt_names()->set_exact("second san"); + auto* san_matcher = dynamic_cvc->add_match_typed_subject_alt_names(); + san_matcher->mutable_matcher()->set_exact("second san"); + san_matcher->set_san_type( + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::DNS); const std::string dynamic_verify_certificate_spki = "QGJRPdmx/r5EGOFLb2MTiZp2isyC0Whht7iazhzXaCM="; dynamic_cvc->add_verify_certificate_spki(dynamic_verify_certificate_spki); @@ -681,7 +684,10 @@ TEST_F(SdsApiTest, DefaultCertificateValidationContextTest) { envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext default_cvc; default_cvc.set_allow_expired_certificate(true); default_cvc.mutable_trusted_ca()->set_inline_bytes("fake trusted ca"); - default_cvc.add_match_subject_alt_names()->set_exact("first san"); + san_matcher = default_cvc.add_match_typed_subject_alt_names(); + san_matcher->mutable_matcher()->set_exact("first san"); + san_matcher->set_san_type( + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::DNS); default_cvc.add_verify_certificate_hash(default_verify_certificate_hash); envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext merged_cvc = default_cvc; @@ -697,8 +703,12 @@ TEST_F(SdsApiTest, DefaultCertificateValidationContextTest) { cvc_config.caCert()); // Verify that repeated fields are concatenated. EXPECT_EQ(2, cvc_config.subjectAltNameMatchers().size()); - EXPECT_EQ("first san", cvc_config.subjectAltNameMatchers()[0].exact()); - EXPECT_EQ("second san", cvc_config.subjectAltNameMatchers()[1].exact()); + EXPECT_EQ("first san", cvc_config.subjectAltNameMatchers()[0].matcher().exact()); + EXPECT_EQ(envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::DNS, + cvc_config.subjectAltNameMatchers()[0].san_type()); + EXPECT_EQ("second san", cvc_config.subjectAltNameMatchers()[1].matcher().exact()); + EXPECT_EQ(envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::DNS, + cvc_config.subjectAltNameMatchers()[1].san_type()); // Verify that if dynamic CertificateValidationContext does not set certificate hash list, the new // secret contains hash list from default CertificateValidationContext. EXPECT_EQ(1, cvc_config.verifyCertificateHashList().size()); diff --git a/test/common/secret/secret_manager_impl_test.cc b/test/common/secret/secret_manager_impl_test.cc index a73f143ba861..1109b7621256 100644 --- a/test/common/secret/secret_manager_impl_test.cc +++ b/test/common/secret/secret_manager_impl_test.cc @@ -1128,6 +1128,43 @@ name: "abc.com" EnvoyException, "Failed to load private key provider: test"); } +// Verify that using the match_subject_alt_names will result in a typed matcher, one for each of +// DNS, URI, EMAIL and IP_ADDRESS. +// TODO(pradeepcrao): Delete this test once the deprecated field is removed. +TEST_F(SecretManagerImplTest, DeprecatedSanMatcher) { + envoy::extensions::transport_sockets::tls::v3::Secret secret_config; + const std::string yaml = + R"EOF( + name: "abc.com" + validation_context: + trusted_ca: { filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" } + allow_expired_certificate: true + match_subject_alt_names: + exact: "example.foo" + )EOF"; + TestUtility::loadFromYaml(TestEnvironment::substitute(yaml), secret_config); + std::unique_ptr secret_manager(new SecretManagerImpl(config_tracker_)); + secret_manager->addStaticSecret(secret_config); + + ASSERT_EQ(secret_manager->findStaticCertificateValidationContextProvider("undefined"), nullptr); + ASSERT_NE(secret_manager->findStaticCertificateValidationContextProvider("abc.com"), nullptr); + Ssl::CertificateValidationContextConfigImpl cvc_config( + *secret_manager->findStaticCertificateValidationContextProvider("abc.com")->secret(), *api_); + EXPECT_EQ(cvc_config.subjectAltNameMatchers().size(), 4); + EXPECT_EQ("example.foo", cvc_config.subjectAltNameMatchers()[0].matcher().exact()); + EXPECT_EQ(envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::DNS, + cvc_config.subjectAltNameMatchers()[0].san_type()); + EXPECT_EQ("example.foo", cvc_config.subjectAltNameMatchers()[1].matcher().exact()); + EXPECT_EQ(envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::URI, + cvc_config.subjectAltNameMatchers()[1].san_type()); + EXPECT_EQ("example.foo", cvc_config.subjectAltNameMatchers()[2].matcher().exact()); + EXPECT_EQ(envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::EMAIL, + cvc_config.subjectAltNameMatchers()[2].san_type()); + EXPECT_EQ("example.foo", cvc_config.subjectAltNameMatchers()[3].matcher().exact()); + EXPECT_EQ(envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::IP_ADDRESS, + cvc_config.subjectAltNameMatchers()[3].san_type()); +} + } // namespace } // namespace Secret } // namespace Envoy diff --git a/test/common/stats/allocator_impl_test.cc b/test/common/stats/allocator_impl_test.cc index cc06acedaef1..a4c0eb2d271b 100644 --- a/test/common/stats/allocator_impl_test.cc +++ b/test/common/stats/allocator_impl_test.cc @@ -1,6 +1,9 @@ #include +#include #include +#include "envoy/stats/sink.h" + #include "source/common/stats/allocator_impl.h" #include "test/test_common/logging.h" @@ -41,6 +44,26 @@ class AllocatorImplTest : public testing::Test { bool are_stats_marked_for_deletion_ = false; }; +class TestSinkPredicates : public SinkPredicates { +public: + ~TestSinkPredicates() override = default; + StatNameHashSet& sinkedStatNames() { return sinked_stat_names_; } + + // SinkPredicates + bool includeCounter(const Counter& counter) override { + return sinked_stat_names_.find(counter.statName()) != sinked_stat_names_.end(); + } + bool includeGauge(const Gauge& gauge) override { + return sinked_stat_names_.find(gauge.statName()) != sinked_stat_names_.end(); + } + bool includeTextReadout(const TextReadout& text_readout) override { + return sinked_stat_names_.find(text_readout.statName()) != sinked_stat_names_.end(); + } + +private: + StatNameHashSet sinked_stat_names_; +}; + // Allocate 2 counters of the same name, and you'll get the same object. TEST_F(AllocatorImplTest, CountersWithSameName) { StatName counter_name = makeStat("counter.name"); @@ -309,7 +332,7 @@ TEST_F(AllocatorImplTest, ForEachWithNullSizeLambda) { } size_t num_iterations = 0; alloc_.forEachCounter(nullptr, [&num_iterations](Stats::Counter& counter) { - (void)counter; + UNREFERENCED_PARAMETER(counter); ++num_iterations; }); EXPECT_EQ(num_iterations, num_stats); @@ -321,7 +344,7 @@ TEST_F(AllocatorImplTest, ForEachWithNullSizeLambda) { } num_iterations = 0; alloc_.forEachGauge(nullptr, [&num_iterations](Stats::Gauge& gauge) { - (void)gauge; + UNREFERENCED_PARAMETER(gauge); ++num_iterations; }); EXPECT_EQ(num_iterations, num_stats); @@ -333,7 +356,7 @@ TEST_F(AllocatorImplTest, ForEachWithNullSizeLambda) { } num_iterations = 0; alloc_.forEachTextReadout(nullptr, [&num_iterations](Stats::TextReadout& text_readout) { - (void)text_readout; + UNREFERENCED_PARAMETER(text_readout); ++num_iterations; }); EXPECT_EQ(num_iterations, num_stats); @@ -410,6 +433,143 @@ TEST_F(AllocatorImplTest, AskForDeletedStat) { EXPECT_EQ(rejected_text_readout.value(), "deleted value"); } +TEST_F(AllocatorImplTest, ForEachSinkedCounter) { + std::unique_ptr moved_sink_predicates = + std::make_unique(); + TestSinkPredicates* sink_predicates = moved_sink_predicates.get(); + std::vector sinked_counters; + std::vector unsinked_counters; + + alloc_.setSinkPredicates(std::move(moved_sink_predicates)); + + const size_t num_stats = 11; + + for (size_t idx = 0; idx < num_stats; ++idx) { + auto stat_name = makeStat(absl::StrCat("counter.", idx)); + // sink every 3rd stat + if ((idx + 1) % 3 == 0) { + sink_predicates->sinkedStatNames().insert(stat_name); + sinked_counters.emplace_back(alloc_.makeCounter(stat_name, StatName(), {})); + } else { + unsinked_counters.emplace_back(alloc_.makeCounter(stat_name, StatName(), {})); + } + } + + EXPECT_EQ(sinked_counters.size(), 3); + EXPECT_EQ(unsinked_counters.size(), 8); + + size_t num_sinked_counters = 0; + size_t num_iterations = 0; + alloc_.forEachSinkedCounter( + [&num_sinked_counters](std::size_t size) { num_sinked_counters = size; }, + [&num_iterations, sink_predicates](Stats::Counter& counter) { + EXPECT_EQ(sink_predicates->sinkedStatNames().count(counter.statName()), 1); + ++num_iterations; + }); + EXPECT_EQ(num_sinked_counters, 3); + EXPECT_EQ(num_iterations, 3); + + // Erase all sinked stats. + sinked_counters.clear(); + num_iterations = 0; + alloc_.forEachSinkedCounter( + [&num_sinked_counters](std::size_t size) { num_sinked_counters = size; }, + [&num_iterations](Stats::Counter&) { ++num_iterations; }); + EXPECT_EQ(num_sinked_counters, 0); + EXPECT_EQ(num_iterations, 0); +} + +TEST_F(AllocatorImplTest, ForEachSinkedGauge) { + std::unique_ptr moved_sink_predicates = + std::make_unique(); + TestSinkPredicates* sink_predicates = moved_sink_predicates.get(); + std::vector sinked_gauges; + std::vector unsinked_gauges; + + alloc_.setSinkPredicates(std::move(moved_sink_predicates)); + const size_t num_stats = 11; + + for (size_t idx = 0; idx < num_stats; ++idx) { + auto stat_name = makeStat(absl::StrCat("gauge.", idx)); + // sink every 5th stat + if ((idx + 1) % 5 == 0) { + sink_predicates->sinkedStatNames().insert(stat_name); + sinked_gauges.emplace_back( + alloc_.makeGauge(stat_name, StatName(), {}, Gauge::ImportMode::Accumulate)); + } else { + unsinked_gauges.emplace_back( + alloc_.makeGauge(stat_name, StatName(), {}, Gauge::ImportMode::Accumulate)); + } + } + + EXPECT_EQ(sinked_gauges.size(), 2); + EXPECT_EQ(unsinked_gauges.size(), 9); + + size_t num_sinked_gauges = 0; + size_t num_iterations = 0; + alloc_.forEachSinkedGauge([&num_sinked_gauges](std::size_t size) { num_sinked_gauges = size; }, + [&num_iterations, sink_predicates](Stats::Gauge& gauge) { + EXPECT_EQ(sink_predicates->sinkedStatNames().count(gauge.statName()), + 1); + ++num_iterations; + }); + EXPECT_EQ(num_sinked_gauges, 2); + EXPECT_EQ(num_iterations, 2); + + // Erase all sinked stats. + sinked_gauges.clear(); + num_iterations = 0; + alloc_.forEachSinkedGauge([&num_sinked_gauges](std::size_t size) { num_sinked_gauges = size; }, + [&num_iterations](Stats::Gauge&) { ++num_iterations; }); + EXPECT_EQ(num_sinked_gauges, 0); + EXPECT_EQ(num_iterations, 0); +} + +TEST_F(AllocatorImplTest, ForEachSinkedTextReadout) { + std::unique_ptr moved_sink_predicates = + std::make_unique(); + TestSinkPredicates* sink_predicates = moved_sink_predicates.get(); + std::vector sinked_text_readouts; + std::vector unsinked_text_readouts; + + alloc_.setSinkPredicates(std::move(moved_sink_predicates)); + const size_t num_stats = 11; + + for (size_t idx = 0; idx < num_stats; ++idx) { + auto stat_name = makeStat(absl::StrCat("text_readout.", idx)); + // sink every 2nd stat + if ((idx + 1) % 2 == 0) { + sink_predicates->sinkedStatNames().insert(stat_name); + sinked_text_readouts.emplace_back(alloc_.makeTextReadout(stat_name, StatName(), {})); + } else { + unsinked_text_readouts.emplace_back(alloc_.makeTextReadout(stat_name, StatName(), {})); + } + } + + EXPECT_EQ(sinked_text_readouts.size(), 5); + EXPECT_EQ(unsinked_text_readouts.size(), 6); + + size_t num_sinked_text_readouts = 0; + size_t num_iterations = 0; + alloc_.forEachSinkedTextReadout( + [&num_sinked_text_readouts](std::size_t size) { num_sinked_text_readouts = size; }, + [&num_iterations, sink_predicates](Stats::TextReadout& text_readout) { + EXPECT_EQ(sink_predicates->sinkedStatNames().count(text_readout.statName()), 1); + ++num_iterations; + }); + EXPECT_EQ(num_sinked_text_readouts, 5); + EXPECT_EQ(num_iterations, 5); + + // Erase all sinked stats. + sinked_text_readouts.clear(); + num_iterations = 0; + alloc_.forEachSinkedTextReadout( + [&num_sinked_text_readouts](std::size_t size) { num_sinked_text_readouts = size; }, + [&num_iterations](Stats::TextReadout&) { ++num_iterations; }); + EXPECT_EQ(num_sinked_text_readouts, 0); + EXPECT_EQ(num_iterations, 0); +} + } // namespace } // namespace Stats } // namespace Envoy diff --git a/test/common/stats/stat_test_utility.h b/test/common/stats/stat_test_utility.h index ea9e5b15c377..875dcf3b5a2f 100644 --- a/test/common/stats/stat_test_utility.h +++ b/test/common/stats/stat_test_utility.h @@ -155,10 +155,25 @@ class TestStore : public SymbolTableProvider, public IsolatedStoreImpl { GaugeOptConstRef findGaugeByString(const std::string& name) const; HistogramOptConstRef findHistogramByString(const std::string& name) const; + void deliverHistogramToSinks(const Histogram& histogram, uint64_t value) override { + histogram_values_map_[histogram.name()].push_back(value); + } + + std::vector histogramValues(const std::string& name, bool clear) { + auto it = histogram_values_map_.find(name); + ASSERT(it != histogram_values_map_.end(), absl::StrCat("Couldn't find histogram ", name)); + std::vector copy = it->second; + if (clear) { + it->second.clear(); + } + return copy; + } + private: absl::flat_hash_map counter_map_; absl::flat_hash_map gauge_map_; absl::flat_hash_map histogram_map_; + absl::flat_hash_map> histogram_values_map_; }; // Compares the memory consumed against an exact expected value, but only on diff --git a/test/common/stats/tag_producer_impl_test.cc b/test/common/stats/tag_producer_impl_test.cc index db7be62da02f..c00074c9b58a 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/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index 0847d9605df2..a5a59f960c78 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -1,9 +1,11 @@ #include +#include #include #include #include "envoy/config/metrics/v3/stats.pb.h" #include "envoy/stats/histogram.h" +#include "envoy/stats/sink.h" #include "source/common/common/c_smart_ptr.h" #include "source/common/event/dispatcher_impl.h" @@ -1564,6 +1566,7 @@ TEST_F(HistogramTest, ParentHistogramBucketSummary) { "B3.6e+06(1,1)", parent_histogram->bucketSummary()); } + class ThreadLocalRealThreadsTestBase : public Thread::RealThreadsTestHelper, public ThreadLocalStoreNoMocksTestBase { protected: @@ -1801,7 +1804,7 @@ TEST_F(HistogramThreadTest, ScopeOverlap) { 2 * NumThreads, ") "))); // Now clear everything, and synchronize the system by calling mergeHistograms(). - // THere should be no more ParentHistograms or TlsHistograms. + // There should be no more ParentHistograms or TlsHistograms. scope2.reset(); histograms.clear(); mergeHistograms(); diff --git a/test/common/stream_info/stream_info_impl_test.cc b/test/common/stream_info/stream_info_impl_test.cc index cfe761f58f3a..2d6c65f70ae7 100644 --- a/test/common/stream_info/stream_info_impl_test.cc +++ b/test/common/stream_info/stream_info_impl_test.cc @@ -8,6 +8,7 @@ #include "source/common/common/fmt.h" #include "source/common/protobuf/utility.h" #include "source/common/stream_info/stream_info_impl.h" +#include "source/common/stream_info/utility.h" #include "test/common/stream_info/test_int_accessor.h" #include "test/mocks/router/mocks.h" @@ -39,7 +40,8 @@ class StreamInfoImplTest : public testing::Test { TEST_F(StreamInfoImplTest, TimingTest) { MonotonicTime pre_start = test_time_.timeSystem().monotonicTime(); StreamInfoImpl info(Http::Protocol::Http2, test_time_.timeSystem(), nullptr); - Envoy::StreamInfo::UpstreamTiming upstream_timing; + info.setUpstreamInfo(std::make_shared()); + UpstreamTiming& upstream_timing = info.upstreamInfo()->upstreamTiming(); MonotonicTime post_start = test_time_.timeSystem().monotonicTime(); const MonotonicTime& start = info.startTimeMonotonic(); @@ -47,38 +49,35 @@ TEST_F(StreamInfoImplTest, TimingTest) { EXPECT_LE(pre_start, start) << "Start time was lower than expected"; EXPECT_GE(post_start, start) << "Start time was higher than expected"; - EXPECT_FALSE(info.lastDownstreamRxByteReceived()); - info.onLastDownstreamRxByteReceived(); + TimingUtility timing(info); + EXPECT_FALSE(timing.lastDownstreamRxByteReceived()); + info.downstreamTiming().onLastDownstreamRxByteReceived(test_time_.timeSystem()); std::chrono::nanoseconds dur = - checkDuration(std::chrono::nanoseconds{0}, info.lastDownstreamRxByteReceived()); + checkDuration(std::chrono::nanoseconds{0}, timing.lastDownstreamRxByteReceived()); - EXPECT_FALSE(info.firstUpstreamTxByteSent()); + EXPECT_FALSE(timing.firstUpstreamTxByteSent()); upstream_timing.onFirstUpstreamTxByteSent(test_time_.timeSystem()); - info.setUpstreamTiming(upstream_timing); - dur = checkDuration(dur, info.firstUpstreamTxByteSent()); + dur = checkDuration(dur, timing.firstUpstreamTxByteSent()); - EXPECT_FALSE(info.lastUpstreamTxByteSent()); + EXPECT_FALSE(timing.lastUpstreamTxByteSent()); upstream_timing.onLastUpstreamTxByteSent(test_time_.timeSystem()); - info.setUpstreamTiming(upstream_timing); - dur = checkDuration(dur, info.lastUpstreamTxByteSent()); + dur = checkDuration(dur, timing.lastUpstreamTxByteSent()); - EXPECT_FALSE(info.firstUpstreamRxByteReceived()); + EXPECT_FALSE(timing.firstUpstreamRxByteReceived()); upstream_timing.onFirstUpstreamRxByteReceived(test_time_.timeSystem()); - info.setUpstreamTiming(upstream_timing); - dur = checkDuration(dur, info.firstUpstreamRxByteReceived()); + dur = checkDuration(dur, timing.firstUpstreamRxByteReceived()); - EXPECT_FALSE(info.lastUpstreamRxByteReceived()); + EXPECT_FALSE(timing.lastUpstreamRxByteReceived()); upstream_timing.onLastUpstreamRxByteReceived(test_time_.timeSystem()); - info.setUpstreamTiming(upstream_timing); - dur = checkDuration(dur, info.lastUpstreamRxByteReceived()); + dur = checkDuration(dur, timing.lastUpstreamRxByteReceived()); - EXPECT_FALSE(info.firstDownstreamTxByteSent()); - info.onFirstDownstreamTxByteSent(); - dur = checkDuration(dur, info.firstDownstreamTxByteSent()); + EXPECT_FALSE(timing.firstDownstreamTxByteSent()); + info.downstreamTiming().onFirstDownstreamTxByteSent(test_time_.timeSystem()); + dur = checkDuration(dur, timing.firstDownstreamTxByteSent()); - EXPECT_FALSE(info.lastDownstreamTxByteSent()); - info.onLastDownstreamTxByteSent(); - dur = checkDuration(dur, info.lastDownstreamTxByteSent()); + EXPECT_FALSE(timing.lastDownstreamTxByteSent()); + info.downstreamTiming().onLastDownstreamTxByteSent(test_time_.timeSystem()); + dur = checkDuration(dur, timing.lastDownstreamTxByteSent()); EXPECT_FALSE(info.requestComplete()); info.onRequestComplete(); @@ -137,7 +136,9 @@ TEST_F(StreamInfoImplTest, MiscSettersAndGetters) { { StreamInfoImpl stream_info(Http::Protocol::Http2, test_time_.timeSystem(), nullptr); + EXPECT_EQ(nullptr, stream_info.upstreamInfo()); EXPECT_EQ(Http::Protocol::Http2, stream_info.protocol().value()); + stream_info.setUpstreamInfo(std::make_shared()); stream_info.protocol(Http::Protocol::Http10); EXPECT_EQ(Http::Protocol::Http10, stream_info.protocol().value()); @@ -147,6 +148,11 @@ TEST_F(StreamInfoImplTest, MiscSettersAndGetters) { ASSERT_TRUE(stream_info.responseCode()); EXPECT_EQ(200, stream_info.responseCode().value()); + EXPECT_FALSE(stream_info.attemptCount().has_value()); + stream_info.setAttemptCount(93); + ASSERT_TRUE(stream_info.attemptCount().has_value()); + EXPECT_EQ(stream_info.attemptCount().value(), 93); + EXPECT_FALSE(stream_info.responseCodeDetails().has_value()); stream_info.setResponseCodeDetails(ResponseCodeDetails::get().ViaUpstream); ASSERT_TRUE(stream_info.responseCodeDetails().has_value()); @@ -157,10 +163,10 @@ TEST_F(StreamInfoImplTest, MiscSettersAndGetters) { ASSERT_TRUE(stream_info.connectionTerminationDetails().has_value()); EXPECT_EQ("access_denied", stream_info.connectionTerminationDetails().value()); - EXPECT_EQ(nullptr, stream_info.upstreamHost()); + EXPECT_EQ(nullptr, stream_info.upstreamInfo()->upstreamHost()); Upstream::HostDescriptionConstSharedPtr host(new NiceMock()); - stream_info.onUpstreamHostSelected(host); - EXPECT_EQ(host, stream_info.upstreamHost()); + stream_info.upstreamInfo()->setUpstreamHost(host); + EXPECT_EQ(host, stream_info.upstreamInfo()->upstreamHost()); EXPECT_FALSE(stream_info.healthCheck()); stream_info.healthCheck(true); @@ -177,9 +183,11 @@ TEST_F(StreamInfoImplTest, MiscSettersAndGetters) { FilterState::LifeSpan::FilterChain); EXPECT_EQ(1, stream_info.filterState()->getDataReadOnly("test").access()); - stream_info.setUpstreamFilterState(stream_info.filterState()); - EXPECT_EQ(1, - stream_info.upstreamFilterState()->getDataReadOnly("test").access()); + stream_info.upstreamInfo()->setUpstreamFilterState(stream_info.filterState()); + EXPECT_EQ(1, stream_info.upstreamInfo() + ->upstreamFilterState() + ->getDataReadOnly("test") + .access()); EXPECT_EQ(absl::nullopt, stream_info.upstreamClusterInfo()); Upstream::ClusterInfoConstSharedPtr cluster_info(new NiceMock()); @@ -191,13 +199,18 @@ TEST_F(StreamInfoImplTest, MiscSettersAndGetters) { "D62A523A65695219D46FE1FFE285A4C371425ACE421B110B5B8D11D3EB4D5F0B"; auto ssl_info = std::make_shared(); EXPECT_CALL(*ssl_info, sessionId()).WillRepeatedly(testing::ReturnRef(session_id)); - stream_info.setUpstreamSslConnection(ssl_info); - EXPECT_EQ(session_id, stream_info.upstreamSslConnection()->sessionId()); - - EXPECT_FALSE(stream_info.upstreamConnectionId().has_value()); - stream_info.setUpstreamConnectionId(12345); - ASSERT_TRUE(stream_info.upstreamConnectionId().has_value()); - EXPECT_EQ(12345, stream_info.upstreamConnectionId().value()); + stream_info.upstreamInfo()->setUpstreamSslConnection(ssl_info); + EXPECT_EQ(session_id, stream_info.upstreamInfo()->upstreamSslConnection()->sessionId()); + + EXPECT_FALSE(stream_info.upstreamInfo()->upstreamConnectionId().has_value()); + stream_info.upstreamInfo()->setUpstreamConnectionId(12345); + ASSERT_TRUE(stream_info.upstreamInfo()->upstreamConnectionId().has_value()); + EXPECT_EQ(12345, stream_info.upstreamInfo()->upstreamConnectionId().value()); + + std::shared_ptr new_info = std::make_shared(); + EXPECT_NE(stream_info.upstreamInfo(), new_info); + stream_info.setUpstreamInfo(new_info); + EXPECT_EQ(stream_info.upstreamInfo(), new_info); } } @@ -261,11 +274,29 @@ TEST_F(StreamInfoImplTest, DefaultRequestIDExtensionTest) { TEST_F(StreamInfoImplTest, Details) { StreamInfoImpl stream_info(test_time_.timeSystem(), nullptr); EXPECT_FALSE(stream_info.responseCodeDetails().has_value()); - stream_info.setResponseCodeDetails("two words"); + stream_info.setResponseCodeDetails("two_words"); ASSERT_TRUE(stream_info.responseCodeDetails().has_value()); EXPECT_EQ(stream_info.responseCodeDetails().value(), "two_words"); } +TEST(UpstreamInfoImplTest, DumpState) { + UpstreamInfoImpl upstream_info; + + { + std::stringstream out; + upstream_info.dumpState(out, 0); + std::string state = out.str(); + EXPECT_THAT(state, testing::HasSubstr("upstream_connection_id_: null")); + } + upstream_info.setUpstreamConnectionId(5); + { + std::stringstream out; + upstream_info.dumpState(out, 0); + std::string state = out.str(); + EXPECT_THAT(state, testing::HasSubstr("upstream_connection_id_: 5")); + } +} + } // namespace } // namespace StreamInfo } // namespace Envoy diff --git a/test/common/stream_info/test_util.h b/test/common/stream_info/test_util.h index 2cebca68cd1a..35302d154bb3 100644 --- a/test/common/stream_info/test_util.h +++ b/test/common/stream_info/test_util.h @@ -7,183 +7,44 @@ #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); + setUpstreamInfo(std::make_shared()); } 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_); - } - - void onLastDownstreamRxByteReceived() override { - last_rx_byte_received_ = timeSystem().monotonicTime(); - } - - 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(first_downstream_tx_byte_sent_); - } - - void onFirstDownstreamTxByteSent() override { - first_downstream_tx_byte_sent_ = timeSystem().monotonicTime(); - } - - absl::optional lastDownstreamTxByteSent() const override { - return duration(last_downstream_tx_byte_sent_); - } - - void onLastDownstreamTxByteSent() override { - last_downstream_tx_byte_sent_ = timeSystem().monotonicTime(); + const absl::optional& virtualClusterName() const override { + return virtual_cluster_name_; } void onRequestComplete() override { end_time_ = timeSystem().monotonicTime(); } - void setUpstreamTiming(const Envoy::StreamInfo::UpstreamTiming& upstream_timing) override { - upstream_timing_ = 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; @@ -192,98 +53,17 @@ 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_; - - 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}; - 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_; + absl::optional virtual_cluster_name_; 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/common/tcp_proxy/BUILD b/test/common/tcp_proxy/BUILD index 4105647647b6..23b6a081fa26 100644 --- a/test/common/tcp_proxy/BUILD +++ b/test/common/tcp_proxy/BUILD @@ -54,6 +54,7 @@ envoy_cc_test( ], deps = [ ":tcp_proxy_test_base", + "//envoy/common:hashable_interface", ], ) diff --git a/test/common/tcp_proxy/config_test.cc b/test/common/tcp_proxy/config_test.cc index 891043958be7..abbe6cf88cd3 100644 --- a/test/common/tcp_proxy/config_test.cc +++ b/test/common/tcp_proxy/config_test.cc @@ -1,3 +1,5 @@ +#include "envoy/common/hashable.h" + #include "test/common/tcp_proxy/tcp_proxy_test_base.h" namespace Envoy { @@ -454,7 +456,22 @@ TEST(ConfigTest, HashWithSourceIpConfig) { EXPECT_NE(nullptr, config_obj.hashPolicy()); } -TEST(ConfigTest, HashWithSourceIpDefaultConfig) { +TEST(ConfigTest, HashWithFilterStateConfig) { + const std::string yaml = R"EOF( + stat_prefix: name + cluster: foo + hash_policy: + - filter_state: { + key: foo + } +)EOF"; + + NiceMock factory_context; + Config config_obj(constructConfigFromYaml(yaml, factory_context)); + EXPECT_NE(nullptr, config_obj.hashPolicy()); +} + +TEST(ConfigTest, HashWithDefaultConfig) { const std::string yaml = R"EOF( stat_prefix: name cluster: foo @@ -545,14 +562,7 @@ TEST_F(TcpProxyNonDeprecatedConfigRoutingTest, ClusterNameSet) { class TcpProxyHashingTest : public testing::Test { public: - void setup() { - const std::string yaml = R"EOF( - stat_prefix: name - cluster: fake_cluster - hash_policy: - - source_ip: {} - )EOF"; - + void setup(const std::string& yaml) { factory_context_.cluster_manager_.initializeThreadLocalClusters({"fake_cluster"}); config_ = std::make_shared(constructConfigFromYaml(yaml, factory_context_)); } @@ -571,23 +581,74 @@ class TcpProxyHashingTest : public testing::Test { NiceMock connection_; NiceMock filter_callbacks_; std::unique_ptr filter_; + + class HashableObj : public StreamInfo::FilterState::Object, public Hashable { + public: + absl::optional hash() const override { return 31337; } + }; }; -// Test TCP proxy use source IP to hash. +// Test TCP proxy using source IP to hash. TEST_F(TcpProxyHashingTest, HashWithSourceIp) { - setup(); + const std::string yaml = R"EOF( + stat_prefix: name + cluster: fake_cluster + hash_policy: + - source_ip: {} + )EOF"; + setup(yaml); initializeFilter(); + + // Ensure there is no remote address (MockStreamInfo sets one by default), and expect no hash. + connection_.stream_info_.downstream_connection_info_provider_->setRemoteAddress(nullptr); EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) .WillOnce(Invoke([](Upstream::ResourcePriority, Upstream::LoadBalancerContext* context) { - EXPECT_TRUE(context->computeHashKey().has_value()); + EXPECT_FALSE(context->computeHashKey().has_value()); return absl::nullopt; })); + filter_->onNewConnection(); + // Set remote address, and expect a hash. connection_.stream_info_.downstream_connection_info_provider_->setRemoteAddress( std::make_shared("1.2.3.4", 1111)); - connection_.stream_info_.downstream_connection_info_provider_->setLocalAddress( - std::make_shared("2.3.4.5", 2222)); + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) + .WillOnce(Invoke([](Upstream::ResourcePriority, Upstream::LoadBalancerContext* context) { + EXPECT_TRUE(context->computeHashKey().has_value()); + return absl::nullopt; + })); + filter_->onNewConnection(); +} + +// Test TCP proxy using filter state to hash. +TEST_F(TcpProxyHashingTest, HashWithFilterState) { + const std::string yaml = R"EOF( + stat_prefix: name + cluster: fake_cluster + hash_policy: + - filter_state: { + key: foo + } + )EOF"; + setup(yaml); + initializeFilter(); + + // Expect no hash when filter state is unset. + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) + .WillOnce(Invoke([](Upstream::ResourcePriority, Upstream::LoadBalancerContext* context) { + EXPECT_FALSE(context->computeHashKey().has_value()); + return absl::nullopt; + })); + filter_->onNewConnection(); + // Set filter state, and expect HashableObj's hash is now used. + connection_.stream_info_.filter_state_->setData("foo", std::make_unique(), + StreamInfo::FilterState::StateType::ReadOnly, + StreamInfo::FilterState::LifeSpan::FilterChain); + EXPECT_CALL(factory_context_.cluster_manager_.thread_local_cluster_, tcpConnPool(_, _)) + .WillOnce(Invoke([](Upstream::ResourcePriority, Upstream::LoadBalancerContext* context) { + EXPECT_EQ(31337, context->computeHashKey().value()); + return absl::nullopt; + })); filter_->onNewConnection(); } diff --git a/test/common/tcp_proxy/tcp_proxy_test.cc b/test/common/tcp_proxy/tcp_proxy_test.cc index 8ca0a1e0ae95..f7b5549bf6fd 100644 --- a/test/common/tcp_proxy/tcp_proxy_test.cc +++ b/test/common/tcp_proxy/tcp_proxy_test.cc @@ -935,8 +935,9 @@ TEST_F(TcpProxyTest, AccessLogUpstreamSSLConnection) { EXPECT_CALL(*upstream_connections_.at(0), streamInfo()).WillRepeatedly(ReturnRef(stream_info)); raiseEventUpstreamConnected(0); - ASSERT_NE(nullptr, filter_->getStreamInfo().upstreamSslConnection()); - EXPECT_EQ(session_id, filter_->getStreamInfo().upstreamSslConnection()->sessionId()); + ASSERT_NE(nullptr, filter_->getStreamInfo().upstreamInfo()->upstreamSslConnection()); + EXPECT_EQ(session_id, + filter_->getStreamInfo().upstreamInfo()->upstreamSslConnection()->sessionId()); } // Tests that upstream flush works properly with no idle timeout configured. @@ -1081,7 +1082,8 @@ TEST_F(TcpProxyTest, ShareFilterState) { raiseEventUpstreamConnected(0); EXPECT_EQ("filter_state_cluster", filter_callbacks_.connection_.streamInfo() - .upstreamFilterState() + .upstreamInfo() + ->upstreamFilterState() ->getDataReadOnly("envoy.tcp_proxy.cluster") .value()); } @@ -1093,9 +1095,10 @@ TEST_F(TcpProxyTest, AccessDownstreamAndUpstreamProperties) { raiseEventUpstreamConnected(0); EXPECT_EQ(filter_callbacks_.connection().streamInfo().downstreamAddressProvider().sslConnection(), filter_callbacks_.connection().ssl()); - EXPECT_EQ(filter_callbacks_.connection().streamInfo().upstreamLocalAddress(), - upstream_connections_.at(0)->streamInfo().downstreamAddressProvider().localAddress()); - EXPECT_EQ(filter_callbacks_.connection().streamInfo().upstreamSslConnection(), + EXPECT_EQ( + filter_callbacks_.connection().streamInfo().upstreamInfo()->upstreamLocalAddress().get(), + upstream_connections_.at(0)->streamInfo().downstreamAddressProvider().localAddress().get()); + EXPECT_EQ(filter_callbacks_.connection().streamInfo().upstreamInfo()->upstreamSslConnection(), upstream_connections_.at(0)->streamInfo().downstreamAddressProvider().sslConnection()); } } // namespace diff --git a/test/common/tcp_proxy/tcp_proxy_test_base.h b/test/common/tcp_proxy/tcp_proxy_test_base.h index b6b437b2f38e..7464ce7738bb 100644 --- a/test/common/tcp_proxy/tcp_proxy_test_base.h +++ b/test/common/tcp_proxy/tcp_proxy_test_base.h @@ -62,11 +62,6 @@ class TcpProxyTestBase : public testing::Test { TcpProxyTestBase() { ON_CALL(*factory_context_.access_log_manager_.file_, write(_)) .WillByDefault(SaveArg<0>(&access_log_data_)); - ON_CALL(filter_callbacks_.connection_.stream_info_, onUpstreamHostSelected(_)) - .WillByDefault(Invoke( - [this](Upstream::HostDescriptionConstSharedPtr host) { upstream_host_ = host; })); - ON_CALL(filter_callbacks_.connection_.stream_info_, upstreamHost()) - .WillByDefault(ReturnPointee(&upstream_host_)); ON_CALL(filter_callbacks_.connection_.stream_info_, setUpstreamClusterInfo(_)) .WillByDefault(Invoke([this](const Upstream::ClusterInfoConstSharedPtr& cluster_info) { upstream_cluster_ = cluster_info; diff --git a/test/common/tcp_proxy/upstream_test.cc b/test/common/tcp_proxy/upstream_test.cc index ce4184067be5..e5970fafa753 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/common/thread_local/thread_local_impl_test.cc b/test/common/thread_local/thread_local_impl_test.cc index d9a774e8bf9a..f498fdb5604c 100644 --- a/test/common/thread_local/thread_local_impl_test.cc +++ b/test/common/thread_local/thread_local_impl_test.cc @@ -16,18 +16,30 @@ namespace Envoy { namespace ThreadLocal { TEST(MainThreadVerificationTest, All) { - // Before threading is on, assertion on main thread should be true. + // Before threading is on, we are in the test thread, not the main thread. + EXPECT_FALSE(Thread::MainThread::isMainThread()); +#if TEST_THREAD_SUPPORTED + EXPECT_TRUE(Thread::TestThread::isTestThread()); EXPECT_TRUE(Thread::MainThread::isMainOrTestThread()); +#endif { InstanceImpl tls; // Tls instance has been initialized. // Call to main thread verification should succeed in main thread. + EXPECT_TRUE(Thread::MainThread::isMainThread()); +#if TEST_THREAD_SUPPORTED EXPECT_TRUE(Thread::MainThread::isMainOrTestThread()); + EXPECT_TRUE(Thread::TestThread::isTestThread()); +#endif tls.shutdownGlobalThreading(); tls.shutdownThread(); } - // After threading is off, assertion on main thread should be true. + // After threading is off, assertion we are again in the test thread, not the main thread. + EXPECT_FALSE(Thread::MainThread::isMainThread()); +#if TEST_THREAD_SUPPORTED + EXPECT_TRUE(Thread::TestThread::isTestThread()); EXPECT_TRUE(Thread::MainThread::isMainOrTestThread()); +#endif } class TestThreadLocalObject : public ThreadLocalObject { @@ -301,7 +313,26 @@ TEST(ThreadLocalInstanceImplDispatcherTest, Dispatcher) { // Verify we have the expected dispatcher for the new thread thread. EXPECT_EQ(thread_dispatcher.get(), &tls.dispatcher()); // Verify that it is inside the worker thread. + EXPECT_FALSE(Thread::MainThread::isMainThread()); + // Verify that is is not in the test thread either. +#if TEST_THREAD_SUPPORTED + EXPECT_FALSE(Thread::TestThread::isTestThread()); EXPECT_FALSE(Thread::MainThread::isMainOrTestThread()); +#endif + + ASSERT_IS_NOT_TEST_THREAD(); + ASSERT_IS_NOT_MAIN_OR_TEST_THREAD(); + { + Thread::SkipAsserts skip; + ASSERT_IS_NOT_TEST_THREAD(); + ASSERT_IS_NOT_MAIN_OR_TEST_THREAD(); + ASSERT_IS_TEST_THREAD(); + ASSERT_IS_MAIN_OR_TEST_THREAD(); + TRY_ASSERT_MAIN_THREAD {} + END_TRY + catch (const std::exception&) { + } + } }); thread->join(); diff --git a/test/common/tracing/http_tracer_impl_test.cc b/test/common/tracing/http_tracer_impl_test.cc index 1b263c8dfe66..35476c421f22 100644 --- a/test/common/tracing/http_tracer_impl_test.cc +++ b/test/common/tracing/http_tracer_impl_test.cc @@ -95,6 +95,10 @@ TEST(HttpTracerUtilityTest, IsTracing) { class HttpConnManFinalizerImplTest : public testing::Test { protected: + HttpConnManFinalizerImplTest() { + Upstream::HostDescriptionConstSharedPtr shared_host(host_); + stream_info.upstreamInfo()->setUpstreamHost(shared_host); + } struct CustomTagCase { std::string custom_tag; bool set; @@ -117,6 +121,7 @@ class HttpConnManFinalizerImplTest : public testing::Test { NiceMock span; NiceMock config; NiceMock stream_info; + Upstream::MockHostDescription* host_{new NiceMock()}; }; TEST_F(HttpConnManFinalizerImplTest, OriginalAndLongPath) { @@ -219,7 +224,7 @@ TEST_F(HttpConnManFinalizerImplTest, NullRequestHeadersAndNullRouteEntry) { EXPECT_CALL(stream_info, bytesSent()).WillOnce(Return(11)); absl::optional response_code; EXPECT_CALL(stream_info, responseCode()).WillRepeatedly(ReturnPointee(&response_code)); - EXPECT_CALL(stream_info, upstreamHost()).WillRepeatedly(Return(nullptr)); + stream_info.upstreamInfo()->setUpstreamHost(nullptr); EXPECT_CALL(stream_info, route()).WillRepeatedly(Return(nullptr)); EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().HttpStatusCode), Eq("0"))); @@ -257,25 +262,30 @@ tag: d } TEST_F(HttpConnManFinalizerImplTest, StreamInfoLogs) { - stream_info.host_->cluster_.name_ = "my_upstream_cluster"; + host_->hostname_ = "my_upstream_cluster"; EXPECT_CALL(stream_info, bytesReceived()).WillOnce(Return(10)); EXPECT_CALL(stream_info, bytesSent()).WillOnce(Return(11)); absl::optional response_code; EXPECT_CALL(stream_info, responseCode()).WillRepeatedly(ReturnPointee(&response_code)); - EXPECT_CALL(stream_info, upstreamHost()).Times(3); const auto start_timestamp = SystemTime{std::chrono::duration_cast(std::chrono::hours{123})}; EXPECT_CALL(stream_info, startTime()).WillRepeatedly(Return(start_timestamp)); const absl::optional nanoseconds = std::chrono::nanoseconds{10}; - EXPECT_CALL(stream_info, lastDownstreamRxByteReceived()).WillRepeatedly(Return(nanoseconds)); - EXPECT_CALL(stream_info, firstUpstreamTxByteSent()).WillRepeatedly(Return(nanoseconds)); - EXPECT_CALL(stream_info, lastUpstreamTxByteSent()).WillRepeatedly(Return(nanoseconds)); - EXPECT_CALL(stream_info, firstUpstreamRxByteReceived()).WillRepeatedly(Return(nanoseconds)); - EXPECT_CALL(stream_info, lastUpstreamRxByteReceived()).WillRepeatedly(Return(nanoseconds)); - EXPECT_CALL(stream_info, firstDownstreamTxByteSent()).WillRepeatedly(Return(nanoseconds)); - EXPECT_CALL(stream_info, lastDownstreamTxByteSent()).WillRepeatedly(Return(nanoseconds)); + const MonotonicTime time = MonotonicTime(nanoseconds.value()); + MockTimeSystem time_system; + EXPECT_CALL(time_system, monotonicTime) + .Times(AnyNumber()) + .WillRepeatedly(Return(MonotonicTime(std::chrono::nanoseconds(10)))); + auto& timing = stream_info.upstream_info_->upstreamTiming(); + timing.first_upstream_tx_byte_sent_ = time; + timing.last_upstream_tx_byte_sent_ = time; + timing.first_upstream_rx_byte_received_ = time; + timing.last_upstream_rx_byte_received_ = time; + stream_info.downstream_timing_.onFirstDownstreamTxByteSent(time_system); + stream_info.downstream_timing_.onLastDownstreamTxByteSent(time_system); + stream_info.downstream_timing_.onLastDownstreamRxByteReceived(time_system); const auto log_timestamp = start_timestamp + std::chrono::duration_cast(*nanoseconds); @@ -292,14 +302,13 @@ TEST_F(HttpConnManFinalizerImplTest, StreamInfoLogs) { } TEST_F(HttpConnManFinalizerImplTest, UpstreamClusterTagSet) { - stream_info.host_->cluster_.name_ = "my_upstream_cluster"; - stream_info.host_->cluster_.observability_name_ = "my_upstream_cluster_observable"; + host_->cluster_.name_ = "my_upstream_cluster"; + host_->cluster_.observability_name_ = "my_upstream_cluster_observable"; EXPECT_CALL(stream_info, bytesReceived()).WillOnce(Return(10)); EXPECT_CALL(stream_info, bytesSent()).WillOnce(Return(11)); absl::optional response_code; EXPECT_CALL(stream_info, responseCode()).WillRepeatedly(ReturnPointee(&response_code)); - EXPECT_CALL(stream_info, upstreamHost()).Times(3); EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().Component), Eq(Tracing::Tags::get().Proxy))); EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().UpstreamCluster), Eq("my_upstream_cluster"))); @@ -341,7 +350,7 @@ TEST_F(HttpConnManFinalizerImplTest, SpanOptionalHeaders) { absl::optional response_code; EXPECT_CALL(stream_info, responseCode()).WillRepeatedly(ReturnPointee(&response_code)); EXPECT_CALL(stream_info, bytesSent()).WillOnce(Return(100)); - EXPECT_CALL(stream_info, upstreamHost()).WillOnce(Return(nullptr)); + stream_info.upstreamInfo()->setUpstreamHost(nullptr); EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().HttpStatusCode), Eq("0"))); EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().Error), Eq(Tracing::Tags::get().True))); @@ -402,8 +411,7 @@ TEST_F(HttpConnManFinalizerImplTest, SpanCustomTags) { std::shared_ptr host_metadata = std::make_shared(); (*host_metadata->mutable_filter_metadata())["m.host"].MergeFrom(fake_struct); - (*stream_info.host_->cluster_.metadata_.mutable_filter_metadata())["m.cluster"].MergeFrom( - fake_struct); + (*host_->cluster_.metadata_.mutable_filter_metadata())["m.cluster"].MergeFrom(fake_struct); absl::optional protocol = Http::Protocol::Http10; EXPECT_CALL(stream_info, bytesReceived()).WillOnce(Return(10)); @@ -411,7 +419,7 @@ TEST_F(HttpConnManFinalizerImplTest, SpanCustomTags) { absl::optional response_code; EXPECT_CALL(stream_info, responseCode()).WillRepeatedly(ReturnPointee(&response_code)); EXPECT_CALL(stream_info, bytesSent()).WillOnce(Return(100)); - EXPECT_CALL(*stream_info.host_, metadata()).WillRepeatedly(Return(host_metadata)); + EXPECT_CALL(*host_, metadata()).WillRepeatedly(Return(host_metadata)); EXPECT_CALL(config, customTags()); EXPECT_CALL(span, setTag(_, _)).Times(testing::AnyNumber()); @@ -532,7 +540,7 @@ TEST_F(HttpConnManFinalizerImplTest, SpanPopulatedFailureResponse) { EXPECT_CALL(stream_info, bytesSent()).WillOnce(Return(100)); ON_CALL(stream_info, hasResponseFlag(StreamInfo::ResponseFlag::UpstreamRequestTimeout)) .WillByDefault(Return(true)); - EXPECT_CALL(stream_info, upstreamHost()).WillOnce(Return(nullptr)); + stream_info.upstreamInfo()->setUpstreamHost(nullptr); EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().Error), Eq(Tracing::Tags::get().True))); EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().HttpStatusCode), Eq("503"))); @@ -728,6 +736,8 @@ class HttpTracerImplTest : public testing::Test { driver_ = new NiceMock(); DriverPtr driver_ptr(driver_); tracer_ = std::make_shared(std::move(driver_ptr), local_info_); + Upstream::HostDescriptionConstSharedPtr shared_host(host_); + stream_info_.upstreamInfo()->setUpstreamHost(shared_host); } Http::TestRequestHeaderMapImpl request_headers_{ @@ -742,6 +752,7 @@ class HttpTracerImplTest : public testing::Test { NiceMock config_; NiceMock* driver_; HttpTracerSharedPtr tracer_; + Upstream::MockHostDescription* host_{new NiceMock()}; }; TEST_F(HttpTracerImplTest, BasicFunctionalityNullSpan) { @@ -799,10 +810,9 @@ TEST_F(HttpTracerImplTest, ChildUpstreamSpanTest) { const std::string ob_cluster_name = "ob fake cluster"; EXPECT_CALL(stream_info_, responseCode()).WillRepeatedly(ReturnPointee(&response_code)); EXPECT_CALL(stream_info_, protocol()).WillRepeatedly(ReturnPointee(&protocol)); - EXPECT_CALL(*(stream_info_.host_), address()).WillOnce(Return(remote_address)); - EXPECT_CALL(stream_info_.host_->cluster_, name()).WillOnce(ReturnRef(cluster_name)); - EXPECT_CALL(stream_info_.host_->cluster_, observabilityName()) - .WillOnce(ReturnRef(ob_cluster_name)); + EXPECT_CALL(*host_, address()).WillOnce(Return(remote_address)); + EXPECT_CALL(host_->cluster_, name()).WillOnce(ReturnRef(cluster_name)); + EXPECT_CALL(host_->cluster_, observabilityName()).WillOnce(ReturnRef(ob_cluster_name)); EXPECT_CALL(*second_span, setTag(_, _)).Times(testing::AnyNumber()); EXPECT_CALL(*second_span, setTag(Eq(Tracing::Tags::get().HttpProtocol), Eq("HTTP/2"))); diff --git a/test/common/upstream/BUILD b/test/common/upstream/BUILD index 15fabdc35322..bc2386677ba4 100644 --- a/test/common/upstream/BUILD +++ b/test/common/upstream/BUILD @@ -699,6 +699,8 @@ envoy_cc_test( "//test/mocks/upstream:cluster_manager_mocks", "//test/mocks/upstream:health_checker_mocks", "//test/mocks/upstream:priority_set_mocks", + "//test/mocks/upstream:thread_aware_load_balancer_mocks", + "//test/mocks/upstream:typed_load_balancer_factory_mocks", "//test/test_common:registry_lib", "//test/test_common:test_runtime_lib", "//test/test_common:utility_lib", diff --git a/test/common/upstream/health_check_fuzz.cc b/test/common/upstream/health_check_fuzz.cc index e0f3d3ad094a..01da3427c1d2 100644 --- a/test/common/upstream/health_check_fuzz.cc +++ b/test/common/upstream/health_check_fuzz.cc @@ -51,7 +51,7 @@ convertToGrpcServingStatus(test::common::upstream::ServingStatus status) { return grpc::health::v1::HealthCheckResponse::SERVICE_UNKNOWN; } default: // shouldn't hit - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } } @@ -84,7 +84,7 @@ makeBufferListToRespondWith(test::common::upstream::GrpcRespondBytes grpc_respon return bufferList; } default: // shouldn't hit - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } } @@ -529,7 +529,7 @@ HealthCheckFuzz::getEventTypeFromProto(const test::common::upstream::RaiseEvent& return Network::ConnectionEvent::LocalClose; } default: // shouldn't hit - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } } diff --git a/test/common/upstream/health_checker_impl_test.cc b/test/common/upstream/health_checker_impl_test.cc index ba2dfe80fce2..04f6686bed63 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/common/upstream/load_balancer_impl_test.cc b/test/common/upstream/load_balancer_impl_test.cc index 82b00ff3f25b..a21c09e641e2 100644 --- a/test/common/upstream/load_balancer_impl_test.cc +++ b/test/common/upstream/load_balancer_impl_test.cc @@ -596,7 +596,7 @@ TEST_F(ZoneAwareLoadBalancerBaseTest, BaseMethods) { EXPECT_FALSE(lb_.lifetimeCallbacks().has_value()); std::vector hash_key; auto mock_host = std::make_shared>(); - EXPECT_FALSE(lb_.selectPool(nullptr, *mock_host, hash_key).has_value()); + EXPECT_FALSE(lb_.selectExistingConnection(nullptr, *mock_host, hash_key).has_value()); } TEST_F(ZoneAwareLoadBalancerBaseTest, CrossPriorityHostMapUpdate) { diff --git a/test/common/upstream/original_dst_cluster_test.cc b/test/common/upstream/original_dst_cluster_test.cc index 1d25394f1476..6fc86b2e056a 100644 --- a/test/common/upstream/original_dst_cluster_test.cc +++ b/test/common/upstream/original_dst_cluster_test.cc @@ -193,7 +193,7 @@ TEST_F(OriginalDstClusterTest, NoContext) { EXPECT_FALSE(lb.lifetimeCallbacks().has_value()); std::vector hash_key; auto mock_host = std::make_shared>(); - EXPECT_FALSE(lb.selectPool(nullptr, *mock_host, hash_key).has_value()); + EXPECT_FALSE(lb.selectExistingConnection(nullptr, *mock_host, hash_key).has_value()); } // Downstream connection is not using original dst => no host. diff --git a/test/common/upstream/ring_hash_lb_test.cc b/test/common/upstream/ring_hash_lb_test.cc index 6c5bcb58b237..99b9d402e6b4 100644 --- a/test/common/upstream/ring_hash_lb_test.cc +++ b/test/common/upstream/ring_hash_lb_test.cc @@ -101,7 +101,10 @@ TEST_P(RingHashLoadBalancerTest, NoHost) { EXPECT_FALSE(lb_->factory()->create()->lifetimeCallbacks().has_value()); std::vector hash_key; auto mock_host = std::make_shared>(); - EXPECT_FALSE(lb_->factory()->create()->selectPool(nullptr, *mock_host, hash_key).has_value()); + EXPECT_FALSE(lb_->factory() + ->create() + ->selectExistingConnection(nullptr, *mock_host, hash_key) + .has_value()); } TEST_P(RingHashLoadBalancerTest, BaseMethods) { @@ -110,7 +113,7 @@ TEST_P(RingHashLoadBalancerTest, BaseMethods) { EXPECT_FALSE(lb_->lifetimeCallbacks().has_value()); std::vector hash_key; auto mock_host = std::make_shared>(); - EXPECT_FALSE(lb_->selectPool(nullptr, *mock_host, hash_key).has_value()); + EXPECT_FALSE(lb_->selectExistingConnection(nullptr, *mock_host, hash_key).has_value()); }; TEST_P(RingHashLoadBalancerTest, SelectOverrideHost) { diff --git a/test/common/upstream/subset_lb_test.cc b/test/common/upstream/subset_lb_test.cc index 71cbeface37e..133d15cacba9 100644 --- a/test/common/upstream/subset_lb_test.cc +++ b/test/common/upstream/subset_lb_test.cc @@ -504,7 +504,7 @@ TEST_F(SubsetLoadBalancerTest, NoFallback) { EXPECT_FALSE(lb_->lifetimeCallbacks().has_value()); std::vector hash_key; auto mock_host = std::make_shared>(); - EXPECT_FALSE(lb_->selectPool(nullptr, *mock_host, hash_key).has_value()); + EXPECT_FALSE(lb_->selectExistingConnection(nullptr, *mock_host, hash_key).has_value()); } TEST_F(SubsetLoadBalancerTest, SelectOverrideHost) { diff --git a/test/common/upstream/upstream_impl_test.cc b/test/common/upstream/upstream_impl_test.cc index 82d281a1cd72..f413d4cfd8a3 100644 --- a/test/common/upstream/upstream_impl_test.cc +++ b/test/common/upstream/upstream_impl_test.cc @@ -39,6 +39,8 @@ #include "test/mocks/upstream/cluster_manager.h" #include "test/mocks/upstream/health_checker.h" #include "test/mocks/upstream/priority_set.h" +#include "test/mocks/upstream/thread_aware_load_balancer.h" +#include "test/mocks/upstream/typed_load_balancer_factory.h" #include "test/test_common/environment.h" #include "test/test_common/registry.h" #include "test/test_common/test_runtime.h" @@ -2079,6 +2081,138 @@ TEST_F(StaticClusterImplTest, UnsupportedLBType) { EnvoyException, "invalid value \"fakelbtype\""); } +// load_balancing_policy should be used when lb_policy is set to LOAD_BALANCING_POLICY_CONFIG. +TEST_F(StaticClusterImplTest, LoadBalancingPolicyWithLbPolicy) { + const std::string yaml = R"EOF( + name: staticcluster + connect_timeout: 0.25s + type: static + lb_policy: LOAD_BALANCING_POLICY_CONFIG + load_balancing_policy: + policies: + - typed_extension_config: + name: custom_lb + typed_config: + "@type": type.googleapis.com/google.protobuf.Struct + value: + foo: "bar" + load_assignment: + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 10.0.0.1 + port_value: 11001 + )EOF"; + + NiceMock factory; + EXPECT_CALL(factory, name()).WillRepeatedly(Return("custom_lb")); + Registry::InjectFactory registered_factory(factory); + + envoy::config::cluster::v3::Cluster cluster_config = parseClusterFromV3Yaml(yaml); + Envoy::Stats::ScopePtr scope = stats_.createScope(fmt::format( + "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() + : cluster_config.alt_stat_name())); + Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, + singleton_manager_, tls_, validation_visitor_, *api_, options_); + StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), true); + cluster.initialize([] {}); + + EXPECT_EQ(1UL, cluster.prioritySet().hostSetsPerPriority()[0]->healthyHosts().size()); + EXPECT_EQ(LoadBalancerType::LoadBalancingPolicyConfig, cluster.info()->lbType()); + EXPECT_TRUE(cluster.info()->addedViaApi()); +} + +// load_balancing_policy should also be used when lb_policy is set to something else besides +// LOAD_BALANCING_POLICY_CONFIG. +TEST_F(StaticClusterImplTest, LoadBalancingPolicyWithOtherLbPolicy) { + const std::string yaml = R"EOF( + name: staticcluster + connect_timeout: 0.25s + type: static + lb_policy: ROUND_ROBIN + load_balancing_policy: + policies: + - typed_extension_config: + name: custom_lb + typed_config: + "@type": type.googleapis.com/google.protobuf.Struct + value: + foo: "bar" + load_assignment: + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 10.0.0.1 + port_value: 11001 + )EOF"; + + NiceMock factory; + EXPECT_CALL(factory, name()).WillRepeatedly(Return("custom_lb")); + Registry::InjectFactory registered_factory(factory); + + envoy::config::cluster::v3::Cluster cluster_config = parseClusterFromV3Yaml(yaml); + Envoy::Stats::ScopePtr scope = stats_.createScope(fmt::format( + "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() + : cluster_config.alt_stat_name())); + Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, + singleton_manager_, tls_, validation_visitor_, *api_, options_); + StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), true); + cluster.initialize([] {}); + + EXPECT_EQ(1UL, cluster.prioritySet().hostSetsPerPriority()[0]->healthyHosts().size()); + EXPECT_EQ(LoadBalancerType::LoadBalancingPolicyConfig, cluster.info()->lbType()); + EXPECT_TRUE(cluster.info()->addedViaApi()); +} + +// load_balancing_policy should also be used when lb_policy is omitted. +TEST_F(StaticClusterImplTest, LoadBalancingPolicyWithoutLbPolicy) { + const std::string yaml = R"EOF( + name: staticcluster + connect_timeout: 0.25s + type: static + load_balancing_policy: + policies: + - typed_extension_config: + name: custom_lb + typed_config: + "@type": type.googleapis.com/google.protobuf.Struct + value: + foo: "bar" + load_assignment: + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 10.0.0.1 + port_value: 11001 + )EOF"; + + NiceMock factory; + EXPECT_CALL(factory, name()).WillRepeatedly(Return("custom_lb")); + Registry::InjectFactory registered_factory(factory); + + envoy::config::cluster::v3::Cluster cluster_config = parseClusterFromV3Yaml(yaml); + Envoy::Stats::ScopePtr scope = stats_.createScope(fmt::format( + "cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name() + : cluster_config.alt_stat_name())); + Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context( + admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_, + singleton_manager_, tls_, validation_visitor_, *api_, options_); + StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), true); + cluster.initialize([] {}); + + EXPECT_EQ(1UL, cluster.prioritySet().hostSetsPerPriority()[0]->healthyHosts().size()); + EXPECT_EQ(LoadBalancerType::LoadBalancingPolicyConfig, cluster.info()->lbType()); + EXPECT_TRUE(cluster.info()->addedViaApi()); +} + TEST_F(StaticClusterImplTest, MalformedHostIP) { const std::string yaml = R"EOF( name: name @@ -3420,9 +3554,13 @@ TEST_F(ClusterInfoImplTest, Http3) { validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" - match_subject_alt_names: - - exact: localhost - - exact: 127.0.0.1 + match_typed_subject_alt_names: + - matcher: + exact: localhost + san_type: URI + - matcher: + exact: 127.0.0.1 + san_type: IP_ADDRESS )EOF", Network::Address::IpVersion::v4); auto cluster1 = makeCluster(yaml); @@ -3493,9 +3631,13 @@ TEST_F(ClusterInfoImplTest, Http3BadConfig) { validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" - match_subject_alt_names: - - exact: localhost - - exact: 127.0.0.1 + match_typed_subject_alt_names: + - matcher: + exact: localhost + san_type: URI + - matcher: + exact: 127.0.0.1 + san_type: IP_ADDRESS typed_extension_protocol_options: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions @@ -3538,9 +3680,13 @@ TEST_F(ClusterInfoImplTest, Http3Auto) { validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" - match_subject_alt_names: - - exact: localhost - - exact: 127.0.0.1 + match_typed_subject_alt_names: + - matcher: + exact: localhost + san_type: URI + - matcher: + exact: 127.0.0.1 + san_type: IP_ADDRESS )EOF", Network::Address::IpVersion::v4); @@ -3597,9 +3743,13 @@ TEST_F(ClusterInfoImplTest, UseDownstreamHttpProtocolWithoutDowngrade) { validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" - match_subject_alt_names: - - exact: localhost - - exact: 127.0.0.1 + match_typed_subject_alt_names: + - matcher: + exact: localhost + san_type: URI + - matcher: + exact: 127.0.0.1 + san_type: IP_ADDRESS typed_extension_protocol_options: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions diff --git a/test/config/utility.cc b/test/config/utility.cc index 0e16fe2b5b42..cfd6fa10e612 100644 --- a/test/config/utility.cc +++ b/test/config/utility.cc @@ -144,10 +144,19 @@ std::string ConfigHelper::startTlsConfig() { TestEnvironment::runfilesPath("test/config/integration/certs/serverkey.pem"))); } -std::string ConfigHelper::tlsInspectorFilter() { +std::string ConfigHelper::tlsInspectorFilter(bool enable_ja3_fingerprinting) { + if (!enable_ja3_fingerprinting) { + return R"EOF( +name: "envoy.filters.listener.tls_inspector" +typed_config: +)EOF"; + } + return R"EOF( name: "envoy.filters.listener.tls_inspector" typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + enable_ja3_fingerprinting: true )EOF"; } @@ -620,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)); @@ -653,6 +674,15 @@ ConfigHelper::ConfigHelper(const Network::Address::IpVersion version, Api::Api& } } +void ConfigHelper::addListenerTypedMetadata(absl::string_view key, ProtobufWkt::Any& packed_value) { + RELEASE_ASSERT(!finalized_, ""); + auto* static_resources = bootstrap_.mutable_static_resources(); + ASSERT_TRUE(static_resources->listeners_size() > 0); + auto* listener = static_resources->mutable_listeners(0); + auto* map = listener->mutable_metadata()->mutable_typed_filter_metadata(); + (*map)[std::string(key)] = packed_value; +}; + void ConfigHelper::addClusterFilterMetadata(absl::string_view metadata_yaml, absl::string_view cluster_name) { RELEASE_ASSERT(!finalized_, ""); @@ -895,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( @@ -1186,8 +1215,8 @@ void ConfigHelper::initializeTls( } } if (!options.san_matchers_.empty()) { - *validation_context->mutable_match_subject_alt_names() = {options.san_matchers_.begin(), - options.san_matchers_.end()}; + *validation_context->mutable_match_typed_subject_alt_names() = {options.san_matchers_.begin(), + options.san_matchers_.end()}; } } diff --git a/test/config/utility.h b/test/config/utility.h index 99ab62cbcc27..33fa29e13b69 100644 --- a/test/config/utility.h +++ b/test/config/utility.h @@ -14,6 +14,7 @@ #include "envoy/config/route/v3/route_components.pb.h" #include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h" #include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h" +#include "envoy/extensions/transport_sockets/tls/v3/common.pb.h" #include "envoy/extensions/upstreams/http/v3/http_protocol_options.pb.h" #include "envoy/http/codes.h" @@ -80,7 +81,8 @@ class ConfigHelper { } ServerSslOptions& - setSanMatchers(std::vector san_matchers) { + setSanMatchers(std::vector + san_matchers) { san_matchers_ = san_matchers; return *this; } @@ -94,7 +96,8 @@ class ConfigHelper { bool ocsp_staple_required_{false}; bool tlsv1_3_{false}; bool expect_client_ecdsa_cert_{false}; - std::vector san_matchers_{}; + std::vector + san_matchers_{}; }; // Set up basic config, using the specified IpVersion for all connections: listeners, upstream, @@ -119,7 +122,7 @@ class ConfigHelper { static std::string baseUdpListenerConfig(std::string listen_address = "0.0.0.0"); // A string for a tls inspector listener filter which can be used with addListenerFilter() - static std::string tlsInspectorFilter(); + static std::string tlsInspectorFilter(bool enable_ja3_fingerprinting = false); // A basic configuration for L4 proxying. static std::string tcpProxyConfig(); @@ -313,6 +316,9 @@ class ConfigHelper { // Add this key value pair to the static runtime. void addRuntimeOverride(const std::string& key, const std::string& value); + // Add typed_filter_metadata to the first listener. + void addListenerTypedMetadata(absl::string_view key, ProtobufWkt::Any& packed_value); + // Add filter_metadata to a cluster with the given name void addClusterFilterMetadata(absl::string_view metadata_yaml, absl::string_view cluster_name = "cluster_0"); diff --git a/test/config_test/config_test.cc b/test/config_test/config_test.cc index d6f8d800f9b6..b323a1938f8c 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/access_loggers/common/grpc_access_logger_test.cc b/test/extensions/access_loggers/common/grpc_access_logger_test.cc index ec6e35ab635e..0a8bd0d81994 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/file/config_test.cc b/test/extensions/access_loggers/file/config_test.cc index 4126b865e7ef..ca8448f7ad9f 100644 --- a/test/extensions/access_loggers/file/config_test.cc +++ b/test/extensions/access_loggers/file/config_test.cc @@ -62,7 +62,7 @@ class FileAccessLogTest : public testing::Test { absl::Time abslStartTime = TestUtility::parseTime("Dec 18 01:50:34 2018 GMT", "%b %e %H:%M:%S %Y GMT"); stream_info_.start_time_ = absl::ToChronoTime(abslStartTime); - EXPECT_CALL(stream_info_, upstreamHost()).WillRepeatedly(Return(nullptr)); + stream_info_.upstreamInfo()->setUpstreamHost(nullptr); stream_info_.response_code_ = 200; EXPECT_CALL(*file, write(_)).WillOnce(Invoke([expected, is_json](absl::string_view got) { 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 3ea77be37f64..44f2d4f6f4d1 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/grpc/http_config_test.cc b/test/extensions/access_loggers/grpc/http_config_test.cc index 933a4a69d967..209763cf6f05 100644 --- a/test/extensions/access_loggers/grpc/http_config_test.cc +++ b/test/extensions/access_loggers/grpc/http_config_test.cc @@ -34,12 +34,6 @@ class HttpGrpcAccessLogConfigTest : public testing::Test { void run(const std::string cluster_name) { const auto good_cluster = "good_cluster"; - EXPECT_CALL(context_.cluster_manager_, checkActiveStaticCluster(cluster_name)) - .WillOnce(Invoke([good_cluster](const std::string& cluster_name) { - if (cluster_name != good_cluster) { - throw EnvoyException("fake"); - } - })); auto* common_config = http_grpc_access_log_.mutable_common_config(); common_config->set_log_name("foo"); @@ -73,9 +67,6 @@ class HttpGrpcAccessLogConfigTest : public testing::Test { // Normal OK configuration. TEST_F(HttpGrpcAccessLogConfigTest, Ok) { run("good_cluster"); } -// Wrong configuration with invalid clusters. -TEST_F(HttpGrpcAccessLogConfigTest, InvalidCluster) { run("invalid"); } - } // namespace } // namespace HttpGrpc } // namespace AccessLoggers diff --git a/test/extensions/access_loggers/grpc/http_grpc_access_log_impl_test.cc b/test/extensions/access_loggers/grpc/http_grpc_access_log_impl_test.cc index 481c249c779c..d90a47af545e 100644 --- a/test/extensions/access_loggers/grpc/http_grpc_access_log_impl_test.cc +++ b/test/extensions/access_loggers/grpc/http_grpc_access_log_impl_test.cc @@ -7,9 +7,11 @@ #include "source/common/network/address_impl.h" #include "source/common/router/string_accessor_impl.h" #include "source/common/stream_info/uint32_accessor_impl.h" +#include "source/common/stream_info/utility.h" #include "source/extensions/access_loggers/grpc/http_grpc_access_log_impl.h" #include "test/mocks/access_log/mocks.h" +#include "test/mocks/common.h" #include "test/mocks/grpc/mocks.h" #include "test/mocks/local_info/mocks.h" #include "test/mocks/ssl/mocks.h" @@ -117,8 +119,8 @@ class HttpGrpcAccessLogTest : public testing::Test { void expectLogRequestMethod(const std::string& request_method) { NiceMock stream_info; - stream_info.host_ = nullptr; stream_info.start_time_ = SystemTime(1h); + stream_info.upstreamInfo()->setUpstreamHost(nullptr); Http::TestRequestHeaderMapImpl request_headers{ {":method", request_method}, @@ -172,13 +174,18 @@ class TestSerializedFilterState : public StreamInfo::FilterState::Object { // Test HTTP log marshaling. TEST_F(HttpGrpcAccessLogTest, Marshalling) { InSequence s; + NiceMock time_system; { NiceMock stream_info; - stream_info.host_ = nullptr; + stream_info.upstreamInfo()->setUpstreamHost(nullptr); stream_info.start_time_ = SystemTime(1h); stream_info.start_time_monotonic_ = MonotonicTime(1h); - stream_info.last_downstream_tx_byte_sent_ = 2ms; + EXPECT_CALL(time_system, monotonicTime) + .WillOnce(Return(MonotonicTime(std::chrono::hours(1) + std::chrono::milliseconds(2)))); + stream_info.downstream_timing_.onLastDownstreamTxByteSent(time_system); + StreamInfo::TimingUtility timing(stream_info); + ASSERT(timing.lastDownstreamTxByteSent().has_value()); stream_info.downstream_connection_info_provider_->setLocalAddress( std::make_shared("/foo")); (*stream_info.metadata_.mutable_filter_metadata())["foo"] = ProtobufWkt::Struct(); @@ -231,9 +238,11 @@ response: {} { NiceMock stream_info; - stream_info.host_ = nullptr; + stream_info.upstreamInfo()->setUpstreamHost(nullptr); stream_info.start_time_ = SystemTime(1h); - stream_info.last_downstream_tx_byte_sent_ = std::chrono::nanoseconds(2000000); + EXPECT_CALL(time_system, monotonicTime) + .WillOnce(Return(MonotonicTime(std::chrono::nanoseconds(2000000)))); + stream_info.downstream_timing_.onLastDownstreamTxByteSent(time_system); expectLog(R"EOF( common_properties: @@ -263,15 +272,26 @@ response: {} NiceMock stream_info; stream_info.start_time_ = SystemTime(1h); - stream_info.last_downstream_rx_byte_received_ = 2ms; - stream_info.first_upstream_tx_byte_sent_ = 4ms; - stream_info.last_upstream_tx_byte_sent_ = 6ms; - stream_info.first_upstream_rx_byte_received_ = 8ms; - stream_info.last_upstream_rx_byte_received_ = 10ms; - stream_info.first_downstream_tx_byte_sent_ = 12ms; - stream_info.last_downstream_tx_byte_sent_ = 14ms; - - stream_info.setUpstreamLocalAddress( + MockTimeSystem time_system; + EXPECT_CALL(time_system, monotonicTime) + .WillOnce(Return(MonotonicTime(std::chrono::milliseconds(2)))); + stream_info.downstream_timing_.onLastDownstreamRxByteReceived(time_system); + stream_info.upstream_info_->upstreamTiming().first_upstream_tx_byte_sent_ = + MonotonicTime(std::chrono::milliseconds(4)); + stream_info.upstream_info_->upstreamTiming().last_upstream_tx_byte_sent_ = + MonotonicTime(std::chrono::milliseconds(6)); + stream_info.upstream_info_->upstreamTiming().first_upstream_rx_byte_received_ = + MonotonicTime(std::chrono::milliseconds(8)); + stream_info.upstream_info_->upstreamTiming().last_upstream_rx_byte_received_ = + MonotonicTime(std::chrono::milliseconds(10)); + EXPECT_CALL(time_system, monotonicTime) + .WillOnce(Return(MonotonicTime(std::chrono::milliseconds(12)))); + stream_info.downstream_timing_.onFirstDownstreamTxByteSent(time_system); + EXPECT_CALL(time_system, monotonicTime) + .WillOnce(Return(MonotonicTime(std::chrono::milliseconds(14)))); + stream_info.downstream_timing_.onLastDownstreamTxByteSent(time_system); + + stream_info.upstream_info_->setUpstreamLocalAddress( std::make_shared("10.0.0.2")); stream_info.protocol_ = Http::Protocol::Http10; stream_info.addBytesReceived(10); @@ -363,9 +383,9 @@ protocol_version: HTTP10 { NiceMock stream_info; - stream_info.host_ = nullptr; + stream_info.upstreamInfo()->setUpstreamHost(nullptr); stream_info.start_time_ = SystemTime(1h); - stream_info.upstream_transport_failure_reason_ = "TLS error"; + stream_info.upstream_info_->setUpstreamTransportFailureReason("TLS error"); Http::TestRequestHeaderMapImpl request_headers{ {":method", "WHACKADOO"}, @@ -398,7 +418,7 @@ response: {} { NiceMock stream_info; - stream_info.host_ = nullptr; + stream_info.upstreamInfo()->setUpstreamHost(nullptr); stream_info.start_time_ = SystemTime(1h); auto connection_info = std::make_shared>(); @@ -465,7 +485,7 @@ response: {} // TLSv1.2 { NiceMock stream_info; - stream_info.host_ = nullptr; + stream_info.upstreamInfo()->setUpstreamHost(nullptr); stream_info.start_time_ = SystemTime(1h); auto connection_info = std::make_shared>(); @@ -515,7 +535,7 @@ response: {} // TLSv1.1 { NiceMock stream_info; - stream_info.host_ = nullptr; + stream_info.upstreamInfo()->setUpstreamHost(nullptr); stream_info.start_time_ = SystemTime(1h); auto connection_info = std::make_shared>(); @@ -565,7 +585,7 @@ response: {} // TLSv1 { NiceMock stream_info; - stream_info.host_ = nullptr; + stream_info.upstreamInfo()->setUpstreamHost(nullptr); stream_info.start_time_ = SystemTime(1h); auto connection_info = std::make_shared>(); @@ -615,7 +635,7 @@ response: {} // Unknown TLS version (TLSv1.4) { NiceMock stream_info; - stream_info.host_ = nullptr; + stream_info.upstreamInfo()->setUpstreamHost(nullptr); stream_info.start_time_ = SystemTime(1h); auto connection_info = std::make_shared>(); @@ -685,7 +705,7 @@ TEST_F(HttpGrpcAccessLogTest, MarshallingAdditionalHeaders) { { NiceMock stream_info; - stream_info.host_ = nullptr; + stream_info.upstreamInfo()->setUpstreamHost(nullptr); stream_info.start_time_ = SystemTime(1h); Http::TestRequestHeaderMapImpl request_headers{ diff --git a/test/extensions/access_loggers/grpc/http_grpc_access_log_integration_test.cc b/test/extensions/access_loggers/grpc/http_grpc_access_log_integration_test.cc index 75b064169115..117316e07476 100644 --- a/test/extensions/access_loggers/grpc/http_grpc_access_log_integration_test.cc +++ b/test/extensions/access_loggers/grpc/http_grpc_access_log_integration_test.cc @@ -15,6 +15,7 @@ #include "gtest/gtest.h" using testing::AssertionResult; +using testing::HasSubstr; namespace Envoy { namespace { @@ -185,7 +186,7 @@ TEST_P(AccessLogIntegrationTest, BasicAccessLogFlow) { test_server_->waitForCounterGe("grpc.accesslog.streams_closed_0", 1); break; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } response = IntegrationUtil::makeSingleRequest(lookupPort("http"), "GET", "/notfound", "", downstream_protocol_, version_); @@ -222,5 +223,128 @@ TEST_P(AccessLogIntegrationTest, BasicAccessLogFlow) { cleanup(); } +// 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(AccessLogIntegrationTest, ConfigureHttpOverGrpcLogs) { + setUpstreamProtocol(Http::CodecType::HTTP2); + setDownstreamProtocol(Http::CodecType::HTTP2); + config_helper_.addConfigModifier( + [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) -> void { + // Configure just enough of an upstream access log to reference the upstream headers. + const std::string yaml_string = R"EOF( +name: router +typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + upstream_log: + name: grpc_accesslog + filter: + not_health_check_filter: {} + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.grpc.v3.HttpGrpcAccessLogConfig + common_config: + log_name: foo + transport_api_version: V3 + grpc_service: + envoy_grpc: + cluster_name: cluster_0 + )EOF"; + // Replace the terminal envoy.router. + hcm.clear_http_filters(); + TestUtility::loadFromYaml(yaml_string, *hcm.add_http_filters()); + }); + + initialize(); + + // Send the request. + codec_client_ = makeHttpConnection(lookupPort("http")); + auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); + waitForNextUpstreamRequest(); + + // Send the response headers. + upstream_request_->encodeHeaders(default_response_headers_, true); + ASSERT_TRUE(response->waitForEndStream()); + EXPECT_EQ("200", response->headers().getStatusValue()); +} + +// Verify the grpc cached logger is available after the initial logger filter is destroyed. +// Regression test for https://github.com/envoyproxy/envoy/issues/18066 +TEST_P(AccessLogIntegrationTest, GrpcLoggerSurvivesAfterReloadConfig) { + autonomous_upstream_ = true; + // The grpc access logger connection never closes. It's ok to see an incomplete logging stream. + autonomous_allow_incomplete_streams_ = true; + + const std::string grpc_logger_string = R"EOF( + name: grpc_accesslog + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.grpc.v3.HttpGrpcAccessLogConfig + common_config: + log_name: bar + transport_api_version: V3 + grpc_service: + envoy_grpc: + cluster_name: cluster_0 + )EOF"; + + config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + auto* listener = bootstrap.mutable_static_resources()->mutable_listeners(0); + listener->set_stat_prefix("listener_0"); + }); + config_helper_.addConfigModifier( + [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) { TestUtility::loadFromYaml(grpc_logger_string, *hcm.add_access_log()); }); + initialize(); + // Given we're using LDS in this test, initialize() will not complete until + // the initial LDS file has loaded. + EXPECT_EQ(1, test_server_->counter("listener_manager.lds.update_success")->value()); + + // HTTP 1.1 is allowed and the connection is kept open until the listener update. + std::string response; + auto connection = + createConnectionDriver(lookupPort("http"), "GET / HTTP/1.1\r\nHost: host\r\n\r\n", + [&response, &dispatcher = *dispatcher_]( + Network::ClientConnection&, const Buffer::Instance& data) -> void { + response.append(data.toString()); + if (response.find("\r\n\r\n") != std::string::npos) { + dispatcher.exit(); + } + }); + connection->run(); + EXPECT_TRUE(response.find("HTTP/1.1 200") == 0); + + test_server_->waitForCounterEq("access_logs.grpc_access_log.logs_written", 2); + + // Create a new config with HTTP/1.0 proxying. The goal is to trigger a listener update. + ConfigHelper new_config_helper( + version_, *api_, MessageUtil::getJsonStringFromMessageOrDie(config_helper_.bootstrap())); + new_config_helper.addConfigModifier( + [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) { + hcm.mutable_http_protocol_options()->set_accept_http_10(true); + hcm.mutable_http_protocol_options()->set_default_host_for_http_10("default.com"); + }); + + // Create an LDS response with the new config, and reload config. + new_config_helper.setLds("1"); + test_server_->waitForCounterGe("listener_manager.listener_in_place_updated", 1); + test_server_->waitForCounterEq("listener_manager.lds.update_success", 2); + + // Wait until the http 1.1 connection is destroyed due to the listener update. It indicates the + // listener starts draining. + test_server_->waitForGaugeEq("listener.listener_0.downstream_cx_active", 0); + // Wait until all the draining filter chain is gone. It indicates the old listener and filter + // chains are destroyed. + test_server_->waitForGaugeEq("listener_manager.total_filter_chains_draining", 0); + + // Verify that the new listener config is applied. + std::string response2; + sendRawHttpAndWaitForResponse(lookupPort("http"), "GET / HTTP/1.0\r\n\r\n", &response2, true); + EXPECT_THAT(response2, HasSubstr("HTTP/1.0 200 OK\r\n")); + + // Verify that the grpc access logger is available after the listener update. + test_server_->waitForCounterEq("access_logs.grpc_access_log.logs_written", 4); +} + } // namespace } // namespace Envoy diff --git a/test/extensions/access_loggers/grpc/tcp_config_test.cc b/test/extensions/access_loggers/grpc/tcp_config_test.cc index b88f752d8609..bb18e7b81b42 100644 --- a/test/extensions/access_loggers/grpc/tcp_config_test.cc +++ b/test/extensions/access_loggers/grpc/tcp_config_test.cc @@ -34,12 +34,6 @@ class TcpGrpcAccessLogConfigTest : public testing::Test { void run(const std::string cluster_name) { const auto good_cluster = "good_cluster"; - EXPECT_CALL(context_.cluster_manager_, checkActiveStaticCluster(cluster_name)) - .WillOnce(Invoke([good_cluster](const std::string& cluster_name) { - if (cluster_name != good_cluster) { - throw EnvoyException("fake"); - } - })); auto* common_config = tcp_grpc_access_log_.mutable_common_config(); common_config->set_log_name("foo"); @@ -73,9 +67,6 @@ class TcpGrpcAccessLogConfigTest : public testing::Test { // Normal OK configuration. TEST_F(TcpGrpcAccessLogConfigTest, Ok) { run("good_cluster"); } -// Wrong configuration with invalid clusters. -TEST_F(TcpGrpcAccessLogConfigTest, InvalidCluster) { run("invalid"); } - class MockGrpcAccessLoggerCache : public GrpcCommon::GrpcAccessLoggerCache { public: // GrpcAccessLoggerCache diff --git a/test/extensions/access_loggers/open_telemetry/BUILD b/test/extensions/access_loggers/open_telemetry/BUILD index d77fbfb8e09c..1b13ed5d33f6 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 8ddbe5b1d91b..0be99aac72e6 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 @@ -12,6 +12,7 @@ #include "source/extensions/access_loggers/open_telemetry/access_log_impl.h" #include "test/mocks/access_log/mocks.h" +#include "test/mocks/common.h" #include "test/mocks/grpc/mocks.h" #include "test/mocks/local_info/mocks.h" #include "test/mocks/ssl/mocks.h" @@ -26,6 +27,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 +61,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 +77,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 +89,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. @@ -121,7 +101,10 @@ TEST_F(AccessLogTest, Marshalling) { InSequence s; NiceMock stream_info; stream_info.start_time_ = SystemTime(1h); - stream_info.last_downstream_rx_byte_received_ = 2ms; + MockTimeSystem time_system; + EXPECT_CALL(time_system, monotonicTime) + .WillOnce(Return(MonotonicTime(std::chrono::milliseconds(2)))); + stream_info.downstream_timing_.onLastDownstreamRxByteReceived(time_system); stream_info.protocol_ = Http::Protocol::Http10; stream_info.addBytesReceived(10); stream_info.response_code_ = 200; @@ -130,7 +113,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 +144,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/access_log_integration_test.cc b/test/extensions/access_loggers/open_telemetry/access_log_integration_test.cc index 4d0a16548516..99de5bae534d 100644 --- a/test/extensions/access_loggers/open_telemetry/access_log_integration_test.cc +++ b/test/extensions/access_loggers/open_telemetry/access_log_integration_test.cc @@ -166,7 +166,7 @@ TEST_P(AccessLogIntegrationTest, BasicAccessLogFlow) { test_server_->waitForCounterGe("grpc.accesslog.streams_closed_0", 1); break; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } response = IntegrationUtil::makeSingleRequest(lookupPort("http"), "GET", "/notfound", "", downstream_protocol_, version_); 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 b40e82c47236..79059617eecc 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" @@ -79,9 +80,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,35 +93,10 @@ 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) { - 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: @@ -143,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 000000000000..6ebfc5cc3748 --- /dev/null +++ b/test/extensions/access_loggers/open_telemetry/substitution_formatter_speed_test.cc @@ -0,0 +1,91 @@ +#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" + +using testing::NiceMock; + +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() { + 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; +} + +} // 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 000000000000..13e8dcc7277c --- /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/test/extensions/access_loggers/stream/stream_test_base.h b/test/extensions/access_loggers/stream/stream_test_base.h index 6a55c3c81f03..021c26766f3d 100644 --- a/test/extensions/access_loggers/stream/stream_test_base.h +++ b/test/extensions/access_loggers/stream/stream_test_base.h @@ -42,7 +42,7 @@ class StreamAccessLogTest : public testing::Test { absl::Time abslStartTime = TestUtility::parseTime("Dec 18 01:50:34 2018 GMT", "%b %e %H:%M:%S %Y GMT"); stream_info_.start_time_ = absl::ToChronoTime(abslStartTime); - EXPECT_CALL(stream_info_, upstreamHost()).WillRepeatedly(Return(nullptr)); + stream_info_.upstreamInfo()->setUpstreamHost(nullptr); stream_info_.response_code_ = 200; EXPECT_CALL(*file, write(_)).WillOnce(Invoke([expected, is_json](absl::string_view got) { diff --git a/test/extensions/clusters/aggregate/cluster_test.cc b/test/extensions/clusters/aggregate/cluster_test.cc index 524237f40d89..97f2ecb6983d 100644 --- a/test/extensions/clusters/aggregate/cluster_test.cc +++ b/test/extensions/clusters/aggregate/cluster_test.cc @@ -187,7 +187,7 @@ TEST_F(AggregateClusterTest, LoadBalancerTest) { EXPECT_FALSE(lifetime_callbacks.has_value()); std::vector hash_key = {1, 2, 3}; absl::optional selection = - lb_->selectPool(nullptr, *host, hash_key); + lb_->selectExistingConnection(nullptr, *host, hash_key); EXPECT_FALSE(selection.has_value()); EXPECT_EQ(host.get(), target.get()); } diff --git a/test/extensions/clusters/dynamic_forward_proxy/cluster_test.cc b/test/extensions/clusters/dynamic_forward_proxy/cluster_test.cc index 4589eaef81d7..4160758acafe 100644 --- a/test/extensions/clusters/dynamic_forward_proxy/cluster_test.cc +++ b/test/extensions/clusters/dynamic_forward_proxy/cluster_test.cc @@ -245,7 +245,7 @@ TEST_F(ClusterTest, LoadBalancer_SelectPoolNoConnections) { std::vector hash_key = {1, 2, 3}; absl::optional selection = - lb_->selectPool(&lb_context_, host, hash_key); + lb_->selectExistingConnection(&lb_context_, host, hash_key); EXPECT_FALSE(selection.has_value()); } @@ -276,7 +276,7 @@ TEST_F(ClusterTest, LoadBalancer_SelectPoolMatchingConnection) { EXPECT_CALL(*ssl_info, dnsSansPeerCertificate()).WillOnce(Return(dns_sans)); absl::optional selection = - lb_->selectPool(&lb_context_, host, hash_key); + lb_->selectExistingConnection(&lb_context_, host, hash_key); ASSERT_TRUE(selection.has_value()); EXPECT_EQ(&pool, &selection->pool_); @@ -309,7 +309,7 @@ TEST_F(ClusterTest, LoadBalancer_SelectPoolMatchingConnectionHttp3) { EXPECT_CALL(*ssl_info, dnsSansPeerCertificate()).WillOnce(Return(dns_sans)); absl::optional selection = - lb_->selectPool(&lb_context_, host, hash_key); + lb_->selectExistingConnection(&lb_context_, host, hash_key); ASSERT_TRUE(selection.has_value()); EXPECT_EQ(&pool, &selection->pool_); @@ -343,7 +343,7 @@ TEST_F(ClusterTest, LoadBalancer_SelectPoolNoMatchingConnectionAfterDraining) { lifetime_callbacks->onConnectionDraining(pool, hash_key, connection); absl::optional selection = - lb_->selectPool(&lb_context_, host, hash_key); + lb_->selectExistingConnection(&lb_context_, host, hash_key); ASSERT_FALSE(selection.has_value()); } @@ -372,7 +372,7 @@ TEST_F(ClusterTest, LoadBalancer_SelectPoolInvalidAlpn) { lifetime_callbacks->onConnectionOpen(pool, hash_key, connection); absl::optional selection = - lb_->selectPool(&lb_context_, host, hash_key); + lb_->selectExistingConnection(&lb_context_, host, hash_key); ASSERT_FALSE(selection.has_value()); } @@ -402,7 +402,7 @@ TEST_F(ClusterTest, LoadBalancer_SelectPoolSanMismatch) { EXPECT_CALL(*ssl_info, dnsSansPeerCertificate()).WillOnce(Return(dns_sans)); absl::optional selection = - lb_->selectPool(&lb_context_, host, hash_key); + lb_->selectExistingConnection(&lb_context_, host, hash_key); ASSERT_FALSE(selection.has_value()); } @@ -431,7 +431,7 @@ TEST_F(ClusterTest, LoadBalancer_SelectPoolHashMismatch) { hash_key[0]++; absl::optional selection = - lb_->selectPool(&lb_context_, host, hash_key); + lb_->selectExistingConnection(&lb_context_, host, hash_key); ASSERT_FALSE(selection.has_value()); } @@ -461,7 +461,7 @@ TEST_F(ClusterTest, LoadBalancer_SelectPoolIpMismatch) { EXPECT_CALL(*ssl_info, dnsSansPeerCertificate()).WillRepeatedly(Return(dns_sans)); absl::optional selection = - lb_->selectPool(&lb_context_, host, hash_key); + lb_->selectExistingConnection(&lb_context_, host, hash_key); ASSERT_FALSE(selection.has_value()); } @@ -495,7 +495,7 @@ TEST_F(ClusterTest, LoadBalancer_SelectPoolEmptyHostname) { EXPECT_CALL(empty_host, hostname()).WillRepeatedly(testing::ReturnRef(empty_hostname)); absl::optional selection = - lb_->selectPool(&lb_context_, empty_host, hash_key); + lb_->selectExistingConnection(&lb_context_, empty_host, hash_key); ASSERT_FALSE(selection.has_value()); } @@ -523,7 +523,7 @@ TEST_F(ClusterTest, LoadBalancer_SelectPoolNoSSSL) { lifetime_callbacks->onConnectionOpen(pool, hash_key, connection); absl::optional selection = - lb_->selectPool(&lb_context_, host, hash_key); + lb_->selectExistingConnection(&lb_context_, host, hash_key); ASSERT_FALSE(selection.has_value()); } diff --git a/test/extensions/common/aws/signer_impl_test.cc b/test/extensions/common/aws/signer_impl_test.cc index b3c060529ff8..ff5565def723 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 d19f8e220356..629f28c2c957 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/common/proxy_protocol/proxy_protocol_regression_test.cc b/test/extensions/common/proxy_protocol/proxy_protocol_regression_test.cc index cb9d97d5593f..0d906a0b7928 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::TestWithParamsetRemoteAddress(remote); info.downstream_connection_info_provider_->setRequestedServerName(sni_name); info.downstream_connection_info_provider_->setSslConnection(downstream_ssl_info); - EXPECT_CALL(info, upstreamSslConnection()).WillRepeatedly(Return(upstream_ssl_info)); - EXPECT_CALL(info, upstreamHost()).WillRepeatedly(Return(upstream_host)); - EXPECT_CALL(info, upstreamLocalAddress()).WillRepeatedly(ReturnRef(upstream_local_address)); const std::string upstream_transport_failure_reason = "ConnectionTermination"; - EXPECT_CALL(info, upstreamTransportFailureReason()) - .WillRepeatedly(ReturnRef(upstream_transport_failure_reason)); + info.upstreamInfo()->setUpstreamSslConnection(upstream_ssl_info); + info.upstreamInfo()->setUpstreamHost(upstream_host); + info.upstreamInfo()->setUpstreamLocalAddress(upstream_local_address); + info.upstreamInfo()->setUpstreamTransportFailureReason(upstream_transport_failure_reason); EXPECT_CALL(info, connectionID()).WillRepeatedly(Return(123)); info.downstream_connection_info_provider_->setConnectionID(123); const absl::optional connection_termination_details = "unauthorized"; diff --git a/test/extensions/filters/common/expr/evaluator_fuzz_test.cc b/test/extensions/filters/common/expr/evaluator_fuzz_test.cc index 108e7211a36d..2446fb4a8ac3 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/common/lua/wrappers_test.cc b/test/extensions/filters/common/lua/wrappers_test.cc index 565843d7e14c..dc7f9e5ff231 100644 --- a/test/extensions/filters/common/lua/wrappers_test.cc +++ b/test/extensions/filters/common/lua/wrappers_test.cc @@ -82,7 +82,8 @@ TEST_F(LuaBufferWrapperTest, Methods) { setup(SCRIPT); Buffer::OwnedImpl data("hello world"); - BufferWrapper::create(coroutine_->luaState(), data); + Http::TestRequestHeaderMapImpl headers; + BufferWrapper::create(coroutine_->luaState(), headers, data); EXPECT_CALL(printer_, testPrint("11")); EXPECT_CALL(printer_, testPrint("he")); EXPECT_CALL(printer_, testPrint("world")); @@ -101,7 +102,8 @@ TEST_F(LuaBufferWrapperTest, GetBytesInvalidParams) { setup(SCRIPT); Buffer::OwnedImpl data("hello world"); - BufferWrapper::create(coroutine_->luaState(), data); + Http::TestRequestHeaderMapImpl headers; + BufferWrapper::create(coroutine_->luaState(), headers, data); EXPECT_THROW_WITH_MESSAGE( start("callMe"), LuaException, "[string \"...\"]:3: index/length must be >= 0 and (index + length) must be <= buffer size"); diff --git a/test/extensions/filters/http/alternate_protocols_cache/filter_test.cc b/test/extensions/filters/http/alternate_protocols_cache/filter_test.cc index 51e0c8516150..dd11f5717fe8 100644 --- a/test/extensions/filters/http/alternate_protocols_cache/filter_test.cc +++ b/test/extensions/filters/http/alternate_protocols_cache/filter_test.cc @@ -104,9 +104,9 @@ TEST_F(FilterTest, ValidAltSvc) { std::string hostname = "host1"; std::shared_ptr hd = std::make_shared(); - StreamInfo::MockStreamInfo stream_info; + testing::NiceMock stream_info; EXPECT_CALL(callbacks_, streamInfo()).WillOnce(ReturnRef(stream_info)); - EXPECT_CALL(stream_info, upstreamHost()).WillOnce(testing::Return(hd)); + stream_info.upstreamInfo()->setUpstreamHost(hd); EXPECT_CALL(*hd, hostname()).WillOnce(ReturnRef(hostname)); EXPECT_CALL(*hd, address()).WillOnce(Return(address)); EXPECT_CALL(*address, ip()).WillOnce(Return(&ip)); 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 58842ca8fd4d..27c9577a4b49 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; diff --git a/test/extensions/filters/http/bandwidth_limit/config_test.cc b/test/extensions/filters/http/bandwidth_limit/config_test.cc index 853a3ca8896a..c7b66374d8f9 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 5d052318111c..7e2961cf90e1 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,18 +205,20 @@ 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_); 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)); @@ -215,6 +233,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 +252,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 +262,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 +275,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 +284,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 +297,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 +317,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 = @@ -303,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)); @@ -403,9 +432,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 +451,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")); @@ -431,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)); @@ -479,6 +512,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 +524,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")); @@ -502,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)); @@ -550,6 +585,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/cache/common.h b/test/extensions/filters/http/cache/common.h index 7b81ac7618c0..7b69f6126225 100644 --- a/test/extensions/filters/http/cache/common.h +++ b/test/extensions/filters/http/cache/common.h @@ -73,7 +73,7 @@ std::ostream& operator<<(std::ostream& os, CacheEntryStatus status) { case CacheEntryStatus::NotSatisfiableRange: return os << "NotSatisfiableRange"; } - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } std::ostream& operator<<(std::ostream& os, const AdjustedByteRange& range) { diff --git a/test/extensions/filters/http/cache/simple_http_cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache/simple_http_cache_test.cc index f2eb5ac181af..31b981ec50c3 100644 --- a/test/extensions/filters/http/cache/simple_http_cache/simple_http_cache_test.cc +++ b/test/extensions/filters/http/cache/simple_http_cache/simple_http_cache_test.cc @@ -343,17 +343,25 @@ TEST_F(SimpleHttpCacheTest, UpdateHeadersAndMetadata) { const std::string time_value_1 = formatter_.fromTime(time_source_.systemTime()); Http::TestResponseHeaderMapImpl response_headers{{"date", time_value_1}, {"cache-control", "public,max-age=3600"}}; + Http::TestResponseHeaderMapImpl response_headers_with_age(response_headers); + response_headers_with_age.setReferenceKey(Http::LowerCaseString("age"), "0"); + insert(request_path_1, response_headers, "body"); - EXPECT_TRUE(expectLookupSuccessWithHeaders(lookup(request_path_1).get(), response_headers)); + EXPECT_TRUE( + expectLookupSuccessWithHeaders(lookup(request_path_1).get(), response_headers_with_age)); // Update the date field in the headers time_source_.advanceTimeWait(Seconds(3601)); const SystemTime time_2 = time_source_.systemTime(); const std::string time_value_2 = formatter_.fromTime(time_2); - response_headers = Http::TestResponseHeaderMapImpl{{"date", time_value_2}, - {"cache-control", "public,max-age=3600"}}; - updateHeaders(request_path_1, response_headers, {time_2}); - EXPECT_TRUE(expectLookupSuccessWithHeaders(lookup(request_path_1).get(), response_headers)); + Http::TestResponseHeaderMapImpl response_headers_2 = Http::TestResponseHeaderMapImpl{ + {"date", time_value_2}, {"cache-control", "public,max-age=3600"}}; + Http::TestResponseHeaderMapImpl response_headers_with_age_2(response_headers_2); + response_headers_with_age_2.setReferenceKey(Http::LowerCaseString("age"), "0"); + + updateHeaders(request_path_1, response_headers_2, {time_2}); + EXPECT_TRUE( + expectLookupSuccessWithHeaders(lookup(request_path_1).get(), response_headers_with_age_2)); } TEST_F(SimpleHttpCacheTest, UpdateHeadersForMissingKey) { @@ -373,6 +381,8 @@ TEST_F(SimpleHttpCacheTest, UpdateHeadersDisabledForVaryHeaders) { {"accept", "image/*"}, {"vary", "accept"}}; insert(request_path_1, response_headers_1, "body"); + // An age header is inserted by `makeLookUpResult` + response_headers_1.setReferenceKey(Http::LowerCaseString("age"), "0"); EXPECT_TRUE(expectLookupSuccessWithHeaders(lookup(request_path_1).get(), response_headers_1)); // Update the date field in the headers @@ -384,7 +394,8 @@ TEST_F(SimpleHttpCacheTest, UpdateHeadersDisabledForVaryHeaders) { {"accept", "image/*"}, {"vary", "accept"}}; updateHeaders(request_path_1, response_headers_2, {time_2}); - + response_headers_1.setReferenceKey(Http::LowerCaseString("age"), "3600"); + // the age is still 0 because an entry is considered fresh after validation EXPECT_TRUE(expectLookupSuccessWithHeaders(lookup(request_path_1).get(), response_headers_1)); } @@ -394,6 +405,8 @@ TEST_F(SimpleHttpCacheTest, UpdateHeadersSkipEtagHeader) { Http::TestResponseHeaderMapImpl response_headers_1{ {"date", time_value_1}, {"cache-control", "public,max-age=3600"}, {"etag", "0000-0000"}}; insert(request_path_1, response_headers_1, "body"); + // An age header is inserted by `makeLookUpResult` + response_headers_1.setReferenceKey(Http::LowerCaseString("age"), "0"); EXPECT_TRUE(expectLookupSuccessWithHeaders(lookup(request_path_1).get(), response_headers_1)); // Update the date field in the headers @@ -407,7 +420,7 @@ TEST_F(SimpleHttpCacheTest, UpdateHeadersSkipEtagHeader) { {"date", time_value_2}, {"cache-control", "public,max-age=3600"}, {"etag", "0000-0000"}}; updateHeaders(request_path_1, response_headers_2, {time_2}); - + response_headers_3.setReferenceKey(Http::LowerCaseString("age"), "0"); EXPECT_TRUE(expectLookupSuccessWithHeaders(lookup(request_path_1).get(), response_headers_3)); } @@ -425,6 +438,9 @@ TEST_F(SimpleHttpCacheTest, UpdateHeadersSkipSpecificHeaders) { {"etag", "1111-1111"}, {"link", "; rel=\"preconnect\""}}; insert(request_path_1, origin_response_headers, "body"); + + // An age header is inserted by `makeLookUpResult` + origin_response_headers.setReferenceKey(Http::LowerCaseString("age"), "0"); EXPECT_TRUE( expectLookupSuccessWithHeaders(lookup(request_path_1).get(), origin_response_headers)); time_source_.advanceTimeWait(Seconds(100)); @@ -454,7 +470,6 @@ TEST_F(SimpleHttpCacheTest, UpdateHeadersSkipSpecificHeaders) { {"link", "; rel=\"preconnect\""}}; updateHeaders(request_path_1, incoming_response_headers, {time_2}); - EXPECT_TRUE( expectLookupSuccessWithHeaders(lookup(request_path_1).get(), expected_response_headers)); } @@ -471,6 +486,9 @@ TEST_F(SimpleHttpCacheTest, UpdateHeadersWithMultivalue) { {"link", "; rel=\"preconnect\""}, {"link", "; rel=\"preconnect\""}}; insert(request_path_1, response_headers_1, "body"); + + // An age header is inserted by `makeLookUpResult` + response_headers_1.setReferenceKey(Http::LowerCaseString("age"), "0"); EXPECT_TRUE(expectLookupSuccessWithHeaders(lookup(request_path_1).get(), response_headers_1)); Http::TestResponseHeaderMapImpl response_headers_2{ @@ -481,6 +499,7 @@ TEST_F(SimpleHttpCacheTest, UpdateHeadersWithMultivalue) { updateHeaders(request_path_1, response_headers_2, {time_1}); + response_headers_2.setReferenceKey(Http::LowerCaseString("age"), "0"); EXPECT_TRUE(expectLookupSuccessWithHeaders(lookup(request_path_1).get(), response_headers_2)); } 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 97f18f5957ca..7fc0aeca1c66 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()); 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 551112736898..30311497e307 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 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 c95efec61682..2a71f84c59c3 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 31c7f211cd25..1a9dd4ed6f74 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/dynamic_forward_proxy/BUILD b/test/extensions/filters/http/dynamic_forward_proxy/BUILD index ec2a0c9534ed..1dcafd08831a 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 906de4085a3c..b9829dd6286d 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 @@ -429,7 +439,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")); @@ -446,5 +460,67 @@ TEST_P(ProxyFilterIntegrationTest, UseCacheFileAndTestHappyEyeballs) { EXPECT_EQ(1, test_server_->counter("dns_cache.foo.host_added")->value()); } +TEST_P(ProxyFilterIntegrationTest, MultipleRequestsLowStreamLimit) { + setDownstreamProtocol(Http::CodecType::HTTP2); + setUpstreamProtocol(Http::CodecType::HTTP2); + + // Ensure we only have one connection upstream, one request active at a time. + config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + envoy::config::bootstrap::v3::Bootstrap::StaticResources* static_resources = + bootstrap.mutable_static_resources(); + envoy::config::cluster::v3::Cluster* cluster = static_resources->mutable_clusters(0); + envoy::config::cluster::v3::CircuitBreakers* circuit_breakers = + cluster->mutable_circuit_breakers(); + circuit_breakers->add_thresholds()->mutable_max_connections()->set_value(1); + ConfigHelper::HttpProtocolOptions protocol_options; + protocol_options.mutable_explicit_http_config() + ->mutable_http2_protocol_options() + ->mutable_max_concurrent_streams() + ->set_value(1); + ConfigHelper::setProtocolOptions(*bootstrap.mutable_static_resources()->mutable_clusters(0), + protocol_options); + }); + + // Start sending the request, but ensure no end stream will be sent, so the + // stream will stay in use. + initialize(); + codec_client_ = makeHttpConnection(lookupPort("http")); + + // Start sending the request, but ensure no end stream will be sent, so the + // stream will stay in use. + std::pair encoder_decoder = + codec_client_->startRequest(default_request_headers_); + request_encoder_ = &encoder_decoder.first; + IntegrationStreamDecoderPtr response = std::move(encoder_decoder.second); + + // Make sure the headers are received. + ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_)); + ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_)); + ASSERT_TRUE(upstream_request_->waitForHeadersComplete()); + + // Start another request. + IntegrationStreamDecoderPtr response2 = + codec_client_->makeHeaderOnlyRequest(default_request_headers_); + test_server_->waitForCounterEq("http.config_test.downstream_rq_total", 2); + // Make sure the stream is not received. + ASSERT_FALSE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_, + std::chrono::milliseconds(100))); + + // Finish the first stream. + codec_client_->sendData(*request_encoder_, 0, true); + upstream_request_->encodeHeaders(default_response_headers_, true); + ASSERT_TRUE(response->waitForEndStream()); + EXPECT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().getStatusValue()); + + // This should allow the second stream to complete + ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_)); + ASSERT_TRUE(upstream_request_->waitForHeadersComplete()); + upstream_request_->encodeHeaders(default_response_headers_, true); + ASSERT_TRUE(response2->waitForEndStream()); + EXPECT_TRUE(response2->complete()); + EXPECT_EQ("200", response2->headers().getStatusValue()); +} + } // namespace } // namespace Envoy 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 c2150e500dd9..4658d20ffd9b 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,11 +155,14 @@ 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})); EXPECT_CALL(callbacks_, sendLocalReply(Http::Code::ServiceUnavailable, Eq("DNS cache overflow"), - _, _, Eq("DNS cache overflow"))); + _, _, Eq("dns_cache_overflow"))); EXPECT_CALL(callbacks_, encodeHeaders_(_, false)); EXPECT_CALL(callbacks_, encodeData(_, true)); EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, @@ -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, _)) @@ -189,7 +200,7 @@ TEST_F(ProxyFilterTest, CircuitBreakerOverflow) { EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_()); EXPECT_CALL(callbacks_, sendLocalReply(Http::Code::ServiceUnavailable, Eq("Dynamic forward proxy pending request overflow"), _, _, - Eq("Dynamic forward proxy pending request overflow"))); + Eq("dynamic_forward_proxy_pending_request_overflow"))); EXPECT_CALL(callbacks_, encodeHeaders_(_, false)); EXPECT_CALL(callbacks_, encodeData(_, true)); EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, @@ -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})); @@ -227,7 +240,7 @@ TEST_F(ProxyFilterTest, CircuitBreakerOverflowWithDnsCacheResourceManager) { EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_()); EXPECT_CALL(callbacks_, sendLocalReply(Http::Code::ServiceUnavailable, Eq("Dynamic forward proxy pending request overflow"), _, _, - Eq("Dynamic forward proxy pending request overflow"))); + Eq("dynamic_forward_proxy_pending_request_overflow"))); EXPECT_CALL(callbacks_, encodeHeaders_(_, false)); EXPECT_CALL(callbacks_, encodeData(_, true)); EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, @@ -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/extensions/filters/http/dynamo/dynamo_filter_test.cc b/test/extensions/filters/http/dynamo/dynamo_filter_test.cc index 612537e8c157..0ce73327d294 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_authz/BUILD b/test/extensions/filters/http/ext_authz/BUILD index 4bdae57469fe..a1de2e868307 100644 --- a/test/extensions/filters/http/ext_authz/BUILD +++ b/test/extensions/filters/http/ext_authz/BUILD @@ -1,6 +1,8 @@ load( "//bazel:envoy_build_system.bzl", + "envoy_cc_fuzz_test", "envoy_package", + "envoy_proto_library", ) load( "//test/extensions:extensions_build_system.bzl", @@ -77,3 +79,31 @@ envoy_extension_cc_test( "@envoy_api//envoy/service/auth/v3:pkg_cc_proto", ], ) + +envoy_proto_library( + name = "ext_authz_fuzz_proto", + srcs = ["ext_authz_fuzz.proto"], + deps = [ + "//test/fuzz:common_proto", + "@envoy_api//envoy/config/core/v3:pkg", + "@envoy_api//envoy/extensions/filters/http/ext_authz/v3:pkg", + ], +) + +envoy_cc_fuzz_test( + name = "ext_authz_fuzz_test", + srcs = ["ext_authz_fuzz_test.cc"], + corpus = "ext_authz_corpus", + deps = [ + ":ext_authz_fuzz_proto_cc_proto", + "//source/common/http:context_lib", + "//source/common/network:address_lib", + "//source/extensions/filters/http/ext_authz", + "//test/extensions/filters/common/ext_authz:ext_authz_mocks", + "//test/extensions/filters/http/common/fuzz:http_filter_fuzzer_lib", + "//test/mocks/http:http_mocks", + "//test/mocks/network:network_mocks", + "//test/mocks/runtime:runtime_mocks", + "@envoy_api//envoy/extensions/filters/http/ext_authz/v3:pkg_cc_proto", + ], +) diff --git a/test/extensions/filters/http/ext_authz/ext_authz_corpus/bad_config b/test/extensions/filters/http/ext_authz/ext_authz_corpus/bad_config new file mode 100644 index 000000000000..0467323519e3 --- /dev/null +++ b/test/extensions/filters/http/ext_authz/ext_authz_corpus/bad_config @@ -0,0 +1,33 @@ +config { + status_on_error { + code: Continue + } + metadata_context_namespaces: "=" + stat_prefix: "r" + filter_enabled_metadata { + filter: "\177\177\177\177\177\177\177\177" + path { + key: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + value { + string_match { + safe_regex { + google_re2 { + } + regex: "z" + } + ignore_case: true + } + } + } + bootstrap_metadata_labels_key: "r" +} +request_data { +} +filter_metadata { + typed_filter_metadata { + key: ":=" + value { + } + } +} \ No newline at end of file diff --git a/test/extensions/filters/http/ext_authz/ext_authz_corpus/custom_status b/test/extensions/filters/http/ext_authz/ext_authz_corpus/custom_status new file mode 100644 index 000000000000..6a9192e46ab3 --- /dev/null +++ b/test/extensions/filters/http/ext_authz/ext_authz_corpus/custom_status @@ -0,0 +1,22 @@ +config { + grpc_service { + envoy_grpc { + cluster_name: "ext_authz_server" + } + } + status_on_error { + code: ServiceUnavilable + } +} +request_data { + headers { + headers { + key: ":host" + value: "example.com" + } + headers { + key: ":method" + value: "GET" + } + } +} diff --git a/test/extensions/filters/http/ext_authz/ext_authz_corpus/error_fail_close b/test/extensions/filters/http/ext_authz/ext_authz_corpus/error_fail_close new file mode 100644 index 000000000000..90eb4a71ef41 --- /dev/null +++ b/test/extensions/filters/http/ext_authz/ext_authz_corpus/error_fail_close @@ -0,0 +1,31 @@ +config { + grpc_service { + envoy_grpc { + cluster_name: "ext_authz_server" + } + } + stat_prefix: "with_stat_prefix" +} +request_data { + headers { + headers { + key: ":host" + value: "example.com" + } + headers { + key: ":method" + value: "GET" + } + headers { + key: ":path" + value: "/users" + } + headers { + key: ":scheme" + value: "https" + } + } +} +result { + error {} +} diff --git a/test/extensions/filters/http/ext_authz/ext_authz_corpus/example b/test/extensions/filters/http/ext_authz/ext_authz_corpus/example new file mode 100644 index 000000000000..f51532b69fc4 --- /dev/null +++ b/test/extensions/filters/http/ext_authz/ext_authz_corpus/example @@ -0,0 +1,9 @@ +config { +} +request_data { + + +} +result { + error {} +} diff --git a/test/extensions/filters/http/ext_authz/ext_authz_corpus/metadata_context b/test/extensions/filters/http/ext_authz/ext_authz_corpus/metadata_context new file mode 100644 index 000000000000..e8ee26efe390 --- /dev/null +++ b/test/extensions/filters/http/ext_authz/ext_authz_corpus/metadata_context @@ -0,0 +1,86 @@ +config { + grpc_service { + envoy_grpc { + cluster_name: "ext_authz_server" + } + } + metadata_context_namespaces: "jazz.sax" + metadata_context_namespaces: "rock.guitar" + metadata_context_namespaces: "hiphop.drums" +} +filter_metadata { +filter_metadata { + key: "jazz.piano" + value { + fields { + key: "hancock" + value { + string_value: "herbie" + } + } + fields { + key: "monk" + value { + string_value: "thelonious" + } + } + } +} +filter_metadata { + key: "jazz.sax" + value { + fields { + key: "coltrane" + value { + string_value: "john" + } + } + fields { + key: "parker" + value { + string_value: "charlie" + } + } + } +} +filter_metadata { + key: "rock.guitar" + value { + fields { + key: "hendrix" + value { + string_value: "jimi" + } + } + fields { + key: "richards" + value { + string_value: "keith" + } + } + } +} +} +result { + ok {} +} +request_data { + headers { + headers { + key: ":host" + value: "example.com" + } + headers { + key: ":method" + value: "GET" + } + headers { + key: ":path" + value: "/users" + } + headers { + key: ":scheme" + value: "https" + } + } +} diff --git a/test/extensions/filters/http/ext_authz/ext_authz_corpus/request_data b/test/extensions/filters/http/ext_authz/ext_authz_corpus/request_data new file mode 100644 index 000000000000..e578bd966943 --- /dev/null +++ b/test/extensions/filters/http/ext_authz/ext_authz_corpus/request_data @@ -0,0 +1,33 @@ +config { + grpc_service { + envoy_grpc { + cluster_name: "ext_authz_server" + } + } + with_request_body { + max_request_bytes: 10 + } +} +request_data { + headers { + headers { + key: ":host" + value: "example.com" + } + headers { + key: ":method" + value: "GET" + } + headers { + key: ":path" + value: "/users" + } + headers { + key: ":scheme" + value: "https" + } + } + http_body { + data: "foobarbaz" + } +} diff --git a/test/extensions/filters/http/ext_authz/ext_authz_fuzz.proto b/test/extensions/filters/http/ext_authz/ext_authz_fuzz.proto new file mode 100644 index 000000000000..21728cb905e1 --- /dev/null +++ b/test/extensions/filters/http/ext_authz/ext_authz_fuzz.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; +package envoy.extensions.filters.http.ext_authz; + +import "envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto"; +import "test/fuzz/common.proto"; +import "envoy/config/core/v3/base.proto"; +import "google/protobuf/empty.proto"; +import "validate/validate.proto"; + +// We only fuzz a single request per iteration. +message ExtAuthzTestCase { + enum AuthResult { + // Possible results for a check call. Taken from + // https://github.com/envoyproxy/envoy/blob/945b5833f094dee31d2971cee8d40553bb0fe714/source/extensions/filters/common/ext_authz/ext_authz.h#L65 + OK = 0; + DENIED = 1; + ERROR = 2; + } + + envoy.extensions.filters.http.ext_authz.v3.ExtAuthz config = 1 + [(validate.rules).message = {required: true}]; + // HTTP request data. + test.fuzz.HttpData request_data = 2 [(validate.rules).message = {required: true}]; + // Set default auth check result. + AuthResult result = 3; + // Filter metadata. + envoy.config.core.v3.Metadata filter_metadata = 4; + // TODO: Add headers and data to ExtAuthz::Response and check that the request headers and data + // were updated. +} diff --git a/test/extensions/filters/http/ext_authz/ext_authz_fuzz_test.cc b/test/extensions/filters/http/ext_authz/ext_authz_fuzz_test.cc new file mode 100644 index 000000000000..411e9d21e2a4 --- /dev/null +++ b/test/extensions/filters/http/ext_authz/ext_authz_fuzz_test.cc @@ -0,0 +1,132 @@ +#include "envoy/extensions/filters/http/ext_authz/v3/ext_authz.pb.validate.h" + +#include "source/common/http/context_impl.h" +#include "source/common/network/address_impl.h" +#include "source/extensions/filters/http/ext_authz/ext_authz.h" + +#include "test/extensions/filters/common/ext_authz/mocks.h" +#include "test/extensions/filters/http/common/fuzz/http_filter_fuzzer.h" +#include "test/extensions/filters/http/ext_authz/ext_authz_fuzz.pb.validate.h" +#include "test/fuzz/fuzz_runner.h" +#include "test/mocks/http/mocks.h" +#include "test/mocks/network/mocks.h" +#include "test/mocks/runtime/mocks.h" + +#include "gmock/gmock.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace ExtAuthz { +namespace { + +Filters::Common::ExtAuthz::ResponsePtr +makeAuthzResponse(const Filters::Common::ExtAuthz::CheckStatus status) { + Filters::Common::ExtAuthz::ResponsePtr response = + std::make_unique(); + response->status = status; + // TODO: We only add the response status. + // Add fuzzed inputs for headers_to_(set/append/add), body, status_code to the Response. + return response; +} + +Filters::Common::ExtAuthz::CheckStatus resultCaseToCheckStatus( + const envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase::AuthResult result) { + Filters::Common::ExtAuthz::CheckStatus check_status; + switch (result) { + case envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase::OK: { + check_status = Filters::Common::ExtAuthz::CheckStatus::OK; + break; + } + case envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase::ERROR: { + check_status = Filters::Common::ExtAuthz::CheckStatus::Error; + break; + } + case envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase::DENIED: { + check_status = Filters::Common::ExtAuthz::CheckStatus::Denied; + break; + } + default: { + // Unhandled status. + NOT_IMPLEMENTED_GCOVR_EXCL_LINE; + } + } + return check_status; +} + +class FuzzerMocks { +public: + FuzzerMocks() : addr_(std::make_shared("/test/test.sock")) { + + ON_CALL(decoder_callbacks_, connection()).WillByDefault(Return(&connection_)); + connection_.stream_info_.downstream_connection_info_provider_->setRemoteAddress(addr_); + connection_.stream_info_.downstream_connection_info_provider_->setLocalAddress(addr_); + } + + NiceMock runtime_; + NiceMock decoder_callbacks_; + NiceMock encoder_callbacks_; + Network::Address::InstanceConstSharedPtr addr_; + NiceMock connection_; +}; + +DEFINE_PROTO_FUZZER(const envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase& input) { + try { + TestUtility::validate(input); + } catch (const EnvoyException& e) { + ENVOY_LOG_MISC(debug, "EnvoyException during validation: {}", e.what()); + return; + } + + static FuzzerMocks mocks; + NiceMock stats_store; + envoy::config::bootstrap::v3::Bootstrap bootstrap; + Http::ContextImpl http_context(stats_store.symbolTable()); + + // Prepare filter. + const envoy::extensions::filters::http::ext_authz::v3::ExtAuthz proto_config = input.config(); + FilterConfigSharedPtr config; + + try { + config = std::make_shared(proto_config, stats_store, mocks.runtime_, http_context, + "ext_authz_prefix", bootstrap); + } catch (const EnvoyException& e) { + ENVOY_LOG_MISC(debug, "EnvoyException during filter config validation: {}", e.what()); + return; + } + + Filters::Common::ExtAuthz::MockClient* client = new Filters::Common::ExtAuthz::MockClient(); + std::unique_ptr filter = + std::make_unique(config, Filters::Common::ExtAuthz::ClientPtr{client}); + filter->setDecoderFilterCallbacks(mocks.decoder_callbacks_); + filter->setEncoderFilterCallbacks(mocks.encoder_callbacks_); + + // Set metadata context. + envoy::config::core::v3::Metadata metadata = input.filter_metadata(); + + ON_CALL(mocks.decoder_callbacks_.stream_info_, dynamicMetadata()) + .WillByDefault(testing::ReturnRef(metadata)); + // Set check result default action. + envoy::service::auth::v3::CheckRequest check_request; + ON_CALL(*client, check(_, _, _, _)) + .WillByDefault(Invoke([&](Filters::Common::ExtAuthz::RequestCallbacks& callbacks, + const envoy::service::auth::v3::CheckRequest& check_param, + Tracing::Span&, const StreamInfo::StreamInfo&) -> void { + check_request = check_param; + callbacks.onComplete(makeAuthzResponse(resultCaseToCheckStatus(input.result()))); + })); + + // TODO: Add response headers. + Envoy::Extensions::HttpFilters::HttpFilterFuzzer fuzzer; + fuzzer.runData(static_cast(filter.get()), + input.request_data()); + // TODO: Query the request header map in HttpFilterFuzzer to test headers_to_(add/remove/append). + // TODO: Test check request attributes against config and filter metadata. + ENVOY_LOG_MISC(trace, "Check Request attributes {}", check_request.attributes().DebugString()); +} + +} // namespace +} // namespace ExtAuthz +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/filters/http/ext_authz/ext_authz_test.cc b/test/extensions/filters/http/ext_authz/ext_authz_test.cc index 8f3033bd225a..20ecd4331a11 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_test.cc +++ b/test/extensions/filters/http/ext_authz/ext_authz_test.cc @@ -1916,14 +1916,15 @@ TEST_P(HttpFilterTestParam, DeniedResponseWith401) { Invoke([&](Filters::Common::ExtAuthz::RequestCallbacks& callbacks, const envoy::service::auth::v3::CheckRequest&, Tracing::Span&, const StreamInfo::StreamInfo&) -> void { request_callbacks_ = &callbacks; })); + + EXPECT_CALL(filter_callbacks_.stream_info_, + setResponseFlag(Envoy::StreamInfo::ResponseFlag::UnauthorizedExternalService)); EXPECT_EQ(Http::FilterHeadersStatus::StopAllIterationAndWatermark, filter_->decodeHeaders(request_headers_, false)); Http::TestResponseHeaderMapImpl response_headers{{":status", "401"}}; EXPECT_CALL(filter_callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), true)); EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); - EXPECT_CALL(filter_callbacks_.stream_info_, - setResponseFlag(Envoy::StreamInfo::ResponseFlag::UnauthorizedExternalService)); Filters::Common::ExtAuthz::Response response{}; response.status = Filters::Common::ExtAuthz::CheckStatus::Denied; @@ -1948,14 +1949,15 @@ TEST_P(HttpFilterTestParam, DeniedResponseWith403) { Invoke([&](Filters::Common::ExtAuthz::RequestCallbacks& callbacks, const envoy::service::auth::v3::CheckRequest&, Tracing::Span&, const StreamInfo::StreamInfo&) -> void { request_callbacks_ = &callbacks; })); + + EXPECT_CALL(filter_callbacks_.stream_info_, + setResponseFlag(Envoy::StreamInfo::ResponseFlag::UnauthorizedExternalService)); EXPECT_EQ(Http::FilterHeadersStatus::StopAllIterationAndWatermark, filter_->decodeHeaders(request_headers_, false)); Http::TestResponseHeaderMapImpl response_headers{{":status", "403"}}; EXPECT_CALL(filter_callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), true)); EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); - EXPECT_CALL(filter_callbacks_.stream_info_, - setResponseFlag(Envoy::StreamInfo::ResponseFlag::UnauthorizedExternalService)); Filters::Common::ExtAuthz::Response response{}; response.status = Filters::Common::ExtAuthz::CheckStatus::Denied; diff --git a/test/extensions/filters/http/ext_proc/ext_proc_integration_test.cc b/test/extensions/filters/http/ext_proc/ext_proc_integration_test.cc index aeffd710edc2..661ebdc6d72b 100644 --- a/test/extensions/filters/http/ext_proc/ext_proc_integration_test.cc +++ b/test/extensions/filters/http/ext_proc/ext_proc_integration_test.cc @@ -447,11 +447,9 @@ TEST_P(ExtProcIntegrationTest, GetAndSetHeaders) { [](Http::HeaderMap& headers) { headers.addCopy(LowerCaseString("x-remove-this"), "yes"); }); processRequestHeadersMessage(true, [](const HttpHeaders& headers, HeadersResponse& headers_resp) { - Http::TestRequestHeaderMapImpl expected_request_headers{{":scheme", "http"}, - {":method", "GET"}, - {"host", "host"}, - {":path", "/"}, - {"x-remove-this", "yes"}}; + Http::TestRequestHeaderMapImpl expected_request_headers{ + {":scheme", "http"}, {":method", "GET"}, {"host", "host"}, + {":path", "/"}, {"x-remove-this", "yes"}, {"x-forwarded-proto", "http"}}; EXPECT_THAT(headers.headers(), HeaderProtosEqual(expected_request_headers)); auto response_header_mutation = headers_resp.mutable_response()->mutable_header_mutation(); diff --git a/test/extensions/filters/http/ext_proc/filter_test.cc b/test/extensions/filters/http/ext_proc/filter_test.cc index 386f7401d48f..63b974685e16 100644 --- a/test/extensions/filters/http/ext_proc/filter_test.cc +++ b/test/extensions/filters/http/ext_proc/filter_test.cc @@ -414,7 +414,7 @@ TEST_F(HttpFilterTest, PostAndRespondImmediately) { Http::TestResponseHeaderMapImpl immediate_response_headers; EXPECT_CALL(encoder_callbacks_, sendLocalReply(Http::Code::BadRequest, "Bad request", _, - Eq(absl::nullopt), "Got a bad request")) + Eq(absl::nullopt), "Got_a_bad_request")) .WillOnce(Invoke([&immediate_response_headers]( Unused, Unused, std::function modify_headers, @@ -486,7 +486,7 @@ TEST_F(HttpFilterTest, PostAndRespondImmediatelyOnResponse) { Http::TestResponseHeaderMapImpl immediate_response_headers; EXPECT_CALL(encoder_callbacks_, sendLocalReply(Http::Code::BadRequest, "Bad request", _, - Eq(absl::nullopt), "Got a bad request")) + Eq(absl::nullopt), "Got_a_bad_request")) .WillOnce(Invoke([&immediate_response_headers]( Unused, Unused, std::function modify_headers, @@ -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; @@ -1540,9 +1540,8 @@ TEST_F(HttpFilterTest, PostAndFail) { // Oh no! The remote server had a failure! Http::TestResponseHeaderMapImpl immediate_response_headers; - EXPECT_CALL(encoder_callbacks_, - sendLocalReply(Http::Code::InternalServerError, "", _, Eq(absl::nullopt), - "ext_proc error: gRPC error 13")) + EXPECT_CALL(encoder_callbacks_, sendLocalReply(Http::Code::InternalServerError, "", _, + Eq(absl::nullopt), "ext_proc_error_gRPC_error_13")) .WillOnce(Invoke([&immediate_response_headers]( Unused, Unused, std::function modify_headers, @@ -1598,9 +1597,8 @@ TEST_F(HttpFilterTest, PostAndFailOnResponse) { // Oh no! The remote server had a failure! Http::TestResponseHeaderMapImpl immediate_response_headers; - EXPECT_CALL(encoder_callbacks_, - sendLocalReply(Http::Code::InternalServerError, "", _, Eq(absl::nullopt), - "ext_proc error: gRPC error 13")) + EXPECT_CALL(encoder_callbacks_, sendLocalReply(Http::Code::InternalServerError, "", _, + Eq(absl::nullopt), "ext_proc_error_gRPC_error_13")) .WillOnce(Invoke([&immediate_response_headers]( Unused, Unused, std::function modify_headers, diff --git a/test/extensions/filters/http/ext_proc/utils.cc b/test/extensions/filters/http/ext_proc/utils.cc index c9bb93b4738d..4bc448909b18 100644 --- a/test/extensions/filters/http/ext_proc/utils.cc +++ b/test/extensions/filters/http/ext_proc/utils.cc @@ -7,13 +7,20 @@ namespace Extensions { namespace HttpFilters { namespace ExternalProcessing { +const absl::flat_hash_set ExtProcTestUtility::ignoredHeaders() { + CONSTRUCT_ON_FIRST_USE(absl::flat_hash_set, "x-request-id", + "x-envoy-upstream-service-time"); +} + bool ExtProcTestUtility::headerProtosEqualIgnoreOrder( const Http::HeaderMap& expected, const envoy::config::core::v3::HeaderMap& actual) { // Comparing header maps is hard because they have duplicates in them. // So we're going to turn them into a HeaderMap and let Envoy do the work. Http::TestRequestHeaderMapImpl actual_headers; for (const auto& header : actual.headers()) { - actual_headers.addCopy(header.key(), header.value()); + if (!ignoredHeaders().contains(header.key())) { + actual_headers.addCopy(header.key(), header.value()); + } } return TestUtility::headerMapEqualIgnoreOrder(expected, actual_headers); } diff --git a/test/extensions/filters/http/ext_proc/utils.h b/test/extensions/filters/http/ext_proc/utils.h index 98274370d945..ad6b0f4af104 100644 --- a/test/extensions/filters/http/ext_proc/utils.h +++ b/test/extensions/filters/http/ext_proc/utils.h @@ -16,6 +16,11 @@ class ExtProcTestUtility { // Compare a reference header map to a proto static bool headerProtosEqualIgnoreOrder(const Http::HeaderMap& expected, const envoy::config::core::v3::HeaderMap& actual); + +private: + // These headers are present in the actual, but cannot be specified in the expected + // ignoredHeaders should not be used for equal comparison + static const absl::flat_hash_set ignoredHeaders(); }; MATCHER_P(HeaderProtosEqual, expected, "HTTP header protos match") { diff --git a/test/extensions/filters/http/fault/fault_filter_test.cc b/test/extensions/filters/http/fault/fault_filter_test.cc index 9b6e1347c9e3..a9c4be91ecc2 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 96f7fb3aac75..8f66fd52f616 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_http1_reverse_bridge/reverse_bridge_integration_test.cc b/test/extensions/filters/http/grpc_http1_reverse_bridge/reverse_bridge_integration_test.cc index 9a05608688b5..029d1153477c 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_; }; diff --git a/test/extensions/filters/http/grpc_json_transcoder/grpc_json_transcoder_integration_test.cc b/test/extensions/filters/http/grpc_json_transcoder/grpc_json_transcoder_integration_test.cc index fccc8902d00c..474d74e96aa8 100644 --- a/test/extensions/filters/http/grpc_json_transcoder/grpc_json_transcoder_integration_test.cc +++ b/test/extensions/filters/http/grpc_json_transcoder/grpc_json_transcoder_integration_test.cc @@ -1321,72 +1321,5 @@ TEST_P(OverrideConfigGrpcJsonTranscoderIntegrationTest, RouteOverride) { R"({"shelves":[{"id":"20","theme":"Children"},{"id":"1","theme":"Foo"}]})"); }; -// Tests to ensure transcoding buffer limits do not apply when the runtime feature is disabled. -class BufferLimitsDisabledGrpcJsonTranscoderIntegrationTest - : public GrpcJsonTranscoderIntegrationTest { -public: - void SetUp() override { - setUpstreamProtocol(Http::CodecType::HTTP2); - const std::string filter = - R"EOF( - name: grpc_json_transcoder - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder - proto_descriptor : "{}" - services : "bookstore.Bookstore" - )EOF"; - config_helper_.prependFilter( - fmt::format(filter, TestEnvironment::runfilesPath("test/proto/bookstore.descriptor"))); - - // Disable runtime feature. - config_helper_.addRuntimeOverride( - "envoy.reloadable_features.grpc_json_transcoder_adhere_to_buffer_limits", "false"); - } -}; -INSTANTIATE_TEST_SUITE_P(IpVersions, BufferLimitsDisabledGrpcJsonTranscoderIntegrationTest, - testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), - TestUtility::ipTestParamsToString); - -TEST_P(BufferLimitsDisabledGrpcJsonTranscoderIntegrationTest, UnaryPostRequestExceedsBufferLimit) { - // Request body is more than 20 bytes. - config_helper_.setBufferLimits(2 << 20, 20); - HttpIntegrationTest::initialize(); - - // Transcoding succeeds. - testTranscoding( - Http::TestRequestHeaderMapImpl{{":method", "POST"}, - {":path", "/shelf"}, - {":authority", "host"}, - {"content-type", "application/json"}}, - R"({"theme": "Children 0123456789 0123456789 0123456789 0123456789"})", - {R"(shelf { theme: "Children 0123456789 0123456789 0123456789 0123456789" })"}, {R"(id: 1)"}, - Status(), - Http::TestResponseHeaderMapImpl{{":status", "200"}, - {"content-type", "application/json"}, - {"content-length", "10"}, - {"grpc-status", "0"}}, - R"({"id":"1"})"); -} - -TEST_P(BufferLimitsDisabledGrpcJsonTranscoderIntegrationTest, UnaryPostResponseExceedsBufferLimit) { - // Request body is less than 35 bytes. - // Response body is more than 35 bytes. - config_helper_.setBufferLimits(2 << 20, 35); - HttpIntegrationTest::initialize(); - - // Transcoding succeeds. However, the downstream client is unable to buffer the full response. - // We can tell these errors are NOT from the transcoder because the response body is too generic. - testTranscoding( - Http::TestRequestHeaderMapImpl{{":method", "POST"}, - {":path", "/shelf"}, - {":authority", "host"}, - {"content-type", "application/json"}}, - R"({"theme": "Children"})", {R"(shelf { theme: "Children" })"}, - {R"(id: 20 theme: "Children 0123456789 0123456789 0123456789 0123456789" )"}, Status(), - Http::TestResponseHeaderMapImpl{ - {":status", "500"}, {"content-type", "text/plain"}, {"content-length", "21"}}, - R"(Internal Server Error)"); -} - } // namespace } // namespace Envoy 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 e72327d13094..47617ff4e354 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 @@ -335,6 +335,79 @@ TEST_F(GrpcJsonTranscoderConfigTest, InvalidVariableBinding) { EXPECT_FALSE(transcoder); } +// By default, the transcoder will treat unregistered custom verb as part of path segment, +// which can be captured in a wildcard. +TEST_F(GrpcJsonTranscoderConfigTest, UnregisteredCustomVerb) { + JsonTranscoderConfig config( + getProtoConfig(TestEnvironment::runfilesPath("test/proto/bookstore.descriptor"), + "bookstore.Bookstore", false), + *api_); + + // It is matched to PostWildcard `POST /wildcard/{arg=**}`. + // ":unknown" was not treated as custom verb but as part of path segment, + // so it matches *. + Http::TestRequestHeaderMapImpl headers{{":method", "POST"}, + {":path", "/wildcard/random:unknown"}}; + + TranscoderInputStreamImpl request_in, response_in; + TranscoderPtr transcoder; + MethodInfoSharedPtr method_info; + const auto status = + config.createTranscoder(headers, request_in, response_in, transcoder, method_info); + + EXPECT_TRUE(status.ok()); + EXPECT_TRUE(transcoder); + EXPECT_EQ("bookstore.Bookstore.PostWildcard", method_info->descriptor_->full_name()); +} + +// By default, the transcoder will always try to match the registered custom +// verbs. +TEST_F(GrpcJsonTranscoderConfigTest, RegisteredCustomVerb) { + JsonTranscoderConfig config( + getProtoConfig(TestEnvironment::runfilesPath("test/proto/bookstore.descriptor"), + "bookstore.Bookstore", false), + *api_); + + // Now, the `verb` is registered by PostCustomVerb `POST /foo/bar:verb`, + // so the transcoder will strictly match `verb`. + Http::TestRequestHeaderMapImpl headers{{":method", "POST"}, {":path", "/wildcard/random:verb"}}; + + TranscoderInputStreamImpl request_in, response_in; + TranscoderPtr transcoder; + MethodInfoSharedPtr method_info; + const auto status = + config.createTranscoder(headers, request_in, response_in, transcoder, method_info); + + EXPECT_EQ(status.code(), StatusCode::kNotFound); + EXPECT_EQ(status.message(), "Could not resolve /wildcard/random:verb to a method."); + EXPECT_FALSE(transcoder); +} + +// When `set_match_unregistered_custom_verb=true`, the transcoder will always +// try to match the unregistered custom verbs like the registered ones. +TEST_F(GrpcJsonTranscoderConfigTest, MatchUnregisteredCustomVerb) { + auto proto_config = + getProtoConfig(TestEnvironment::runfilesPath("test/proto/bookstore.descriptor"), + "bookstore.Bookstore", false); + proto_config.set_match_unregistered_custom_verb(true); + JsonTranscoderConfig config(proto_config, *api_); + + // Even though the `unknown` is not registered, but as match_unregistered_custom_verb=true, the + // transcoder will strictly try to match it. + Http::TestRequestHeaderMapImpl headers{{":method", "POST"}, + {":path", "/wildcard/random:unknown"}}; + + TranscoderInputStreamImpl request_in, response_in; + TranscoderPtr transcoder; + MethodInfoSharedPtr method_info; + const auto status = + config.createTranscoder(headers, request_in, response_in, transcoder, method_info); + + EXPECT_EQ(status.code(), StatusCode::kNotFound); + EXPECT_EQ(status.message(), "Could not resolve /wildcard/random:unknown to a method."); + EXPECT_FALSE(transcoder); +} + class GrpcJsonTranscoderFilterTest : public testing::Test, public GrpcJsonTranscoderFilterTestBase { protected: GrpcJsonTranscoderFilterTest( @@ -476,8 +549,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 +616,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 +676,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 +739,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 +1314,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 50602a60e3a9..959a05c076cb 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)); @@ -423,12 +422,10 @@ TEST_F(GrpcStatsFilterConfigTest, UpstreamStats) { {"content-type", "application/grpc+proto"}, {":path", "/lyft.users.BadCompanions/GetBadCompanions"}}; - ON_CALL(stream_info_, lastUpstreamRxByteReceived()) - .WillByDefault(testing::Return( - absl::optional(std::chrono::nanoseconds(30000000)))); - ON_CALL(stream_info_, lastUpstreamTxByteSent()) - .WillByDefault(testing::Return( - absl::optional(std::chrono::nanoseconds(20000000)))); + stream_info_.upstream_info_->upstreamTiming().last_upstream_tx_byte_sent_ = + MonotonicTime(std::chrono::nanoseconds(20000000)); + stream_info_.upstream_info_->upstreamTiming().last_upstream_rx_byte_received_ = + MonotonicTime(std::chrono::nanoseconds(30000000)); EXPECT_CALL(stats_store_, deliverHistogramToSinks( @@ -445,12 +442,10 @@ TEST_F(GrpcStatsFilterConfigTest, UpstreamStatsWithTrailersOnly) { config_.set_enable_upstream_stats(true); initialize(); - ON_CALL(stream_info_, lastUpstreamRxByteReceived()) - .WillByDefault(testing::Return( - absl::optional(std::chrono::nanoseconds(30000000)))); - ON_CALL(stream_info_, lastUpstreamTxByteSent()) - .WillByDefault(testing::Return( - absl::optional(std::chrono::nanoseconds(20000000)))); + stream_info_.upstream_info_->upstreamTiming().last_upstream_tx_byte_sent_ = + MonotonicTime(std::chrono::nanoseconds(20000000)); + stream_info_.upstream_info_->upstreamTiming().last_upstream_rx_byte_received_ = + MonotonicTime(std::chrono::nanoseconds(30000000)); EXPECT_CALL(stats_store_, deliverHistogramToSinks( diff --git a/test/extensions/filters/http/grpc_web/BUILD b/test/extensions/filters/http/grpc_web/BUILD index a8e637e6d9be..3fe28ff3e5e3 100644 --- a/test/extensions/filters/http/grpc_web/BUILD +++ b/test/extensions/filters/http/grpc_web/BUILD @@ -19,7 +19,6 @@ envoy_extension_cc_test( "//source/extensions/filters/http/grpc_web:grpc_web_filter_lib", "//test/mocks/http:http_mocks", "//test/test_common:global_lib", - "//test/test_common:test_runtime_lib", "//test/test_common:utility_lib", ], ) 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 243815d0b282..561484f56d0d 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 @@ -14,7 +14,6 @@ #include "test/mocks/http/mocks.h" #include "test/test_common/global.h" #include "test/test_common/printers.h" -#include "test/test_common/test_runtime.h" #include "test/test_common/utility.h" #include "gmock/gmock.h" @@ -419,25 +418,6 @@ TEST_F(GrpcWebFilterTest, InvalidUpstreamResponseForTextWithTrailers) { EXPECT_EQ("hellohello", response_headers.get_(Http::Headers::get().GrpcMessage)); } -TEST_F(GrpcWebFilterTest, InvalidUpstreamResponseForTextSkipTransformation) { - TestScopedRuntime scoped_runtime; - Runtime::LoaderSingleton::getExisting()->mergeValues( - {{"envoy.reloadable_features.grpc_web_fix_non_proto_encoded_response_handling", "false"}}); - - Http::TestRequestHeaderMapImpl request_headers{ - {"content-type", Http::Headers::get().ContentTypeValues.GrpcWebText}, - {"accept", Http::Headers::get().ContentTypeValues.GrpcWebText}, - {":path", "/"}}; - EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_.decodeHeaders(request_headers, false)); - - Http::TestResponseHeaderMapImpl response_headers{{":status", "400"}}; - EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_.encodeHeaders(response_headers, false)); - Buffer::OwnedImpl data("hello"); - // Since the client expects grpc-web-text and the upstream response does not contain gRPC frames, - // the iteration is paused. - EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, filter_.encodeData(data, false)); -} - TEST_P(GrpcWebFilterTest, StatsNoCluster) { Http::TestRequestHeaderMapImpl request_headers{ {"content-type", request_content_type()}, @@ -455,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 02c5d56f6e8e..82b2dceffc24 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 2eaf8db8acb3..7dcf99d5129c 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/jwt_authn/BUILD b/test/extensions/filters/http/jwt_authn/BUILD index 1a7c3f072324..41398042e718 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/extensions/filters/http/kill_request/kill_request_filter_test.cc b/test/extensions/filters/http/kill_request/kill_request_filter_test.cc index b6fde6589e2a..37801cf88bb5 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 ce401a0c5cd7..f1438a29cb25 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)); @@ -2449,6 +2448,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 diff --git a/test/extensions/filters/http/lua/lua_integration_test.cc b/test/extensions/filters/http/lua/lua_integration_test.cc index 14734bb5389f..cc41bb5f353b 100644 --- a/test/extensions/filters/http/lua/lua_integration_test.cc +++ b/test/extensions/filters/http/lua/lua_integration_test.cc @@ -1163,5 +1163,21 @@ name: lua testRewriteResponse(FILTER_AND_CODE); } +TEST_P(LuaIntegrationTest, RewriteResponseBufferWithoutHeaderReplaceContentLength) { + const std::string FILTER_AND_CODE = + R"EOF( +name: lua +typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua + inline_code: | + function envoy_on_response(response_handle) + local content_length = response_handle:body():setBytes("ok") + response_handle:logTrace(content_length) + end +)EOF"; + + testRewriteResponse(FILTER_AND_CODE); +} + } // namespace } // namespace Envoy diff --git a/test/extensions/filters/http/oauth2/filter_test.cc b/test/extensions/filters/http/oauth2/filter_test.cc index 52b2e81b9700..80445e0effdc 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 d7f9a29d443b..7f9ea40d8663 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)); })); diff --git a/test/extensions/filters/http/ratelimit/ratelimit_integration_test.cc b/test/extensions/filters/http/ratelimit/ratelimit_integration_test.cc index 1f3112bca21f..f125f8f722ef 100644 --- a/test/extensions/filters/http/ratelimit/ratelimit_integration_test.cc +++ b/test/extensions/filters/http/ratelimit/ratelimit_integration_test.cc @@ -344,7 +344,7 @@ TEST_P(RatelimitIntegrationTest, Timeout) { EXPECT_EQ(1, test_server_->counter("grpc.ratelimit.streams_total")->value()); break; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } // Rate limiter fails open diff --git a/test/extensions/filters/http/ratelimit/ratelimit_test.cc b/test/extensions/filters/http/ratelimit/ratelimit_test.cc index 5c63045c7d87..9ce82f406696 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_)); @@ -501,14 +491,15 @@ TEST_F(HttpRateLimitFilterTest, LimitResponse) { EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, filter_->decodeHeaders(request_headers_, false)); + EXPECT_CALL(filter_callbacks_.stream_info_, + setResponseFlag(StreamInfo::ResponseFlag::RateLimited)); + Http::ResponseHeaderMapPtr h{new Http::TestResponseHeaderMapImpl()}; Http::TestResponseHeaderMapImpl response_headers{ {":status", "429"}, {"x-envoy-ratelimited", Http::Headers::get().EnvoyRateLimitedValues.True}}; EXPECT_CALL(filter_callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), true)); EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); - EXPECT_CALL(filter_callbacks_.stream_info_, - setResponseFlag(StreamInfo::ResponseFlag::RateLimited)); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OverLimit, nullptr, std::move(h), nullptr, "", nullptr); @@ -553,14 +544,15 @@ TEST_F(HttpRateLimitFilterTest, LimitResponseWithDynamicMetadata) { EXPECT_TRUE(TestUtility::protoEqual(returned_dynamic_metadata, *dynamic_metadata)); })); + EXPECT_CALL(filter_callbacks_.stream_info_, + setResponseFlag(StreamInfo::ResponseFlag::RateLimited)); + Http::ResponseHeaderMapPtr h{new Http::TestResponseHeaderMapImpl()}; Http::TestResponseHeaderMapImpl response_headers{ {":status", "429"}, {"x-envoy-ratelimited", Http::Headers::get().EnvoyRateLimitedValues.True}}; EXPECT_CALL(filter_callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), true)); EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); - EXPECT_CALL(filter_callbacks_.stream_info_, - setResponseFlag(StreamInfo::ResponseFlag::RateLimited)); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OverLimit, nullptr, std::move(h), nullptr, "", std::move(dynamic_metadata)); @@ -592,12 +584,14 @@ 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_)); + EXPECT_CALL(filter_callbacks_.stream_info_, + setResponseFlag(StreamInfo::ResponseFlag::RateLimited)); + Http::HeaderMapPtr rl_headers{new Http::TestResponseHeaderMapImpl{ {"x-ratelimit-limit", "1000"}, {"x-ratelimit-remaining", "0"}, {"retry-after", "33"}}}; Http::TestResponseHeaderMapImpl expected_headers(*rl_headers); @@ -606,8 +600,6 @@ TEST_F(HttpRateLimitFilterTest, LimitResponseWithHeaders) { EXPECT_CALL(filter_callbacks_, encodeHeaders_(HeaderMapEqualRef(&expected_headers), true)); EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); - EXPECT_CALL(filter_callbacks_.stream_info_, - setResponseFlag(StreamInfo::ResponseFlag::RateLimited)); Http::HeaderMapPtr request_headers_to_add{ new Http::TestRequestHeaderMapImpl{{"x-rls-rate-limited", "true"}}}; @@ -644,12 +636,14 @@ 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_)); + EXPECT_CALL(filter_callbacks_.stream_info_, + setResponseFlag(StreamInfo::ResponseFlag::RateLimited)); + const std::string response_body = "this is a custom over limit response body."; const std::string content_length = std::to_string(response_body.length()); Http::HeaderMapPtr rl_headers{new Http::TestResponseHeaderMapImpl{ @@ -669,8 +663,6 @@ TEST_F(HttpRateLimitFilterTest, LimitResponseWithBody) { EXPECT_CALL(filter_callbacks_, encodeData(_, true)) .WillOnce( Invoke([&](Buffer::Instance& data, bool) { EXPECT_EQ(data.toString(), response_body); })); - EXPECT_CALL(filter_callbacks_.stream_info_, - setResponseFlag(StreamInfo::ResponseFlag::RateLimited)); Http::HeaderMapPtr request_headers_to_add{ new Http::TestRequestHeaderMapImpl{{"x-rls-rate-limited", "true"}}}; @@ -707,12 +699,14 @@ 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_)); + EXPECT_CALL(filter_callbacks_.stream_info_, + setResponseFlag(StreamInfo::ResponseFlag::RateLimited)); + const std::string response_body = R"EOF( { "message": "this is a custom over limit response body as json.", "retry-after": "33" } )EOF"; @@ -738,8 +732,6 @@ TEST_F(HttpRateLimitFilterTest, LimitResponseWithBodyAndContentType) { EXPECT_CALL(filter_callbacks_, encodeData(_, true)) .WillOnce( Invoke([&](Buffer::Instance& data, bool) { EXPECT_EQ(data.toString(), response_body); })); - EXPECT_CALL(filter_callbacks_.stream_info_, - setResponseFlag(StreamInfo::ResponseFlag::RateLimited)); Http::HeaderMapPtr request_headers_to_add{ new Http::TestRequestHeaderMapImpl{{"x-rls-rate-limited", "true"}}}; @@ -774,10 +766,12 @@ TEST_F(HttpRateLimitFilterTest, LimitResponseWithFilterHeaders) { request_callbacks_ = &callbacks; }))); + EXPECT_CALL(filter_callbacks_.stream_info_, + setResponseFlag(StreamInfo::ResponseFlag::RateLimited)); + 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_)); @@ -790,8 +784,6 @@ TEST_F(HttpRateLimitFilterTest, LimitResponseWithFilterHeaders) { {"x-ratelimit-reset", "3"}}; EXPECT_CALL(filter_callbacks_, encodeHeaders_(HeaderMapEqualRef(&expected_headers), true)); EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); - EXPECT_CALL(filter_callbacks_.stream_info_, - setResponseFlag(StreamInfo::ResponseFlag::RateLimited)); auto descriptor_statuses = { Envoy::RateLimit::buildDescriptorStatus( @@ -829,12 +821,13 @@ TEST_F(HttpRateLimitFilterTest, LimitResponseWithoutEnvoyRateLimitedHeader) { EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, filter_->decodeHeaders(request_headers_, false)); + EXPECT_CALL(filter_callbacks_.stream_info_, + setResponseFlag(StreamInfo::ResponseFlag::RateLimited)); + Http::ResponseHeaderMapPtr h{new Http::TestResponseHeaderMapImpl()}; Http::TestResponseHeaderMapImpl response_headers{{":status", "429"}}; EXPECT_CALL(filter_callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), true)); EXPECT_CALL(filter_callbacks_, continueDecoding()).Times(0); - EXPECT_CALL(filter_callbacks_.stream_info_, - setResponseFlag(StreamInfo::ResponseFlag::RateLimited)); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OverLimit, nullptr, std::move(h), nullptr, "", nullptr); @@ -876,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_)); @@ -926,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_)); @@ -946,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_)); @@ -968,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_)); @@ -989,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_)); @@ -1029,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_)); @@ -1073,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_)); @@ -1127,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_)); @@ -1178,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_)); @@ -1229,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_)); @@ -1277,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_)); @@ -1327,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_)); @@ -1375,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_)); @@ -1411,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/rbac/rbac_filter_integration_test.cc b/test/extensions/filters/http/rbac/rbac_filter_integration_test.cc index 8894afc1303a..3aed76982535 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: diff --git a/test/extensions/filters/http/tap/tap_filter_test.cc b/test/extensions/filters/http/tap/tap_filter_test.cc index 06ca5fcbff38..c7666fcd5b56 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/test_data/test_cpp.cc b/test/extensions/filters/http/wasm/test_data/test_cpp.cc index f049ee8bc6b6..039713e0e76a 100644 --- a/test/extensions/filters/http/wasm/test_data/test_cpp.cc +++ b/test/extensions/filters/http/wasm/test_data/test_cpp.cc @@ -175,8 +175,8 @@ FilterHeadersStatus TestContext::onRequestHeaders(uint32_t, bool) { { // Validate a valid CEL expression const std::string expr = R"( - envoy.api.v2.core.GrpcService{ - envoy_grpc: envoy.api.v2.core.GrpcService.EnvoyGrpc { + envoy.config.core.v3.GrpcService{ + envoy_grpc: envoy.config.core.v3.GrpcService.EnvoyGrpc { cluster_name: "test" } })"; diff --git a/test/extensions/filters/http/wasm/test_data/test_grpc_stream_cpp.cc b/test/extensions/filters/http/wasm/test_data/test_grpc_stream_cpp.cc index e0ebbe048ca2..a7bd41884a7f 100644 --- a/test/extensions/filters/http/wasm/test_data/test_grpc_stream_cpp.cc +++ b/test/extensions/filters/http/wasm/test_data/test_grpc_stream_cpp.cc @@ -81,16 +81,18 @@ FilterHeadersStatus GrpcStreamContextProto::onRequestHeaders(uint32_t, bool) { std::make_pair("source", "grpc_stream_proto")); if (root()->grpcStreamHandler("bogus service string", "service", "method", initial_metadata, std::unique_ptr( - new MyGrpcStreamHandler())) != WasmResult::ParseFailure) { - logError("unexpected bogus service string OK"); + new MyGrpcStreamHandler())) == WasmResult::ParseFailure) { + logError("expected bogus service parse failure"); } if (root()->grpcStreamHandler(grpc_service_string, "service", "bad method", initial_metadata, std::unique_ptr( - new MyGrpcStreamHandler())) != WasmResult::InternalFailure) { - logError("unexpected bogus method OK"); + new MyGrpcStreamHandler())) == WasmResult::InternalFailure) { + logError("expected bogus method call failure"); + } + if (root()->grpcStreamHandler(grpc_service_string, "service", "method", initial_metadata, + std::unique_ptr(new MyGrpcStreamHandler())) == WasmResult::Ok) { + logError("cluster call succeeded"); } - root()->grpcStreamHandler(grpc_service_string, "service", "method", initial_metadata, - std::unique_ptr(new MyGrpcStreamHandler())); return FilterHeadersStatus::StopIteration; } diff --git a/test/extensions/filters/http/wasm/wasm_filter_test.cc b/test/extensions/filters/http/wasm/wasm_filter_test.cc index 773886c5c9f6..c5227d88a369 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(); } @@ -949,8 +948,6 @@ TEST_P(WasmHttpFilterTest, GrpcCall) { setupFilter(); if (id == "grpc_call_proto") { - Runtime::LoaderSingleton::getExisting()->mergeValues( - {{"envoy.reloadable_features.wasm_cluster_name_envoy_grpc", "false"}}); EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("bogus grpc_service accepted error")))); } else { @@ -1029,8 +1026,6 @@ TEST_P(WasmHttpFilterTest, GrpcCallBadCall) { setupFilter(); if (id == "grpc_call_proto") { - Runtime::LoaderSingleton::getExisting()->mergeValues( - {{"envoy.reloadable_features.wasm_cluster_name_envoy_grpc", "false"}}); EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("bogus grpc_service accepted error")))); } else { @@ -1073,8 +1068,6 @@ TEST_P(WasmHttpFilterTest, GrpcCallFailure) { setupFilter(); if (id == "grpc_call_proto") { - Runtime::LoaderSingleton::getExisting()->mergeValues( - {{"envoy.reloadable_features.wasm_cluster_name_envoy_grpc", "false"}}); EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("bogus grpc_service accepted error")))); } else { @@ -1165,8 +1158,6 @@ TEST_P(WasmHttpFilterTest, GrpcCallCancel) { setupFilter(); if (id == "grpc_call_proto") { - Runtime::LoaderSingleton::getExisting()->mergeValues( - {{"envoy.reloadable_features.wasm_cluster_name_envoy_grpc", "false"}}); EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("bogus grpc_service accepted error")))); } else { @@ -1226,8 +1217,6 @@ TEST_P(WasmHttpFilterTest, GrpcCallClose) { setupFilter(); if (id == "grpc_call_proto") { - Runtime::LoaderSingleton::getExisting()->mergeValues( - {{"envoy.reloadable_features.wasm_cluster_name_envoy_grpc", "false"}}); EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("bogus grpc_service accepted error")))); } else { @@ -1287,8 +1276,6 @@ TEST_P(WasmHttpFilterTest, GrpcCallAfterDestroyed) { setupFilter(); if (id == "grpc_call_proto") { - Runtime::LoaderSingleton::getExisting()->mergeValues( - {{"envoy.reloadable_features.wasm_cluster_name_envoy_grpc", "false"}}); EXPECT_CALL(filter(), log_(spdlog::level::err, Eq(absl::string_view("bogus grpc_service accepted error")))); } else { @@ -1397,8 +1384,12 @@ TEST_P(WasmHttpFilterTest, GrpcStream) { setupGrpcStreamTest(callbacks, id); if (id == "grpc_stream_proto") { - Runtime::LoaderSingleton::getExisting()->mergeValues( - {{"envoy.reloadable_features.wasm_cluster_name_envoy_grpc", "false"}}); + EXPECT_CALL(filter(), log_(spdlog::level::err, + Eq(absl::string_view("expected bogus service parse failure")))); + EXPECT_CALL(filter(), log_(spdlog::level::err, + Eq(absl::string_view("expected bogus method call failure")))); + EXPECT_CALL(filter(), + log_(spdlog::level::err, Eq(absl::string_view("cluster call succeeded")))); } else { cluster_manager_.initializeThreadLocalClusters({"cluster"}); EXPECT_CALL(filter(), log_(spdlog::level::err, @@ -1459,8 +1450,12 @@ TEST_P(WasmHttpFilterTest, GrpcStreamCloseLocal) { setupGrpcStreamTest(callbacks, id); if (id == "grpc_stream_proto") { - Runtime::LoaderSingleton::getExisting()->mergeValues( - {{"envoy.reloadable_features.wasm_cluster_name_envoy_grpc", "false"}}); + EXPECT_CALL(filter(), log_(spdlog::level::err, + Eq(absl::string_view("expected bogus service parse failure")))); + EXPECT_CALL(filter(), log_(spdlog::level::err, + Eq(absl::string_view("expected bogus method call failure")))); + EXPECT_CALL(filter(), + log_(spdlog::level::err, Eq(absl::string_view("cluster call succeeded")))); } else { cluster_manager_.initializeThreadLocalClusters({"cluster"}); EXPECT_CALL(filter(), log_(spdlog::level::err, @@ -1520,8 +1515,12 @@ TEST_P(WasmHttpFilterTest, GrpcStreamCloseRemote) { setupGrpcStreamTest(callbacks, id); if (id == "grpc_stream_proto") { - Runtime::LoaderSingleton::getExisting()->mergeValues( - {{"envoy.reloadable_features.wasm_cluster_name_envoy_grpc", "false"}}); + EXPECT_CALL(filter(), log_(spdlog::level::err, + Eq(absl::string_view("expected bogus service parse failure")))); + EXPECT_CALL(filter(), log_(spdlog::level::err, + Eq(absl::string_view("expected bogus method call failure")))); + EXPECT_CALL(filter(), + log_(spdlog::level::err, Eq(absl::string_view("cluster call succeeded")))); } else { cluster_manager_.initializeThreadLocalClusters({"cluster"}); EXPECT_CALL(filter(), log_(spdlog::level::err, @@ -1576,8 +1575,12 @@ TEST_P(WasmHttpFilterTest, GrpcStreamCancel) { setupGrpcStreamTest(callbacks, id); if (id == "grpc_stream_proto") { - Runtime::LoaderSingleton::getExisting()->mergeValues( - {{"envoy.reloadable_features.wasm_cluster_name_envoy_grpc", "false"}}); + EXPECT_CALL(filter(), log_(spdlog::level::err, + Eq(absl::string_view("expected bogus service parse failure")))); + EXPECT_CALL(filter(), log_(spdlog::level::err, + Eq(absl::string_view("expected bogus method call failure")))); + EXPECT_CALL(filter(), + log_(spdlog::level::err, Eq(absl::string_view("cluster call succeeded")))); } else { cluster_manager_.initializeThreadLocalClusters({"cluster"}); EXPECT_CALL(filter(), log_(spdlog::level::err, @@ -1627,8 +1630,12 @@ TEST_P(WasmHttpFilterTest, GrpcStreamOpenAtShutdown) { setupGrpcStreamTest(callbacks, id); if (id == "grpc_stream_proto") { - Runtime::LoaderSingleton::getExisting()->mergeValues( - {{"envoy.reloadable_features.wasm_cluster_name_envoy_grpc", "false"}}); + EXPECT_CALL(filter(), log_(spdlog::level::err, + Eq(absl::string_view("expected bogus service parse failure")))); + EXPECT_CALL(filter(), log_(spdlog::level::err, + Eq(absl::string_view("expected bogus method call failure")))); + EXPECT_CALL(filter(), + log_(spdlog::level::err, Eq(absl::string_view("cluster call succeeded")))); } else { cluster_manager_.initializeThreadLocalClusters({"cluster"}); EXPECT_CALL(filter(), log_(spdlog::level::err, @@ -1789,7 +1796,7 @@ TEST_P(WasmHttpFilterTest, Property) { key: endpoint )EOF")); EXPECT_CALL(*host_description, metadata()).WillRepeatedly(Return(metadata)); - EXPECT_CALL(request_stream_info_, upstreamHost()).WillRepeatedly(Return(host_description)); + request_stream_info_.upstreamInfo()->setUpstreamHost(host_description); filter().log(&request_headers, nullptr, nullptr, log_stream_info); } @@ -1819,11 +1826,11 @@ TEST_P(WasmHttpFilterTest, ClusterMetadata) { EXPECT_CALL(encoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(request_stream_info_)); EXPECT_CALL(*cluster, metadata()).WillRepeatedly(ReturnRef(*cluster_metadata)); EXPECT_CALL(*host_description, cluster()).WillRepeatedly(ReturnRef(*cluster)); - EXPECT_CALL(request_stream_info_, upstreamHost()).WillRepeatedly(Return(host_description)); + request_stream_info_.upstreamInfo()->setUpstreamHost(host_description); filter().log(&request_headers, nullptr, nullptr, log_stream_info); // If upstream host is empty, fallback to upstream cluster info for cluster metadata. - EXPECT_CALL(request_stream_info_, upstreamHost()).WillRepeatedly(Return(nullptr)); + request_stream_info_.upstreamInfo()->setUpstreamHost(nullptr); EXPECT_CALL(request_stream_info_, upstreamClusterInfo()).WillRepeatedly(Return(cluster)); EXPECT_CALL(filter(), log_(spdlog::level::warn, Eq(absl::string_view("cluster metadata: cluster")))); diff --git a/test/extensions/filters/listener/common/fuzz/listener_filter_fakes.cc b/test/extensions/filters/listener/common/fuzz/listener_filter_fakes.cc index 766bc5898c0c..548a4f4ce025 100644 --- a/test/extensions/filters/listener/common/fuzz/listener_filter_fakes.cc +++ b/test/extensions/filters/listener/common/fuzz/listener_filter_fakes.cc @@ -47,6 +47,12 @@ void FakeConnectionSocket::setRequestedServerName(absl::string_view server_name) absl::string_view FakeConnectionSocket::requestedServerName() const { return server_name_; } +void FakeConnectionSocket::setJA3Hash(absl::string_view ja3_hash) { + ja3_hash_ = std::string(ja3_hash); +} + +absl::string_view FakeConnectionSocket::ja3Hash() const { return ja3_hash_; } + Api::SysCallIntResult FakeConnectionSocket::getSocketOption([[maybe_unused]] int level, int, [[maybe_unused]] void* optval, socklen_t*) const { @@ -59,7 +65,7 @@ Api::SysCallIntResult FakeConnectionSocket::getSocketOption([[maybe_unused]] int static_cast(optval)->ss_family = AF_INET; break; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } return Api::SysCallIntResult{0, 0}; diff --git a/test/extensions/filters/listener/common/fuzz/listener_filter_fakes.h b/test/extensions/filters/listener/common/fuzz/listener_filter_fakes.h index 602202240eb3..e8a2582c059d 100644 --- a/test/extensions/filters/listener/common/fuzz/listener_filter_fakes.h +++ b/test/extensions/filters/listener/common/fuzz/listener_filter_fakes.h @@ -40,6 +40,10 @@ class FakeConnectionSocket : public Network::MockConnectionSocket { absl::string_view requestedServerName() const override; + void setJA3Hash(absl::string_view ja3_hash) override; + + absl::string_view ja3Hash() const override; + Api::SysCallIntResult getSocketOption(int level, int, void* optval, socklen_t*) const override; absl::optional 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/http_inspector/http_inspector_test.cc b/test/extensions/filters/listener/http_inspector/http_inspector_test.cc index 8499c8e3428d..f5e68eae1d9a 100644 --- a/test/extensions/filters/listener/http_inspector/http_inspector_test.cc +++ b/test/extensions/filters/listener/http_inspector/http_inspector_test.cc @@ -44,11 +44,10 @@ class HttpInspectorTest : public testing::Test { if (include_inline_recv) { EXPECT_CALL(os_sys_calls_, recv(42, _, _, MSG_PEEK)) - .WillOnce(Return(Api::SysCallSizeResult{static_cast(0), 0})); + .WillOnce(Return(Api::SysCallSizeResult{ssize_t(-1), SOCKET_ERROR_AGAIN})); - EXPECT_CALL(dispatcher_, - createFileEvent_(_, _, Event::PlatformDefaultTriggerType, - Event::FileReadyType::Read | Event::FileReadyType::Closed)) + EXPECT_CALL(dispatcher_, createFileEvent_(_, _, Event::PlatformDefaultTriggerType, + Event::FileReadyType::Read)) .WillOnce(DoAll(SaveArg<1>(&file_event_callback_), ReturnNew>())); @@ -334,11 +333,10 @@ TEST_F(HttpInspectorTest, InspectHttp2) { TEST_F(HttpInspectorTest, ReadClosed) { init(); - EXPECT_CALL(os_sys_calls_, recv(42, _, _, MSG_PEEK)); - EXPECT_CALL(socket_, close()); - EXPECT_CALL(cb_, continueFilterChain(true)); - socket_.close(); - file_event_callback_(Event::FileReadyType::Closed); + EXPECT_CALL(os_sys_calls_, recv(42, _, _, MSG_PEEK)) + .WillOnce(Return(Api::SysCallSizeResult{0, 0})); + EXPECT_CALL(cb_, continueFilterChain(false)); + file_event_callback_(Event::FileReadyType::Read); EXPECT_EQ(0, cfg_->stats().http2_found_.value()); } diff --git a/test/extensions/filters/listener/proxy_protocol/BUILD b/test/extensions/filters/listener/proxy_protocol/BUILD index d9149b598de1..fc156709f7b3 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 4d48a8aee68e..9aaf9fee88d2 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/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc b/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc index 6a7b0634a8bb..7fb371218a3d 100644 --- a/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc +++ b/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc @@ -87,6 +87,9 @@ class ProxyProtocolTest : public testing::TestWithParam 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 b722a2c50ec5..d1be04b6f8bc 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 a48b71bd77b6..37c9423dac38 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 902855a7b4c8..5a1b214dbe20 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::_; @@ -17,6 +20,7 @@ using testing::InSequence; using testing::Invoke; using testing::InvokeWithoutArgs; using testing::NiceMock; +using testing::Return; using testing::ReturnNew; using testing::ReturnRef; using testing::SaveArg; @@ -30,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(); } @@ -46,16 +51,18 @@ class TlsInspectorTest : public testing::TestWithParam Api::SysCallSizeResult { ENVOY_LOG_MISC(error, "In mock syscall recv {} {} {} {}", fd, buffer, length, flag); - return Api::SysCallSizeResult{static_cast(0), 0}; + return Api::SysCallSizeResult{ssize_t(-1), SOCKET_ERROR_AGAIN}; })); - EXPECT_CALL(dispatcher_, - createFileEvent_(_, _, Event::PlatformDefaultTriggerType, - Event::FileReadyType::Read | Event::FileReadyType::Closed)) + EXPECT_CALL(dispatcher_, createFileEvent_(_, _, Event::PlatformDefaultTriggerType, + Event::FileReadyType::Read)) .WillOnce( DoAll(SaveArg<1>(&file_event_callback_), ReturnNew>())); filter_->onAccept(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,15 +85,19 @@ 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."); } // Test that the filter detects Closed events and terminates. TEST_P(TlsInspectorTest, ConnectionClosed) { init(); + EXPECT_CALL(os_sys_calls_, recv(42, _, _, MSG_PEEK)) + .WillOnce(Return(Api::SysCallSizeResult{0, 0})); EXPECT_CALL(cb_, continueFilterChain(false)); - file_event_callback_(Event::FileReadyType::Closed); + file_event_callback_(Event::FileReadyType::Read); EXPECT_EQ(1, cfg_->stats().connection_closed_.value()); } @@ -212,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); @@ -230,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 c3777c479fdf..8a353de4bb4f 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 13e911e9e3c5..213339ded21d 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/extensions/filters/network/dubbo_proxy/conn_manager_test.cc b/test/extensions/filters/network/dubbo_proxy/conn_manager_test.cc index 8cdbe6162927..d74e12fa8f77 100644 --- a/test/extensions/filters/network/dubbo_proxy/conn_manager_test.cc +++ b/test/extensions/filters/network/dubbo_proxy/conn_manager_test.cc @@ -832,7 +832,7 @@ TEST_F(ConnectionManagerTest, ResponseWithUnknownSequenceID) { TEST_F(ConnectionManagerTest, OnDataWithFilterSendsLocalReply) { initializeFilter(); - writeHessianRequestMessage(buffer_, false, false, 1); + writeHessianRequestMessage(buffer_, false, false, 233333); config_->setupFilterChain(2, 0); config_->expectOnDestroy(); @@ -847,8 +847,10 @@ TEST_F(ConnectionManagerTest, OnDataWithFilterSendsLocalReply) { const std::string fake_response("mock dubbo response"); NiceMock direct_response; EXPECT_CALL(direct_response, encode(_, _, _)) - .WillOnce(Invoke([&](MessageMetadata&, Protocol&, + .WillOnce(Invoke([&](MessageMetadata& metadata, Protocol&, Buffer::Instance& buffer) -> DubboFilters::DirectResponse::ResponseType { + // Validate request id. + EXPECT_EQ(metadata.requestId(), 233333); buffer.add(fake_response); return DubboFilters::DirectResponse::ResponseType::SuccessReply; })); @@ -878,7 +880,7 @@ TEST_F(ConnectionManagerTest, OnDataWithFilterSendsLocalReply) { TEST_F(ConnectionManagerTest, OnDataWithFilterSendsLocalErrorReply) { initializeFilter(); - writeHessianRequestMessage(buffer_, false, false, 1); + writeHessianRequestMessage(buffer_, false, false, 233334); config_->setupFilterChain(2, 0); config_->expectOnDestroy(); @@ -893,8 +895,10 @@ TEST_F(ConnectionManagerTest, OnDataWithFilterSendsLocalErrorReply) { const std::string fake_response("mock dubbo response"); NiceMock direct_response; EXPECT_CALL(direct_response, encode(_, _, _)) - .WillOnce(Invoke([&](MessageMetadata&, Protocol&, + .WillOnce(Invoke([&](MessageMetadata& metadata, Protocol&, Buffer::Instance& buffer) -> DubboFilters::DirectResponse::ResponseType { + // Validate request id. + EXPECT_EQ(metadata.requestId(), 233334); buffer.add(fake_response); return DubboFilters::DirectResponse::ResponseType::ErrorReply; })); diff --git a/test/extensions/filters/network/echo/BUILD b/test/extensions/filters/network/echo/BUILD new file mode 100644 index 000000000000..d84aeebaac91 --- /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/extensions/filters/network/ext_authz/ext_authz_test.cc b/test/extensions/filters/network/ext_authz/ext_authz_test.cc index 0f2190a17d97..4fa54f5d13ba 100644 --- a/test/extensions/filters/network/ext_authz/ext_authz_test.cc +++ b/test/extensions/filters/network/ext_authz/ext_authz_test.cc @@ -206,6 +206,11 @@ TEST_F(ExtAuthzFilterTest, DeniedWithOnData) { stats_store_.gauge("ext_authz.name.active", Stats::Gauge::ImportMode::Accumulate).value()); EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(filter_callbacks_.connection_.stream_info_, + setResponseFlag(StreamInfo::ResponseFlag::UnauthorizedExternalService)); + EXPECT_CALL( + filter_callbacks_.connection_.stream_info_, + setResponseCodeDetails(Filters::Common::ExtAuthz::ResponseCodeDetails::get().AuthzDenied)); EXPECT_CALL(*client_, cancel()).Times(0); request_callbacks_->onComplete(makeAuthzResponse(Filters::Common::ExtAuthz::CheckStatus::Denied)); @@ -276,6 +281,11 @@ TEST_F(ExtAuthzFilterTest, FailClose) { EXPECT_CALL(filter_callbacks_.connection_, close(_)); EXPECT_CALL(filter_callbacks_, continueReading()).Times(0); + EXPECT_CALL(filter_callbacks_.connection_.stream_info_, + setResponseFlag(StreamInfo::ResponseFlag::UnauthorizedExternalService)); + EXPECT_CALL( + filter_callbacks_.connection_.stream_info_, + setResponseCodeDetails(Filters::Common::ExtAuthz::ResponseCodeDetails::get().AuthzError)); request_callbacks_->onComplete(makeAuthzResponse(Filters::Common::ExtAuthz::CheckStatus::Error)); EXPECT_EQ(0U, stats_store_.counter("ext_authz.name.disabled").value()); @@ -429,7 +439,11 @@ TEST_F(ExtAuthzFilterTest, ImmediateNOK) { EXPECT_EQ(ns, NetworkFilterNames::get().ExtAuthorization); EXPECT_TRUE(TestUtility::protoEqual(returned_dynamic_metadata, dynamic_metadata)); })); - + EXPECT_CALL(filter_callbacks_.connection_.stream_info_, + setResponseFlag(StreamInfo::ResponseFlag::UnauthorizedExternalService)); + EXPECT_CALL( + filter_callbacks_.connection_.stream_info_, + setResponseCodeDetails(Filters::Common::ExtAuthz::ResponseCodeDetails::get().AuthzDenied)); EXPECT_EQ(Network::FilterStatus::Continue, filter_->onNewConnection()); Buffer::OwnedImpl data("hello"); EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onData(data, false)); diff --git a/test/extensions/filters/network/mongo_proxy/proxy_test.cc b/test/extensions/filters/network/mongo_proxy/proxy_test.cc index b59f4cc2bfeb..a2718f9595b2 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/extensions/filters/network/thrift_proxy/decoder_test.cc b/test/extensions/filters/network/thrift_proxy/decoder_test.cc index a0ef74a6d207..e418373498ef 100644 --- a/test/extensions/filters/network/thrift_proxy/decoder_test.cc +++ b/test/extensions/filters/network/thrift_proxy/decoder_test.cc @@ -80,7 +80,7 @@ ExpectationSet expectValue(MockProtocol& proto, MockDecoderEventHandler& handler } break; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } return s; } @@ -139,7 +139,7 @@ ExpectationSet expectContainerStart(MockProtocol& proto, MockDecoderEventHandler })); break; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } return s; } @@ -169,7 +169,7 @@ ExpectationSet expectContainerEnd(MockProtocol& proto, MockDecoderEventHandler& s += EXPECT_CALL(handler, setEnd()).WillOnce(Return(FilterStatus::Continue)); break; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } return s; } 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 000000000000..45f0eec6b570 --- /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 000000000000..e3dadf9eedf2 --- /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 000000000000..587bef6389dc --- /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 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 33cc7e7fb4f6..24b603149c1f 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/integration.cc b/test/extensions/filters/network/thrift_proxy/integration.cc index cd907b4e084a..1c3c74c52968 100644 --- a/test/extensions/filters/network/thrift_proxy/integration.cc +++ b/test/extensions/filters/network/thrift_proxy/integration.cc @@ -21,7 +21,7 @@ std::string PayloadOptions::modeName() const { case DriverMode::Exception: return "exception"; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } } @@ -34,7 +34,7 @@ std::string PayloadOptions::transportName() const { case TransportType::Header: return "header"; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } } @@ -47,7 +47,7 @@ std::string PayloadOptions::protocolName() const { case ProtocolType::Twitter: return "finagle"; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } } diff --git a/test/extensions/filters/network/thrift_proxy/metadata_test.cc b/test/extensions/filters/network/thrift_proxy/metadata_test.cc index 6ca84143e6dc..1f1ff8c2ae8c 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/router_test.cc b/test/extensions/filters/network/thrift_proxy/router_test.cc index c001013f0188..4e2f9957d134 100644 --- a/test/extensions/filters/network/thrift_proxy/router_test.cc +++ b/test/extensions/filters/network/thrift_proxy/router_test.cc @@ -438,7 +438,7 @@ class ThriftRouterTestBase { EXPECT_EQ(FilterStatus::Continue, router_->stringValue(v)); } break; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } } @@ -759,7 +759,7 @@ TEST_F(ThriftRouterTest, PoolOverflowFailure) { auto& app_ex = dynamic_cast(response); EXPECT_EQ(AppExceptionType::InternalError, app_ex.type_); EXPECT_THAT(app_ex.what(), ContainsRegex(".*too many connections.*")); - EXPECT_TRUE(end_stream); + EXPECT_FALSE(end_stream); })); context_.cluster_manager_.thread_local_cluster_.tcp_conn_pool_.poolFailure( ConnectionPool::PoolFailureReason::Overflow, true); @@ -1618,7 +1618,7 @@ TEST_P(ThriftRouterContainerTest, DecoderFilterCallbacks) { EXPECT_EQ(FilterStatus::Continue, router_->setEnd()); break; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } EXPECT_CALL(*protocol_, writeFieldEnd(_)); diff --git a/test/extensions/filters/network/thrift_proxy/shadow_writer_test.cc b/test/extensions/filters/network/thrift_proxy/shadow_writer_test.cc index 1a15a105ea1d..0725be676bdc 100644 --- a/test/extensions/filters/network/thrift_proxy/shadow_writer_test.cc +++ b/test/extensions/filters/network/thrift_proxy/shadow_writer_test.cc @@ -220,7 +220,7 @@ class ShadowWriterTest : public testing::Test { .value()); break; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } } diff --git a/test/extensions/filters/network/thrift_proxy/thrift_object_impl_test.cc b/test/extensions/filters/network/thrift_proxy/thrift_object_impl_test.cc index 9d9a206c1f87..5008f3970824 100644 --- a/test/extensions/filters/network/thrift_proxy/thrift_object_impl_test.cc +++ b/test/extensions/filters/network/thrift_proxy/thrift_object_impl_test.cc @@ -71,7 +71,7 @@ class ThriftObjectImplTestBase { return true; })); default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } } @@ -125,7 +125,7 @@ class ThriftObjectImplTestBase { EXPECT_EQ("six", value.getValueTyped()); break; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } } diff --git a/test/extensions/filters/network/thrift_proxy/utility.h b/test/extensions/filters/network/thrift_proxy/utility.h index a19f9ef1b057..f7338df0d5f7 100644 --- a/test/extensions/filters/network/thrift_proxy/utility.h +++ b/test/extensions/filters/network/thrift_proxy/utility.h @@ -72,7 +72,7 @@ transportTypeToProto(TransportType transport_type) { case TransportType::Header: return envoy::extensions::filters::network::thrift_proxy::v3::HEADER; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } } @@ -84,7 +84,7 @@ protocolTypeToProto(ProtocolType protocol_type) { case ProtocolType::Compact: return envoy::extensions::filters::network::thrift_proxy::v3::COMPACT; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } } @@ -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, "") { diff --git a/test/extensions/filters/udp/dns_filter/dns_filter_integration_test.cc b/test/extensions/filters/udp/dns_filter/dns_filter_integration_test.cc index ea3bbf2c8b00..23280bd51238 100644 --- a/test/extensions/filters/udp/dns_filter/dns_filter_integration_test.cc +++ b/test/extensions/filters/udp/dns_filter/dns_filter_integration_test.cc @@ -87,14 +87,17 @@ name: listener_0 stat_prefix: "my_prefix" client_config: resolver_timeout: 1s - dns_resolution_config: - resolvers: - - socket_address: - address: {} - port_value: {} - dns_resolver_options: - use_tcp_for_dns_lookups: false - no_default_search_domain: false + typed_dns_resolver_config: + name: envoy.network.dns_resolver.cares + typed_config: + "@type": type.googleapis.com/envoy.extensions.network.dns_resolver.cares.v3.CaresDnsResolverConfig + resolvers: + - socket_address: + address: {} + port_value: {} + dns_resolver_options: + use_tcp_for_dns_lookups: false + no_default_search_domain: false max_pending_lookups: 256 server_config: inline_dns_table: diff --git a/test/extensions/filters/udp/dns_filter/dns_filter_test.cc b/test/extensions/filters/udp/dns_filter/dns_filter_test.cc index 01cc07ba8659..e212f9775e11 100644 --- a/test/extensions/filters/udp/dns_filter/dns_filter_test.cc +++ b/test/extensions/filters/udp/dns_filter/dns_filter_test.cc @@ -189,17 +189,20 @@ stat_prefix: "my_prefix" stat_prefix: "my_prefix" client_config: resolver_timeout: 1s - dns_resolution_config: - resolvers: - - socket_address: - address: "1.1.1.1" - port_value: 53 - - socket_address: - address: "8.8.8.8" - port_value: 53 - - socket_address: - address: "8.8.4.4" - port_value: 53 + typed_dns_resolver_config: + name: envoy.network.dns_resolver.cares + typed_config: + "@type": type.googleapis.com/envoy.extensions.network.dns_resolver.cares.v3.CaresDnsResolverConfig + resolvers: + - socket_address: + address: "1.1.1.1" + port_value: 53 + - socket_address: + address: "8.8.8.8" + port_value: 53 + - socket_address: + address: "8.8.4.4" + port_value: 53 max_pending_lookups: 1 server_config: inline_dns_table: @@ -216,11 +219,14 @@ stat_prefix: "my_prefix" stat_prefix: "my_prefix" client_config: resolver_timeout: 1s - dns_resolution_config: - resolvers: - - socket_address: - address: "1.1.1.1" - port_value: 53 + typed_dns_resolver_config: + name: envoy.network.dns_resolver.cares + typed_config: + "@type": type.googleapis.com/envoy.extensions.network.dns_resolver.cares.v3.CaresDnsResolverConfig + resolvers: + - socket_address: + address: "1.1.1.1" + port_value: 53 max_pending_lookups: 256 server_config: external_dns_table: @@ -231,11 +237,14 @@ stat_prefix: "my_prefix" stat_prefix: "my_prefix" client_config: resolver_timeout: 1s - dns_resolution_config: - resolvers: - - socket_address: - address: "1.1.1.1" - port_value: 53 + typed_dns_resolver_config: + name: envoy.network.dns_resolver.cares + typed_config: + "@type": type.googleapis.com/envoy.extensions.network.dns_resolver.cares.v3.CaresDnsResolverConfig + resolvers: + - socket_address: + address: "1.1.1.1" + port_value: 53 max_pending_lookups: 256 server_config: external_dns_table: @@ -246,14 +255,17 @@ stat_prefix: "my_prefix" stat_prefix: "my_prefix" client_config: resolver_timeout: 1s - dns_resolution_config: - dns_resolver_options: - use_tcp_for_dns_lookups: false - no_default_search_domain: false - resolvers: - - socket_address: - address: "1.1.1.1" - port_value: 53 + typed_dns_resolver_config: + name: envoy.network.dns_resolver.cares + typed_config: + "@type": type.googleapis.com/envoy.extensions.network.dns_resolver.cares.v3.CaresDnsResolverConfig + dns_resolver_options: + use_tcp_for_dns_lookups: false + no_default_search_domain: false + resolvers: + - socket_address: + address: "1.1.1.1" + port_value: 53 max_pending_lookups: 256 server_config: external_dns_table: @@ -264,14 +276,17 @@ stat_prefix: "my_prefix" stat_prefix: "my_prefix" client_config: resolver_timeout: 1s - dns_resolution_config: - dns_resolver_options: - use_tcp_for_dns_lookups: true - no_default_search_domain: true - resolvers: - - socket_address: - address: "1.1.1.1" - port_value: 53 + typed_dns_resolver_config: + name: envoy.network.dns_resolver.cares + typed_config: + "@type": type.googleapis.com/envoy.extensions.network.dns_resolver.cares.v3.CaresDnsResolverConfig + dns_resolver_options: + use_tcp_for_dns_lookups: true + no_default_search_domain: true + resolvers: + - socket_address: + address: "1.1.1.1" + port_value: 53 max_pending_lookups: 256 server_config: external_dns_table: @@ -2154,7 +2169,7 @@ TEST_F(DnsFilterTest, DnsResolverOptionsSetFalse) { EXPECT_EQ(false, dns_resolver_options_.no_default_search_domain()); } -TEST_F(DnsFilterTest, DnsResolutionConfigExist) { +TEST_F(DnsFilterTest, DEPRECATED_FEATURE_TEST(DnsResolutionConfigExist)) { const std::string dns_resolution_config_exist = R"EOF( stat_prefix: "my_prefix" client_config: @@ -2180,9 +2195,7 @@ stat_prefix: "my_prefix" std::string config_to_use = fmt::format(dns_resolution_config_exist, temp_path); setup(config_to_use); - // `true` here means use_tcp_for_dns_lookups is set true EXPECT_EQ(false, dns_resolver_options_.use_tcp_for_dns_lookups()); - // `true` here means no_default_search_domain is set true EXPECT_EQ(false, dns_resolver_options_.no_default_search_domain()); // address matches @@ -2194,7 +2207,7 @@ stat_prefix: "my_prefix" } // test typed_dns_resolver_config exits which overrides dns_resolution_config. -TEST_F(DnsFilterTest, TypedDnsResolverConfigExist) { +TEST_F(DnsFilterTest, DEPRECATED_FEATURE_TEST(TypedDnsResolverConfigOverrideDnsResolutionConfig)) { const std::string typed_dns_resolver_config_exist = R"EOF( stat_prefix: "my_prefix" client_config: @@ -2231,9 +2244,47 @@ stat_prefix: "my_prefix" std::string config_to_use = fmt::format(typed_dns_resolver_config_exist, temp_path); setup(config_to_use); - // `true` here means use_tcp_for_dns_lookups is set true EXPECT_EQ(true, dns_resolver_options_.use_tcp_for_dns_lookups()); - // `true` here means no_default_search_domain is set true + EXPECT_EQ(true, dns_resolver_options_.no_default_search_domain()); + + // address matches + auto resolvers = envoy::config::core::v3::Address(); + resolvers.mutable_socket_address()->set_address("1.2.3.4"); + resolvers.mutable_socket_address()->set_port_value(80); + EXPECT_EQ(true, TestUtility::protoEqual(cares_.resolvers(0), resolvers)); +} + +// test typed_dns_resolver_config exits. +TEST_F(DnsFilterTest, TypedDnsResolverConfigExist) { + const std::string typed_dns_resolver_config_exist = R"EOF( +stat_prefix: "my_prefix" +client_config: + resolver_timeout: 1s + typed_dns_resolver_config: + name: envoy.network.dns_resolver.cares + typed_config: + "@type": type.googleapis.com/envoy.extensions.network.dns_resolver.cares.v3.CaresDnsResolverConfig + resolvers: + - socket_address: + address: "1.2.3.4" + port_value: 80 + dns_resolver_options: + use_tcp_for_dns_lookups: true + no_default_search_domain: true + max_pending_lookups: 256 +server_config: + external_dns_table: + filename: {} +)EOF"; + + InSequence s; + + std::string temp_path = + TestEnvironment::writeStringToFileForTest("dns_table.yaml", max_records_table_yaml); + std::string config_to_use = fmt::format(typed_dns_resolver_config_exist, temp_path); + setup(config_to_use); + + EXPECT_EQ(true, dns_resolver_options_.use_tcp_for_dns_lookups()); EXPECT_EQ(true, dns_resolver_options_.no_default_search_domain()); // address matches @@ -2262,9 +2313,7 @@ stat_prefix: "my_prefix" std::string config_to_use = fmt::format(no_dns_config_exist, temp_path); setup(config_to_use); - // `true` here means use_tcp_for_dns_lookups is set true EXPECT_EQ(false, dns_resolver_options_.use_tcp_for_dns_lookups()); - // `true` here means no_default_search_domain is set true EXPECT_EQ(false, dns_resolver_options_.no_default_search_domain()); // No address diff --git a/test/extensions/filters/udp/udp_proxy/udp_proxy_filter_test.cc b/test/extensions/filters/udp/udp_proxy/udp_proxy_filter_test.cc index f59a51f7a833..60a01885b00f 100644 --- a/test/extensions/filters/udp/udp_proxy/udp_proxy_filter_test.cc +++ b/test/extensions/filters/udp/udp_proxy/udp_proxy_filter_test.cc @@ -21,6 +21,7 @@ using testing::AtLeast; using testing::ByMove; using testing::DoAll; +using testing::DoDefault; using testing::InSequence; using testing::InvokeWithoutArgs; using testing::Return; @@ -639,6 +640,176 @@ TEST_F(UdpProxyFilterTest, SocketOptionForUseOriginalSrcIp) { ensureIpTransparentSocketOptions(upstream_address_, "10.0.0.2:80", 1, 0); } +// Verify that on second data packet sent from the client, another upstream host is selected. +TEST_F(UdpProxyFilterTest, PerPacketLoadBalancingBasicFlow) { + InSequence s; + + setup(R"EOF( +stat_prefix: foo +cluster: fake_cluster +use_per_packet_load_balancing: true + )EOF"); + + // Allow for two sessions. + cluster_manager_.thread_local_cluster_.cluster_.info_->resetResourceManager(2, 0, 0, 0, 0); + + expectSessionCreate(upstream_address_); + test_sessions_[0].expectWriteToUpstream("hello"); + recvDataFromDownstream("10.0.0.1:1000", "10.0.0.2:80", "hello"); + EXPECT_EQ(1, config_->stats().downstream_sess_total_.value()); + EXPECT_EQ(1, config_->stats().downstream_sess_active_.value()); + checkTransferStats(5 /*rx_bytes*/, 1 /*rx_datagrams*/, 0 /*tx_bytes*/, 0 /*tx_datagrams*/); + test_sessions_[0].recvDataFromUpstream("world"); + checkTransferStats(5 /*rx_bytes*/, 1 /*rx_datagrams*/, 5 /*tx_bytes*/, 1 /*tx_datagrams*/); + + auto new_host_address = Network::Utility::parseInternetAddressAndPort("20.0.0.2:443"); + auto new_host = createHost(new_host_address); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.lb_, chooseHost(_)).WillOnce(Return(new_host)); + expectSessionCreate(new_host_address); + test_sessions_[1].expectWriteToUpstream("hello2"); + recvDataFromDownstream("10.0.0.1:1000", "10.0.0.2:80", "hello2"); + EXPECT_EQ(2, config_->stats().downstream_sess_total_.value()); + EXPECT_EQ(2, config_->stats().downstream_sess_active_.value()); + checkTransferStats(11 /*rx_bytes*/, 2 /*rx_datagrams*/, 5 /*tx_bytes*/, 1 /*tx_datagrams*/); + + // On next datagram, first session should be used + EXPECT_CALL(cluster_manager_.thread_local_cluster_.lb_, chooseHost(_)) + .WillRepeatedly(DoDefault()); + test_sessions_[0].expectWriteToUpstream("hello3"); + recvDataFromDownstream("10.0.0.1:1000", "10.0.0.2:80", "hello3"); + EXPECT_EQ(2, config_->stats().downstream_sess_total_.value()); + EXPECT_EQ(2, config_->stats().downstream_sess_active_.value()); + checkTransferStats(17 /*rx_bytes*/, 3 /*rx_datagrams*/, 5 /*tx_bytes*/, 1 /*tx_datagrams*/); +} + +// Verify that when no host is available, message is dropped. +TEST_F(UdpProxyFilterTest, PerPacketLoadBalancingFirstInvalidHost) { + InSequence s; + + setup(R"EOF( +stat_prefix: foo +cluster: fake_cluster +use_per_packet_load_balancing: true + )EOF"); + + EXPECT_CALL(cluster_manager_.thread_local_cluster_.lb_, chooseHost(_)).WillOnce(Return(nullptr)); + recvDataFromDownstream("10.0.0.1:1000", "10.0.0.2:80", "hello"); + EXPECT_EQ(0, config_->stats().downstream_sess_total_.value()); + EXPECT_EQ(0, config_->stats().downstream_sess_active_.value()); + EXPECT_EQ(1, cluster_manager_.thread_local_cluster_.cluster_.info_->stats_ + .upstream_cx_none_healthy_.value()); +} + +// Verify that when on second packet no host is available, message is dropped. +TEST_F(UdpProxyFilterTest, PerPacketLoadBalancingSecondInvalidHost) { + InSequence s; + + setup(R"EOF( +stat_prefix: foo +cluster: fake_cluster +use_per_packet_load_balancing: true + )EOF"); + + expectSessionCreate(upstream_address_); + test_sessions_[0].expectWriteToUpstream("hello"); + recvDataFromDownstream("10.0.0.1:1000", "10.0.0.2:80", "hello"); + EXPECT_EQ(1, config_->stats().downstream_sess_total_.value()); + EXPECT_EQ(1, config_->stats().downstream_sess_active_.value()); + EXPECT_EQ(0, cluster_manager_.thread_local_cluster_.cluster_.info_->stats_ + .upstream_cx_none_healthy_.value()); + + EXPECT_CALL(cluster_manager_.thread_local_cluster_.lb_, chooseHost(_)).WillOnce(Return(nullptr)); + recvDataFromDownstream("10.0.0.1:1000", "10.0.0.2:80", "hello2"); + EXPECT_EQ(1, config_->stats().downstream_sess_total_.value()); + EXPECT_EQ(1, config_->stats().downstream_sess_active_.value()); + EXPECT_EQ(1, cluster_manager_.thread_local_cluster_.cluster_.info_->stats_ + .upstream_cx_none_healthy_.value()); +} + +// Verify that all sessions for a host are removed when a host is removed. +TEST_F(UdpProxyFilterTest, PerPacketLoadBalancingRemoveHostSessions) { + InSequence s; + + setup(R"EOF( +stat_prefix: foo +cluster: fake_cluster +use_per_packet_load_balancing: true + )EOF"); + + expectSessionCreate(upstream_address_); + test_sessions_[0].expectWriteToUpstream("hello"); + recvDataFromDownstream("10.0.0.1:1000", "10.0.0.2:80", "hello"); + EXPECT_EQ(1, config_->stats().downstream_sess_total_.value()); + EXPECT_EQ(1, config_->stats().downstream_sess_active_.value()); + + cluster_manager_.thread_local_cluster_.cluster_.priority_set_.runUpdateCallbacks( + 0, {}, {cluster_manager_.thread_local_cluster_.lb_.host_}); + EXPECT_EQ(1, config_->stats().downstream_sess_total_.value()); + EXPECT_EQ(0, config_->stats().downstream_sess_active_.value()); + + expectSessionCreate(upstream_address_); + test_sessions_[1].expectWriteToUpstream("hello2"); + recvDataFromDownstream("10.0.0.1:1000", "10.0.0.2:80", "hello2"); + EXPECT_EQ(2, config_->stats().downstream_sess_total_.value()); + EXPECT_EQ(1, config_->stats().downstream_sess_active_.value()); +} + +// Verify that all sessions for hosts in cluster are removed when a cluster is removed. +TEST_F(UdpProxyFilterTest, PerPacketLoadBalancingRemoveCluster) { + InSequence s; + + setup(R"EOF( +stat_prefix: foo +cluster: fake_cluster +use_per_packet_load_balancing: true + )EOF"); + + // Allow for two sessions. + cluster_manager_.thread_local_cluster_.cluster_.info_->resetResourceManager(2, 0, 0, 0, 0); + + expectSessionCreate(upstream_address_); + test_sessions_[0].expectWriteToUpstream("hello"); + recvDataFromDownstream("10.0.0.1:1000", "10.0.0.2:80", "hello"); + EXPECT_EQ(1, config_->stats().downstream_sess_total_.value()); + EXPECT_EQ(1, config_->stats().downstream_sess_active_.value()); + + auto new_host_address = Network::Utility::parseInternetAddressAndPort("20.0.0.2:443"); + auto new_host = createHost(new_host_address); + EXPECT_CALL(cluster_manager_.thread_local_cluster_.lb_, chooseHost(_)).WillOnce(Return(new_host)); + expectSessionCreate(new_host_address); + test_sessions_[1].expectWriteToUpstream("hello2"); + recvDataFromDownstream("10.0.0.1:1000", "10.0.0.2:80", "hello2"); + EXPECT_EQ(2, config_->stats().downstream_sess_total_.value()); + EXPECT_EQ(2, config_->stats().downstream_sess_active_.value()); + + // Remove a cluster we don't care about. + cluster_update_callbacks_->onClusterRemoval("other_cluster"); + EXPECT_EQ(2, config_->stats().downstream_sess_active_.value()); + + // Remove the cluster we do care about. This should purge all sessions. + cluster_update_callbacks_->onClusterRemoval("fake_cluster"); + EXPECT_EQ(0, config_->stats().downstream_sess_active_.value()); +} + +// Verify that specific stat is included when connection limit is hit. +TEST_F(UdpProxyFilterTest, PerPacketLoadBalancingCannotCreateConnection) { + InSequence s; + + setup(R"EOF( +stat_prefix: foo +cluster: fake_cluster +use_per_packet_load_balancing: true + )EOF"); + + // Don't allow for any session. + cluster_manager_.thread_local_cluster_.cluster_.info_->resetResourceManager(0, 0, 0, 0, 0); + + recvDataFromDownstream("10.0.0.1:1000", "10.0.0.2:80", "hello"); + EXPECT_EQ( + 1, + cluster_manager_.thread_local_cluster_.cluster_.info_->stats_.upstream_cx_overflow_.value()); +} + // Make sure socket option is set correctly if use_original_src_ip is set in case of ipv6. TEST_F(UdpProxyFilterIpv6Test, SocketOptionForUseOriginalSrcIpInCaseOfIpv6) { if (!isTransparentSocketOptionsSupported()) { diff --git a/test/extensions/http/header_formatters/preserve_case/BUILD b/test/extensions/http/header_formatters/preserve_case/BUILD index f3998938cc5e..d8e102c206a1 100644 --- a/test/extensions/http/header_formatters/preserve_case/BUILD +++ b/test/extensions/http/header_formatters/preserve_case/BUILD @@ -35,3 +35,17 @@ envoy_extension_cc_test( "//test/integration:http_integration_lib", ], ) + +envoy_extension_cc_test( + name = "preserve_case_formatter_reason_phrase_integration_test", + srcs = [ + "preserve_case_formatter_reason_phrase_integration_test.cc", + ], + extension_names = ["envoy.http.stateful_header_formatters.preserve_case"], + deps = [ + "//source/extensions/http/header_formatters/preserve_case:preserve_case_formatter", + "//test/integration:http_integration_lib", + "//test/integration:http_protocol_integration_lib", + "@envoy_api//envoy/extensions/http/header_formatters/preserve_case/v3:pkg_cc_proto", + ], +) diff --git a/test/extensions/http/header_formatters/preserve_case/preserve_case_formatter_reason_phrase_integration_test.cc b/test/extensions/http/header_formatters/preserve_case/preserve_case_formatter_reason_phrase_integration_test.cc new file mode 100644 index 000000000000..d98a04a95e4e --- /dev/null +++ b/test/extensions/http/header_formatters/preserve_case/preserve_case_formatter_reason_phrase_integration_test.cc @@ -0,0 +1,93 @@ +#include "envoy/extensions/http/header_formatters/preserve_case/v3/preserve_case.pb.h" + +#include "test/integration/filters/common.h" +#include "test/integration/http_integration.h" +#include "test/test_common/registry.h" + +namespace Envoy { +namespace { + +struct TestParams { + Network::Address::IpVersion ip_version; + bool forward_reason_phrase; +}; + +std::string testParamsToString(const ::testing::TestParamInfo& p) { + return fmt::format("{}_{}", + p.param.ip_version == Network::Address::IpVersion::v4 ? "IPv4" : "IPv6", + p.param.forward_reason_phrase ? "enabled" : "disabled"); +} + +std::vector getTestsParams() { + std::vector ret; + + for (auto ip_version : TestEnvironment::getIpVersionsForTest()) { + ret.push_back(TestParams{ip_version, true}); + ret.push_back(TestParams{ip_version, false}); + } + + return ret; +} + +class PreserveCaseFormatterReasonPhraseIntegrationTest : public testing::TestWithParam, + public HttpIntegrationTest { +public: + PreserveCaseFormatterReasonPhraseIntegrationTest() + : HttpIntegrationTest(Http::CodecType::HTTP1, GetParam().ip_version) {} + + void SetUp() override { + setDownstreamProtocol(Http::CodecType::HTTP1); + setUpstreamProtocol(Http::CodecType::HTTP1); + } + + void initialize() override { + if (upstreamProtocol() == Http::CodecType::HTTP1) { + config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + ConfigHelper::HttpProtocolOptions protocol_options; + auto typed_extension_config = protocol_options.mutable_explicit_http_config() + ->mutable_http_protocol_options() + ->mutable_header_key_format() + ->mutable_stateful_formatter(); + typed_extension_config->set_name("preserve_case"); + + auto config = + TestUtility::parseYaml(fmt::format( + "forward_reason_phrase: {}", GetParam().forward_reason_phrase ? "true" : "false")); + typed_extension_config->mutable_typed_config()->PackFrom(config); + + ConfigHelper::setProtocolOptions(*bootstrap.mutable_static_resources()->mutable_clusters(0), + protocol_options); + }); + } + + HttpIntegrationTest::initialize(); + } +}; + +INSTANTIATE_TEST_SUITE_P(CaseFormatter, PreserveCaseFormatterReasonPhraseIntegrationTest, + testing::ValuesIn(getTestsParams()), testParamsToString); + +TEST_P(PreserveCaseFormatterReasonPhraseIntegrationTest, VerifyReasonPhraseEnabled) { + initialize(); + + IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("http")); + auto request = "GET / HTTP/1.1\r\nhost: host\r\n\r\n"; + ASSERT_TRUE(tcp_client->write(request, false)); + + Envoy::FakeRawConnectionPtr upstream_connection; + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(upstream_connection)); + + auto response = "HTTP/1.1 503 Slow Down\r\ncontent-length: 0\r\n\r\n"; + ASSERT_TRUE(upstream_connection->write(response)); + + auto expected_reason_phrase = + GetParam().forward_reason_phrase ? "Slow Down" : "Service Unavailable"; + // Verify that the downstream response has proper reason phrase + tcp_client->waitForData(fmt::format("HTTP/1.1 503 {}", expected_reason_phrase), false); + + tcp_client->close(); +} + +} // namespace +} // namespace Envoy diff --git a/test/extensions/http/header_formatters/preserve_case/preserve_case_formatter_test.cc b/test/extensions/http/header_formatters/preserve_case/preserve_case_formatter_test.cc index 053425ec8835..ab24ed9de9ce 100644 --- a/test/extensions/http/header_formatters/preserve_case/preserve_case_formatter_test.cc +++ b/test/extensions/http/header_formatters/preserve_case/preserve_case_formatter_test.cc @@ -9,7 +9,7 @@ namespace HeaderFormatters { namespace PreserveCase { TEST(PreserveCaseFormatterTest, All) { - PreserveCaseHeaderFormatter formatter; + PreserveCaseHeaderFormatter formatter(false); formatter.processKey("Foo"); formatter.processKey("Bar"); formatter.processKey("BAR"); @@ -22,6 +22,22 @@ TEST(PreserveCaseFormatterTest, All) { EXPECT_EQ("baz", formatter.format("baz")); } +TEST(PreserveCaseFormatterTest, ReasonPhraseEnabled) { + PreserveCaseHeaderFormatter formatter(true); + + formatter.setReasonPhrase(absl::string_view("Slow Down")); + + EXPECT_EQ("Slow Down", formatter.getReasonPhrase()); +} + +TEST(PreserveCaseFormatterTest, ReasonPhraseDisabled) { + PreserveCaseHeaderFormatter formatter(false); + + formatter.setReasonPhrase(absl::string_view("Slow Down")); + + EXPECT_TRUE(formatter.getReasonPhrase().empty()); +} + } // namespace PreserveCase } // namespace HeaderFormatters } // namespace Http diff --git a/test/extensions/io_socket/user_space/io_handle_impl_test.cc b/test/extensions/io_socket/user_space/io_handle_impl_test.cc index d75246348a8f..ab4303dd0e62 100644 --- a/test/extensions/io_socket/user_space/io_handle_impl_test.cc +++ b/test/extensions/io_socket/user_space/io_handle_impl_test.cc @@ -1056,6 +1056,32 @@ TEST_F(IoHandleImplTest, ActivateEvent) { ASSERT_TRUE(schedulable_cb_->enabled()); } +// This is a compatibility test for Envoy Connection. When a connection is destroyed, the Envoy +// connection may close the underlying handle but not destroy that io handle. Meanwhile, the +// Connection object does not expect any further event be invoked because the connection in destroy +// pending state can not support read/write. +TEST_F(IoHandleImplTest, EventCallbackIsNotInvokedIfHandleIsClosed) { + testing::MockFunction check_event_cb; + testing::MockFunction check_schedulable_cb_destroyed; + + schedulable_cb_ = + new NiceMock(&dispatcher_, &check_schedulable_cb_destroyed); + io_handle_->initializeFileEvent( + dispatcher_, [&, handle = io_handle_.get()](uint32_t) { check_event_cb.Call(); }, + Event::FileTriggerType::Edge, Event::FileReadyType::Read); + EXPECT_FALSE(schedulable_cb_->enabled()); + io_handle_->activateFileEvents(Event::FileReadyType::Read); + EXPECT_TRUE(schedulable_cb_->enabled()); + + { + EXPECT_CALL(check_event_cb, Call()).Times(0); + EXPECT_CALL(check_schedulable_cb_destroyed, Call()); + io_handle_->close(); + // Verify that the schedulable_cb is destroyed along with close(), not later. + testing::Mock::VerifyAndClearExpectations(&check_schedulable_cb_destroyed); + } +} + TEST_F(IoHandleImplTest, DeathOnActivatingDestroyedEvents) { io_handle_->resetFileEvents(); ASSERT_DEBUG_DEATH(io_handle_->activateFileEvents(Event::FileReadyType::Read), diff --git a/test/extensions/network/dns_resolver/apple/BUILD b/test/extensions/network/dns_resolver/apple/BUILD index 0e48f5fee940..d949920ad9d9 100644 --- a/test/extensions/network/dns_resolver/apple/BUILD +++ b/test/extensions/network/dns_resolver/apple/BUILD @@ -23,7 +23,7 @@ envoy_cc_test( "//source/common/stats:isolated_store_lib", "//source/common/event:dispatcher_lib", "//source/common/network:address_lib", - "//source/common/network/dns_resolver:dns_factory_lib", + "//source/common/network/dns_resolver:dns_factory_util_lib", "//source/common/common:random_generator_lib", "//test/test_common:environment_lib", "//test/test_common:network_utility_lib", diff --git a/test/extensions/network/dns_resolver/apple/apple_dns_impl_test.cc b/test/extensions/network/dns_resolver/apple/apple_dns_impl_test.cc index 2c70208fc518..4fb50f3398c8 100644 --- a/test/extensions/network/dns_resolver/apple/apple_dns_impl_test.cc +++ b/test/extensions/network/dns_resolver/apple/apple_dns_impl_test.cc @@ -11,7 +11,7 @@ #include "envoy/network/dns.h" #include "source/common/network/address_impl.h" -#include "source/common/network/dns_resolver/dns_factory.h" +#include "source/common/network/dns_resolver/dns_factory_util.h" #include "source/common/network/utility.h" #include "source/common/stats/isolated_store_impl.h" #include "source/extensions/network/dns_resolver/apple/apple_dns_impl.h" @@ -90,20 +90,21 @@ class AppleDnsImplTest : public testing::Test { EXPECT_FALSE(results.empty()); absl::optional is_v4{}; for (const auto& result : results) { + const auto& addrinfo = result.addrInfo(); switch (lookup_family) { case DnsLookupFamily::V4Only: - EXPECT_NE(nullptr, result.address_->ip()->ipv4()); + EXPECT_NE(nullptr, addrinfo.address_->ip()->ipv4()); break; case DnsLookupFamily::V6Only: - EXPECT_NE(nullptr, result.address_->ip()->ipv6()); + EXPECT_NE(nullptr, addrinfo.address_->ip()->ipv6()); break; - // In CI these modes could return either V4 or V6 with the non-mocked API calls. But - // regardless of the family all returned addresses need to be one _or_ the other. + // In CI these modes could return either IPv4 or IPv6 with the non-mocked API calls. + // But regardless of the family all returned addresses need to be one _or_ the other. case DnsLookupFamily::V4Preferred: case DnsLookupFamily::Auto: // Set the expectation for subsequent responses based on the first one. if (!is_v4.has_value()) { - if (result.address_->ip()->ipv4()) { + if (addrinfo.address_->ip()->ipv4()) { is_v4 = true; } else { is_v4 = false; @@ -111,13 +112,21 @@ class AppleDnsImplTest : public testing::Test { } if (is_v4.value()) { - EXPECT_NE(nullptr, result.address_->ip()->ipv4()); + EXPECT_NE(nullptr, addrinfo.address_->ip()->ipv4()); } else { - EXPECT_NE(nullptr, result.address_->ip()->ipv6()); + EXPECT_NE(nullptr, addrinfo.address_->ip()->ipv6()); + } + break; + // All could be either IPv4 or IPv6. + case DnsLookupFamily::All: + if (addrinfo.address_->ip()->ipv4()) { + EXPECT_NE(nullptr, addrinfo.address_->ip()->ipv4()); + } else { + EXPECT_NE(nullptr, addrinfo.address_->ip()->ipv6()); } break; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } } } @@ -276,6 +285,10 @@ TEST_F(AppleDnsImplTest, DnsIpAddressVersionV6Only) { EXPECT_NE(nullptr, resolveWithExpectations("google.com", DnsLookupFamily::V6Only, DnsResolver::ResolutionStatus::Success, true)); dispatcher_->run(Event::Dispatcher::RunType::Block); + + EXPECT_NE(nullptr, resolveWithExpectations("google.com", DnsLookupFamily::All, + DnsResolver::ResolutionStatus::Success, true)); + dispatcher_->run(Event::Dispatcher::RunType::Block); } // dns_sd is very opaque and does not explicitly call out the state that is kept across queries. @@ -317,6 +330,10 @@ TEST_F(AppleDnsImplTest, DnsIpAddressVersionInvalid) { EXPECT_NE(nullptr, resolveWithExpectations("invalidDnsName", DnsLookupFamily::V6Only, DnsResolver::ResolutionStatus::Failure, false)); dispatcher_->run(Event::Dispatcher::RunType::Block); + + EXPECT_NE(nullptr, resolveWithExpectations("invalidDnsName", DnsLookupFamily::All, + DnsResolver::ResolutionStatus::Failure, false)); + dispatcher_->run(Event::Dispatcher::RunType::Block); } TEST_F(AppleDnsImplTest, CallbackException) { @@ -356,8 +373,8 @@ TEST_F(AppleDnsImplTest, LocalResolution) { [](DnsResolver::ResolutionStatus status, std::list&& results) -> void { EXPECT_EQ(DnsResolver::ResolutionStatus::Success, status); EXPECT_EQ(1, results.size()); - EXPECT_EQ("0.0.0.0:0", results.front().address_->asString()); - EXPECT_EQ(std::chrono::seconds(60), results.front().ttl_); + EXPECT_EQ("0.0.0.0:0", results.front().addrInfo().address_->asString()); + EXPECT_EQ(std::chrono::seconds(60), results.front().addrInfo().ttl_); }); EXPECT_EQ(nullptr, pending_resolution); // Note that the dispatcher does NOT have to run because resolution is synchronous. @@ -441,7 +458,8 @@ class AppleDnsImplFakeApiTest : public testing::Test { enum AddressType { V4, V6, Both }; - void fallbackWith(DnsLookupFamily dns_lookup_family, AddressType address_type) { + void fallbackWith(DnsLookupFamily dns_lookup_family, AddressType address_type, + uint32_t expected_address_size = 1) { const std::string hostname = "foo.com"; sockaddr_in addr4; addr4.sin_family = AF_INET; @@ -468,24 +486,41 @@ class AppleDnsImplFakeApiTest : public testing::Test { auto query = resolver_->resolve( hostname, dns_lookup_family, - [&dns_callback_executed, dns_lookup_family, address_type]( + [&dns_callback_executed, dns_lookup_family, address_type, expected_address_size]( DnsResolver::ResolutionStatus status, std::list&& response) -> void { EXPECT_EQ(DnsResolver::ResolutionStatus::Success, status); - EXPECT_EQ(1, response.size()); + EXPECT_EQ(expected_address_size, response.size()); if (dns_lookup_family == DnsLookupFamily::Auto) { if (address_type == AddressType::V4) { - EXPECT_NE(nullptr, response.front().address_->ip()->ipv4()); + EXPECT_NE(nullptr, response.front().addrInfo().address_->ip()->ipv4()); } else { - EXPECT_NE(nullptr, response.front().address_->ip()->ipv6()); + EXPECT_NE(nullptr, response.front().addrInfo().address_->ip()->ipv6()); } } if (dns_lookup_family == DnsLookupFamily::V4Preferred) { if (address_type == AddressType::V6) { - EXPECT_NE(nullptr, response.front().address_->ip()->ipv6()); + EXPECT_NE(nullptr, response.front().addrInfo().address_->ip()->ipv6()); } else { - EXPECT_NE(nullptr, response.front().address_->ip()->ipv4()); + EXPECT_NE(nullptr, response.front().addrInfo().address_->ip()->ipv4()); + } + } + + if (dns_lookup_family == DnsLookupFamily::All) { + switch (address_type) { + case AddressType::V4: + EXPECT_NE(nullptr, response.front().addrInfo().address_->ip()->ipv4()); + break; + case AddressType::V6: + EXPECT_NE(nullptr, response.front().addrInfo().address_->ip()->ipv6()); + break; + case AddressType::Both: + EXPECT_NE(nullptr, response.front().addrInfo().address_->ip()->ipv4()); + EXPECT_NE(nullptr, response.back().addrInfo().address_->ip()->ipv6()); + break; + default: + PANIC("reached unexpected code"); } } dns_callback_executed.Notify(); @@ -509,7 +544,7 @@ class AppleDnsImplFakeApiTest : public testing::Test { address_v6.sockAddr(), 30, query); break; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } dns_callback_executed.WaitForNotification(); @@ -671,16 +706,16 @@ TEST_F(AppleDnsImplFakeApiTest, QuerySynchronousCompletion) { // The returned value is nullptr because the query has already been fulfilled. Verify that the // callback ran via notification. - EXPECT_EQ(nullptr, - resolver_->resolve(hostname, Network::DnsLookupFamily::Auto, - [&dns_callback_executed](DnsResolver::ResolutionStatus status, - std::list&& response) -> void { - EXPECT_EQ(DnsResolver::ResolutionStatus::Success, status); - EXPECT_EQ(1, response.size()); - EXPECT_EQ("1.2.3.4:0", response.front().address_->asString()); - EXPECT_EQ(std::chrono::seconds(30), response.front().ttl_); - dns_callback_executed.Notify(); - })); + EXPECT_EQ(nullptr, resolver_->resolve( + hostname, Network::DnsLookupFamily::Auto, + [&dns_callback_executed](DnsResolver::ResolutionStatus status, + std::list&& response) -> void { + EXPECT_EQ(DnsResolver::ResolutionStatus::Success, status); + EXPECT_EQ(1, response.size()); + EXPECT_EQ("1.2.3.4:0", response.front().addrInfo().address_->asString()); + EXPECT_EQ(std::chrono::seconds(30), response.front().addrInfo().ttl_); + dns_callback_executed.Notify(); + })); dns_callback_executed.WaitForNotification(); } @@ -746,6 +781,7 @@ TEST_F(AppleDnsImplFakeApiTest, MultipleAddresses) { dns_callback_executed.WaitForNotification(); } +// TODO: write a TEST_P harness to eliminate duplication. TEST_F(AppleDnsImplFakeApiTest, AutoOnlyV6IfBothV6andV4) { fallbackWith(DnsLookupFamily::Auto, AddressType::Both); } @@ -770,6 +806,18 @@ TEST_F(AppleDnsImplFakeApiTest, V4PreferredV4IfOnlyV4) { fallbackWith(DnsLookupFamily::V4Preferred, AddressType::V4); } +TEST_F(AppleDnsImplFakeApiTest, AllIfBothV6andV4) { + fallbackWith(DnsLookupFamily::All, AddressType::Both, 2 /* expected_address_size*/); +} + +TEST_F(AppleDnsImplFakeApiTest, AllV6IfOnlyV6) { + fallbackWith(DnsLookupFamily::All, AddressType::V6); +} + +TEST_F(AppleDnsImplFakeApiTest, AllV4IfOnlyV4) { + fallbackWith(DnsLookupFamily::All, AddressType::V4); +} + TEST_F(AppleDnsImplFakeApiTest, MultipleAddressesSecondOneFails) { const std::string hostname = "foo.com"; sockaddr_in addr4; @@ -843,8 +891,8 @@ TEST_F(AppleDnsImplFakeApiTest, MultipleQueries) { std::list&& response) -> void { EXPECT_EQ(DnsResolver::ResolutionStatus::Success, status); EXPECT_EQ(1, response.size()); - EXPECT_EQ("1.2.3.4:0", response.front().address_->asString()); - EXPECT_EQ(std::chrono::seconds(30), response.front().ttl_); + EXPECT_EQ("1.2.3.4:0", response.front().addrInfo().address_->asString()); + EXPECT_EQ(std::chrono::seconds(30), response.front().addrInfo().ttl_); dns_callback_executed.Notify(); }); ASSERT_NE(nullptr, query); @@ -865,8 +913,8 @@ TEST_F(AppleDnsImplFakeApiTest, MultipleQueries) { std::list&& response) -> void { EXPECT_EQ(DnsResolver::ResolutionStatus::Success, status); EXPECT_EQ(1, response.size()); - EXPECT_EQ("5.6.7.8:0", response.front().address_->asString()); - EXPECT_EQ(std::chrono::seconds(30), response.front().ttl_); + EXPECT_EQ("5.6.7.8:0", response.front().addrInfo().address_->asString()); + EXPECT_EQ(std::chrono::seconds(30), response.front().addrInfo().ttl_); dns_callback_executed2.Notify(); }); ASSERT_NE(nullptr, query2); @@ -914,8 +962,8 @@ TEST_F(AppleDnsImplFakeApiTest, MultipleQueriesOneFails) { // state it had. EXPECT_EQ(DnsResolver::ResolutionStatus::Success, status); EXPECT_EQ(1, response.size()); - EXPECT_EQ("1.2.3.4:0", response.front().address_->asString()); - EXPECT_EQ(std::chrono::seconds(30), response.front().ttl_); + EXPECT_EQ("1.2.3.4:0", response.front().addrInfo().address_->asString()); + EXPECT_EQ(std::chrono::seconds(30), response.front().addrInfo().ttl_); dns_callback_executed.Notify(); }); ASSERT_NE(nullptr, query); diff --git a/test/extensions/network/dns_resolver/cares/dns_impl_test.cc b/test/extensions/network/dns_resolver/cares/dns_impl_test.cc index 2387b80c3795..4ec35f25ff88 100644 --- a/test/extensions/network/dns_resolver/cares/dns_impl_test.cc +++ b/test/extensions/network/dns_resolver/cares/dns_impl_test.cc @@ -45,6 +45,7 @@ using testing::IsSupersetOf; using testing::NiceMock; using testing::Not; using testing::Return; +using testing::UnorderedElementsAreArray; namespace Envoy { namespace Network { @@ -56,9 +57,6 @@ using IpList = std::list; using HostMap = absl::node_hash_map; // Map from hostname to CNAME using CNameMap = absl::node_hash_map; -// Represents a single TestDnsServer query state and lifecycle. This implements -// just enough of RFC 1035 to handle queries we generate in the tests below. -enum class RecordType { A, AAAA }; class TestDnsServerQuery { public: @@ -586,7 +584,7 @@ class DnsImplTest : public testing::TestWithParam { server_ = std::make_unique(*dispatcher_); socket_ = std::make_shared( Network::Test::getCanonicalLoopbackAddress(GetParam())); - listener_ = dispatcher_->createListener(socket_, *server_, true); + listener_ = dispatcher_->createListener(socket_, *server_, true, false); updateDnsResolverOptions(); // Create a resolver options on stack here to emulate what actually happens in envoy bootstrap. @@ -624,7 +622,7 @@ class DnsImplTest : public testing::TestWithParam { std::list address; for_each(response.begin(), response.end(), - [&](DnsResponse resp) { address.emplace_back(resp.address_); }); + [&](DnsResponse resp) { address.emplace_back(resp.addrInfo().address_); }); return address; } @@ -632,7 +630,7 @@ class DnsImplTest : public testing::TestWithParam { std::list address; for_each(response.begin(), response.end(), [&](DnsResponse resp) { - address.emplace_back(resp.address_->ip()->addressAsString()); + address.emplace_back(resp.addrInfo().address_->ip()->addressAsString()); }); return address; } @@ -656,7 +654,7 @@ class DnsImplTest : public testing::TestWithParam { if (address == "localhost" && lookup_family == DnsLookupFamily::V4Only) { EXPECT_THAT(address_as_string_list, IsSupersetOf(expected_results)); } else { - EXPECT_EQ(expected_results, address_as_string_list); + EXPECT_THAT(address_as_string_list, UnorderedElementsAreArray(expected_results)); } for (const auto& expected_absent_result : expected_absent_results) { @@ -666,7 +664,7 @@ class DnsImplTest : public testing::TestWithParam { if (expected_ttl) { std::list address_list = getAddressList(results); for (const auto& address : results) { - EXPECT_EQ(address.ttl_, expected_ttl.value()); + EXPECT_EQ(address.addrInfo().ttl_, expected_ttl.value()); } } @@ -1004,6 +1002,33 @@ TEST_P(DnsImplTest, V4PreferredV4IfOnlyV4) { dispatcher_->run(Event::Dispatcher::RunType::Block); } +TEST_P(DnsImplTest, AllIfBothV6andV4) { + server_->addHosts("some.good.domain", {"201.134.56.7"}, RecordType::A); + server_->addHosts("some.good.domain", {"1::2"}, RecordType::AAAA); + + EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::All, + DnsResolver::ResolutionStatus::Success, + {{"201.134.56.7"}, {"1::2"}}, {}, absl::nullopt)); + dispatcher_->run(Event::Dispatcher::RunType::Block); +} + +TEST_P(DnsImplTest, AllV6IfOnlyV6) { + server_->addHosts("some.good.domain", {"1::2"}, RecordType::AAAA); + + EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::All, + DnsResolver::ResolutionStatus::Success, {{"1::2"}}, {}, + absl::nullopt)); + dispatcher_->run(Event::Dispatcher::RunType::Block); +} + +TEST_P(DnsImplTest, AllV4IfOnlyV4) { + server_->addHosts("some.good.domain", {"201.134.56.7"}, RecordType::A); + EXPECT_NE(nullptr, resolveWithExpectations("some.good.domain", DnsLookupFamily::All, + DnsResolver::ResolutionStatus::Success, + {{"201.134.56.7"}}, {}, absl::nullopt)); + dispatcher_->run(Event::Dispatcher::RunType::Block); +} + // Validate working of cancellation provided by ActiveDnsQuery return. TEST_P(DnsImplTest, Cancel) { server_->addHosts("some.good.domain", {"201.134.56.7"}, RecordType::A); diff --git a/test/extensions/resource_monitors/fixed_heap/fixed_heap_monitor_test.cc b/test/extensions/resource_monitors/fixed_heap/fixed_heap_monitor_test.cc index 24b451ee6fe7..00d8e42665f6 100644 --- a/test/extensions/resource_monitors/fixed_heap/fixed_heap_monitor_test.cc +++ b/test/extensions/resource_monitors/fixed_heap/fixed_heap_monitor_test.cc @@ -20,7 +20,7 @@ class MockMemoryStatsReader : public MemoryStatsReader { MOCK_METHOD(uint64_t, unmappedHeapBytes, ()); }; -class ResourcePressure : public Server::ResourceMonitor::Callbacks { +class ResourcePressure : public Server::ResourceUpdateCallbacks { public: void onSuccess(const Server::ResourceUsage& usage) override { pressure_ = usage.resource_pressure_; diff --git a/test/extensions/resource_monitors/injected_resource/injected_resource_monitor_test.cc b/test/extensions/resource_monitors/injected_resource/injected_resource_monitor_test.cc index 5d05210ad4f1..7010e02241b5 100644 --- a/test/extensions/resource_monitors/injected_resource/injected_resource_monitor_test.cc +++ b/test/extensions/resource_monitors/injected_resource/injected_resource_monitor_test.cc @@ -38,7 +38,7 @@ class TestableInjectedResourceMonitor : public InjectedResourceMonitor { Event::Dispatcher& dispatcher_; }; -class MockedCallbacks : public Server::ResourceMonitor::Callbacks { +class MockedCallbacks : public Server::ResourceUpdateCallbacks { public: MOCK_METHOD(void, onSuccess, (const Server::ResourceUsage&)); MOCK_METHOD(void, onFailure, (const EnvoyException&)); diff --git a/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc b/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc index f57a94360b4a..57bed7796eff 100644 --- a/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc +++ b/test/extensions/stats_sinks/common/statsd/udp_statsd_test.cc @@ -63,7 +63,7 @@ TEST(UdpOverUdsStatsdSinkTest, InitWithPipeAddress) { sink.flush(snapshot); // Start the server. - Network::SocketImpl sock(Network::Socket::Type::Datagram, uds_address, nullptr); + Network::SocketImpl sock(Network::Socket::Type::Datagram, uds_address, nullptr, {}); RELEASE_ASSERT(sock.setBlockingForTest(false).return_value_ != -1, ""); sock.bind(uds_address); diff --git a/test/extensions/stats_sinks/metrics_service/metrics_service_integration_test.cc b/test/extensions/stats_sinks/metrics_service/metrics_service_integration_test.cc index 8c7d8a16a854..ed894a630c68 100644 --- a/test/extensions/stats_sinks/metrics_service/metrics_service_integration_test.cc +++ b/test/extensions/stats_sinks/metrics_service/metrics_service_integration_test.cc @@ -179,7 +179,7 @@ TEST_P(MetricsServiceIntegrationTest, BasicFlow) { test_server_->waitForCounterGe("grpc.metrics_service.streams_closed_0", 1); break; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } cleanup(); } diff --git a/test/extensions/tracers/xray/localized_sampling_test.cc b/test/extensions/tracers/xray/localized_sampling_test.cc index c52fc80703fb..4e47b99c3ef0 100644 --- a/test/extensions/tracers/xray/localized_sampling_test.cc +++ b/test/extensions/tracers/xray/localized_sampling_test.cc @@ -24,24 +24,43 @@ class LocalizedSamplingStrategyTest : public ::testing::Test { TEST_F(LocalizedSamplingStrategyTest, EmptyRules) { NiceMock random_generator; LocalizedSamplingStrategy strategy{"", random_generator, time_system_}; - ASSERT_TRUE(strategy.usingDefaultManifest()); + ASSERT_FALSE(strategy.manifest().hasCustomRules()); } TEST_F(LocalizedSamplingStrategyTest, BadJson) { NiceMock random_generator; LocalizedSamplingStrategy strategy{"{{}", random_generator, time_system_}; - ASSERT_TRUE(strategy.usingDefaultManifest()); + ASSERT_FALSE(strategy.manifest().hasCustomRules()); } TEST_F(LocalizedSamplingStrategyTest, EmptyRulesDefaultRate) { NiceMock random_generator; LocalizedSamplingStrategy strategy{"{{}", random_generator, time_system_}; - ASSERT_TRUE(strategy.usingDefaultManifest()); + ASSERT_FALSE(strategy.manifest().hasCustomRules()); // Make a copy of default_manifest_(LocalizedSamplingManifest object) since the // object returned is a const reference and defaultRule() function is not a // 'const member function' of LocalizedSamplingManifest class. - LocalizedSamplingManifest default_manifest_copy{strategy.defaultManifest()}; - ASSERT_EQ(default_manifest_copy.defaultRule().rate(), 0.05); + LocalizedSamplingManifest manifest_copy{strategy.manifest()}; + // default sampling rate of 0.05 + ASSERT_EQ(manifest_copy.defaultRule().rate(), 0.05); +} + +TEST_F(LocalizedSamplingStrategyTest, MissingRulesUseCustomDefault) { + NiceMock random_generator; + constexpr auto rules_json = R"EOF( +{ + "version": 2, + "rules": [], + "default": { + "fixed_target": 1, + "rate": 0.1 + } +} + )EOF"; + LocalizedSamplingStrategy strategy{rules_json, random_generator, time_system_}; + ASSERT_FALSE(strategy.manifest().hasCustomRules()); + LocalizedSamplingManifest manifest_copy{strategy.manifest()}; + ASSERT_EQ(manifest_copy.defaultRule().rate(), 0.1); } TEST_F(LocalizedSamplingStrategyTest, ValidCustomRules) { @@ -66,10 +85,40 @@ TEST_F(LocalizedSamplingStrategyTest, ValidCustomRules) { } )EOF"; LocalizedSamplingStrategy strategy{rules_json, random_generator, time_system_}; - ASSERT_FALSE(strategy.usingDefaultManifest()); + ASSERT_TRUE(strategy.manifest().hasCustomRules()); + LocalizedSamplingManifest manifest_copy{strategy.manifest()}; + ASSERT_EQ(manifest_copy.defaultRule().rate(), 0.1); +} + +TEST_F(LocalizedSamplingStrategyTest, InvalidDefaultRuleRate) { + NiceMock random_generator; + constexpr auto rules_json = R"EOF( +{ + "version": 2, + "rules": [ + { + "description": "X-Ray rule", + "host": "*", + "http_method": "*", + "url_path": "/api/move/*", + "fixed_target": 0, + "rate": 0.5 + } + ], + "default": { + "fixed_target": 1, + "rate": 1.5 + } +} + )EOF"; + LocalizedSamplingStrategy strategy{rules_json, random_generator, time_system_}; + ASSERT_FALSE(strategy.manifest().hasCustomRules()); + LocalizedSamplingManifest manifest_copy{strategy.manifest()}; + // default sampling rate of 0.05 + ASSERT_EQ(manifest_copy.defaultRule().rate(), 0.05); } -TEST_F(LocalizedSamplingStrategyTest, InvalidRate) { +TEST_F(LocalizedSamplingStrategyTest, InvalidRulesRate) { NiceMock random_generator; constexpr auto rules_json = R"EOF( { @@ -91,7 +140,9 @@ TEST_F(LocalizedSamplingStrategyTest, InvalidRate) { } )EOF"; LocalizedSamplingStrategy strategy{rules_json, random_generator, time_system_}; - ASSERT_TRUE(strategy.usingDefaultManifest()); + ASSERT_FALSE(strategy.manifest().hasCustomRules()); + LocalizedSamplingManifest manifest_copy{strategy.manifest()}; + ASSERT_EQ(manifest_copy.defaultRule().rate(), 0); } TEST_F(LocalizedSamplingStrategyTest, InvalidFixedTarget) { @@ -116,7 +167,7 @@ TEST_F(LocalizedSamplingStrategyTest, InvalidFixedTarget) { } )EOF"; LocalizedSamplingStrategy strategy{rules_json, random_generator, time_system_}; - ASSERT_TRUE(strategy.usingDefaultManifest()); + ASSERT_FALSE(strategy.manifest().hasCustomRules()); } TEST_F(LocalizedSamplingStrategyTest, DefaultRuleMissingRate) { @@ -140,7 +191,10 @@ TEST_F(LocalizedSamplingStrategyTest, DefaultRuleMissingRate) { } )EOF"; LocalizedSamplingStrategy strategy{rules_json, random_generator, time_system_}; - ASSERT_TRUE(strategy.usingDefaultManifest()); + ASSERT_FALSE(strategy.manifest().hasCustomRules()); + LocalizedSamplingManifest manifest_copy{strategy.manifest()}; + // default sampling rate of 0.05 + ASSERT_EQ(manifest_copy.defaultRule().rate(), 0.05); } TEST_F(LocalizedSamplingStrategyTest, DefaultRuleMissingFixedTarget) { @@ -164,7 +218,10 @@ TEST_F(LocalizedSamplingStrategyTest, DefaultRuleMissingFixedTarget) { } )EOF"; LocalizedSamplingStrategy strategy{rules_json, random_generator, time_system_}; - ASSERT_TRUE(strategy.usingDefaultManifest()); + ASSERT_FALSE(strategy.manifest().hasCustomRules()); + LocalizedSamplingManifest manifest_copy{strategy.manifest()}; + // default sampling rate of 0.05 + ASSERT_EQ(manifest_copy.defaultRule().rate(), 0.05); } TEST_F(LocalizedSamplingStrategyTest, WrongVersion) { @@ -189,7 +246,10 @@ TEST_F(LocalizedSamplingStrategyTest, WrongVersion) { } )EOF"; LocalizedSamplingStrategy strategy{wrong_version, random_generator, time_system_}; - ASSERT_TRUE(strategy.usingDefaultManifest()); + ASSERT_FALSE(strategy.manifest().hasCustomRules()); + LocalizedSamplingManifest manifest_copy{strategy.manifest()}; + // default sampling rate of 0.05 + ASSERT_EQ(manifest_copy.defaultRule().rate(), 0.05); } TEST_F(LocalizedSamplingStrategyTest, MissingVersion) { @@ -213,7 +273,10 @@ TEST_F(LocalizedSamplingStrategyTest, MissingVersion) { } )EOF"; LocalizedSamplingStrategy strategy{missing_version, random_generator, time_system_}; - ASSERT_TRUE(strategy.usingDefaultManifest()); + ASSERT_FALSE(strategy.manifest().hasCustomRules()); + LocalizedSamplingManifest manifest_copy{strategy.manifest()}; + // default sampling rate of 0.05 + ASSERT_EQ(manifest_copy.defaultRule().rate(), 0.05); } TEST_F(LocalizedSamplingStrategyTest, MissingDefaultRules) { @@ -234,7 +297,10 @@ TEST_F(LocalizedSamplingStrategyTest, MissingDefaultRules) { } )EOF"; LocalizedSamplingStrategy strategy{rules_json, random_generator, time_system_}; - ASSERT_TRUE(strategy.usingDefaultManifest()); + ASSERT_FALSE(strategy.manifest().hasCustomRules()); + LocalizedSamplingManifest manifest_copy{strategy.manifest()}; + // default sampling rate of 0.05 + ASSERT_EQ(manifest_copy.defaultRule().rate(), 0.05); } TEST_F(LocalizedSamplingStrategyTest, CustomRuleHostIsNotString) { @@ -259,7 +325,9 @@ TEST_F(LocalizedSamplingStrategyTest, CustomRuleHostIsNotString) { } )EOF"; LocalizedSamplingStrategy strategy{rules_json, random_generator, time_system_}; - ASSERT_TRUE(strategy.usingDefaultManifest()); + ASSERT_FALSE(strategy.manifest().hasCustomRules()); + LocalizedSamplingManifest manifest_copy{strategy.manifest()}; + ASSERT_EQ(manifest_copy.defaultRule().rate(), 0.1); } TEST_F(LocalizedSamplingStrategyTest, CustomRuleHttpMethodIsNotString) { @@ -284,7 +352,9 @@ TEST_F(LocalizedSamplingStrategyTest, CustomRuleHttpMethodIsNotString) { } )EOF"; LocalizedSamplingStrategy strategy{rules_json, random_generator, time_system_}; - ASSERT_TRUE(strategy.usingDefaultManifest()); + ASSERT_FALSE(strategy.manifest().hasCustomRules()); + LocalizedSamplingManifest manifest_copy{strategy.manifest()}; + ASSERT_EQ(manifest_copy.defaultRule().rate(), 0.1); } TEST_F(LocalizedSamplingStrategyTest, CustomRuleUrlPathIsNotString) { @@ -309,7 +379,10 @@ TEST_F(LocalizedSamplingStrategyTest, CustomRuleUrlPathIsNotString) { } )EOF"; LocalizedSamplingStrategy strategy{rules_json, random_generator, time_system_}; - ASSERT_TRUE(strategy.usingDefaultManifest()); + ASSERT_FALSE(strategy.manifest().hasCustomRules()); + LocalizedSamplingManifest manifest_copy{strategy.manifest()}; + // custom default rate + ASSERT_EQ(manifest_copy.defaultRule().rate(), 0.1); } TEST_F(LocalizedSamplingStrategyTest, CustomRuleMissingFixedTarget) { @@ -333,7 +406,10 @@ TEST_F(LocalizedSamplingStrategyTest, CustomRuleMissingFixedTarget) { } )EOF"; LocalizedSamplingStrategy strategy{rules_json, random_generator, time_system_}; - ASSERT_TRUE(strategy.usingDefaultManifest()); + ASSERT_FALSE(strategy.manifest().hasCustomRules()); + LocalizedSamplingManifest manifest_copy{strategy.manifest()}; + // custom default rate + ASSERT_EQ(manifest_copy.defaultRule().rate(), 0.1); } TEST_F(LocalizedSamplingStrategyTest, CustomRuleMissingRate) { @@ -357,7 +433,10 @@ TEST_F(LocalizedSamplingStrategyTest, CustomRuleMissingRate) { } )EOF"; LocalizedSamplingStrategy strategy{rules_json, random_generator, time_system_}; - ASSERT_TRUE(strategy.usingDefaultManifest()); + ASSERT_FALSE(strategy.manifest().hasCustomRules()); + LocalizedSamplingManifest manifest_copy{strategy.manifest()}; + // custom default rate + ASSERT_EQ(manifest_copy.defaultRule().rate(), 0.1); } TEST_F(LocalizedSamplingStrategyTest, CustomRuleArrayElementWithWrongType) { @@ -382,10 +461,13 @@ TEST_F(LocalizedSamplingStrategyTest, CustomRuleArrayElementWithWrongType) { } )EOF"; LocalizedSamplingStrategy strategy{rules_json, random_generator, time_system_}; - ASSERT_TRUE(strategy.usingDefaultManifest()); + ASSERT_FALSE(strategy.manifest().hasCustomRules()); + LocalizedSamplingManifest manifest_copy{strategy.manifest()}; + // custom default rate + ASSERT_EQ(manifest_copy.defaultRule().rate(), 0.1); } -TEST_F(LocalizedSamplingStrategyTest, CustomRuleNegativeFixedRate) { +TEST_F(LocalizedSamplingStrategyTest, CustomRuleNegativeFixedTarget) { NiceMock random_generator; constexpr auto rules_json = R"EOF( { @@ -407,7 +489,10 @@ TEST_F(LocalizedSamplingStrategyTest, CustomRuleNegativeFixedRate) { } )EOF"; LocalizedSamplingStrategy strategy{rules_json, random_generator, time_system_}; - ASSERT_TRUE(strategy.usingDefaultManifest()); + ASSERT_FALSE(strategy.manifest().hasCustomRules()); + LocalizedSamplingManifest manifest_copy{strategy.manifest()}; + // custom default rate + ASSERT_EQ(manifest_copy.defaultRule().rate(), 0.1); } TEST_F(LocalizedSamplingStrategyTest, CustomRuleNegativeRate) { @@ -422,17 +507,20 @@ TEST_F(LocalizedSamplingStrategyTest, CustomRuleNegativeRate) { "http_method": "*", "url_path": "/api/move/*", "fixed_target": 0, - "rate": 0.05 + "rate": -0.05 } ], "default": { "fixed_target": 1, - "rate": -0.1 + "rate": 0.1 } } )EOF"; LocalizedSamplingStrategy strategy{rules_json, random_generator, time_system_}; - ASSERT_TRUE(strategy.usingDefaultManifest()); + ASSERT_FALSE(strategy.manifest().hasCustomRules()); + LocalizedSamplingManifest manifest_copy{strategy.manifest()}; + // custom default rate + ASSERT_EQ(manifest_copy.defaultRule().rate(), 0.1); } TEST_F(LocalizedSamplingStrategyTest, TraceOnlyFromReservoir) { @@ -459,12 +547,11 @@ TEST_F(LocalizedSamplingStrategyTest, TraceOnlyFromReservoir) { )EOF"; LocalizedSamplingStrategy strategy{rules_json, rng, time_system_}; - ASSERT_FALSE(strategy.usingDefaultManifest()); + ASSERT_TRUE(strategy.manifest().hasCustomRules()); SamplingRequest req; ASSERT_TRUE(strategy.shouldTrace(req)); // first one should be traced - int i = 10; - while (i-- > 0) { + for (int i = 0; i < 10; ++i) { ASSERT_FALSE(strategy.shouldTrace(req)); } } @@ -493,11 +580,10 @@ TEST_F(LocalizedSamplingStrategyTest, TraceFromReservoirAndByRate) { )EOF"; LocalizedSamplingStrategy strategy{rules_json, rng, time_system_}; - ASSERT_FALSE(strategy.usingDefaultManifest()); + ASSERT_TRUE(strategy.manifest().hasCustomRules()); SamplingRequest req; - int i = 10; - while (i-- > 0) { + for (int i = 0; i < 10; ++i) { ASSERT_TRUE(strategy.shouldTrace(req)); } } @@ -530,12 +616,11 @@ TEST_F(LocalizedSamplingStrategyTest, NoMatchingHost) { )EOF"; LocalizedSamplingStrategy strategy{rules_json, rng, time_system_}; - ASSERT_FALSE(strategy.usingDefaultManifest()); + ASSERT_TRUE(strategy.manifest().hasCustomRules()); SamplingRequest req; req.host_ = "amazon.com"; // host does not match, so default rules apply. - int i = 10; - while (i-- > 0) { + for (int i = 0; i < 10; ++i) { ASSERT_FALSE(strategy.shouldTrace(req)); } } @@ -568,12 +653,11 @@ TEST_F(LocalizedSamplingStrategyTest, NoMatchingHttpMethod) { )EOF"; LocalizedSamplingStrategy strategy{rules_json, rng, time_system_}; - ASSERT_FALSE(strategy.usingDefaultManifest()); + ASSERT_TRUE(strategy.manifest().hasCustomRules()); SamplingRequest req; req.http_method_ = "GET"; // method does not match, so default rules apply. - int i = 10; - while (i-- > 0) { + for (int i = 0; i < 10; ++i) { ASSERT_FALSE(strategy.shouldTrace(req)); } } @@ -606,12 +690,61 @@ TEST_F(LocalizedSamplingStrategyTest, NoMatchingPath) { )EOF"; LocalizedSamplingStrategy strategy{rules_json, rng, time_system_}; - ASSERT_FALSE(strategy.usingDefaultManifest()); + ASSERT_TRUE(strategy.manifest().hasCustomRules()); SamplingRequest req; req.http_url_ = "/"; // method does not match, so default rules apply. - int i = 10; - while (i-- > 0) { + for (int i = 0; i < 10; ++i) { + ASSERT_FALSE(strategy.shouldTrace(req)); + } +} + +TEST_F(LocalizedSamplingStrategyTest, CustomDefaultRule) { + NiceMock rng; + // this following value doesn't affect the test + EXPECT_CALL(rng, random()).WillRepeatedly(Return(50 /*50 percent*/)); + + constexpr auto rules_json = R"EOF( +{ + "version": 2, + "default": { + "fixed_target": 0, + "rate": 0 + } +} + )EOF"; + + LocalizedSamplingStrategy strategy{rules_json, rng, time_system_}; + ASSERT_FALSE(strategy.manifest().hasCustomRules()); + + SamplingRequest req; + req.http_url_ = "/"; + for (int i = 0; i < 10; ++i) { + ASSERT_FALSE(strategy.shouldTrace(req)); + } +} + +TEST_F(LocalizedSamplingStrategyTest, InvalidCustomDefaultRule) { + NiceMock rng; + // this following value doesn't affect the test + EXPECT_CALL(rng, random()).WillRepeatedly(Return(50 /*50 percent*/)); + constexpr auto rules_json = R"EOF( +{ +"version": 2, +"default": { + "fixed_target": 0, + "rate": 2.0 + } +} +)EOF"; + + LocalizedSamplingStrategy strategy{rules_json, rng, time_system_}; + ASSERT_FALSE(strategy.manifest().hasCustomRules()); + + SamplingRequest req; + req.http_url_ = "/"; + ASSERT_TRUE(strategy.shouldTrace(req)); // The default rule traces the first request each second + for (int i = 0; i < 10; ++i) { ASSERT_FALSE(strategy.shouldTrace(req)); } } diff --git a/test/extensions/transport_sockets/starttls/starttls_integration_test.cc b/test/extensions/transport_sockets/starttls/starttls_integration_test.cc index 597c0939d312..5d0225e8d3fd 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"); diff --git a/test/extensions/transport_sockets/tcp_stats/BUILD b/test/extensions/transport_sockets/tcp_stats/BUILD new file mode 100644 index 000000000000..525b3d61ce8d --- /dev/null +++ b/test/extensions/transport_sockets/tcp_stats/BUILD @@ -0,0 +1,42 @@ +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 = "tcp_stats_test", + srcs = ["tcp_stats_test.cc"], + extension_names = ["envoy.transport_sockets.tcp_stats"], + deps = [ + "//source/extensions/transport_sockets/raw_buffer:config", + "//source/extensions/transport_sockets/tcp_stats:config", + "//source/extensions/transport_sockets/tcp_stats:tcp_stats_lib", + "//test/mocks/buffer:buffer_mocks", + "//test/mocks/network:io_handle_mocks", + "//test/mocks/network:network_mocks", + "//test/mocks/network:transport_socket_mocks", + "//test/mocks/server:transport_socket_factory_context_mocks", + "@envoy_api//envoy/extensions/transport_sockets/tcp_stats/v3:pkg_cc_proto", + ], +) + +envoy_extension_cc_test( + name = "tcp_stats_integration_test", + srcs = ["tcp_stats_integration_test.cc"], + extension_names = ["envoy.transport_sockets.tcp_stats"], + deps = [ + "//source/extensions/filters/network/tcp_proxy:config", + "//source/extensions/transport_sockets/tcp_stats:config", + "//test/integration:http_integration_lib", + "//test/integration:integration_lib", + "@envoy_api//envoy/extensions/transport_sockets/tcp_stats/v3:pkg_cc_proto", + ], +) diff --git a/test/extensions/transport_sockets/tcp_stats/tcp_stats_integration_test.cc b/test/extensions/transport_sockets/tcp_stats/tcp_stats_integration_test.cc new file mode 100644 index 000000000000..2b8d37ee37e2 --- /dev/null +++ b/test/extensions/transport_sockets/tcp_stats/tcp_stats_integration_test.cc @@ -0,0 +1,125 @@ +#if defined(__linux__) +#include "envoy/extensions/transport_sockets/tcp_stats/v3/tcp_stats.pb.h" + +#include "test/integration/integration.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace TcpStats { +namespace { +class TcpStatsSocketIntegrationTest : public testing::TestWithParam, + public BaseIntegrationTest { +public: + TcpStatsSocketIntegrationTest() + : BaseIntegrationTest(GetParam(), ConfigHelper::tcpProxyConfig()) {} + + void initialize() override { + config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + envoy::config::core::v3::TransportSocket inner_socket; + inner_socket.set_name("envoy.transport_sockets.raw_buffer"); + envoy::extensions::transport_sockets::tcp_stats::v3::Config proto_config; + proto_config.mutable_transport_socket()->MergeFrom(inner_socket); + + auto* cluster_transport_socket = + bootstrap.mutable_static_resources()->mutable_clusters(0)->mutable_transport_socket(); + cluster_transport_socket->set_name("envoy.transport_sockets.tcp_stats"); + cluster_transport_socket->mutable_typed_config()->PackFrom(proto_config); + + auto* listener = bootstrap.mutable_static_resources()->mutable_listeners(0); + auto* listener_transport_socket = + listener->mutable_filter_chains(0)->mutable_transport_socket(); + listener_transport_socket->set_name("envoy.transport_sockets.tcp_stats"); + listener_transport_socket->mutable_typed_config()->PackFrom(proto_config); + + listener->set_stat_prefix("test"); + }); + BaseIntegrationTest::initialize(); + } + + FakeRawConnectionPtr fake_upstream_connection_; +}; + +INSTANTIATE_TEST_SUITE_P(IpVersions, TcpStatsSocketIntegrationTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + TestUtility::ipTestParamsToString); + +// Verify that: +// * Stats are in the correct scope/namespace. +// * The syscall to get the data is producing meaningful results. +TEST_P(TcpStatsSocketIntegrationTest, Basic) { + initialize(); + + auto begin = std::chrono::steady_clock::now(); // NO_CHECK_FORMAT(real_time) + + auto listener_port = lookupPort("listener_0"); + auto tcp_client = makeTcpConnection(listener_port); + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection_)); + + ASSERT_TRUE(tcp_client->write("data")); + ASSERT_TRUE(fake_upstream_connection_->waitForData(4)); + ASSERT_TRUE(fake_upstream_connection_->write("response")); + tcp_client->waitForData("response"); + + tcp_client->close(); + ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); + + auto end = std::chrono::steady_clock::now(); // NO_CHECK_FORMAT(real_time) + + // Record the duration of the test to use as an upper bound on the round trip time measurement. + std::chrono::microseconds test_duration_us = + std::chrono::duration_cast(end - begin); + + // Validate that these stats exist (in the correct namespace), and Wait for values to be available + // before validating values and ranges. Gauges/counters and histograms go through slightly + // different paths, so check each to avoid test flakes. + test_server_->waitUntilHistogramHasSamples("cluster.cluster_0.tcp_stats.cx_rtt_us"); + test_server_->waitForCounterGe("cluster.cluster_0.tcp_stats.cx_tx_segments", 1); + test_server_->waitUntilHistogramHasSamples("listener.test.tcp_stats.cx_rtt_us"); + test_server_->waitForCounterGe("listener.test.tcp_stats.cx_tx_segments", 1); + + auto validateCounterRange = [this](const std::string& name, uint64_t lower, uint64_t upper) { + auto counter = test_server_->counter(absl::StrCat("cluster.cluster_0.tcp_stats.", name)); + EXPECT_GE(counter->value(), lower); + EXPECT_LE(counter->value(), upper); + }; + auto validateGaugeRange = [this](const std::string& name, int64_t lower, int64_t upper) { + auto counter = test_server_->gauge(absl::StrCat("cluster.cluster_0.tcp_stats.", name)); + EXPECT_GE(counter->value(), lower); + EXPECT_LE(counter->value(), upper); + }; + auto validateHistogramRange = [this](const std::string& name, int64_t lower, int64_t upper) { + auto histogram = test_server_->histogram(absl::StrCat("cluster.cluster_0.tcp_stats.", name)); + auto& summary = histogram->cumulativeStatistics(); + + // With only 1 sample, the `sampleSum()` is the one value that has been recorded. + EXPECT_EQ(1, summary.sampleCount()); + EXPECT_GE(summary.sampleSum(), lower); + EXPECT_LE(summary.sampleSum(), upper); + }; + + // These values are intentionally very loose to avoid test flakes. They're just trying to verify + // that the values are at least in the approximate range of what we expect, and in the units we + // expect. + validateCounterRange("cx_tx_segments", 2, 20); + validateCounterRange("cx_rx_segments", 2, 20); + validateCounterRange("cx_tx_data_segments", 1, 10); + validateCounterRange("cx_rx_data_segments", 1, 10); + validateCounterRange("cx_tx_retransmitted_segments", 0, 10); + + // After the connection is closed, there should be no unsent or unacked data. + validateGaugeRange("cx_tx_unsent_bytes", 0, 0); + validateGaugeRange("cx_tx_unacked_segments", 0, 0); + + validateHistogramRange("cx_tx_percent_retransmitted_segments", 0, + Stats::Histogram::PercentScale); // 0-100% + validateHistogramRange("cx_rtt_us", 1, test_duration_us.count()); + validateHistogramRange("cx_rtt_variance_us", 1, test_duration_us.count()); +} + +} // namespace +} // namespace TcpStats +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy +#endif diff --git a/test/extensions/transport_sockets/tcp_stats/tcp_stats_test.cc b/test/extensions/transport_sockets/tcp_stats/tcp_stats_test.cc new file mode 100644 index 000000000000..15836373be8c --- /dev/null +++ b/test/extensions/transport_sockets/tcp_stats/tcp_stats_test.cc @@ -0,0 +1,329 @@ +#if defined(__linux__) +#define DO_NOT_INCLUDE_NETINET_TCP_H 1 + +#include + +#include "envoy/extensions/transport_sockets/tcp_stats/v3/tcp_stats.pb.h" + +#include "source/extensions/transport_sockets/tcp_stats/config.h" +#include "source/extensions/transport_sockets/tcp_stats/tcp_stats.h" + +#include "test/mocks/network/io_handle.h" +#include "test/mocks/network/mocks.h" +#include "test/mocks/network/transport_socket.h" +#include "test/mocks/server/transport_socket_factory_context.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using testing::_; +using testing::AtLeast; +using testing::Return; +using testing::ReturnNull; + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace TcpStats { +namespace { + +class TcpStatsTest : public testing::Test { +public: + void initialize(bool enable_periodic) { + envoy::extensions::transport_sockets::tcp_stats::v3::Config proto_config; + if (enable_periodic) { + proto_config.mutable_update_period()->MergeFrom( + ProtobufUtil::TimeUtil::MillisecondsToDuration(1000)); + } + config_ = std::make_shared(proto_config, store_); + ON_CALL(transport_callbacks_, ioHandle()).WillByDefault(ReturnRef(io_handle_)); + ON_CALL(io_handle_, getOption(IPPROTO_TCP, TCP_INFO, _, _)) + .WillByDefault(Invoke([this](int, int, void* optval, socklen_t* optlen) { + ASSERT(*optlen == sizeof(tcp_info_)); + memcpy(optval, &tcp_info_, sizeof(tcp_info_)); + return Api::SysCallIntResult{0, 0}; + })); + createTcpStatsSocket(enable_periodic, timer_, inner_socket_, tcp_stats_socket_); + } + + void createTcpStatsSocket(bool enable_periodic, NiceMock*& timer, + NiceMock*& inner_socket_out, + std::unique_ptr& tcp_stats_socket) { + if (enable_periodic) { + timer = new NiceMock(&transport_callbacks_.connection_.dispatcher_); + EXPECT_CALL(*timer, enableTimer(std::chrono::milliseconds(1000), _)).Times(AtLeast(1)); + } + auto inner_socket = std::make_unique>(); + inner_socket_out = inner_socket.get(); + tcp_stats_socket = std::make_unique(config_, std::move(inner_socket)); + tcp_stats_socket->setTransportSocketCallbacks(transport_callbacks_); + tcp_stats_socket->onConnected(); + } + + uint64_t counterValue(absl::string_view name) { + auto opt_ref = store_.findCounterByString(absl::StrCat("tcp_stats.", name)); + ASSERT(opt_ref.has_value()); + return opt_ref.value().get().value(); + } + + int64_t gaugeValue(absl::string_view name) { + auto opt_ref = store_.findGaugeByString(absl::StrCat("tcp_stats.", name)); + ASSERT(opt_ref.has_value()); + return opt_ref.value().get().value(); + } + + absl::optional histogramValue(absl::string_view name) { + std::vector values = store_.histogramValues(absl::StrCat("tcp_stats.", name), true); + ASSERT(values.size() <= 1, + absl::StrCat(name, " didn't have <=1 value, instead had ", values.size())); + if (values.empty()) { + return absl::nullopt; + } else { + return values[0]; + } + } + + Stats::TestUtil::TestStore store_; + NiceMock* inner_socket_; + NiceMock io_handle_; + std::shared_ptr config_; + std::unique_ptr tcp_stats_socket_; + NiceMock transport_callbacks_; + NiceMock* timer_; + struct tcp_info tcp_info_; +}; + +// Validate that the configured update_period is honored, and that stats are updated when the timer +// fires. +TEST_F(TcpStatsTest, Periodic) { + initialize(true); + + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1000), _)); + tcp_info_.tcpi_notsent_bytes = 42; + timer_->callback_(); + EXPECT_EQ(42, gaugeValue("cx_tx_unsent_bytes")); + + EXPECT_CALL(*timer_, disableTimer()); + tcp_stats_socket_->closeSocket(Network::ConnectionEvent::RemoteClose); +} + +// Validate that stats are updated when the connection is closed. Gauges should be set to zero, +// and counters should be appropriately updated. +TEST_F(TcpStatsTest, CloseSocket) { + initialize(false); + + tcp_info_.tcpi_segs_out = 42; + tcp_info_.tcpi_notsent_bytes = 1; + tcp_info_.tcpi_unacked = 2; + EXPECT_CALL(*inner_socket_, closeSocket(Network::ConnectionEvent::RemoteClose)); + tcp_stats_socket_->closeSocket(Network::ConnectionEvent::RemoteClose); + EXPECT_EQ(42, counterValue("cx_tx_segments")); + EXPECT_EQ(0, gaugeValue("cx_tx_unsent_bytes")); + EXPECT_EQ(0, gaugeValue("cx_tx_unacked_segments")); +} + +TEST_F(TcpStatsTest, SyscallFailureShortRead) { + initialize(true); + tcp_info_.tcpi_notsent_bytes = 42; + EXPECT_CALL(io_handle_, getOption(IPPROTO_TCP, TCP_INFO, _, _)) + .WillOnce(Invoke([this](int, int, void* optval, socklen_t* optlen) { + *optlen = *optlen - 1; + memcpy(optval, &tcp_info_, sizeof(*optlen)); + return Api::SysCallIntResult{0, 0}; + })); + EXPECT_LOG_CONTAINS( + "debug", + fmt::format("Failed getsockopt(IPPROTO_TCP, TCP_INFO): rc 0 errno 0 optlen {}", + sizeof(tcp_info_) - 1), + timer_->callback_()); + + // Not updated on failed syscall. + EXPECT_EQ(0, gaugeValue("cx_tx_unsent_bytes")); +} + +TEST_F(TcpStatsTest, SyscallFailureReturnCode) { + initialize(true); + tcp_info_.tcpi_notsent_bytes = 42; + EXPECT_CALL(io_handle_, getOption(IPPROTO_TCP, TCP_INFO, _, _)) + .WillOnce(Return(Api::SysCallIntResult{-1, 42})); + EXPECT_LOG_CONTAINS( + "debug", + fmt::format("Failed getsockopt(IPPROTO_TCP, TCP_INFO): rc -1 errno 42 optlen {}", + sizeof(tcp_info_)), + timer_->callback_()); + + // Not updated on failed syscall. + EXPECT_EQ(0, gaugeValue("cx_tx_unsent_bytes")); +} + +// Validate that the emitted values are correct, that delta updates from a counter move the value by +// the delta (not the entire value), and that multiple sockets interact correctly (stats are +// summed). +TEST_F(TcpStatsTest, Values) { + initialize(true); + + NiceMock* timer2; + NiceMock* inner_socket2; + std::unique_ptr tcp_stats_socket2; + createTcpStatsSocket(true, timer2, inner_socket2, tcp_stats_socket2); + + // After the first call, stats should be set to exactly these values. + tcp_info_.tcpi_total_retrans = 1; + tcp_info_.tcpi_segs_out = 2; + tcp_info_.tcpi_segs_in = 3; + tcp_info_.tcpi_data_segs_out = 4; + tcp_info_.tcpi_data_segs_in = 5; + tcp_info_.tcpi_notsent_bytes = 6; + tcp_info_.tcpi_unacked = 7; + tcp_info_.tcpi_rtt = 8; + tcp_info_.tcpi_rttvar = 9; + timer_->callback_(); + EXPECT_EQ(1, counterValue("cx_tx_retransmitted_segments")); + EXPECT_EQ(2, counterValue("cx_tx_segments")); + EXPECT_EQ(3, counterValue("cx_rx_segments")); + EXPECT_EQ(4, counterValue("cx_tx_data_segments")); + EXPECT_EQ(5, counterValue("cx_rx_data_segments")); + EXPECT_EQ(6, gaugeValue("cx_tx_unsent_bytes")); + EXPECT_EQ(7, gaugeValue("cx_tx_unacked_segments")); + EXPECT_EQ(8U, histogramValue("cx_rtt_us")); + EXPECT_EQ(9U, histogramValue("cx_rtt_variance_us")); + EXPECT_EQ((1U * Stats::Histogram::PercentScale) / 4U, + histogramValue("cx_tx_percent_retransmitted_segments")); + + // Trigger the timer again with unchanged values. The metrics should be unchanged (but the + // histograms should have emitted the value again). + timer_->callback_(); + EXPECT_EQ(1, counterValue("cx_tx_retransmitted_segments")); + EXPECT_EQ(2, counterValue("cx_tx_segments")); + EXPECT_EQ(3, counterValue("cx_rx_segments")); + EXPECT_EQ(4, counterValue("cx_tx_data_segments")); + EXPECT_EQ(5, counterValue("cx_rx_data_segments")); + EXPECT_EQ(6, gaugeValue("cx_tx_unsent_bytes")); + EXPECT_EQ(7, gaugeValue("cx_tx_unacked_segments")); + EXPECT_EQ(8U, histogramValue("cx_rtt_us")); + EXPECT_EQ(9U, histogramValue("cx_rtt_variance_us")); + // No more packets were transmitted (numerator and denominator deltas are zero), so no value + // should be emitted. + EXPECT_EQ(absl::nullopt, histogramValue("cx_tx_percent_retransmitted_segments")); + + // Set stats on 2nd socket. Values should be combined. + tcp_info_.tcpi_total_retrans = 1; + tcp_info_.tcpi_segs_out = 1; + tcp_info_.tcpi_segs_in = 1; + tcp_info_.tcpi_data_segs_out = 1; + tcp_info_.tcpi_data_segs_in = 1; + tcp_info_.tcpi_notsent_bytes = 1; + tcp_info_.tcpi_unacked = 1; + tcp_info_.tcpi_rtt = 1; + tcp_info_.tcpi_rttvar = 1; + timer2->callback_(); + EXPECT_EQ(2, counterValue("cx_tx_retransmitted_segments")); + EXPECT_EQ(3, counterValue("cx_tx_segments")); + EXPECT_EQ(4, counterValue("cx_rx_segments")); + EXPECT_EQ(5, counterValue("cx_tx_data_segments")); + EXPECT_EQ(6, counterValue("cx_rx_data_segments")); + EXPECT_EQ(7, gaugeValue("cx_tx_unsent_bytes")); + EXPECT_EQ(8, gaugeValue("cx_tx_unacked_segments")); + EXPECT_EQ(1U, histogramValue("cx_rtt_us")); + EXPECT_EQ(1U, histogramValue("cx_rtt_variance_us")); + EXPECT_EQ(Stats::Histogram::PercentScale /* 100% */, + histogramValue("cx_tx_percent_retransmitted_segments")); + + // Update the first socket again. + tcp_info_.tcpi_total_retrans = 2; + tcp_info_.tcpi_segs_out = 3; + tcp_info_.tcpi_segs_in = 4; + tcp_info_.tcpi_data_segs_out = 5; + tcp_info_.tcpi_data_segs_in = 6; + tcp_info_.tcpi_notsent_bytes = 7; + tcp_info_.tcpi_unacked = 8; + tcp_info_.tcpi_rtt = 9; + tcp_info_.tcpi_rttvar = 10; + timer_->callback_(); + EXPECT_EQ(3, counterValue("cx_tx_retransmitted_segments")); + EXPECT_EQ(4, counterValue("cx_tx_segments")); + EXPECT_EQ(5, counterValue("cx_rx_segments")); + EXPECT_EQ(6, counterValue("cx_tx_data_segments")); + EXPECT_EQ(7, counterValue("cx_rx_data_segments")); + EXPECT_EQ(8, gaugeValue("cx_tx_unsent_bytes")); + EXPECT_EQ(9, gaugeValue("cx_tx_unacked_segments")); + EXPECT_EQ(9U, histogramValue("cx_rtt_us")); + EXPECT_EQ(10U, histogramValue("cx_rtt_variance_us")); + // Delta of 1 on numerator and denominator. + EXPECT_EQ(Stats::Histogram::PercentScale /* 100% */, + histogramValue("cx_tx_percent_retransmitted_segments")); +} + +class TcpStatsSocketFactoryTest : public testing::Test { +public: + void initialize() { + envoy::extensions::transport_sockets::tcp_stats::v3::Config proto_config; + auto inner_factory = std::make_unique>(); + inner_factory_ = inner_factory.get(); + factory_ = + std::make_unique(context_, proto_config, std::move(inner_factory)); + } + + NiceMock context_; + NiceMock* inner_factory_; + std::unique_ptr factory_; +}; + +// Test createTransportSocket returns nullptr if inner call returns nullptr +TEST_F(TcpStatsSocketFactoryTest, CreateSocketReturnsNullWhenInnerFactoryReturnsNull) { + initialize(); + EXPECT_CALL(*inner_factory_, createTransportSocket(_)).WillOnce(ReturnNull()); + EXPECT_EQ(nullptr, factory_->createTransportSocket(nullptr)); +} + +// Test implementsSecureTransport calls inner factory +TEST_F(TcpStatsSocketFactoryTest, ImplementsSecureTransportCallInnerFactory) { + initialize(); + EXPECT_CALL(*inner_factory_, implementsSecureTransport()).WillOnce(Return(true)); + EXPECT_TRUE(factory_->implementsSecureTransport()); + + EXPECT_CALL(*inner_factory_, implementsSecureTransport()).WillOnce(Return(false)); + EXPECT_FALSE(factory_->implementsSecureTransport()); +} + +} // namespace +} // namespace TcpStats +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy + +#else // #if defined(__linux__) + +#include "envoy/extensions/transport_sockets/tcp_stats/v3/tcp_stats.pb.h" + +#include "test/mocks/server/transport_socket_factory_context.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace TcpStats { + +TEST(TcpStatsTest, ConfigErrorOnUnsupportedPlatform) { + envoy::extensions::transport_sockets::tcp_stats::v3::Config proto_config; + proto_config.mutable_transport_socket()->set_name("envoy.transport_sockets.raw_buffer"); + NiceMock context; + + envoy::config::core::v3::TransportSocket transport_socket_config; + transport_socket_config.set_name("envoy.transport_sockets.tcp_stats"); + transport_socket_config.mutable_typed_config()->PackFrom(proto_config); + auto& config_factory = Config::Utility::getAndCheckFactory< + Server::Configuration::DownstreamTransportSocketConfigFactory>(transport_socket_config); + EXPECT_THROW_WITH_MESSAGE(config_factory.createTransportSocketFactory(proto_config, context, {}), + EnvoyException, + "envoy.transport_sockets.tcp_stats is not supported on this platform."); +} + +} // namespace TcpStats +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy + +#endif // #if defined(__linux__) diff --git a/test/extensions/transport_sockets/tls/cert_validator/BUILD b/test/extensions/transport_sockets/tls/cert_validator/BUILD index 7fd2c97084a1..9d880d51fa05 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/BUILD +++ b/test/extensions/transport_sockets/tls/cert_validator/BUILD @@ -57,3 +57,16 @@ envoy_cc_test_library( "//test/test_common:utility_lib", ], ) + +envoy_cc_test( + name = "san_matcher_test", + srcs = [ + "san_matcher_test.cc", + ], + deps = [ + "//source/common/protobuf:utility_lib", + "//source/extensions/transport_sockets/tls/cert_validator:cert_validator_lib", + "//test/test_common:utility_lib", + "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", + ], +) diff --git a/test/extensions/transport_sockets/tls/cert_validator/default_validator_test.cc b/test/extensions/transport_sockets/tls/cert_validator/default_validator_test.cc index 036c3679fa1f..d25eb20c0a03 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/default_validator_test.cc +++ b/test/extensions/transport_sockets/tls/cert_validator/default_validator_test.cc @@ -2,6 +2,7 @@ #include #include "source/extensions/transport_sockets/tls/cert_validator/default_validator.h" +#include "source/extensions/transport_sockets/tls/cert_validator/san_matcher.h" #include "test/extensions/transport_sockets/tls/ssl_test_utility.h" #include "test/test_common/environment.h" @@ -28,22 +29,33 @@ TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameDNSMatched) { bssl::UniquePtr cert = readCertFromFile(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; - matcher.MergeFrom(TestUtility::createRegexMatcher(".*.example.com")); - std::vector> - subject_alt_name_matchers; - subject_alt_name_matchers.push_back(Matchers::StringMatcherImpl(matcher)); + matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw([^.]*\.example.com)raw")); + std::vector subject_alt_name_matchers; + subject_alt_name_matchers.push_back( + SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); EXPECT_TRUE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } +TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameIncorrectTypeMatched) { + bssl::UniquePtr cert = readCertFromFile(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); + envoy::type::matcher::v3::StringMatcher matcher; + matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw([^.]*\.example.com)raw")); + std::vector subject_alt_name_matchers; + subject_alt_name_matchers.push_back( + SanMatcherPtr{std::make_unique(GEN_URI, matcher)}); + EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); +} + TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameWildcardDNSMatched) { bssl::UniquePtr cert = readCertFromFile(TestEnvironment::substitute( "{{ test_rundir " "}}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.set_exact("api.example.com"); - std::vector> - subject_alt_name_matchers; - subject_alt_name_matchers.push_back(Matchers::StringMatcherImpl(matcher)); + std::vector subject_alt_name_matchers; + subject_alt_name_matchers.push_back( + SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); EXPECT_TRUE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } @@ -54,9 +66,9 @@ TEST(DefaultCertValidatorTest, TestMultiLevelMatch) { "}}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; matcher.set_exact("foo.api.example.com"); - std::vector> - subject_alt_name_matchers; - subject_alt_name_matchers.push_back(Matchers::StringMatcherImpl(matcher)); + std::vector subject_alt_name_matchers; + subject_alt_name_matchers.push_back( + SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } @@ -81,10 +93,10 @@ TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameURIMatched) { bssl::UniquePtr cert = readCertFromFile(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_uri_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; - matcher.MergeFrom(TestUtility::createRegexMatcher("spiffe://lyft.com/.*-team")); - std::vector> - subject_alt_name_matchers; - subject_alt_name_matchers.push_back(Matchers::StringMatcherImpl(matcher)); + matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw(spiffe://lyft.com/[^/]*-team)raw")); + std::vector subject_alt_name_matchers; + subject_alt_name_matchers.push_back( + SanMatcherPtr{std::make_unique(GEN_URI, matcher)}); EXPECT_TRUE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } @@ -100,10 +112,16 @@ TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameNotMatched) { bssl::UniquePtr cert = readCertFromFile(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; - matcher.MergeFrom(TestUtility::createRegexMatcher(".*.foo.com")); - std::vector> - subject_alt_name_matchers; - subject_alt_name_matchers.push_back(Matchers::StringMatcherImpl(matcher)); + matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw([^.]*\.example\.net)raw")); + std::vector subject_alt_name_matchers; + subject_alt_name_matchers.push_back( + SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); + subject_alt_name_matchers.push_back( + SanMatcherPtr{std::make_unique(GEN_IPADD, matcher)}); + subject_alt_name_matchers.push_back( + SanMatcherPtr{std::make_unique(GEN_URI, matcher)}); + subject_alt_name_matchers.push_back( + SanMatcherPtr{std::make_unique(GEN_EMAIL, matcher)}); EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } @@ -119,18 +137,18 @@ TEST(DefaultCertValidatorTest, TestCertificateVerificationWithSANMatcher) { bssl::UniquePtr cert = readCertFromFile(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher matcher; - matcher.MergeFrom(TestUtility::createRegexMatcher(".*.example.com")); - std::vector> san_matchers; - san_matchers.push_back(Matchers::StringMatcherImpl(matcher)); + matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw([^.]*\.example.com)raw")); + std::vector san_matchers; + san_matchers.push_back(SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); // Verify the certificate with correct SAN regex matcher. EXPECT_EQ(default_validator->verifyCertificate(cert.get(), /*verify_san_list=*/{}, san_matchers), Envoy::Ssl::ClientValidationStatus::Validated); EXPECT_EQ(stats.fail_verify_san_.value(), 0); matcher.MergeFrom(TestUtility::createExactMatcher("hello.example.com")); - std::vector> - invalid_san_matchers; - invalid_san_matchers.push_back(Matchers::StringMatcherImpl(matcher)); + std::vector invalid_san_matchers; + invalid_san_matchers.push_back( + SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); // Verify the certificate with incorrect SAN exact matcher. EXPECT_EQ(default_validator->verifyCertificate(cert.get(), /*verify_san_list=*/{}, invalid_san_matchers), @@ -158,6 +176,17 @@ TEST(DefaultCertValidatorTest, TestCertificateVerificationWithNoValidationContex 0); } +TEST(DefaultCertValidatorTest, NoSanInCert) { + bssl::UniquePtr cert = readCertFromFile(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/fake_ca_cert.pem")); + envoy::type::matcher::v3::StringMatcher matcher; + matcher.MergeFrom(TestUtility::createRegexMatcher(R"raw([^.]*\.example\.net)raw")); + std::vector subject_alt_name_matchers; + subject_alt_name_matchers.push_back( + SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); + EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); +} + } // namespace Tls } // namespace TransportSockets } // namespace Extensions diff --git a/test/extensions/transport_sockets/tls/cert_validator/san_matcher_test.cc b/test/extensions/transport_sockets/tls/cert_validator/san_matcher_test.cc new file mode 100644 index 000000000000..b7729de8d41e --- /dev/null +++ b/test/extensions/transport_sockets/tls/cert_validator/san_matcher_test.cc @@ -0,0 +1,57 @@ +#include "envoy/extensions/transport_sockets/tls/v3/common.pb.validate.h" + +#include "source/common/protobuf/message_validator_impl.h" +#include "source/common/protobuf/utility.h" +#include "source/extensions/transport_sockets/tls/cert_validator/san_matcher.h" + +#include "test/test_common/utility.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace Tls { + +// Verify that we get a valid string san matcher for all valid san types. +TEST(SanMatcherConfigTest, TestValidSanType) { + // Iterate over all san type enums. + for (envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SanType san_type = + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SanType_MIN; + san_type <= + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SanType_MAX; + san_type = static_cast< + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SanType>( + static_cast(san_type + 1))) { + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher san_matcher; + san_matcher.mutable_matcher()->set_exact("foo.example"); + san_matcher.set_san_type(san_type); + if (san_type == envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher:: + SAN_TYPE_UNSPECIFIED) { + continue; + } else { + const SanMatcherPtr matcher = createStringSanMatcher(san_matcher); + EXPECT_NE(matcher.get(), nullptr); + // Verify that the message is valid. + TestUtility::validate(san_matcher); + } + } +} + +TEST(SanMatcherConfigTest, UnspecifiedSanType) { + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher san_matcher; + san_matcher.mutable_matcher()->set_exact("foo.example"); + // Do not set san_type + EXPECT_THROW_WITH_REGEX(TestUtility::validate(san_matcher), EnvoyException, + "Proto constraint validation failed"); + san_matcher.set_san_type( + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SAN_TYPE_UNSPECIFIED); + EXPECT_THROW_WITH_REGEX(TestUtility::validate(san_matcher), EnvoyException, + "Proto constraint validation failed"); +} + +} // namespace Tls +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_integration_test.cc b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_integration_test.cc index 47acf98c4f53..fe47ef14f6e3 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_integration_test.cc +++ b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_integration_test.cc @@ -53,6 +53,26 @@ void SslSPIFFECertValidatorIntegrationTest::checkVerifyErrorCouter(uint64_t valu counter->reset(); } +void SslSPIFFECertValidatorIntegrationTest::addStringMatcher( + const envoy::type::matcher::v3::StringMatcher& matcher) { + san_matchers_.emplace_back(); + *san_matchers_.back().mutable_matcher() = matcher; + san_matchers_.back().set_san_type( + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::DNS); + san_matchers_.emplace_back(); + *san_matchers_.back().mutable_matcher() = matcher; + san_matchers_.back().set_san_type( + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::URI); + san_matchers_.emplace_back(); + *san_matchers_.back().mutable_matcher() = matcher; + san_matchers_.back().set_san_type( + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::EMAIL); + san_matchers_.emplace_back(); + *san_matchers_.back().mutable_matcher() = matcher; + san_matchers_.back().set_san_type( + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::IP_ADDRESS); +} + INSTANTIATE_TEST_SUITE_P( IpVersionsClientVersions, SslSPIFFECertValidatorIntegrationTest, testing::Combine( @@ -124,7 +144,7 @@ name: envoy.tls.cert_validator.spiffe envoy::type::matcher::v3::StringMatcher matcher; matcher.set_prefix("spiffe://lyft.com/"); - san_matchers_ = {matcher}; + addStringMatcher(matcher); ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { return makeSslClientConnection({}); @@ -152,7 +172,7 @@ name: envoy.tls.cert_validator.spiffe matcher.set_prefix("spiffe://example.com/"); // The cert has "DNS.1 = lyft.com" but SPIFFE validator must ignore SAN types other than URI. matcher.set_prefix("www.lyft.com"); - san_matchers_ = {matcher}; + addStringMatcher(matcher); initialize(); auto conn = makeSslClientConnection({}); if (tls_version_ == envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_2) { @@ -223,8 +243,8 @@ name: envoy.tls.cert_validator.spiffe checkVerifyErrorCouter(1); } -// clientcert.pem's san is "spiffe://lyft.com/frontend-team" but the corresponding trust bundle does -// not match with the client cert. So this should also be rejected. +// clientcert.pem's san is "spiffe://lyft.com/frontend-team" but the corresponding trust bundle +// does not match with the client cert. So this should also be rejected. TEST_P(SslSPIFFECertValidatorIntegrationTest, ServerRsaSPIFFEValidatorRejected2) { auto typed_conf = new envoy::config::core::v3::TypedExtensionConfig(); TestUtility::loadFromYaml(TestEnvironment::substitute(R"EOF( diff --git a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_integration_test.h b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_integration_test.h index b1e57169ea18..01d08f5a811d 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_integration_test.h +++ b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_integration_test.h @@ -39,10 +39,11 @@ class SslSPIFFECertValidatorIntegrationTest } protected: + void addStringMatcher(envoy::type::matcher::v3::StringMatcher const& matcher); bool allow_expired_cert_{}; envoy::config::core::v3::TypedExtensionConfig* custom_validator_config_{nullptr}; std::unique_ptr context_manager_; - std::vector san_matchers_; + std::vector san_matchers_; const envoy::extensions::transport_sockets::tls::v3::TlsParameters::TlsProtocol tls_version_{ std::get<1>(GetParam())}; }; 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 cf77bdf3737a..3be9706e76be 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 @@ -64,13 +64,34 @@ class TestSPIFFEValidator : public testing::Test { // Setter. void setAllowExpiredCertificate(bool val) { allow_expired_certificate_ = val; } void setSanMatchers(std::vector san_matchers) { - san_matchers_ = san_matchers; + san_matchers_.clear(); + for (auto& matcher : san_matchers) { + san_matchers_.emplace_back(); + san_matchers_.back().set_san_type( + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::DNS); + *san_matchers_.back().mutable_matcher() = matcher; + + san_matchers_.emplace_back(); + san_matchers_.back().set_san_type( + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::URI); + *san_matchers_.back().mutable_matcher() = matcher; + + san_matchers_.emplace_back(); + san_matchers_.back().set_san_type( + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::EMAIL); + *san_matchers_.back().mutable_matcher() = matcher; + + san_matchers_.emplace_back(); + san_matchers_.back().set_san_type( + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::IP_ADDRESS); + *san_matchers_.back().mutable_matcher() = matcher; + } }; private: bool allow_expired_certificate_{false}; TestCertificateValidationContextConfigPtr config_; - std::vector san_matchers_{}; + std::vector san_matchers_{}; Stats::TestUtil::TestStore store_; SslStats stats_; Event::TestRealTimeSystem time_system_; @@ -193,7 +214,8 @@ TEST_F(TestSPIFFEValidator, TestGetTrustBundleStore) { // Non-SPIFFE SAN cert = readCertFromFile(TestEnvironment::substitute( - "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/non_spiffe_san_cert.pem")); + "{{ test_rundir " + "}}/test/extensions/transport_sockets/tls/test_data/non_spiffe_san_cert.pem")); EXPECT_FALSE(validator().getTrustBundleStore(cert.get())); // SPIFFE SAN @@ -374,6 +396,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/cert_validator/test_common.h b/test/extensions/transport_sockets/tls/cert_validator/test_common.h index b958f1727207..0eb5066e1c76 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/test_common.h +++ b/test/extensions/transport_sockets/tls/cert_validator/test_common.h @@ -33,7 +33,8 @@ class TestCertificateValidationContextConfig public: TestCertificateValidationContextConfig( envoy::config::core::v3::TypedExtensionConfig config, bool allow_expired_certificate = false, - std::vector san_matchers = {}) + std::vector + san_matchers = {}) : allow_expired_certificate_(allow_expired_certificate), api_(Api::createApiForTest()), custom_validator_config_(config), san_matchers_(san_matchers){}; TestCertificateValidationContextConfig() @@ -47,7 +48,7 @@ class TestCertificateValidationContextConfig const std::string& certificateRevocationListPath() const final { CONSTRUCT_ON_FIRST_USE(std::string, ""); } - const std::vector& + const std::vector& subjectAltNameMatchers() const override { return san_matchers_; } @@ -72,12 +73,14 @@ class TestCertificateValidationContextConfig } Api::Api& api() const override { return *api_; } + bool onlyVerifyLeafCertificateCrl() const override { return false; } private: bool allow_expired_certificate_{false}; Api::ApiPtr api_; const absl::optional custom_validator_config_; - const std::vector san_matchers_{}; + const std::vector + san_matchers_{}; }; } // namespace Tls diff --git a/test/extensions/transport_sockets/tls/context_impl_test.cc b/test/extensions/transport_sockets/tls/context_impl_test.cc index 0967e77d25b0..f4568860b223 100644 --- a/test/extensions/transport_sockets/tls/context_impl_test.cc +++ b/test/extensions/transport_sockets/tls/context_impl_test.cc @@ -923,8 +923,10 @@ TEST_F(SslServerContextImplTicketTest, VerifySanWithNoCA) { private_key: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem" validation_context: - match_subject_alt_names: - exact : "spiffe://lyft.com/testclient" + match_typed_subject_alt_names: + - san_type: URI + matcher: + exact: "spiffe://lyft.com/testclient" )EOF"; EXPECT_THROW_WITH_MESSAGE(loadConfigYaml(yaml), EnvoyException, "SAN-based verification of peer certificates without trusted CA " @@ -1096,6 +1098,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 +1199,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 +1390,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 +1909,91 @@ TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadFailureBothKeyAndMethod) "Certificate configuration can't have both private_key and private_key_provider"); } +// Test that we don't allow specification of both typed and untyped matchers for +// sans. +TEST_F(ServerContextConfigImplTest, DeprecatedSanMatcher) { + 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 yaml = + R"EOF( + common_tls_context: + validation_context: + trusted_ca: { filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" } + allow_expired_certificate: true + match_typed_subject_alt_names: + - san_type: DNS + matcher: + exact: "foo.example" + match_subject_alt_names: + exact: "foo.example" + )EOF"; + TestUtility::loadFromYaml(TestEnvironment::substitute(yaml), tls_context); + + EXPECT_THROW_WITH_MESSAGE( + ServerContextConfigImpl server_context_config(tls_context, factory_context_), EnvoyException, + "SAN-based verification using both match_typed_subject_alt_names and " + "the deprecated match_subject_alt_names is not allowed"); +} + +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 24b1952640a5..283cf9109a30 100644 --- a/test/extensions/transport_sockets/tls/ssl_socket_test.cc +++ b/test/extensions/transport_sockets/tls/ssl_socket_test.cc @@ -342,7 +342,7 @@ void testUtil(const TestUtilOptions& options) { auto socket = std::make_shared( Network::Test::getCanonicalLoopbackAddress(options.version())); Network::MockTcpListenerCallbacks callbacks; - Network::ListenerPtr listener = dispatcher->createListener(socket, callbacks, true); + Network::ListenerPtr listener = dispatcher->createListener(socket, callbacks, true, false); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(options.clientCtxYaml()), @@ -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( @@ -677,7 +677,7 @@ void testUtilV2(const TestUtilOptionsV2& options) { auto socket = std::make_shared( Network::Test::getCanonicalLoopbackAddress(options.version())); NiceMock callbacks; - Network::ListenerPtr listener = dispatcher->createListener(socket, callbacks, true); + Network::ListenerPtr listener = dispatcher->createListener(socket, callbacks, true, false); Stats::TestUtil::TestStore client_stats_store; Api::ApiPtr client_api = Api::createApiForTest(client_stats_store, time_system); @@ -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 && @@ -1089,8 +1089,10 @@ TEST_P(SslSocketTest, GetUriWithUriSan) { validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" - match_subject_alt_names: - exact: "spiffe://lyft.com/test-team" + match_typed_subject_alt_names: + - san_type: URI + matcher: + exact: "spiffe://lyft.com/test-team" )EOF"; TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, true, GetParam()); @@ -1105,8 +1107,10 @@ TEST_P(SslSocketTest, Ipv4San) { validation_context: trusted_ca: filename: "{{ test_rundir }}/test/config/integration/certs/upstreamcacert.pem" - match_subject_alt_names: - exact: "127.0.0.1" + match_typed_subject_alt_names: + - san_type: IP_ADDRESS + matcher: + exact: "127.0.0.1" )EOF"; const std::string server_ctx_yaml = R"EOF( @@ -1129,8 +1133,10 @@ TEST_P(SslSocketTest, Ipv6San) { validation_context: trusted_ca: filename: "{{ test_rundir }}/test/config/integration/certs/upstreamcacert.pem" - match_subject_alt_names: - exact: "::1" + match_typed_subject_alt_names: + - san_type: IP_ADDRESS + matcher: + exact: "::1" )EOF"; const std::string server_ctx_yaml = R"EOF( @@ -1533,8 +1539,10 @@ TEST_P(SslSocketTest, FailedClientAuthSanVerificationNoClientCert) { validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" - match_subject_alt_names: - exact: "example.com" + match_typed_subject_alt_names: + - san_type: DNS + matcher: + exact: "example.com" )EOF"; TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, false, GetParam()); @@ -1561,8 +1569,10 @@ TEST_P(SslSocketTest, FailedClientAuthSanVerification) { validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" - match_subject_alt_names: - exact: "example.com" + match_typed_subject_alt_names: + - san_type: DNS + matcher: + exact: "example.com" )EOF"; TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, false, GetParam()); @@ -1967,6 +1977,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(); @@ -2469,7 +2557,7 @@ TEST_P(SslSocketTest, FlushCloseDuringHandshake) { auto socket = std::make_shared( Network::Test::getCanonicalLoopbackAddress(GetParam())); Network::MockTcpListenerCallbacks callbacks; - Network::ListenerPtr listener = dispatcher_->createListener(socket, callbacks, true); + Network::ListenerPtr listener = dispatcher_->createListener(socket, callbacks, true, false); Network::ClientConnectionPtr client_connection = dispatcher_->createClientConnection( socket->connectionInfoProvider().localAddress(), Network::Address::InstanceConstSharedPtr(), @@ -2524,7 +2612,8 @@ TEST_P(SslSocketTest, HalfClose) { auto socket = std::make_shared( Network::Test::getCanonicalLoopbackAddress(GetParam())); Network::MockTcpListenerCallbacks listener_callbacks; - Network::ListenerPtr listener = dispatcher_->createListener(socket, listener_callbacks, true); + Network::ListenerPtr listener = + dispatcher_->createListener(socket, listener_callbacks, true, false); std::shared_ptr server_read_filter(new Network::MockReadFilter()); std::shared_ptr client_read_filter(new Network::MockReadFilter()); @@ -2605,7 +2694,8 @@ TEST_P(SslSocketTest, ShutdownWithCloseNotify) { auto socket = std::make_shared( Network::Test::getCanonicalLoopbackAddress(GetParam())); Network::MockTcpListenerCallbacks listener_callbacks; - Network::ListenerPtr listener = dispatcher_->createListener(socket, listener_callbacks, true); + Network::ListenerPtr listener = + dispatcher_->createListener(socket, listener_callbacks, true, false); std::shared_ptr server_read_filter(new Network::MockReadFilter()); std::shared_ptr client_read_filter(new Network::MockReadFilter()); @@ -2692,7 +2782,8 @@ TEST_P(SslSocketTest, ShutdownWithoutCloseNotify) { auto socket = std::make_shared( Network::Test::getCanonicalLoopbackAddress(GetParam())); Network::MockTcpListenerCallbacks listener_callbacks; - Network::ListenerPtr listener = dispatcher_->createListener(socket, listener_callbacks, true); + Network::ListenerPtr listener = + dispatcher_->createListener(socket, listener_callbacks, true, false); std::shared_ptr server_read_filter(new Network::MockReadFilter()); std::shared_ptr client_read_filter(new Network::MockReadFilter()); @@ -2795,7 +2886,7 @@ TEST_P(SslSocketTest, ClientAuthMultipleCAs) { auto socket = std::make_shared( Network::Test::getCanonicalLoopbackAddress(GetParam())); Network::MockTcpListenerCallbacks callbacks; - Network::ListenerPtr listener = dispatcher_->createListener(socket, callbacks, true); + Network::ListenerPtr listener = dispatcher_->createListener(socket, callbacks, true, false); const std::string client_ctx_yaml = R"EOF( common_tls_context: @@ -2892,8 +2983,8 @@ void testTicketSessionResumption(const std::string& server_ctx_yaml1, Network::Test::getCanonicalLoopbackAddress(ip_version)); NiceMock callbacks; Event::DispatcherPtr dispatcher(server_api->allocateDispatcher("test_thread")); - Network::ListenerPtr listener1 = dispatcher->createListener(socket1, callbacks, true); - Network::ListenerPtr listener2 = dispatcher->createListener(socket2, callbacks, true); + Network::ListenerPtr listener1 = dispatcher->createListener(socket1, callbacks, true, false); + Network::ListenerPtr listener2 = dispatcher->createListener(socket2, callbacks, true, false); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(client_ctx_yaml), client_tls_context); @@ -3031,7 +3122,7 @@ void testSupportForStatelessSessionResumption(const std::string& server_ctx_yaml Network::Test::getCanonicalLoopbackAddress(ip_version)); NiceMock callbacks; Event::DispatcherPtr dispatcher(server_api->allocateDispatcher("test_thread")); - Network::ListenerPtr listener = dispatcher->createListener(tcp_socket, callbacks, true); + Network::ListenerPtr listener = dispatcher->createListener(tcp_socket, callbacks, true, false); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(client_ctx_yaml), client_tls_context); @@ -3473,8 +3564,8 @@ TEST_P(SslSocketTest, ClientAuthCrossListenerSessionResumption) { auto socket2 = std::make_shared( Network::Test::getCanonicalLoopbackAddress(GetParam())); Network::MockTcpListenerCallbacks callbacks; - Network::ListenerPtr listener = dispatcher_->createListener(socket, callbacks, true); - Network::ListenerPtr listener2 = dispatcher_->createListener(socket2, callbacks, true); + Network::ListenerPtr listener = dispatcher_->createListener(socket, callbacks, true, false); + Network::ListenerPtr listener2 = dispatcher_->createListener(socket2, callbacks, true, false); const std::string client_ctx_yaml = R"EOF( common_tls_context: tls_certificates: @@ -3591,7 +3682,7 @@ void SslSocketTest::testClientSessionResumption(const std::string& server_ctx_ya NiceMock callbacks; Api::ApiPtr api = Api::createApiForTest(server_stats_store, time_system_); Event::DispatcherPtr dispatcher(server_api->allocateDispatcher("test_thread")); - Network::ListenerPtr listener = dispatcher->createListener(socket, callbacks, true); + Network::ListenerPtr listener = dispatcher->createListener(socket, callbacks, true, false); Network::ConnectionPtr server_connection; Network::MockConnectionCallbacks server_connection_callbacks; @@ -3850,7 +3941,7 @@ TEST_P(SslSocketTest, SslError) { auto socket = std::make_shared( Network::Test::getCanonicalLoopbackAddress(GetParam())); Network::MockTcpListenerCallbacks callbacks; - Network::ListenerPtr listener = dispatcher_->createListener(socket, callbacks, true); + Network::ListenerPtr listener = dispatcher_->createListener(socket, callbacks, true, false); Network::ClientConnectionPtr client_connection = dispatcher_->createClientConnection( socket->connectionInfoProvider().localAddress(), Network::Address::InstanceConstSharedPtr(), @@ -4610,6 +4701,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(); @@ -4856,7 +5026,7 @@ class SslReadBufferLimitTest : public SslSocketTest { socket_ = std::make_shared( Network::Test::getCanonicalLoopbackAddress(GetParam())); - listener_ = dispatcher_->createListener(socket_, listener_callbacks_, true); + listener_ = dispatcher_->createListener(socket_, listener_callbacks_, true, false); TestUtility::loadFromYaml(TestEnvironment::substitute(client_ctx_yaml_), upstream_tls_context_); auto client_cfg = diff --git a/test/extensions/transport_sockets/tls/test_data/README.md b/test/extensions/transport_sockets/tls/test_data/README.md index ab49d3da8cb2..6b16516074cf 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 4afb702255f7..aee99bc2d936 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 @@ -277,3 +289,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/password_protected_certkey.p12 b/test/extensions/transport_sockets/tls/test_data/password_protected_certkey.p12 new file mode 100644 index 000000000000..3c6e231b3e81 Binary files /dev/null and b/test/extensions/transport_sockets/tls/test_data/password_protected_certkey.p12 differ 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 000000000000..3210780bd553 Binary files /dev/null and b/test/extensions/transport_sockets/tls/test_data/san_dns3_certkeychain.p12 differ 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 000000000000..53dc6afa5627 Binary files /dev/null and b/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p384_certkey.p12 differ diff --git a/test/extensions/transport_sockets/tls/test_data/selfsigned_rsa_1024_certkey.p12 b/test/extensions/transport_sockets/tls/test_data/selfsigned_rsa_1024_certkey.p12 new file mode 100644 index 000000000000..2baed4ab1faf Binary files /dev/null and b/test/extensions/transport_sockets/tls/test_data/selfsigned_rsa_1024_certkey.p12 differ 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 000000000000..81a786bc61aa --- /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 000000000000..7d9a92c283a5 --- /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 000000000000..ad5b35d7c7ab --- /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----- diff --git a/test/extensions/upstreams/tcp/generic/config_test.cc b/test/extensions/upstreams/tcp/generic/config_test.cc index dd222bedcc0c..1054b6fadfcc 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/fuzz/fuzz_runner.cc b/test/fuzz/fuzz_runner.cc index 5a657476e168..618116a2f382 100644 --- a/test/fuzz/fuzz_runner.cc +++ b/test/fuzz/fuzz_runner.cc @@ -31,8 +31,6 @@ void Runner::setupEnvironment(int argc, char** argv, spdlog::level::level_enum d // state. ProcessWide process_wide; TestEnvironment::initializeOptions(argc, argv); - static auto* test_thread = new Envoy::Thread::TestThread; - UNREFERENCED_PARAMETER(test_thread); const auto environment_log_level = TestEnvironment::getOptions().logLevel(); // We only override the default log level if it looks like we're debugging; diff --git a/test/fuzz/utility.h b/test/fuzz/utility.h index 1c1340045004..55b4772e1563 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. @@ -156,7 +157,8 @@ inline std::unique_ptr fromStreamInfo(const test::fuzz::StreamIn auto upstream_metadata = std::make_shared( replaceInvalidStringValues(stream_info.upstream_metadata())); ON_CALL(*upstream_host, metadata()).WillByDefault(testing::Return(upstream_metadata)); - test_stream_info->upstream_host_ = upstream_host; + test_stream_info->setUpstreamInfo(std::make_shared()); + test_stream_info->upstreamInfo()->setUpstreamHost(upstream_host); auto address = stream_info.has_address() ? Envoy::Network::Address::resolveProtoAddress(stream_info.address()) : Network::Utility::resolveUrl("tcp://10.0.0.1:443"); @@ -164,7 +166,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->upstreamInfo()->setUpstreamLocalAddress(upstream_local_address); test_stream_info->downstream_connection_info_provider_ = std::make_shared(address, address); test_stream_info->downstream_connection_info_provider_->setRequestedServerName( diff --git a/test/integration/BUILD b/test/integration/BUILD index 12d76944fe33..5504fcaea4d2 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -176,7 +176,6 @@ envoy_cc_test( ":filter_manager_integration_proto_cc_proto", ":http_integration_lib", ":integration_lib", - "//source/extensions/filters/listener/original_dst:config", "//source/extensions/filters/listener/tls_inspector:config", "//source/extensions/filters/network/common:factory_base_lib", "//source/extensions/filters/network/echo:config", @@ -354,6 +353,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", @@ -366,7 +366,6 @@ envoy_cc_test( name = "multiplexed_integration_test", srcs = [ "multiplexed_integration_test.cc", - "multiplexed_integration_test.h", ], shard_count = 8, deps = [ @@ -501,6 +500,7 @@ envoy_cc_test_library( ], deps = [ ":http_protocol_integration_lib", + ":socket_interface_swap_lib", "//source/common/http:header_map_lib", "//source/extensions/filters/http/buffer:config", "//test/common/http/http2:http2_frame", @@ -514,6 +514,7 @@ envoy_cc_test_library( "//test/integration/filters:random_pause_filter_lib", "//test/integration/filters:remove_response_headers_lib", "//test/test_common:logging_lib", + "//test/test_common:threadsafe_singleton_injector_lib", "//test/test_common:utility_lib", "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", "@envoy_api//envoy/config/route/v3:pkg_cc_proto", @@ -528,7 +529,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", ], @@ -538,13 +539,11 @@ envoy_cc_test( name = "multiplexed_upstream_integration_test", srcs = [ "multiplexed_upstream_integration_test.cc", - "multiplexed_upstream_integration_test.h", ], shard_count = 2, deps = [ ":http_protocol_integration_lib", "//source/common/http:header_map_lib", - "//source/extensions/access_loggers/grpc:http_config", "//source/extensions/filters/http/buffer:config", "//test/integration/filters:encoder_decoder_buffer_filter_lib", "//test/integration/filters:random_pause_filter_lib", @@ -638,6 +637,7 @@ envoy_cc_test_library( deps = [ ":http_integration_lib", "//test/common/upstream:utility_lib", + "//test/integration/filters:stream_info_to_headers_filter_lib", ], ) @@ -705,6 +705,7 @@ envoy_cc_test_library( "fake_upstream.h", ], deps = [ + "//source/server:listener_manager_lib", "//envoy/api:api_interface", "//envoy/grpc:status", "//envoy/http:codec_interface", @@ -742,6 +743,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", ]), ) @@ -1002,24 +1004,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"], @@ -1147,27 +1131,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"], @@ -1449,7 +1412,6 @@ envoy_cc_test( deps = [ ":http_integration_lib", ":http_protocol_integration_lib", - "//source/extensions/access_loggers/grpc:http_config", "//source/extensions/filters/listener/tls_inspector:config", "//source/extensions/filters/listener/tls_inspector:tls_inspector_lib", "//source/extensions/filters/network/tcp_proxy:config", @@ -1579,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"], @@ -1662,6 +1631,32 @@ envoy_cc_test( ], ) +envoy_cc_test( + name = "internal_listener_integration_test", + srcs = [ + "internal_listener_integration_test.cc", + ], + deps = [ + ":http_integration_lib", + "//source/common/config:api_version_lib", + "//source/common/event:dispatcher_includes", + "//source/common/event:dispatcher_lib", + "//source/common/network:connection_lib", + "//source/common/network:utility_lib", + "//source/extensions/filters/network/tcp_proxy:config", + "//test/common/grpc:grpc_client_integration_lib", + "//test/config:v2_link_hacks", + "//test/integration/filters:address_restore_listener_filter_lib", + "//test/test_common:network_utility_lib", + "//test/test_common:resources_lib", + "//test/test_common:utility_lib", + "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/filters/network/tcp_proxy/v3:pkg_cc_proto", + "@envoy_api//envoy/service/discovery/v3:pkg_cc_proto", + ], +) + envoy_cc_test( name = "listener_filter_integration_test", srcs = [ @@ -1856,3 +1851,17 @@ envoy_cc_test( "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", ], ) + +envoy_cc_test( + name = "typed_metadata_integration_test", + srcs = [ + "typed_metadata_integration_test.cc", + ], + deps = [ + ":http_protocol_integration_lib", + "//source/common/protobuf", + "//test/integration/filters:listener_typed_metadata_filter_lib", + "//test/server:utility_lib", + "//test/test_common:utility_lib", + ], +) diff --git a/test/integration/ads_integration.cc b/test/integration/ads_integration.cc index f97ad753fe52..a85242d72d1b 100644 --- a/test/integration/ads_integration.cc +++ b/test/integration/ads_integration.cc @@ -145,7 +145,10 @@ void AdsIntegrationTest::initializeAds(const bool rate_limiting) { auto* validation_context = context.mutable_common_tls_context()->mutable_validation_context(); validation_context->mutable_trusted_ca()->set_filename( TestEnvironment::runfilesPath("test/config/integration/certs/upstreamcacert.pem")); - validation_context->add_match_subject_alt_names()->set_suffix("lyft.com"); + auto* san_matcher = validation_context->add_match_typed_subject_alt_names(); + san_matcher->mutable_matcher()->set_suffix("lyft.com"); + san_matcher->set_san_type( + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::DNS); if (clientType() == Grpc::ClientType::GoogleGrpc) { auto* google_grpc = grpc_service->mutable_google_grpc(); auto* ssl_creds = google_grpc->mutable_channel_credentials()->mutable_ssl_credentials(); diff --git a/test/integration/base_integration_test.h b/test/integration/base_integration_test.h index 303efca46b25..d1a38f1005ba 100644 --- a/test/integration/base_integration_test.h +++ b/test/integration/base_integration_test.h @@ -362,11 +362,12 @@ class BaseIntegrationTest : protected Logger::Loggable { void mergeOptions(envoy::config::core::v3::Http2ProtocolOptions& options) { upstream_config_.http2_options_.MergeFrom(options); } + void mergeOptions(envoy::config::listener::v3::QuicProtocolOptions& options) { + upstream_config_.quic_options_.MergeFrom(options); + } std::unique_ptr upstream_stats_store_; - Thread::TestThread test_thread_; - // Make sure the test server will be torn down after any fake client. // The test server owns the runtime, which is often accessed by client and // fake upstream codecs and must outlast them. diff --git a/test/integration/buffer_accounting_integration_test.cc b/test/integration/buffer_accounting_integration_test.cc index 4543850d83aa..d996dbdf601f 100644 --- a/test/integration/buffer_accounting_integration_test.cc +++ b/test/integration/buffer_accounting_integration_test.cc @@ -23,14 +23,17 @@ namespace Envoy { namespace { +using testing::HasSubstr; + 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 +67,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 +100,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 +141,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 @@ -208,8 +213,8 @@ TEST_P(Http2BufferWatermarksTest, ShouldTrackAllocatedBytesToUpstream) { buffer_factory_->setExpectedAccountBalance(request_body_size, num_requests); // Makes us have Envoy's writes to upstream return EAGAIN - writev_matcher_->setDestinationPort(fake_upstreams_[0]->localAddress()->ip()->port()); - writev_matcher_->setWritevReturnsEgain(); + write_matcher_->setDestinationPort(fake_upstreams_[0]->localAddress()->ip()->port()); + write_matcher_->setWriteReturnsEgain(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -223,7 +228,7 @@ TEST_P(Http2BufferWatermarksTest, ShouldTrackAllocatedBytesToUpstream) { << " buffer max: " << buffer_factory_->maxBufferSize() << printAccounts(); } - writev_matcher_->setResumeWrites(); + write_matcher_->setResumeWrites(); for (auto& response : responses) { ASSERT_TRUE(response->waitForEndStream()); @@ -241,12 +246,12 @@ TEST_P(Http2BufferWatermarksTest, ShouldTrackAllocatedBytesToDownstream) { initialize(); buffer_factory_->setExpectedAccountBalance(response_body_size, num_requests); - writev_matcher_->setSourcePort(lookupPort("http")); + write_matcher_->setSourcePort(lookupPort("http")); codec_client_ = makeHttpConnection(lookupPort("http")); // Simulate TCP push back on the Envoy's downstream network socket, so that outbound frames // start to accumulate in the transport socket buffer. - writev_matcher_->setWritevReturnsEgain(); + write_matcher_->setWriteReturnsEgain(); auto responses = sendRequests(num_requests, request_body_size, response_body_size); @@ -258,7 +263,7 @@ TEST_P(Http2BufferWatermarksTest, ShouldTrackAllocatedBytesToDownstream) { << " buffer max: " << buffer_factory_->maxBufferSize() << printAccounts(); } - writev_matcher_->setResumeWrites(); + write_matcher_->setResumeWrites(); // Wait for streams to terminate. for (auto& response : responses) { @@ -272,7 +277,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 +292,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 +310,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 +435,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, @@ -446,8 +454,8 @@ TEST_P(Http2OverloadManagerIntegrationTest, initialize(); // Makes us have Envoy's writes to upstream return EAGAIN - writev_matcher_->setDestinationPort(fake_upstreams_[0]->localAddress()->ip()->port()); - writev_matcher_->setWritevReturnsEgain(); + write_matcher_->setDestinationPort(fake_upstreams_[0]->localAddress()->ip()->port()); + write_matcher_->setWriteReturnsEgain(); codec_client_ = makeHttpConnection(lookupPort("http")); auto smallest_request_response = std::move(sendRequests(1, 4096, 4096)[0]); @@ -492,7 +500,7 @@ TEST_P(Http2OverloadManagerIntegrationTest, "overload.envoy.overload_actions.reset_high_memory_stream.scale_percent", 0); // Resume writes to upstream, any request streams that survive can go through. - writev_matcher_->setResumeWrites(); + write_matcher_->setResumeWrites(); if (!streamBufferAccounting()) { // If we're not doing the accounting, we didn't end up resetting these @@ -525,9 +533,9 @@ TEST_P(Http2OverloadManagerIntegrationTest, initialize(); // Makes us have Envoy's writes to downstream return EAGAIN - writev_matcher_->setSourcePort(lookupPort("http")); + write_matcher_->setSourcePort(lookupPort("http")); codec_client_ = makeHttpConnection(lookupPort("http")); - writev_matcher_->setWritevReturnsEgain(); + write_matcher_->setWriteReturnsEgain(); auto smallest_response = std::move(sendRequests(1, 10, 4096)[0]); waitForNextUpstreamRequest(); @@ -581,7 +589,7 @@ TEST_P(Http2OverloadManagerIntegrationTest, "overload.envoy.overload_actions.reset_high_memory_stream.scale_percent", 0); // Resume writes to downstream, any responses that survive can go through. - writev_matcher_->setResumeWrites(); + write_matcher_->setResumeWrites(); if (streamBufferAccounting()) { EXPECT_TRUE(largest_response->waitForReset()); @@ -611,4 +619,89 @@ TEST_P(Http2OverloadManagerIntegrationTest, EXPECT_EQ(smallest_response->headers().getStatusValue(), "200"); } +TEST_P(Http2OverloadManagerIntegrationTest, CanResetStreamIfEnvoyLevelStreamEnded) { + useAccessLog("%RESPONSE_CODE%"); + initializeOverloadManagerInBootstrap( + TestUtility::parseYaml(R"EOF( + name: "envoy.overload_actions.reset_high_memory_stream" + triggers: + - name: "envoy.resource_monitors.testonly.fake_resource_monitor" + scaled: + scaling_threshold: 0.90 + saturation_threshold: 0.98 + )EOF")); + initialize(); + + // Set 10MiB receive window for the client. + const int downstream_window_size = 10 * 1024 * 1024; + envoy::config::core::v3::Http2ProtocolOptions http2_options = + ::Envoy::Http2::Utility::initializeAndValidateOptions( + envoy::config::core::v3::Http2ProtocolOptions()); + http2_options.mutable_initial_stream_window_size()->set_value(downstream_window_size); + http2_options.mutable_initial_connection_window_size()->set_value(downstream_window_size); + codec_client_ = makeRawHttpConnection(makeClientConnection(lookupPort("http")), http2_options); + + // Makes us have Envoy's writes to downstream return EAGAIN + write_matcher_->setSourcePort(lookupPort("http")); + write_matcher_->setWriteReturnsEgain(); + + // Send a request + auto encoder_decoder = codec_client_->startRequest(Http::TestRequestHeaderMapImpl{ + {":method", "POST"}, + {":path", "/"}, + {":scheme", "http"}, + {":authority", "host"}, + {"content-length", "10"}, + }); + auto& encoder = encoder_decoder.first; + const std::string data(10, 'a'); + codec_client_->sendData(encoder, data, true); + auto response = std::move(encoder_decoder.second); + + waitForNextUpstreamRequest(); + FakeStreamPtr upstream_request_for_response = std::move(upstream_request_); + + // Send the responses back. It is larger than the downstream's receive window + // size. Thus, the codec will not end the stream, but the Envoy level stream + // should. + upstream_request_for_response->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}}, + false); + const int response_size = downstream_window_size + 1024; // Slightly over the window size. + upstream_request_for_response->encodeData(response_size, true); + + if (streamBufferAccounting()) { + // Wait for access log to know the Envoy level stream has been deleted. + EXPECT_THAT(waitForAccessLog(access_log_name_), HasSubstr("200")); + } + + // Set the pressure so the overload action kills the response if doing stream + // accounting + updateResource(0.95); + test_server_->waitForGaugeEq( + "overload.envoy.overload_actions.reset_high_memory_stream.scale_percent", 62); + + if (streamBufferAccounting()) { + test_server_->waitForCounterGe("envoy.overload_actions.reset_high_memory_stream.count", 1); + } + + // Reduce resource pressure + updateResource(0.80); + test_server_->waitForGaugeEq( + "overload.envoy.overload_actions.reset_high_memory_stream.scale_percent", 0); + + // Resume writes to downstream. + write_matcher_->setResumeWrites(); + + if (streamBufferAccounting()) { + EXPECT_TRUE(response->waitForReset()); + EXPECT_TRUE(response->reset()); + } else { + // If we're not doing the accounting, we didn't end up resetting the + // streams. + ASSERT_TRUE(response->waitForEndStream()); + ASSERT_TRUE(response->complete()); + EXPECT_EQ(response->headers().getStatusValue(), "200"); + } +} + } // namespace Envoy diff --git a/test/integration/cx_limit_integration_test.cc b/test/integration/cx_limit_integration_test.cc index fd3c1bbb6fb1..1be10dcd5c97 100644 --- a/test/integration/cx_limit_integration_test.cc +++ b/test/integration/cx_limit_integration_test.cc @@ -31,8 +31,23 @@ class ConnectionLimitIntegrationTest : public testing::TestWithParammutable_listeners(0); + listener->set_ignore_global_conn_limit(true); + }); + } + + void setAdminGlobalLimitOptOut() { + config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + auto* admin = bootstrap.mutable_admin(); + admin->set_ignore_global_conn_limit(true); + }); } void initialize() override { BaseIntegrationTest::initialize(); } @@ -95,9 +110,6 @@ class ConnectionLimitIntegrationTest : public testing::TestWithParamconnected()); const bool isV4 = (version_ == Network::Address::IpVersion::v4); - auto local_address = isV4 ? Network::Utility::getCanonicalIpv4LoopbackAddress() - : Network::Utility::getIpv6LoopbackAddress(); - const std::string counter_prefix = (isV4 ? "listener.127.0.0.1_0." : "listener.[__1]_0."); test_server_->waitForCounterEq(counter_prefix + check_stat, 1); @@ -145,7 +157,7 @@ TEST_P(ConnectionLimitIntegrationTest, TestGlobalLimit) { // Includes twice the number of connections expected because the tracking is performed via a // static variable and the fake upstream has a listener. This causes upstream connections to the // fake upstream to also be tracked as part of the global downstream connection tracking. - setGlobalLimit("4"); + setGlobalLimit(4); initialize(); }; @@ -154,9 +166,12 @@ TEST_P(ConnectionLimitIntegrationTest, TestGlobalLimit) { TEST_P(ConnectionLimitIntegrationTest, TestBothLimits) { std::function init_func = [this]() { + // Includes twice the number of connections expected because the tracking is performed via a + // static variable and the fake upstream has a listener. This causes upstream connections to the + // fake upstream to also be tracked as part of the global downstream connection tracking. + setGlobalLimit(4); // Setting the listener limit to a much higher value and making sure the right stat gets // incremented when both limits are set. - setGlobalLimit("4"); setListenerLimit(100); initialize(); }; @@ -164,5 +179,113 @@ TEST_P(ConnectionLimitIntegrationTest, TestBothLimits) { doTest(init_func, "downstream_global_cx_overflow"); } +TEST_P(ConnectionLimitIntegrationTest, TestGlobalLimitOptOut) { + // Includes 4 connections because the tracking is performed regardless of whether a specific + // listener has opted out. Since the fake upstream has a listener, we need to keep value at 4 so + // it can accept connections. (2 downstream listener conns + 2 upstream listener conns) + setGlobalLimit(4); + setGlobalLimitOptOut(); + setAdminGlobalLimitOptOut(); + setListenerLimit(100); + initialize(); + + std::vector tcp_clients; + std::vector raw_conns; + tcp_clients.emplace_back(makeTcpConnection(lookupPort("listener_0"))); + raw_conns.emplace_back(); + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(raw_conns.back())); + ASSERT_TRUE(tcp_clients.back()->connected()); + + tcp_clients.emplace_back(makeTcpConnection(lookupPort("listener_0"))); + raw_conns.emplace_back(); + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(raw_conns.back())); + ASSERT_TRUE(tcp_clients.back()->connected()); + + // 3rd connection should fail, not because listener_0 hit a limit, but because the + // upstream listener hit a limit (5 conns would exist when it goes to accept, so it rejects it). + // We can see that listener_0 didn't hit any limits because it's downstream_global_cx_overflow + // stat is still at 0, in contrast with the TestGlobalLimit test where it is 1 + tcp_clients.emplace_back(makeTcpConnection(lookupPort("listener_0"))); + raw_conns.emplace_back(); + ASSERT_FALSE( + fake_upstreams_[0]->waitForRawConnection(raw_conns.back(), std::chrono::milliseconds(500))); + tcp_clients.back()->waitForDisconnect(); + + // Get rid of the client that failed to connect. + tcp_clients.back()->close(); + tcp_clients.pop_back(); + + // admin connections should succeed + tcp_clients.emplace_back(makeTcpConnection(lookupPort("admin"))); + raw_conns.emplace_back(); + ASSERT_TRUE(tcp_clients.back()->connected()); + + const bool isV4 = (version_ == Network::Address::IpVersion::v4); + const std::string counter_prefix = (isV4 ? "listener.127.0.0.1_0." : "listener.[__1]_0."); + + // listener_0 does not hit any connection limits + test_server_->waitForCounterEq(counter_prefix + "downstream_global_cx_overflow", 0); + test_server_->waitForCounterEq(counter_prefix + "downstream_cx_overflow", 0); + test_server_->waitForCounterEq("listener.admin.downstream_global_cx_overflow", 0); + test_server_->waitForCounterEq("listener.admin.downstream_cx_overflow", 0); + + for (auto& tcp_client : tcp_clients) { + tcp_client->close(); + } + + tcp_clients.clear(); + raw_conns.clear(); +} + +TEST_P(ConnectionLimitIntegrationTest, TestListenerLimitWithGlobalOptOut) { + // Includes 4 connections because the tracking is performed regardless of whether a specific + // listener has opted out. Since the fake upstream has a listener, we need to keep value at 4 so + // it can accept connections. (2 downstream listener conns + 2 upstream listener conns) + setGlobalLimit(4); + setGlobalLimitOptOut(); + // Only allow 2 connections for the listener even though it has opted out of the global connection + // limit + setListenerLimit(2); + initialize(); + + std::vector tcp_clients; + std::vector raw_conns; + tcp_clients.emplace_back(makeTcpConnection(lookupPort("listener_0"))); + raw_conns.emplace_back(); + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(raw_conns.back())); + ASSERT_TRUE(tcp_clients.back()->connected()); + + tcp_clients.emplace_back(makeTcpConnection(lookupPort("listener_0"))); + raw_conns.emplace_back(); + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(raw_conns.back())); + ASSERT_TRUE(tcp_clients.back()->connected()); + + // 3rd connection should fail because we've hit the listener connection limit, not because + // we've hit a global limit + tcp_clients.emplace_back(makeTcpConnection(lookupPort("listener_0"))); + raw_conns.emplace_back(); + ASSERT_FALSE( + fake_upstreams_[0]->waitForRawConnection(raw_conns.back(), std::chrono::milliseconds(500))); + tcp_clients.back()->waitForDisconnect(); + + // Get rid of the client that failed to connect. + tcp_clients.back()->close(); + tcp_clients.pop_back(); + + const bool isV4 = (version_ == Network::Address::IpVersion::v4); + const std::string counter_prefix = (isV4 ? "listener.127.0.0.1_0." : "listener.[__1]_0."); + + // listener_0 does hits the listener connection limit + test_server_->waitForCounterEq(counter_prefix + "downstream_global_cx_overflow", 0); + test_server_->waitForCounterEq(counter_prefix + "downstream_cx_overflow", 1); + + for (auto& tcp_client : tcp_clients) { + tcp_client->close(); + } + + tcp_clients.clear(); + raw_conns.clear(); +} + } // namespace } // namespace Envoy diff --git a/test/integration/extension_discovery_integration_test.cc b/test/integration/extension_discovery_integration_test.cc index 7a6457027fcc..2461fde04775 100644 --- a/test/integration/extension_discovery_integration_test.cc +++ b/test/integration/extension_discovery_integration_test.cc @@ -129,8 +129,6 @@ class ExtensionDiscoveryIntegrationTest : public Grpc::GrpcClientIntegrationPara void initialize() override { defer_listener_finalization_ = true; setUpstreamCount(1); - config_helper_.addRuntimeOverride("envoy.reloadable_features.experimental_matching_api", - "true"); // Add an xDS cluster for extension config discovery. config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { diff --git a/test/integration/fake_resource_monitor.cc b/test/integration/fake_resource_monitor.cc index 85a11084f6e1..eb57a5ba6669 100644 --- a/test/integration/fake_resource_monitor.cc +++ b/test/integration/fake_resource_monitor.cc @@ -4,7 +4,7 @@ namespace Envoy { FakeResourceMonitor::~FakeResourceMonitor() { factory_.onMonitorDestroyed(this); } -void FakeResourceMonitor::updateResourceUsage(Callbacks& callbacks) { +void FakeResourceMonitor::updateResourceUsage(Server::ResourceUpdateCallbacks& callbacks) { Server::ResourceUsage usage; usage.resource_pressure_ = pressure_; callbacks.onSuccess(usage); diff --git a/test/integration/fake_resource_monitor.h b/test/integration/fake_resource_monitor.h index 84d40eb70dcb..b32b581912d1 100644 --- a/test/integration/fake_resource_monitor.h +++ b/test/integration/fake_resource_monitor.h @@ -15,7 +15,7 @@ class FakeResourceMonitor : public Server::ResourceMonitor { : dispatcher_(dispatcher), factory_(factory), pressure_(0.0) {} // Server::ResourceMonitor ~FakeResourceMonitor() override; - void updateResourceUsage(Callbacks& callbacks) override; + void updateResourceUsage(Server::ResourceUpdateCallbacks& callbacks) override; void setResourcePressure(double pressure) { dispatcher_.post([this, pressure] { pressure_ = pressure; }); diff --git a/test/integration/fake_upstream.cc b/test/integration/fake_upstream.cc index 2129334241ae..38558114b684 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" @@ -72,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 { @@ -83,7 +84,7 @@ void FakeStream::encode100ContinueHeaders(const Http::ResponseHeaderMap& headers return; } } - encoder_.encode100ContinueHeaders(*headers_copy); + encoder_.encode1xxHeaders(*headers_copy); }); } @@ -307,17 +308,17 @@ class TestHttp1ServerConnectionImpl : public Http::Http1::ServerConnectionImpl { Http::Http1::ParserStatus onMessageCompleteBase() override { auto rc = ServerConnectionImpl::onMessageCompleteBase(); - if (activeRequest().has_value() && activeRequest().value().request_decoder_) { + if (activeRequest() && activeRequest()->request_decoder_) { // Undo the read disable from the base class - we have many tests which // waitForDisconnect after a full request has been read which will not // receive the disconnect if reading is disabled. - activeRequest().value().response_encoder_.readDisable(false); + activeRequest()->response_encoder_.readDisable(false); } return rc; } ~TestHttp1ServerConnectionImpl() override { - if (activeRequest().has_value()) { - activeRequest().value().response_encoder_.clearReadDisableCallsForTests(); + if (activeRequest()) { + activeRequest()->response_encoder_.clearReadDisableCallsForTests(); } } }; @@ -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); @@ -510,7 +526,7 @@ FakeUpstream::FakeUpstream(Network::TransportSocketFactoryPtr&& transport_socket FakeUpstream::FakeUpstream(Network::TransportSocketFactoryPtr&& transport_socket_factory, Network::SocketPtr&& listen_socket, const FakeUpstreamConfig& config) : http_type_(config.upstream_protocol_), http2_options_(config.http2_options_), - http3_options_(config.http3_options_), + http3_options_(config.http3_options_), quic_options_(config.quic_options_), socket_(Network::SocketSharedPtr(listen_socket.release())), socket_factory_(std::make_unique(socket_)), api_(Api::createApiForTest(stats_store_)), time_system_(config.time_system_), diff --git a/test/integration/fake_upstream.h b/test/integration/fake_upstream.h index eb404facf543..acb76221012a 100644 --- a/test/integration/fake_upstream.h +++ b/test/integration/fake_upstream.h @@ -13,6 +13,7 @@ #include "envoy/network/connection.h" #include "envoy/network/connection_handler.h" #include "envoy/network/filter.h" +#include "envoy/network/listener.h" #include "envoy/stats/scope.h" #include "source/common/buffer/buffer_impl.h" @@ -80,7 +81,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); @@ -462,6 +463,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) {} @@ -579,6 +584,7 @@ struct FakeUpstreamConfig { absl::optional udp_fake_upstream_; envoy::config::core::v3::Http2ProtocolOptions http2_options_; envoy::config::core::v3::Http3ProtocolOptions http3_options_; + envoy::config::listener::v3::QuicProtocolOptions quic_options_; uint32_t max_request_headers_kb_ = Http::DEFAULT_MAX_REQUEST_HEADERS_KB; uint32_t max_request_headers_count_ = Http::DEFAULT_MAX_HEADERS_COUNT; envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction @@ -760,7 +766,9 @@ class FakeUpstream : Logger::Loggable, if (is_quic) { #if defined(ENVOY_ENABLE_QUIC) udp_listener_config_.listener_factory_ = std::make_unique( - envoy::config::listener::v3::QuicProtocolOptions(), 1, parent_.quic_stat_names_); + parent_.quic_options_, 1, parent_.quic_stat_names_); + // Initialize QUICHE flags. + quiche::FlagRegistry::getInstance(); #else ASSERT(false, "Running a test that requires QUIC without compiling QUIC"); #endif @@ -788,6 +796,9 @@ class FakeUpstream : Logger::Loggable, 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; @@ -798,6 +809,7 @@ class FakeUpstream : Logger::Loggable, ResourceLimit& openConnections() override { return connection_resource_; } uint32_t tcpBacklogSize() const override { return ENVOY_TCP_BACKLOG_SIZE; } Init::Manager& initManager() override { return *init_manager_; } + bool ignoreGlobalConnLimit() const override { return false; } void setMaxConnections(const uint32_t num_connections) { connection_resource_.setMax(num_connections); @@ -821,6 +833,7 @@ class FakeUpstream : Logger::Loggable, const envoy::config::core::v3::Http2ProtocolOptions http2_options_; const envoy::config::core::v3::Http3ProtocolOptions http3_options_; + envoy::config::listener::v3::QuicProtocolOptions quic_options_; Network::SocketSharedPtr socket_; Network::ListenSocketFactoryPtr socket_factory_; ConditionalInitializer server_initialized_; diff --git a/test/integration/filters/BUILD b/test/integration/filters/BUILD index 79e08f2533a4..9ebfa7cb928a 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", ], @@ -618,3 +619,34 @@ envoy_cc_test_library( "@com_google_absl//absl/strings:str_format", ], ) + +envoy_cc_test_library( + name = "listener_typed_metadata_filter_lib", + srcs = [ + "listener_typed_metadata_filter.cc", + ], + deps = [ + "//envoy/http:filter_interface", + "//envoy/network:listener_interface", + "//envoy/registry", + "//source/common/protobuf", + "//source/extensions/filters/http/common:factory_base_lib", + "//source/extensions/filters/http/common:pass_through_filter_lib", + "//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/invalid_header_filter.cc b/test/integration/filters/invalid_header_filter.cc index ca3aef326c44..ed838c97345a 100644 --- a/test/integration/filters/invalid_header_filter.cc +++ b/test/integration/filters/invalid_header_filter.cc @@ -30,7 +30,7 @@ class InvalidHeaderFilter : public Http::PassThroughFilter { } if (!headers.get(Http::LowerCaseString("send-reply")).empty()) { decoder_callbacks_->sendLocalReply(Envoy::Http::Code::OK, "", nullptr, absl::nullopt, - "InvalidHeaderFilter ready"); + "invalid_header_filter_ready"); return Http::FilterHeadersStatus::StopIteration; } return Http::FilterHeadersStatus::Continue; diff --git a/test/integration/filters/listener_typed_metadata_filter.cc b/test/integration/filters/listener_typed_metadata_filter.cc new file mode 100644 index 000000000000..3d797e2a18e6 --- /dev/null +++ b/test/integration/filters/listener_typed_metadata_filter.cc @@ -0,0 +1,80 @@ +#include "envoy/http/filter.h" +#include "envoy/network/listener.h" +#include "envoy/registry/registry.h" + +#include "source/common/protobuf/protobuf.h" +#include "source/extensions/filters/http/common/pass_through_filter.h" + +#include "test/extensions/filters/http/common/empty_http_filter_config.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace { + +constexpr absl::string_view kFilterName = "listener-typed-metadata-filter"; +constexpr absl::string_view kMetadataKey = "test.listener.typed.metadata"; +constexpr absl::string_view kExpectedMetadataValue = "hello world"; + +// A test filter that verifies the typed metadata attached to the listener is stored correctly. +class Baz : public Config::TypedMetadata::Object { +public: + std::string item_; +}; + +class BazTypedMetadataFactory : public Network::ListenerTypedMetadataFactory { +public: + std::string name() const override { return std::string(kMetadataKey); } + + std::unique_ptr + parse(const ProtobufWkt::Struct&) const override { + ADD_FAILURE() << "Filter should not parse struct-typed metadata."; + return nullptr; + } + std::unique_ptr + parse(const ProtobufWkt::Any& d) const override { + ProtobufWkt::StringValue v; + EXPECT_TRUE(d.UnpackTo(&v)); + auto object = std::make_unique(); + object->item_ = v.value(); + return object; + } +}; + +class ListenerTypedMetadataFilter : public Http::PassThroughFilter { +public: + ListenerTypedMetadataFilter() = default; + + Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap&, bool) override { + decoder_callbacks_->sendLocalReply(Envoy::Http::Code::OK, "", nullptr, absl::nullopt, + "successfully_handled_request"); + return Http::FilterHeadersStatus::Continue; + } +}; + +class ListenerTypedMetadataFilterFactory + : public Extensions::HttpFilters::Common::EmptyHttpFilterConfig { +public: + ListenerTypedMetadataFilterFactory() : EmptyHttpFilterConfig(std::string(kFilterName)) {} + +private: + Http::FilterFactoryCb createFilter(const std::string&, + Server::Configuration::FactoryContext& context) override { + + // Main assertions to ensure the metadata from the listener was parsed correctly. + const auto& typed_metadata = context.listenerTypedMetadata(); + const Baz* value = typed_metadata.get(std::string(kMetadataKey)); + EXPECT_NE(value, nullptr); + EXPECT_EQ(value->item_, kExpectedMetadataValue); + + return [](Http::FilterChainFactoryCallbacks& callbacks) -> void { + callbacks.addStreamFilter(std::make_shared()); + }; + } +}; + +REGISTER_FACTORY(BazTypedMetadataFactory, Network::ListenerTypedMetadataFactory); +REGISTER_FACTORY(ListenerTypedMetadataFilterFactory, + Server::Configuration::NamedHttpFilterConfigFactory); +} // namespace +} // namespace Envoy diff --git a/test/integration/filters/local_reply_with_metadata_filter.cc b/test/integration/filters/local_reply_with_metadata_filter.cc index 68647dc3bbec..3ad9ecbe2a9e 100644 --- a/test/integration/filters/local_reply_with_metadata_filter.cc +++ b/test/integration/filters/local_reply_with_metadata_filter.cc @@ -20,7 +20,7 @@ class LocalReplyWithMetadataFilter : public Http::PassThroughFilter { Http::MetadataMapPtr metadata_map_ptr = std::make_unique(metadata_map); decoder_callbacks_->addDecodedMetadata().emplace_back(std::move(metadata_map_ptr)); decoder_callbacks_->sendLocalReply(Envoy::Http::Code::OK, "", nullptr, absl::nullopt, - "LocalReplyWithMetadataFilter is ready"); + "local_reply_with_metadata_filter_is_ready"); return Http::FilterHeadersStatus::StopIteration; } diff --git a/test/integration/filters/response_metadata_filter.cc b/test/integration/filters/response_metadata_filter.cc index 73fdee7008ec..ababbe0dbd5e 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/filters/stream_info_to_headers_filter.cc b/test/integration/filters/stream_info_to_headers_filter.cc new file mode 100644 index 000000000000..f3acdd19ef0b --- /dev/null +++ b/test/integration/filters/stream_info_to_headers_filter.cc @@ -0,0 +1,86 @@ +#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 { +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 { +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 { + 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().upstreamInfo()) { + if (decoder_callbacks_->streamInfo().upstreamInfo()->upstreamSslConnection()) { + headers.addCopy( + Http::LowerCaseString("alpn"), + decoder_callbacks_->streamInfo().upstreamInfo()->upstreamSslConnection()->alpn()); + } + headers.addCopy(Http::LowerCaseString("num_streams"), + decoder_callbacks_->streamInfo().upstreamInfo()->upstreamNumStreams()); + } + + return Http::FilterHeadersStatus::Continue; + } + Http::FilterTrailersStatus encodeTrailers(Http::ResponseTrailerMap& trailers) override { + ASSERT(decoder_callbacks_->streamInfo().upstreamInfo()); + StreamInfo::UpstreamTiming& upstream_timing = + decoder_callbacks_->streamInfo().upstreamInfo()->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[]; +static Registry::RegisterFactory, + Server::Configuration::NamedHttpFilterConfigFactory> + register_; + +} // namespace Envoy diff --git a/test/integration/filters/test_socket_interface.cc b/test/integration/filters/test_socket_interface.cc index e32d5ace315e..3356cecc8761 100644 --- a/test/integration/filters/test_socket_interface.cc +++ b/test/integration/filters/test_socket_interface.cc @@ -13,15 +13,27 @@ namespace Envoy { namespace Network { +Api::IoCallUint64Result TestIoSocketHandle::sendmsg(const Buffer::RawSlice* slices, + uint64_t num_slice, int flags, + const Address::Ip* self_ip, + const Address::Instance& peer_address) { + if (write_override_) { + auto result = write_override_(this, slices, num_slice); + if (result.has_value()) { + return std::move(result).value(); + } + } + return Test::IoSocketHandlePlatformImpl::sendmsg(slices, num_slice, flags, self_ip, peer_address); +} Api::IoCallUint64Result TestIoSocketHandle::writev(const Buffer::RawSlice* slices, uint64_t num_slice) { - if (writev_override_) { - auto result = writev_override_(this, slices, num_slice); + if (write_override_) { + auto result = write_override_(this, slices, num_slice); if (result.has_value()) { 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) { @@ -30,8 +42,8 @@ IoHandlePtr TestIoSocketHandle::accept(struct sockaddr* addr, socklen_t* addrlen return nullptr; } - return std::make_unique(writev_override_, result.return_value_, - socket_v6only_, domain_); + return std::make_unique(write_override_, result.return_value_, socket_v6only_, + domain_); } IoHandlePtr TestIoSocketHandle::duplicate() { @@ -40,13 +52,13 @@ IoHandlePtr TestIoSocketHandle::duplicate() { throw EnvoyException(fmt::format("duplicate failed for '{}': ({}) {}", fd_, result.errno_, errorDetails(result.errno_))); } - return std::make_unique(writev_override_, result.return_value_, - socket_v6only_, domain_); + return std::make_unique(write_override_, result.return_value_, socket_v6only_, + domain_); } IoHandlePtr TestSocketInterface::makeSocket(int socket_fd, bool socket_v6only, absl::optional domain) const { - return std::make_unique(writev_override_proc_, socket_fd, socket_v6only, + return std::make_unique(write_override_proc_, socket_fd, socket_v6only, domain); } diff --git a/test/integration/filters/test_socket_interface.h b/test/integration/filters/test_socket_interface.h index 905b227dae25..abb48f77da9e 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,22 +19,23 @@ 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, - uint64_t num_slice); - using WritevOverrideProc = std::function; + using WriteOverrideType = absl::optional(TestIoSocketHandle* io_handle, + const Buffer::RawSlice* slices, + uint64_t num_slice); + using WriteOverrideProc = std::function; - TestIoSocketHandle(WritevOverrideProc writev_override_proc, os_fd_t fd = INVALID_SOCKET, + TestIoSocketHandle(WriteOverrideProc write_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), + write_override_(write_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 @@ -46,9 +50,13 @@ class TestIoSocketHandle : public IoSocketHandleImpl { private: IoHandlePtr accept(struct sockaddr* addr, socklen_t* addrlen) override; Api::IoCallUint64Result writev(const Buffer::RawSlice* slices, uint64_t num_slice) 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; + IoHandlePtr duplicate() override; - const WritevOverrideProc writev_override_; + const WriteOverrideProc write_override_; absl::Mutex mutex_; Event::Dispatcher* dispatcher_ ABSL_GUARDED_BY(mutex_) = nullptr; }; @@ -64,22 +72,22 @@ class TestIoSocketHandle : public IoSocketHandleImpl { class TestSocketInterface : public SocketInterfaceImpl { public: /** - * Override the behavior of the IoSocketHandleImpl::writev() method. - * The supplied callback is invoked with the arguments of the writev method and the index + * Override the behavior of the IoSocketHandleImpl::writev() and + * IoSocketHandleImpl::sendmsg() methods. + * The supplied callback is invoked with the slices arguments of the write method and the index * of the accepted socket. * Returning absl::nullopt from the callback continues normal execution of the - * IoSocketHandleImpl::writev() method. Returning a Api::IoCallUint64Result from callback skips - * the IoSocketHandleImpl::writev() with the returned result value. + * write methods. Returning a Api::IoCallUint64Result from callback skips + * the write methods with the returned result value. */ - TestSocketInterface(TestIoSocketHandle::WritevOverrideProc writev) - : writev_override_proc_(writev) {} + TestSocketInterface(TestIoSocketHandle::WriteOverrideProc write) : write_override_proc_(write) {} private: // SocketInterfaceImpl IoHandlePtr makeSocket(int socket_fd, bool socket_v6only, absl::optional domain) const override; - const TestIoSocketHandle::WritevOverrideProc writev_override_proc_; + const TestIoSocketHandle::WriteOverrideProc write_override_proc_; }; } // namespace Network 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 000000000000..8a8c98a67107 --- /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 ddd9810a86b9..9950e49d58cf 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,23 +32,35 @@ 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: - 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, @@ -62,17 +75,18 @@ 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() { +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() { @@ -105,7 +119,7 @@ void Http2FloodMitigationTest::beginSession() { options->emplace_back(std::make_shared( envoy::config::core::v3::SocketOption::STATE_PREBIND, ENVOY_MAKE_SOCKET_OPTION_NAME(SOL_SOCKET, SO_RCVBUF), 1024)); - writev_matcher_->setSourcePort(lookupPort("http")); + write_matcher_->setSourcePort(lookupPort("http")); tcp_client_ = makeTcpConnection(lookupPort("http"), options); startHttp2Session(); } @@ -142,11 +156,11 @@ void Http2FloodMitigationTest::floodClient(const Http2Frame& frame, uint32_t num waitForNextUpstreamRequest(); // Make Envoy's writes into the upstream connection to return EAGAIN - writev_matcher_->setDestinationPort(fake_upstreams_[0]->localAddress()->ip()->port()); + write_matcher_->setDestinationPort(fake_upstreams_[0]->localAddress()->ip()->port()); auto buf = serializeFrames(frame, num_frames); - writev_matcher_->setWritevReturnsEgain(); + write_matcher_->setWriteReturnsEgain(); auto* upstream = fake_upstreams_.front().get(); ASSERT_TRUE(upstream->rawWriteConnection(0, std::string(buf.begin(), buf.end()))); @@ -170,7 +184,7 @@ void Http2FloodMitigationTest::floodServer(absl::string_view host, absl::string_ auto frame = readFrame(); EXPECT_EQ(Http2Frame::Type::Headers, frame.type()); EXPECT_EQ(expected_http_status, frame.responseStatus()); - writev_matcher_->setWritevReturnsEgain(); + write_matcher_->setWriteReturnsEgain(); for (uint32_t frame = 0; frame < num_frames; ++frame) { request = Http2Frame::makeRequest(Http2Frame::makeClientStreamId(++request_idx), host, path); sendFrame(request); @@ -196,7 +210,7 @@ void Http2FloodMitigationTest::prefillOutboundDownstreamQueue(uint32_t data_fram // such the next response triggers flood protection. // Simulate TCP push back on the Envoy's downstream network socket, so that outbound frames // start to accumulate in the transport socket buffer. - writev_matcher_->setWritevReturnsEgain(); + write_matcher_->setWriteReturnsEgain(); const auto request = Http2Frame::makeRequest( Http2Frame::makeClientStreamId(0), "host", "/test/long/url", @@ -244,11 +258,11 @@ Http2FloodMitigationTest::prefillOutboundUpstreamQueue(uint32_t frame_count) { EXPECT_TRUE(upstream_request_->waitForData(*dispatcher_, 1)); // Make Envoy's writes into the upstream connection to return EAGAIN - writev_matcher_->setDestinationPort(fake_upstreams_[0]->localAddress()->ip()->port()); + write_matcher_->setDestinationPort(fake_upstreams_[0]->localAddress()->ip()->port()); auto buf = serializeFrames(Http2Frame::makePingFrame(), frame_count); - writev_matcher_->setWritevReturnsEgain(); + write_matcher_->setWriteReturnsEgain(); auto* upstream = fake_upstreams_.front().get(); EXPECT_TRUE(upstream->rawWriteConnection(0, std::string(buf.begin(), buf.end()))); // Wait for pre-fill data to arrive to Envoy @@ -270,7 +284,7 @@ void Http2FloodMitigationTest::triggerListenerDrain() { TEST_P(Http2FloodMitigationTest, Ping) { setNetworkConnectionBufferSize(); beginSession(); - writev_matcher_->setWritevReturnsEgain(); + write_matcher_->setWriteReturnsEgain(); floodServer(Http2Frame::makePingFrame(), "http2.outbound_control_flood", ControlFrameFloodLimit + 1); } @@ -278,7 +292,7 @@ TEST_P(Http2FloodMitigationTest, Ping) { TEST_P(Http2FloodMitigationTest, Settings) { setNetworkConnectionBufferSize(); beginSession(); - writev_matcher_->setWritevReturnsEgain(); + write_matcher_->setWriteReturnsEgain(); floodServer(Http2Frame::makeEmptySettingsFrame(), "http2.outbound_control_flood", ControlFrameFloodLimit + 1); } @@ -311,7 +325,7 @@ TEST_P(Http2FloodMitigationTest, Data) { // 1000 DATA frames should trigger flood protection. // Simulate TCP push back on the Envoy's downstream network socket, so that outbound frames start // to accumulate in the transport socket buffer. - writev_matcher_->setWritevReturnsEgain(); + write_matcher_->setWriteReturnsEgain(); const auto request = Http2Frame::makeRequest(1, "host", "/test/long/url", {Http2Frame::Header("response_data_blocks", "1000"), @@ -418,7 +432,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); @@ -551,7 +565,7 @@ TEST_P(Http2FloodMitigationTest, Trailers) { // 999 DATA frames and trailers should trigger flood protection. // Simulate TCP push back on the Envoy's downstream network socket, so that outbound frames start // to accumulate in the transport socket buffer. - writev_matcher_->setWritevReturnsEgain(); + write_matcher_->setWriteReturnsEgain(); static_cast(fake_upstreams_.front().get()) ->setResponseTrailers(std::make_unique( @@ -594,7 +608,7 @@ TEST_P(Http2FloodMitigationTest, WindowUpdateOnLowWatermarkFlood) { autonomous_allow_incomplete_streams_ = true; beginSession(); - writev_matcher_->setWritevReturnsEgain(); + write_matcher_->setWriteReturnsEgain(); // pre-fill two away from overflow const auto request = Http2Frame::makePostRequest( @@ -659,7 +673,7 @@ TEST_P(Http2FloodMitigationTest, RST_STREAM) { // Simulate TCP push back on the Envoy's downstream network socket, so that outbound frames start // to accumulate in the transport socket buffer. - writev_matcher_->setWritevReturnsEgain(); + write_matcher_->setWriteReturnsEgain(); for (++stream_index; stream_index < ControlFrameFloodLimit + 2; ++stream_index) { request = @@ -935,7 +949,7 @@ TEST_P(Http2FloodMitigationTest, TooManyStreams) { // writing by the upstream server. In this case Envoy will not see upstream responses and will // keep client streams open, eventually maxing them out and causing client connection to be // closed. - writev_matcher_->setSourcePort(fake_upstreams_[0]->localAddress()->ip()->port()); + write_matcher_->setSourcePort(fake_upstreams_[0]->localAddress()->ip()->port()); // Exceed the number of streams allowed by the server. The server should stop reading from the // client. @@ -1124,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 * @@ -1166,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_); @@ -1187,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_); @@ -1223,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")); @@ -1265,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")); @@ -1294,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_); @@ -1319,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, @@ -1330,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(); @@ -1360,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_); @@ -1401,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); @@ -1420,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); @@ -1449,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_); @@ -1465,9 +1446,9 @@ TEST_P(Http2FloodMitigationTest, RequestMetadata) { // Make Envoy's writes into the upstream connection to return EAGAIN, preventing proxying of the // METADATA frames - writev_matcher_->setDestinationPort(fake_upstreams_[0]->localAddress()->ip()->port()); + write_matcher_->setDestinationPort(fake_upstreams_[0]->localAddress()->ip()->port()); - writev_matcher_->setWritevReturnsEgain(); + write_matcher_->setWriteReturnsEgain(); // Send AllFrameFloodLimit + 1 number of METADATA frames from the downstream client to trigger the // outbound upstream flood when they are proxied. diff --git a/test/integration/http_integration.cc b/test/integration/http_integration.cc index 711ca5d1bddf..559d47f6e0b5 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 @@ -944,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")); @@ -961,7 +971,7 @@ void HttpIntegrationTest::testEnvoyHandling100Continue(bool additional_continue_ 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. @@ -980,12 +990,11 @@ 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) { - response->waitForContinueHeaders(); + response->waitFor1xxHeaders(); codec_client_->close(); EXPECT_THAT(waitForAccessLog(access_log_name_), HasSubstr("100")); ASSERT_TRUE(fake_upstream_connection_->close()); @@ -997,9 +1006,9 @@ void HttpIntegrationTest::testEnvoyHandling100Continue(bool additional_continue_ 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()); @@ -1011,7 +1020,8 @@ void HttpIntegrationTest::testEnvoyHandling100Continue(bool additional_continue_ 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"); @@ -1035,43 +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_->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"}}); - 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_->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"}}); - 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->continueHeaders() != nullptr); - EXPECT_EQ("100", response->continueHeaders()->getStatusValue()); + ASSERT(response->informationalHeaders() != nullptr); + EXPECT_EQ(initial_code, response->informationalHeaders()->getStatusValue()); EXPECT_EQ("200", response->headers().getStatusValue()); } @@ -1420,6 +1426,11 @@ void HttpIntegrationTest::testAdminDrain(Http::CodecType admin_request_type) { void HttpIntegrationTest::simultaneousRequest(uint32_t request1_bytes, uint32_t request2_bytes, uint32_t response1_bytes, uint32_t response2_bytes) { + config_helper_.prependFilter(fmt::format(R"EOF( + name: stream-info-to-headers-filter + typed_config: + "@type": type.googleapis.com/google.protobuf.Empty)EOF")); + FakeStreamPtr upstream_request1; FakeStreamPtr upstream_request2; initialize(); @@ -1473,6 +1484,15 @@ void HttpIntegrationTest::simultaneousRequest(uint32_t request1_bytes, uint32_t EXPECT_TRUE(response1->complete()); EXPECT_EQ("200", response1->headers().getStatusValue()); EXPECT_EQ(response1_bytes, response1->body().size()); + + ASSERT_FALSE(response1->headers().get(Http::LowerCaseString("num_streams")).empty()); + ASSERT_FALSE(response2->headers().get(Http::LowerCaseString("num_streams")).empty()); + EXPECT_EQ( + response1->headers().get(Http::LowerCaseString("num_streams"))[0]->value().getStringView(), + "1"); + EXPECT_EQ( + response2->headers().get(Http::LowerCaseString("num_streams"))[0]->value().getStringView(), + upstreamProtocol() == Http::CodecType::HTTP1 ? "1" : "2"); } std::string HttpIntegrationTest::downstreamProtocolStatsRoot() const { diff --git a/test/integration/http_integration.h b/test/integration/http_integration.h index 01327b015a92..c7fd491026ab 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 { @@ -242,11 +248,12 @@ 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); + 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/http_protocol_integration.cc b/test/integration/http_protocol_integration.cc index 72dfbac95d8a..3773920e7f93 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( @@ -94,7 +107,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/http_protocol_integration.h b/test/integration/http_protocol_integration.h index ba47a134d948..f68d5ee8f502 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::TestWithParammutable_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 b4cc939b87bf..775641b8d0c7 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); @@ -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 317ea6e1c847..e4e5b3c85b20 100644 --- a/test/integration/integration_stream_decoder.h +++ b/test/integration/integration_stream_decoder.h @@ -29,13 +29,13 @@ 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_; } 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 @@ -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_tcp_client.cc b/test/integration/integration_tcp_client.cc index 2396d74c06da..0e145ae12c83 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/integration_test.cc b/test/integration/integration_test.cc index 6b036954ea21..13163620fe9a 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: @@ -429,7 +429,6 @@ TEST_P(IntegrationTest, EnvoyProxying100ContinueWithDecodeDataPause) { // filter invocation through the match tree. TEST_P(IntegrationTest, MatchingHttpFilterConstruction) { concurrency_ = 2; - config_helper_.addRuntimeOverride("envoy.reloadable_features.experimental_matching_api", "true"); config_helper_.prependFilter(R"EOF( name: matcher @@ -496,7 +495,6 @@ name: matcher // that we are able to skip filter invocation through the match tree. TEST_P(IntegrationTest, MatchingHttpFilterConstructionNewProto) { concurrency_ = 2; - config_helper_.addRuntimeOverride("envoy.reloadable_features.experimental_matching_api", "true"); config_helper_.prependFilter(R"EOF( name: matcher @@ -561,8 +559,6 @@ name: matcher // Verifies routing via the match tree API. TEST_P(IntegrationTest, MatchTreeRouting) { - config_helper_.addRuntimeOverride("envoy.reloadable_features.experimental_matching_api", "true"); - const std::string vhost_yaml = R"EOF( name: vhost domains: ["matcher.com"] @@ -1239,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(); @@ -1395,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")); @@ -1472,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 @@ -2199,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/internal_listener_integration_test.cc b/test/integration/internal_listener_integration_test.cc new file mode 100644 index 000000000000..11614b84874a --- /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/listener_filter_integration_test.cc b/test/integration/listener_filter_integration_test.cc index ccd5f360a77a..c487ad2d2895 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/integration/load_balancers/custom_lb_policy.h b/test/integration/load_balancers/custom_lb_policy.h index 50ff0bfaea9e..132e5e5f03b7 100644 --- a/test/integration/load_balancers/custom_lb_policy.h +++ b/test/integration/load_balancers/custom_lb_policy.h @@ -32,9 +32,9 @@ class ThreadAwareLbImpl : public Upstream::ThreadAwareLoadBalancer { OptRef lifetimeCallbacks() override { return {}; } - absl::optional selectPool(Upstream::LoadBalancerContext*, - const Upstream::Host&, - std::vector&) override { + absl::optional + selectExistingConnection(Upstream::LoadBalancerContext*, const Upstream::Host&, + std::vector&) override { return {}; } diff --git a/test/integration/multiplexed_integration_test.cc b/test/integration/multiplexed_integration_test.cc index e0705e8f3865..b1599264761f 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,9 +170,8 @@ TEST_P(Http2IntegrationTest, CodecStreamIdleTimeout) { ASSERT_TRUE(response->waitForReset()); } -TEST_P(Http2IntegrationTest, Http2DownstreamKeepalive) { - // TODO(#16751) Need to support keepalive. - EXCLUDE_DOWNSTREAM_HTTP3; +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; config_helper_.addConfigModifier( @@ -197,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(); @@ -369,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")); @@ -397,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) { @@ -463,8 +509,8 @@ TEST_P(Http2MetadataIntegrationTest, TestResponseMetadata) { 10); waitForNextUpstreamRequest(); - upstream_request_->encode100ContinueHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); - response->waitForContinueHeaders(); + upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); + response->waitFor1xxHeaders(); upstream_request_->encodeHeaders(default_response_headers_, false); upstream_request_->encodeData(100, true); @@ -888,7 +934,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(); @@ -901,10 +947,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")); @@ -921,7 +967,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; @@ -937,7 +983,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(); @@ -953,7 +999,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(); @@ -971,14 +1017,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 { @@ -1011,7 +1057,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 { @@ -1044,7 +1090,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; @@ -1133,7 +1179,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 { @@ -1181,7 +1227,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; @@ -1250,15 +1297,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& @@ -1288,7 +1335,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& @@ -1315,7 +1362,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: @@ -1345,7 +1392,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: @@ -1368,7 +1415,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")); @@ -1386,7 +1433,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(); @@ -1405,7 +1467,7 @@ Http2RingHashIntegrationTest::Http2RingHashIntegrationTest() { }); } -Http2RingHashIntegrationTest::~Http2RingHashIntegrationTest() { +MultiplexedRingHashIntegrationTest::~MultiplexedRingHashIntegrationTest() { if (codec_client_) { codec_client_->close(); codec_client_ = nullptr; @@ -1418,15 +1480,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, @@ -1434,7 +1497,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; @@ -1481,7 +1544,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 { @@ -1515,7 +1578,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 { @@ -1546,7 +1609,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 { @@ -1577,7 +1640,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 { @@ -1609,7 +1672,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 { @@ -1826,7 +1889,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(); @@ -1860,7 +1923,8 @@ TEST_P(Http2IntegrationTest, OnLocalReply) { } } -TEST_P(Http2IntegrationTest, InvalidTrailers) { +TEST_P(MultiplexedIntegrationTest, InvalidTrailers) { + autonomous_allow_incomplete_streams_ = true; useAccessLog("%RESPONSE_CODE_DETAILS%"); autonomous_upstream_ = true; initialize(); @@ -1880,7 +1944,7 @@ TEST_P(Http2IntegrationTest, InvalidTrailers) { EXPECT_THAT(waitForAccessLog(access_log_name_), HasSubstr("invalid")); } -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 81876304b44c..000000000000 --- 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 diff --git a/test/integration/multiplexed_upstream_integration_test.cc b/test/integration/multiplexed_upstream_integration_test.cc index d47abf5f0ea2..836ca8de7290 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,12 @@ TEST_P(Http2UpstreamIntegrationTest, TestSchemeAndXFP) { } // Ensure Envoy handles streaming requests and responses simultaneously. -void Http2UpstreamIntegrationTest::bidirectionalStreaming(uint32_t bytes) { +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")); @@ -124,34 +148,47 @@ void Http2UpstreamIntegrationTest::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); + + 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->headers().get(Http::LowerCaseString("num_streams")).empty()); + EXPECT_EQ( + "1", + response->headers().get(Http::LowerCaseString("num_streams"))[0]->value().getStringView()); } -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,19 +230,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) { - autonomous_allow_incomplete_streams_ = true; +void MultiplexedUpstreamIntegrationTest::manySimultaneousRequests(uint32_t request_bytes, + uint32_t max_response_bytes, + uint32_t num_requests) { TestRandomGenerator rand; std::vector encoders; std::vector responses; @@ -236,11 +272,8 @@ void Http2UpstreamIntegrationTest::manySimultaneousRequests(uint32_t request_byt ASSERT_TRUE(responses[i]->waitForEndStream()); if (i % 2 != 0) { EXPECT_TRUE(responses[i]->complete()); - // TODO(18160) remove this if and always check for 200 and body length. - if (num_requests <= 100 || upstreamProtocol() != Http::CodecType::HTTP3) { - EXPECT_EQ("200", responses[i]->headers().getStatusValue()); - EXPECT_EQ(response_bytes[i], responses[i]->body().length()); - } + EXPECT_EQ("200", responses[i]->headers().getStatusValue()); + EXPECT_EQ(response_bytes[i], responses[i]->body().length()); } else { // Upstream stream reset. EXPECT_EQ("503", responses[i]->headers().getStatusValue()); @@ -251,24 +284,60 @@ 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); } -#ifdef NDEBUG -// TODO(alyssawilk) this causes crashes in debug mode for QUIC due to a race -// condition between Envoy's stream accounting and QUICE's. Debug and fix. -TEST_P(Http2UpstreamIntegrationTest, TooManySimultaneousRequests) { +TEST_P(MultiplexedUpstreamIntegrationTest, TooManySimultaneousRequests) { manySimultaneousRequests(1024, 1024, 200); } -#endif -TEST_P(Http2UpstreamIntegrationTest, ManyLargeSimultaneousRequestWithBufferLimits) { +TEST_P(MultiplexedUpstreamIntegrationTest, ManySimultaneousRequestsTightUpstreamLimits) { + if (upstreamProtocol() == Http::CodecType::HTTP2) { + return; + } + envoy::config::core::v3::Http2ProtocolOptions config; + config.mutable_max_concurrent_streams()->set_value(1); + mergeOptions(config); + envoy::config::listener::v3::QuicProtocolOptions options; + options.mutable_quic_protocol_options()->mutable_max_concurrent_streams()->set_value(1); + mergeOptions(options); + + 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); } -TEST_P(Http2UpstreamIntegrationTest, ManyLargeSimultaneousRequestWithRandomBackup) { +TEST_P(MultiplexedUpstreamIntegrationTest, ManyLargeSimultaneousRequestWithRandomBackup) { if (upstreamProtocol() == Http::CodecType::HTTP3 && downstreamProtocol() == Http::CodecType::HTTP2) { // This test depends on fragile preconditions. @@ -287,7 +356,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; @@ -349,7 +418,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 { @@ -417,7 +486,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")); @@ -436,7 +505,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; @@ -464,7 +533,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")); @@ -479,51 +548,8 @@ TEST_P(Http2UpstreamIntegrationTest, LargeResponseHeadersRejected) { EXPECT_EQ("503", response->headers().getStatusValue()); } -// 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) { - config_helper_.addConfigModifier( - [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& - hcm) -> void { - // Configure just enough of an upstream access log to reference the upstream headers. - const std::string yaml_string = R"EOF( -name: router -typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - upstream_log: - name: grpc_accesslog - filter: - not_health_check_filter: {} - typed_config: - "@type": type.googleapis.com/envoy.extensions.access_loggers.grpc.v3.HttpGrpcAccessLogConfig - common_config: - log_name: foo - transport_api_version: V3 - grpc_service: - envoy_grpc: - cluster_name: cluster_0 - )EOF"; - // Replace the terminal envoy.router. - hcm.clear_http_filters(); - TestUtility::loadFromYaml(yaml_string, *hcm.add_http_filters()); - }); - - initialize(); - - // Send the request. - codec_client_ = makeHttpConnection(lookupPort("http")); - auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); - waitForNextUpstreamRequest(); - - // Send the response headers. - upstream_request_->encodeHeaders(default_response_headers_, true); - ASSERT_TRUE(response->waitForEndStream()); - EXPECT_EQ("200", response->headers().getStatusValue()); -} - // 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); @@ -539,10 +565,11 @@ TEST_P(Http2UpstreamIntegrationTest, MultipleRequestsLowStreamLimit) { {":path", "/test/long/url"}, {":scheme", "http"}, {":authority", "host"}, - {AutonomousStream::NO_END_STREAM, ""}}); + {AutonomousStream::NO_END_STREAM, "true"}}); // Wait until the response is sent to ensure the SETTINGS frame has been read // by Envoy. response->waitForHeaders(); + ASSERT_FALSE(response->complete()); // Now send a second request and make sure it is processed. Previously it // would be queued on the original connection, as Envoy would ignore the @@ -551,10 +578,11 @@ TEST_P(Http2UpstreamIntegrationTest, MultipleRequestsLowStreamLimit) { FakeStreamPtr upstream_request2; auto response2 = codec_client_->makeHeaderOnlyRequest(default_request_headers_); ASSERT_TRUE(response2->waitForEndStream()); + cleanupUpstreamAndDownstream(); } // 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 14aeb56a49c0..000000000000 --- 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 diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index 50a7d249fe9c..1926ea61e3bb 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -30,6 +30,7 @@ #include "test/common/upstream/utility.h" #include "test/integration/autonomous_upstream.h" #include "test/integration/http_integration.h" +#include "test/integration/socket_interface_swap.h" #include "test/integration/test_host_predicate_config.h" #include "test/integration/utility.h" #include "test/mocks/upstream/retry_priority.h" @@ -38,6 +39,7 @@ #include "test/test_common/logging.h" #include "test/test_common/network_utility.h" #include "test/test_common/registry.h" +#include "test/test_common/threadsafe_singleton_injector.h" #include "absl/time/time.h" #include "gtest/gtest.h" @@ -612,12 +614,12 @@ TEST_P(DownstreamProtocolIntegrationTest, MissingHeadersLocalReply) { ASSERT_TRUE(response->waitForEndStream()); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().getStatusValue()); - EXPECT_THAT(waitForAccessLog(access_log_name_), HasSubstr("InvalidHeaderFilter_ready\n")); + EXPECT_THAT(waitForAccessLog(access_log_name_), HasSubstr("invalid_header_filter_ready\n")); } 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 +642,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(); @@ -680,12 +682,12 @@ TEST_P(DownstreamProtocolIntegrationTest, MissingHeadersLocalReplyWithBody) { ASSERT_TRUE(response->waitForEndStream()); EXPECT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().getStatusValue()); - EXPECT_THAT(waitForAccessLog(access_log_name_), HasSubstr("InvalidHeaderFilter_ready\n")); + EXPECT_THAT(waitForAccessLog(access_log_name_), HasSubstr("invalid_header_filter_ready\n")); } 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 +753,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( @@ -1318,19 +1320,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,13 +1336,21 @@ 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) { 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); } @@ -2746,7 +2752,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. @@ -3237,7 +3243,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)); @@ -3260,7 +3266,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)); @@ -3272,19 +3278,78 @@ 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)); } +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) { 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()); @@ -3300,7 +3365,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()); @@ -3316,7 +3381,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); @@ -3330,7 +3395,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); @@ -3344,7 +3409,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(); @@ -3358,7 +3423,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(); @@ -3372,7 +3437,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'); @@ -3470,4 +3535,41 @@ TEST_P(DownstreamProtocolIntegrationTest, ContentLengthLargerThanPayload) { EXPECT_EQ(Http::StreamResetReason::RemoteReset, response->resetReason()); } +class NoUdpGso : public Api::OsSysCallsImpl { +public: + bool supportsUdpGso() const override { return false; } +}; + +TEST_P(DownstreamProtocolIntegrationTest, HandleSocketFail) { + // Make sure for HTTP/3 Envoy will use sendmsg, so the write_matcher will work. + NoUdpGso reject_gso_; + TestThreadsafeSingletonInjector os_calls{&reject_gso_}; + ASSERT(!Api::OsSysCallsSingleton::get().supportsUdpGso()); + SocketInterfaceSwap socket_swap; + + initialize(); + codec_client_ = makeHttpConnection(lookupPort("http")); + auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); + waitForNextUpstreamRequest(); + + // Makes us have Envoy's writes to downstream return EBADF + Network::IoSocketError* ebadf = Network::IoSocketError::getIoSocketEbadfInstance(); + socket_swap.write_matcher_->setSourcePort(lookupPort("http")); + socket_swap.write_matcher_->setWriteOverride(ebadf); + upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}}, true); + + if (downstreamProtocol() == Http::CodecType::HTTP3) { + // For HTTP/3 since the packets are black holed, there is no client side + // indication of connection close. Wait on Envoy stats instead. + test_server_->waitForCounterEq("http.config_test.downstream_rq_rx_reset", 1); + codec_client_->close(); + } else { + ASSERT_TRUE(codec_client_->waitForDisconnect()); + } + socket_swap.write_matcher_->setWriteOverride(nullptr); + // Shut down the server before os_calls goes out of scope to avoid syscalls + // during its removal. + test_server_.reset(); +} + } // namespace Envoy diff --git a/test/integration/quic_http_integration_test.cc b/test/integration/quic_http_integration_test.cc index 3b64a397240e..43f00246bd0a 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 @@ -204,6 +205,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 +227,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 @@ -343,6 +353,33 @@ TEST_P(QuicHttpIntegrationTest, GetRequestAndEmptyResponse) { testRouterHeaderOnlyRequestAndResponse(); } +TEST_P(QuicHttpIntegrationTest, Draft29NotSupportedByDefault) { + supported_versions_ = {quic::ParsedQuicVersion::Draft29()}; + initialize(); + codec_client_ = makeRawHttpConnection(makeClientConnection(lookupPort("http")), absl::nullopt); + EXPECT_TRUE(codec_client_->disconnected()); + EXPECT_EQ(quic::QUIC_INVALID_VERSION, + static_cast(codec_client_->connection())->error()); +} + +TEST_P(QuicHttpIntegrationTest, RuntimeEnableDraft29) { + supported_versions_ = {quic::ParsedQuicVersion::Draft29()}; + config_helper_.addRuntimeOverride( + "envoy.reloadable_features.FLAGS_quic_reloadable_flag_quic_disable_version_draft_29", + "false"); + initialize(); + + codec_client_ = makeRawHttpConnection(makeClientConnection(lookupPort("http")), absl::nullopt); + EXPECT_EQ(transport_socket_factory_->clientContextConfig().serverNameIndication(), + codec_client_->connection()->requestedServerName()); + auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); + waitForNextUpstreamRequest(0); + upstream_request_->encodeHeaders(default_response_headers_, true); + ASSERT_TRUE(response->waitForEndStream()); + codec_client_->close(); + test_server_->waitForCounterEq("http3.quic_version_h3_29", 1u); +} + TEST_P(QuicHttpIntegrationTest, ZeroRtt) { // Make sure both connections use the same PersistentQuicInfoImpl. concurrency_ = 1; @@ -359,10 +396,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. @@ -387,6 +426,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 @@ -475,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"}, @@ -666,6 +743,156 @@ TEST_P(QuicHttpIntegrationTest, ResetRequestWithInvalidCharacter) { ASSERT_TRUE(response->waitForReset()); } +TEST_P(QuicHttpIntegrationTest, Http3ClientKeepalive) { + initialize(); + + constexpr uint64_t max_interval_sec = 5; + constexpr uint64_t initial_interval_sec = 1; + // Set connection idle network timeout to be a little larger than max interval. + dynamic_cast(*quic_connection_persistent_info_) + .quic_config_.SetIdleNetworkTimeout(quic::QuicTime::Delta::FromSeconds(max_interval_sec + 2)); + client_quic_options_.mutable_connection_keepalive()->mutable_max_interval()->set_seconds( + max_interval_sec); + client_quic_options_.mutable_connection_keepalive()->mutable_initial_interval()->set_seconds( + initial_interval_sec); + codec_client_ = makeHttpConnection(makeClientConnection(lookupPort("http"))); + auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); + waitForNextUpstreamRequest(); + + // Wait for 10s before sending back response. If keepalive is disabled, the + // connection would have idle timed out. + Event::TimerPtr timer(dispatcher_->createTimer([this]() -> void { dispatcher_->exit(); })); + timer->enableTimer(std::chrono::seconds(10)); + dispatcher_->run(Event::Dispatcher::RunType::Block); + + upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}, + {"set-cookie", "foo"}, + {"set-cookie", "bar"}}, + true); + EXPECT_TRUE(response->waitForEndStream()); + ASSERT_TRUE(response->complete()); + // First 6 PING frames should be sent every 1s, and the following ones less frequently. + EXPECT_LE(quic_connection_->GetStats().ping_frames_sent, 8u); +} + +TEST_P(QuicHttpIntegrationTest, Http3ClientKeepaliveDisabled) { + initialize(); + + constexpr uint64_t max_interval_sec = 0; + constexpr uint64_t initial_interval_sec = 1; + // Set connection idle network timeout to be a little larger than max interval. + dynamic_cast(*quic_connection_persistent_info_) + .quic_config_.SetIdleNetworkTimeout(quic::QuicTime::Delta::FromSeconds(5)); + client_quic_options_.mutable_connection_keepalive()->mutable_max_interval()->set_seconds( + max_interval_sec); + client_quic_options_.mutable_connection_keepalive()->mutable_initial_interval()->set_seconds( + initial_interval_sec); + codec_client_ = makeHttpConnection(makeClientConnection(lookupPort("http"))); + auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); + waitForNextUpstreamRequest(); + + // As keepalive is disabled, the connection will timeout after 5s. + EXPECT_TRUE(response->waitForReset()); + EXPECT_EQ(quic_connection_->GetStats().ping_frames_sent, 0u); +} + +TEST_P(QuicHttpIntegrationTest, Http3DownstreamKeepalive) { + constexpr uint64_t max_interval_sec = 5; + constexpr uint64_t initial_interval_sec = 1; + config_helper_.addConfigModifier( + [=](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) { + auto* keepalive_options = hcm.mutable_http3_protocol_options() + ->mutable_quic_protocol_options() + ->mutable_connection_keepalive(); + keepalive_options->mutable_initial_interval()->set_seconds(initial_interval_sec); + keepalive_options->mutable_max_interval()->set_seconds(max_interval_sec); + }); + // Set connection idle network timeout to be a little larger than max interval. + config_helper_.addConfigModifier([=](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { + bootstrap.mutable_static_resources() + ->mutable_listeners(0) + ->mutable_udp_listener_config() + ->mutable_quic_options() + ->mutable_idle_timeout() + ->set_seconds(max_interval_sec + 2); + }); + initialize(); + + codec_client_ = makeHttpConnection(makeClientConnection(lookupPort("http"))); + auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); + waitForNextUpstreamRequest(); + + // Wait for 10s before sending back response. If keepalive is disabled, the + // connection would have idle timed out. + Event::TimerPtr timer(dispatcher_->createTimer([this]() -> void { dispatcher_->exit(); })); + timer->enableTimer(std::chrono::seconds(10)); + dispatcher_->run(Event::Dispatcher::RunType::Block); + + upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}, + {"set-cookie", "foo"}, + {"set-cookie", "bar"}}, + true); + EXPECT_TRUE(response->waitForEndStream()); + 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) { diff --git a/test/integration/scoped_rds_integration_test.cc b/test/integration/scoped_rds_integration_test.cc index f114338e4358..1454c60ce370 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: diff --git a/test/integration/sds_dynamic_integration_test.cc b/test/integration/sds_dynamic_integration_test.cc index 8b361628100f..682750212913 100644 --- a/test/integration/sds_dynamic_integration_test.cc +++ b/test/integration/sds_dynamic_integration_test.cc @@ -311,7 +311,7 @@ version_info: "0" Network::Utility::resolveUrl(url), local_address, quic_stat_names_, stats_store_); #else - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); #endif } diff --git a/test/integration/sds_static_integration_test.cc b/test/integration/sds_static_integration_test.cc index ac712f715602..67cd1499d248 100644 --- a/test/integration/sds_static_integration_test.cc +++ b/test/integration/sds_static_integration_test.cc @@ -67,7 +67,7 @@ class SdsStaticDownstreamIntegrationTest tls_certificate->mutable_private_key()->set_filename( TestEnvironment::runfilesPath("test/config/integration/certs/serverkey.pem")); }); - ASSERT(Thread::MainThread::isMainOrTestThread()); + ASSERT_IS_MAIN_OR_TEST_THREAD(); HttpIntegrationTest::initialize(); registerTestServerPorts({"http"}); diff --git a/test/integration/server.h b/test/integration/server.h index 28d9aa96691f..48e4c5eb08ae 100644 --- a/test/integration/server.h +++ b/test/integration/server.h @@ -9,6 +9,7 @@ #include "envoy/config/listener/v3/listener.pb.h" #include "envoy/server/options.h" #include "envoy/server/process_context.h" +#include "envoy/stats/histogram.h" #include "envoy/stats/stats.h" #include "source/common/common/assert.h" @@ -281,21 +282,34 @@ class TestIsolatedStoreImpl : public StoreRoot { Thread::LockGuard lock(lock_); return store_.counterFromStatNameWithTags(name, tags); } - void forEachCounter(std::function f_size, - std::function f_stat) const override { + void forEachCounter(Stats::SizeFn f_size, StatFn f_stat) const override { Thread::LockGuard lock(lock_); store_.forEachCounter(f_size, f_stat); } - void forEachGauge(std::function f_size, - std::function f_stat) const override { + void forEachGauge(Stats::SizeFn f_size, StatFn f_stat) const override { Thread::LockGuard lock(lock_); store_.forEachGauge(f_size, f_stat); } - void forEachTextReadout(std::function f_size, - std::function f_stat) const override { + void forEachTextReadout(Stats::SizeFn f_size, StatFn f_stat) const override { Thread::LockGuard lock(lock_); store_.forEachTextReadout(f_size, f_stat); } + void forEachSinkedCounter(Stats::SizeFn f_size, StatFn f_stat) const override { + Thread::LockGuard lock(lock_); + store_.forEachSinkedCounter(f_size, f_stat); + } + void forEachSinkedGauge(Stats::SizeFn f_size, StatFn f_stat) const override { + Thread::LockGuard lock(lock_); + store_.forEachSinkedGauge(f_size, f_stat); + } + void forEachSinkedTextReadout(Stats::SizeFn f_size, StatFn f_stat) const override { + Thread::LockGuard lock(lock_); + store_.forEachSinkedTextReadout(f_size, f_stat); + } + void setSinkPredicates(std::unique_ptr&& sink_predicates) override { + UNREFERENCED_PARAMETER(sink_predicates); + } + Counter& counterFromString(const std::string& name) override { Thread::LockGuard lock(lock_); return store_.counterFromString(name); @@ -502,6 +516,10 @@ class IntegrationTestServer : public Logger::Loggable, return TestUtility::findGauge(statStore(), name); } + Stats::ParentHistogramSharedPtr histogram(const std::string& name) { + return TestUtility::findHistogram(statStore(), name); + } + std::vector counters() override { return statStore().counters(); } std::vector gauges() override { return statStore().gauges(); } diff --git a/test/integration/socket_interface_integration_test.cc b/test/integration/socket_interface_integration_test.cc index 63a531a41575..9cae29698a00 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(); @@ -117,8 +116,9 @@ TEST_P(SocketInterfaceIntegrationTest, UdpRecvFromInternalAddressWithSocketInter Network::Address::InstanceConstSharedPtr address = std::make_shared("listener_0", sock_interface); - ASSERT_DEATH( - std::make_unique(Network::Socket::Type::Datagram, address, nullptr), ""); + ASSERT_DEATH(std::make_unique(Network::Socket::Type::Datagram, address, + nullptr, Network::SocketCreationOptions{}), + ""); } // Test that send to internal address will return io error. @@ -132,8 +132,9 @@ TEST_P(SocketInterfaceIntegrationTest, UdpSendToInternalAddressWithSocketInterfa Network::Address::InstanceConstSharedPtr local_valid_address = Network::Test::getCanonicalLoopbackAddress(version_); - auto socket = std::make_unique(Network::Socket::Type::Datagram, - local_valid_address, nullptr); + auto socket = + std::make_unique(Network::Socket::Type::Datagram, local_valid_address, + nullptr, Network::SocketCreationOptions{}); Buffer::OwnedImpl buffer; auto reservation = buffer.reserveSingleSlice(100); diff --git a/test/integration/socket_interface_swap.cc b/test/integration/socket_interface_swap.cc index 521153817a25..f2b716127d48 100644 --- a/test/integration/socket_interface_swap.cc +++ b/test/integration/socket_interface_swap.cc @@ -2,17 +2,18 @@ namespace Envoy { +void preserveIoError(Api::IoError*) {} + SocketInterfaceSwap::SocketInterfaceSwap() { Envoy::Network::SocketInterfaceSingleton::clear(); test_socket_interface_loader_ = std::make_unique( std::make_unique( - [writev_matcher = writev_matcher_](Envoy::Network::TestIoSocketHandle* io_handle, - const Buffer::RawSlice*, - uint64_t) -> absl::optional { - if (writev_matcher->shouldReturnEgain(io_handle)) { - return Api::IoCallUint64Result( - 0, Api::IoErrorPtr(Network::IoSocketError::getIoSocketEagainInstance(), - Network::IoSocketError::deleteIoError)); + [write_matcher = write_matcher_](Envoy::Network::TestIoSocketHandle* io_handle, + const Buffer::RawSlice*, + uint64_t) -> absl::optional { + Network::IoSocketError* error_override = write_matcher->returnOverride(io_handle); + if (error_override) { + return Api::IoCallUint64Result(0, Api::IoErrorPtr(error_override, preserveIoError)); } return absl::nullopt; })); @@ -23,7 +24,7 @@ void SocketInterfaceSwap::IoHandleMatcher::setResumeWrites() { mutex_.Await(absl::Condition( +[](Network::TestIoSocketHandle** matched_iohandle) { return *matched_iohandle != nullptr; }, &matched_iohandle_)); - writev_returns_egain_ = false; + error_ = nullptr; matched_iohandle_->activateInDispatcherThread(Event::FileReadyType::Write); } diff --git a/test/integration/socket_interface_swap.h b/test/integration/socket_interface_swap.h index ee4f84db4765..676b8989453e 100644 --- a/test/integration/socket_interface_swap.h +++ b/test/integration/socket_interface_swap.h @@ -12,18 +12,18 @@ namespace Envoy { class SocketInterfaceSwap { public: // Object of this class hold the state determining the IoHandle which - // should return EAGAIN from the `writev` call. + // should return the supplied return from the `writev` or `sendmsg` calls. struct IoHandleMatcher { - bool shouldReturnEgain(Envoy::Network::TestIoSocketHandle* io_handle) { + Network::IoSocketError* returnOverride(Envoy::Network::TestIoSocketHandle* io_handle) { absl::MutexLock lock(&mutex_); - if (writev_returns_egain_ && (io_handle->localAddress()->ip()->port() == src_port_ || - io_handle->peerAddress()->ip()->port() == dst_port_)) { + if (error_ && (io_handle->localAddress()->ip()->port() == src_port_ || + (dst_port_ && io_handle->peerAddress()->ip()->port() == dst_port_))) { ASSERT(matched_iohandle_ == nullptr || matched_iohandle_ == io_handle, "Matched multiple io_handles, expected at most one to match."); matched_iohandle_ = io_handle; - return true; + return error_; } - return false; + return nullptr; } // Source port to match. The port specified should be associated with a listener. @@ -40,10 +40,15 @@ class SocketInterfaceSwap { dst_port_ = port; } - void setWritevReturnsEgain() { + void setWriteReturnsEgain() { + setWriteOverride(Network::IoSocketError::getIoSocketEagainInstance()); + } + + // The caller is responsible for memory management. + void setWriteOverride(Network::IoSocketError* error) { absl::WriterMutexLock lock(&mutex_); ASSERT(src_port_ != 0 || dst_port_ != 0); - writev_returns_egain_ = true; + error_ = error; } void setResumeWrites(); @@ -52,7 +57,7 @@ class SocketInterfaceSwap { mutable absl::Mutex mutex_; uint32_t src_port_ ABSL_GUARDED_BY(mutex_) = 0; uint32_t dst_port_ ABSL_GUARDED_BY(mutex_) = 0; - bool writev_returns_egain_ ABSL_GUARDED_BY(mutex_) = false; + Network::IoSocketError* error_ ABSL_GUARDED_BY(mutex_) = nullptr; Network::TestIoSocketHandle* matched_iohandle_{}; }; @@ -63,10 +68,9 @@ class SocketInterfaceSwap { Envoy::Network::SocketInterfaceSingleton::initialize(previous_socket_interface_); } -protected: Envoy::Network::SocketInterface* const previous_socket_interface_{ Envoy::Network::SocketInterfaceSingleton::getExisting()}; - std::shared_ptr writev_matcher_{std::make_shared()}; + std::shared_ptr write_matcher_{std::make_shared()}; std::unique_ptr test_socket_interface_loader_; }; diff --git a/test/integration/ssl_utility.cc b/test/integration/ssl_utility.cc index 36b93b6f81bc..c629d6f2e3c0 100644 --- a/test/integration/ssl_utility.cc +++ b/test/integration/ssl_utility.cc @@ -66,8 +66,23 @@ void initializeUpstreamTlsContextConfig( common_context->add_alpn_protocols(Http::Utility::AlpnNames::get().Http3); } if (!options.san_.empty()) { - common_context->mutable_validation_context()->add_match_subject_alt_names()->set_exact( - options.san_); + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher* matcher = + common_context->mutable_validation_context()->add_match_typed_subject_alt_names(); + matcher->mutable_matcher()->set_exact(options.san_); + matcher->set_san_type( + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::DNS); + matcher = common_context->mutable_validation_context()->add_match_typed_subject_alt_names(); + matcher->mutable_matcher()->set_exact(options.san_); + matcher->set_san_type( + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::URI); + matcher = common_context->mutable_validation_context()->add_match_typed_subject_alt_names(); + matcher->mutable_matcher()->set_exact(options.san_); + matcher->set_san_type( + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::EMAIL); + matcher = common_context->mutable_validation_context()->add_match_typed_subject_alt_names(); + matcher->mutable_matcher()->set_exact(options.san_); + matcher->set_san_type( + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::IP_ADDRESS); } for (const std::string& cipher_suite : options.cipher_suites_) { common_context->mutable_tls_params()->add_cipher_suites(cipher_suite); diff --git a/test/integration/tcp_proxy_integration_test.cc b/test/integration/tcp_proxy_integration_test.cc index 691bcd8df611..435a7884b219 100644 --- a/test/integration/tcp_proxy_integration_test.cc +++ b/test/integration/tcp_proxy_integration_test.cc @@ -172,8 +172,24 @@ TEST_P(TcpProxyIntegrationTest, TcpProxyDownstreamDisconnect) { TEST_P(TcpProxyIntegrationTest, TcpProxyManyConnections) { autonomous_upstream_ = true; + config_helper_.addConfigModifier([&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { + auto* static_resources = bootstrap.mutable_static_resources(); + for (int i = 0; i < static_resources->clusters_size(); ++i) { + auto* cluster = static_resources->mutable_clusters(i); + auto* thresholds = cluster->mutable_circuit_breakers()->add_thresholds(); + thresholds->mutable_max_connections()->set_value(1027); + thresholds->mutable_max_pending_requests()->set_value(1027); + } + }); initialize(); +// The large number of connection is meant to regression test +// https://github.com/envoyproxy/envoy/issues/19033 but fails on apple CI +// TODO(alyssawilk) debug. +#if defined(__APPLE__) const int num_connections = 50; +#else + const int num_connections = 1026; +#endif std::vector clients(num_connections); for (int i = 0; i < num_connections; ++i) { diff --git a/test/integration/tcp_tunneling_integration_test.cc b/test/integration/tcp_tunneling_integration_test.cc index ea3d79afd589..7c59ac5e98f1 100644 --- a/test/integration/tcp_tunneling_integration_test.cc +++ b/test/integration/tcp_tunneling_integration_test.cc @@ -488,39 +488,83 @@ class TcpTunnelingIntegrationTest : public HttpProtocolIntegrationTest { }); HttpProtocolIntegrationTest::SetUp(); } + + void setUpConnection(FakeHttpConnectionPtr& fake_upstream_connection) { + // Start a connection, and verify the upgrade headers are received upstream. + tcp_client_ = makeTcpConnection(lookupPort("tcp_proxy")); + if (!fake_upstream_connection) { + ASSERT_TRUE( + fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection)); + } + ASSERT_TRUE(fake_upstream_connection->waitForNewStream(*dispatcher_, upstream_request_)); + ASSERT_TRUE(upstream_request_->waitForHeadersComplete()); + + // Send upgrade headers downstream, fully establishing the connection. + upstream_request_->encodeHeaders(default_response_headers_, false); + } + + void sendBidiData(FakeHttpConnectionPtr& fake_upstream_connection, bool send_goaway = false) { + // Send some data from downstream to upstream, and make sure it goes through. + ASSERT_TRUE(tcp_client_->write("hello", false)); + ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, 5)); + + if (send_goaway) { + fake_upstream_connection->encodeGoAway(); + } + // Send data from upstream to downstream. + upstream_request_->encodeData(12, false); + ASSERT_TRUE(tcp_client_->waitForData(12)); + } + + void closeConnection(FakeHttpConnectionPtr& fake_upstream_connection) { + // Now send more data and close the TCP client. This should be treated as half close, so the + // data should go through. + ASSERT_TRUE(tcp_client_->write("hello", false)); + tcp_client_->close(); + ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, 5)); + if (upstreamProtocol() == Http::CodecType::HTTP1) { + ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); + } else { + ASSERT_TRUE(upstream_request_->waitForEndStream(*dispatcher_)); + // If the upstream now sends 'end stream' the connection is fully closed. + upstream_request_->encodeData(0, true); + } + } + + IntegrationTcpClientPtr tcp_client_; }; TEST_P(TcpTunnelingIntegrationTest, Basic) { initialize(); - // Start a connection, and verify the upgrade headers are received upstream. - IntegrationTcpClientPtr 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()); - - // Send upgrade headers downstream, fully establishing the connection. - upstream_request_->encodeHeaders(default_response_headers_, false); + setUpConnection(fake_upstream_connection_); + sendBidiData(fake_upstream_connection_); + closeConnection(fake_upstream_connection_); +} - // Send some data from downstream to upstream, and make sure it goes through. - ASSERT_TRUE(tcp_client->write("hello", false)); - ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, 5)); +TEST_P(TcpTunnelingIntegrationTest, SendDataUpstreamAfterUpstreamClose) { + if (upstreamProtocol() == Http::CodecType::HTTP1) { + // HTTP/1.1 can't frame with FIN bits. + return; + } + initialize(); - // Send data from upstream to downstream. - upstream_request_->encodeData(12, false); - ASSERT_TRUE(tcp_client->waitForData(12)); + setUpConnection(fake_upstream_connection_); + sendBidiData(fake_upstream_connection_); + // Close upstream. + upstream_request_->encodeData(2, true); + tcp_client_->waitForHalfClose(); - // Now send more data and close the TCP client. This should be treated as half close, so the data - // should go through. - ASSERT_TRUE(tcp_client->write("hello", false)); - tcp_client->close(); + // Now send data upstream. + ASSERT_TRUE(tcp_client_->write("hello", false)); ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, 5)); + + // Finally close and clean up. + tcp_client_->close(); if (upstreamProtocol() == Http::CodecType::HTTP1) { ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); } else { ASSERT_TRUE(upstream_request_->waitForEndStream(*dispatcher_)); - // If the upstream now sends 'end stream' the connection is fully closed. - upstream_request_->encodeData(0, true); } } @@ -548,7 +592,7 @@ TEST_P(TcpTunnelingIntegrationTest, BasicUsePost) { initialize(); // Start a connection, and verify the upgrade headers are received upstream. - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); + 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()); @@ -557,33 +601,88 @@ TEST_P(TcpTunnelingIntegrationTest, BasicUsePost) { // Send upgrade headers downstream, fully establishing the connection. upstream_request_->encodeHeaders(default_response_headers_, false); - // Send some data from downstream to upstream, and make sure it goes through. - ASSERT_TRUE(tcp_client->write("hello", false)); - ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, 5)); + sendBidiData(fake_upstream_connection_); + closeConnection(fake_upstream_connection_); +} - // Send data from upstream to downstream. - upstream_request_->encodeData(12, false); - ASSERT_TRUE(tcp_client->waitForData(12)); +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%"); - // Now send more data and close the TCP client. This should be treated as half close, so the data - // should go through. - ASSERT_TRUE(tcp_client->write("hello", false)); - tcp_client->close(); - ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, 5)); + 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) { - ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); - } else { - ASSERT_TRUE(upstream_request_->waitForEndStream(*dispatcher_)); - // If the upstream now sends 'end stream' the connection is fully closed. - upstream_request_->encodeData(0, true); + return; } + initialize(); + + // Send bidirectional data, including a goaway. + // This should result in the first connection being torn down. + setUpConnection(fake_upstream_connection_); + sendBidiData(fake_upstream_connection_, true); + closeConnection(fake_upstream_connection_); + test_server_->waitForCounterGe("cluster.cluster_0.upstream_cx_destroy", 1); + + // Make sure a subsequent connection can be established successfully. + FakeHttpConnectionPtr fake_upstream_connection; + setUpConnection(fake_upstream_connection); + sendBidiData(fake_upstream_connection); + closeConnection(fake_upstream_connection_); + + // Make sure the last stream is finished before doing test teardown. + fake_upstream_connection->encodeGoAway(); + test_server_->waitForCounterGe("cluster.cluster_0.upstream_cx_destroy", 2); } TEST_P(TcpTunnelingIntegrationTest, InvalidResponseHeaders) { initialize(); // Start a connection, and verify the upgrade headers are received upstream. - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); + 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()); @@ -600,23 +699,15 @@ TEST_P(TcpTunnelingIntegrationTest, InvalidResponseHeaders) { // The connection should be fully closed, but the client has no way of knowing // that. Ensure the FIN is read and clean up state. - tcp_client->waitForHalfClose(); - tcp_client->close(); + tcp_client_->waitForHalfClose(); + tcp_client_->close(); } TEST_P(TcpTunnelingIntegrationTest, CloseUpstreamFirst) { initialize(); - // Establish a connection. - IntegrationTcpClientPtr 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()); - upstream_request_->encodeHeaders(default_response_headers_, false); - - // Send data in both directions. - ASSERT_TRUE(tcp_client->write("hello", false)); - ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, 5)); + setUpConnection(fake_upstream_connection_); + sendBidiData(fake_upstream_connection_); // Send data from upstream to downstream with an end stream and make sure the data is received // before the connection is half-closed. @@ -624,19 +715,19 @@ TEST_P(TcpTunnelingIntegrationTest, CloseUpstreamFirst) { if (upstreamProtocol() == Http::CodecType::HTTP1) { ASSERT_TRUE(fake_upstream_connection_->close()); } - ASSERT_TRUE(tcp_client->waitForData(12)); - tcp_client->waitForHalfClose(); + ASSERT_TRUE(tcp_client_->waitForData(12)); + tcp_client_->waitForHalfClose(); if (upstreamProtocol() == Http::CodecType::HTTP1) { ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); - tcp_client->close(); + tcp_client_->close(); } else { // Attempt to send data upstream. // should go through. - ASSERT_TRUE(tcp_client->write("hello", false)); + ASSERT_TRUE(tcp_client_->write("hello", false)); ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, 5)); - ASSERT_TRUE(tcp_client->write("hello", true)); + ASSERT_TRUE(tcp_client_->write("hello", true)); ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, 5)); ASSERT_TRUE(upstream_request_->waitForEndStream(*dispatcher_)); } @@ -649,16 +740,11 @@ TEST_P(TcpTunnelingIntegrationTest, ResetStreamTest) { enableHalfClose(false); initialize(); - // Establish a connection. - IntegrationTcpClientPtr 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()); - upstream_request_->encodeHeaders(default_response_headers_, false); + setUpConnection(fake_upstream_connection_); // Reset the stream. upstream_request_->encodeResetStream(); - tcp_client->waitForDisconnect(); + tcp_client_->waitForDisconnect(); } TEST_P(TcpTunnelingIntegrationTest, TestIdletimeoutWithLargeOutstandingData) { @@ -681,20 +767,16 @@ TEST_P(TcpTunnelingIntegrationTest, TestIdletimeoutWithLargeOutstandingData) { initialize(); - IntegrationTcpClientPtr 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()); - upstream_request_->encodeHeaders(default_response_headers_, false); + setUpConnection(fake_upstream_connection_); std::string data(1024 * 16, 'a'); - ASSERT_TRUE(tcp_client->write(data)); + ASSERT_TRUE(tcp_client_->write(data)); upstream_request_->encodeData(data, false); - tcp_client->waitForDisconnect(); + tcp_client_->waitForDisconnect(); if (upstreamProtocol() == Http::CodecType::HTTP1) { ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); - tcp_client->close(); + tcp_client_->close(); } else { ASSERT_TRUE(upstream_request_->waitForReset()); } @@ -707,36 +789,32 @@ TEST_P(TcpTunnelingIntegrationTest, TcpProxyDownstreamFlush) { config_helper_.setBufferLimits(size / 4, size / 4); initialize(); - std::string data(size, 'a'); - IntegrationTcpClientPtr 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()); - upstream_request_->encodeHeaders(default_response_headers_, false); + setUpConnection(fake_upstream_connection_); - tcp_client->readDisable(true); + tcp_client_->readDisable(true); + std::string data(size, 'a'); if (upstreamProtocol() == Http::CodecType::HTTP1) { - ASSERT_TRUE(tcp_client->write("hello", false)); + ASSERT_TRUE(tcp_client_->write("hello", false)); ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, 5)); upstream_request_->encodeData(data, true); ASSERT_TRUE(fake_upstream_connection_->close()); } else { - ASSERT_TRUE(tcp_client->write("", true)); + ASSERT_TRUE(tcp_client_->write("", true)); // This ensures that readDisable(true) has been run on its thread - // before tcp_client starts writing. + // before tcp_client_ starts writing. ASSERT_TRUE(upstream_request_->waitForEndStream(*dispatcher_)); upstream_request_->encodeData(data, true); } test_server_->waitForCounterGe("cluster.cluster_0.upstream_flow_control_paused_reading_total", 1); - tcp_client->readDisable(false); - tcp_client->waitForData(data); - tcp_client->waitForHalfClose(); + tcp_client_->readDisable(false); + tcp_client_->waitForData(data); + tcp_client_->waitForHalfClose(); if (upstreamProtocol() == Http::CodecType::HTTP1) { - tcp_client->close(); + tcp_client_->close(); } } @@ -754,22 +832,19 @@ TEST_P(TcpTunnelingIntegrationTest, TcpProxyUpstreamFlush) { config_helper_.setBufferLimits(size, size); initialize(); - std::string data(size, 'a'); - IntegrationTcpClientPtr 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()); - upstream_request_->encodeHeaders(default_response_headers_, false); + setUpConnection(fake_upstream_connection_); + upstream_request_->readDisable(true); upstream_request_->encodeData("hello", false); // This ensures that fake_upstream_connection->readDisable has been run on its thread - // before tcp_client starts writing. - ASSERT_TRUE(tcp_client->waitForData(5)); + // before tcp_client_ starts writing. + ASSERT_TRUE(tcp_client_->waitForData(5)); - ASSERT_TRUE(tcp_client->write(data, true)); + std::string data(size, 'a'); + ASSERT_TRUE(tcp_client_->write(data, true)); if (upstreamProtocol() == Http::CodecType::HTTP1) { - tcp_client->close(); + tcp_client_->close(); upstream_request_->readDisable(false); ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, size)); @@ -782,7 +857,7 @@ TEST_P(TcpTunnelingIntegrationTest, TcpProxyUpstreamFlush) { ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, size)); ASSERT_TRUE(upstream_request_->waitForEndStream(*dispatcher_)); upstream_request_->encodeData("world", true); - tcp_client->waitForHalfClose(); + tcp_client_->waitForHalfClose(); } } @@ -793,42 +868,37 @@ TEST_P(TcpTunnelingIntegrationTest, ConnectionReuse) { } initialize(); - // Establish a connection. - IntegrationTcpClientPtr tcp_client1 = 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()); - upstream_request_->encodeHeaders(default_response_headers_, false); + setUpConnection(fake_upstream_connection_); // Send data in both directions. - ASSERT_TRUE(tcp_client1->write("hello1", false)); + ASSERT_TRUE(tcp_client_->write("hello1", false)); ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, "hello1")); // Send data from upstream to downstream with an end stream and make sure the data is received // before the connection is half-closed. upstream_request_->encodeData("world1", true); - tcp_client1->waitForData("world1"); - tcp_client1->waitForHalfClose(); - tcp_client1->close(); + tcp_client_->waitForData("world1"); + tcp_client_->waitForHalfClose(); + tcp_client_->close(); ASSERT_TRUE(upstream_request_->waitForEndStream(*dispatcher_)); // Establish a new connection. - IntegrationTcpClientPtr tcp_client2 = makeTcpConnection(lookupPort("tcp_proxy")); + IntegrationTcpClientPtr tcp_client_2 = makeTcpConnection(lookupPort("tcp_proxy")); // The new CONNECT stream is established in the existing h2 connection. ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_)); ASSERT_TRUE(upstream_request_->waitForHeadersComplete()); upstream_request_->encodeHeaders(default_response_headers_, false); - ASSERT_TRUE(tcp_client2->write("hello2", false)); + ASSERT_TRUE(tcp_client_2->write("hello2", false)); ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, "hello2")); // Send data from upstream to downstream with an end stream and make sure the data is received // before the connection is half-closed. upstream_request_->encodeData("world2", true); - tcp_client2->waitForData("world2"); - tcp_client2->waitForHalfClose(); - tcp_client2->close(); + tcp_client_2->waitForData("world2"); + tcp_client_2->waitForHalfClose(); + tcp_client_2->close(); ASSERT_TRUE(upstream_request_->waitForEndStream(*dispatcher_)); } @@ -839,36 +909,31 @@ TEST_P(TcpTunnelingIntegrationTest, H1NoConnectionReuse) { } initialize(); - // Establish a connection. - IntegrationTcpClientPtr tcp_client1 = 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()); - upstream_request_->encodeHeaders(default_response_headers_, false); + setUpConnection(fake_upstream_connection_); // Send data in both directions. - ASSERT_TRUE(tcp_client1->write("hello1", false)); + ASSERT_TRUE(tcp_client_->write("hello1", false)); ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, "hello1")); // Send data from upstream to downstream and close the connection // from downstream. upstream_request_->encodeData("world1", false); - tcp_client1->waitForData("world1"); - tcp_client1->close(); + tcp_client_->waitForData("world1"); + tcp_client_->close(); ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); // Establish a new connection. - IntegrationTcpClientPtr tcp_client2 = makeTcpConnection(lookupPort("tcp_proxy")); + IntegrationTcpClientPtr tcp_client_2 = makeTcpConnection(lookupPort("tcp_proxy")); // A new connection is established ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_)); ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_)); ASSERT_TRUE(upstream_request_->waitForHeadersComplete()); upstream_request_->encodeHeaders(default_response_headers_, false); - ASSERT_TRUE(tcp_client2->write("hello1", false)); + ASSERT_TRUE(tcp_client_2->write("hello1", false)); ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, "hello1")); - tcp_client2->close(); + tcp_client_2->close(); ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); } @@ -881,41 +946,41 @@ TEST_P(TcpTunnelingIntegrationTest, H1UpstreamCloseNoConnectionReuse) { initialize(); // Establish a connection. - IntegrationTcpClientPtr tcp_client1 = makeTcpConnection(lookupPort("tcp_proxy")); + IntegrationTcpClientPtr tcp_client_1 = 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()); upstream_request_->encodeHeaders(default_response_headers_, false); // Send data in both directions. - ASSERT_TRUE(tcp_client1->write("hello1", false)); + ASSERT_TRUE(tcp_client_1->write("hello1", false)); ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, "hello1")); // Send data from upstream to downstream and close the connection // from the upstream. upstream_request_->encodeData("world1", false); - tcp_client1->waitForData("world1"); + tcp_client_1->waitForData("world1"); ASSERT_TRUE(fake_upstream_connection_->close()); ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); - tcp_client1->waitForHalfClose(); - tcp_client1->close(); + tcp_client_1->waitForHalfClose(); + tcp_client_1->close(); // Establish a new connection. - IntegrationTcpClientPtr tcp_client2 = makeTcpConnection(lookupPort("tcp_proxy")); + IntegrationTcpClientPtr tcp_client_2 = makeTcpConnection(lookupPort("tcp_proxy")); // A new connection is established ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_)); ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_)); ASSERT_TRUE(upstream_request_->waitForHeadersComplete()); upstream_request_->encodeHeaders(default_response_headers_, false); - ASSERT_TRUE(tcp_client2->write("hello2", false)); + ASSERT_TRUE(tcp_client_2->write("hello2", false)); ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, "hello2")); ASSERT_TRUE(fake_upstream_connection_->close()); ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); - tcp_client2->waitForHalfClose(); - tcp_client2->close(); + tcp_client_2->waitForHalfClose(); + tcp_client_2->close(); } TEST_P(TcpTunnelingIntegrationTest, 2xxStatusCodeValidHttp1) { @@ -925,7 +990,7 @@ TEST_P(TcpTunnelingIntegrationTest, 2xxStatusCodeValidHttp1) { initialize(); // Start a connection, and verify the upgrade headers are received upstream. - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); + 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()); @@ -935,16 +1000,10 @@ TEST_P(TcpTunnelingIntegrationTest, 2xxStatusCodeValidHttp1) { default_response_headers_.setStatus(enumToInt(Http::Code::Accepted)); upstream_request_->encodeHeaders(default_response_headers_, false); - // Send some data from downstream to upstream, and make sure it goes through. - ASSERT_TRUE(tcp_client->write("hello", false)); - ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, 5)); - - // Send data from upstream to downstream. - upstream_request_->encodeData(12, false); - ASSERT_TRUE(tcp_client->waitForData(12)); + sendBidiData(fake_upstream_connection_); // Close the downstream connection and wait for upstream disconnect - tcp_client->close(); + tcp_client_->close(); ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); } @@ -955,7 +1014,7 @@ TEST_P(TcpTunnelingIntegrationTest, ContentLengthHeaderIgnoredHttp1) { initialize(); // Start a connection, and verify the upgrade headers are received upstream. - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); + 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()); @@ -968,11 +1027,11 @@ TEST_P(TcpTunnelingIntegrationTest, ContentLengthHeaderIgnoredHttp1) { // Send data from upstream to downstream. upstream_request_->encodeData(12, false); - ASSERT_TRUE(tcp_client->waitForData(12)); + ASSERT_TRUE(tcp_client_->waitForData(12)); // Now send some data and close the TCP client. - ASSERT_TRUE(tcp_client->write("hello", false)); - tcp_client->close(); + ASSERT_TRUE(tcp_client_->write("hello", false)); + tcp_client_->close(); ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, 5)); ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); } @@ -984,7 +1043,7 @@ TEST_P(TcpTunnelingIntegrationTest, TransferEncodingHeaderIgnoredHttp1) { initialize(); // Start a connection, and verify the upgrade headers are received upstream. - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); + tcp_client_ = makeTcpConnection(lookupPort("tcp_proxy")); // Using raw connection to be able to set Transfer-encoding header. FakeRawConnectionPtr fake_upstream_connection; ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); @@ -998,24 +1057,24 @@ TEST_P(TcpTunnelingIntegrationTest, TransferEncodingHeaderIgnoredHttp1) { fake_upstream_connection->write("HTTP/1.1 200 OK\r\nTransfer-encoding: chunked\r\n\r\n")); // Now send some data and close the TCP client. - ASSERT_TRUE(tcp_client->write("hello")); + ASSERT_TRUE(tcp_client_->write("hello")); ASSERT_TRUE( fake_upstream_connection->waitForData(FakeRawConnection::waitForInexactMatch("hello"))); // Close connections. ASSERT_TRUE(fake_upstream_connection->close()); ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); - tcp_client->close(); + tcp_client_->close(); } TEST_P(TcpTunnelingIntegrationTest, DeferTransmitDataUntilSuccessConnectResponseIsReceived) { initialize(); // Start a connection, and verify the upgrade headers are received upstream. - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); + tcp_client_ = makeTcpConnection(lookupPort("tcp_proxy")); // Send some data straight away. - ASSERT_TRUE(tcp_client->write("hello", false)); + ASSERT_TRUE(tcp_client_->write("hello", false)); ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_)); ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_)); @@ -1028,7 +1087,7 @@ TEST_P(TcpTunnelingIntegrationTest, DeferTransmitDataUntilSuccessConnectResponse ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, 5)); - tcp_client->close(); + tcp_client_->close(); if (upstreamProtocol() == Http::CodecType::HTTP1) { ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); } else { @@ -1042,10 +1101,10 @@ TEST_P(TcpTunnelingIntegrationTest, NoDataTransmittedIfConnectFailureResponseIsR initialize(); // Start a connection, and verify the upgrade headers are received upstream. - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); + tcp_client_ = makeTcpConnection(lookupPort("tcp_proxy")); // Send some data straight away. - ASSERT_TRUE(tcp_client->write("hello", false)); + ASSERT_TRUE(tcp_client_->write("hello", false)); ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_)); ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_)); @@ -1057,7 +1116,7 @@ TEST_P(TcpTunnelingIntegrationTest, NoDataTransmittedIfConnectFailureResponseIsR // Wait a bit, no data should go through. ASSERT_FALSE(upstream_request_->waitForData(*dispatcher_, 1, std::chrono::milliseconds(100))); - tcp_client->close(); + tcp_client_->close(); if (upstreamProtocol() == Http::CodecType::HTTP1) { ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); } else { @@ -1069,15 +1128,15 @@ TEST_P(TcpTunnelingIntegrationTest, UpstreamDisconnectBeforeResponseReceived) { initialize(); // Start a connection, and verify the upgrade headers are received upstream. - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); + 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()); ASSERT_TRUE(fake_upstream_connection_->close()); - tcp_client->waitForHalfClose(); - tcp_client->close(); + tcp_client_->waitForHalfClose(); + tcp_client_->close(); } INSTANTIATE_TEST_SUITE_P(IpAndHttpVersions, TcpTunnelingIntegrationTest, diff --git a/test/integration/typed_metadata_integration_test.cc b/test/integration/typed_metadata_integration_test.cc new file mode 100644 index 000000000000..b5aac630c186 --- /dev/null +++ b/test/integration/typed_metadata_integration_test.cc @@ -0,0 +1,46 @@ +#include "source/common/protobuf/protobuf.h" + +#include "test/integration/http_protocol_integration.h" +#include "test/integration/integration.h" +#include "test/integration/utility.h" +#include "test/server/utility.h" +#include "test/test_common/utility.h" + +#include "gtest/gtest.h" + +namespace Envoy { + +using ListenerTypedMetadataIntegrationTest = ::Envoy::HttpProtocolIntegrationTest; + +INSTANTIATE_TEST_SUITE_P(Protocols, ListenerTypedMetadataIntegrationTest, + testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams()), + HttpProtocolIntegrationTest::protocolTestParamsToString); + +TEST_P(ListenerTypedMetadataIntegrationTest, Hello) { + // Add some typed metadata to the listener. + ProtobufWkt::StringValue value; + value.set_value("hello world"); + ProtobufWkt::Any packed_value; + packed_value.PackFrom(value); + config_helper_.addListenerTypedMetadata("test.listener.typed.metadata", packed_value); + + // Add the filter that reads the listener typed metadata. + config_helper_.addFilter(R"EOF({ + name: listener-typed-metadata-filter, + typed_config: { + "@type": type.googleapis.com/google.protobuf.Empty + } + })EOF"); + + initialize(); + codec_client_ = makeHttpConnection(lookupPort("http")); + + // Main assertion on parsing the typed metadata is in the filter. + // Here we just ensure the filter was created (so we know those assertions ran). + auto response = codec_client_->makeRequestWithBody(default_request_headers_, 10); + ASSERT_TRUE(response->waitForEndStream()); + EXPECT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().getStatusValue()); +} + +} // namespace Envoy diff --git a/test/integration/utility.h b/test/integration/utility.h index 8f7e36178c91..8db43d1d86e9 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/integration/weighted_cluster_integration_test.cc b/test/integration/weighted_cluster_integration_test.cc index e43d784419b7..268bf0a215c0 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); diff --git a/test/integration/xds_integration_test.cc b/test/integration/xds_integration_test.cc index b225c4e1f527..8e866132782b 100644 --- a/test/integration/xds_integration_test.cc +++ b/test/integration/xds_integration_test.cc @@ -610,84 +610,6 @@ TEST_P(LdsIntegrationTest, NewListenerWithBadPostListenSocketOption) { test_server_->waitForCounterGe("listener_manager.listener_create_failure", 1); } -// Verify the grpc cached logger is available after the initial logger filter is destroyed. -// Regression test for https://github.com/envoyproxy/envoy/issues/18066 -TEST_P(LdsIntegrationTest, GrpcLoggerSurvivesAfterReloadConfig) { - autonomous_upstream_ = true; - // The grpc access logger connection never closes. It's ok to see an incomplete logging stream. - autonomous_allow_incomplete_streams_ = true; - - const std::string grpc_logger_string = R"EOF( - name: grpc_accesslog - typed_config: - "@type": type.googleapis.com/envoy.extensions.access_loggers.grpc.v3.HttpGrpcAccessLogConfig - common_config: - log_name: bar - transport_api_version: V3 - grpc_service: - envoy_grpc: - cluster_name: cluster_0 - )EOF"; - - config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { - auto* listener = bootstrap.mutable_static_resources()->mutable_listeners(0); - listener->set_stat_prefix("listener_0"); - }); - config_helper_.addConfigModifier( - [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& - hcm) { TestUtility::loadFromYaml(grpc_logger_string, *hcm.add_access_log()); }); - initialize(); - // Given we're using LDS in this test, initialize() will not complete until - // the initial LDS file has loaded. - EXPECT_EQ(1, test_server_->counter("listener_manager.lds.update_success")->value()); - - // HTTP 1.1 is allowed and the connection is kept open until the listener update. - std::string response; - auto connection = - createConnectionDriver(lookupPort("http"), "GET / HTTP/1.1\r\nHost: host\r\n\r\n", - [&response, &dispatcher = *dispatcher_]( - Network::ClientConnection&, const Buffer::Instance& data) -> void { - response.append(data.toString()); - if (response.find("\r\n\r\n") != std::string::npos) { - dispatcher.exit(); - } - }); - connection->run(); - EXPECT_TRUE(response.find("HTTP/1.1 200") == 0); - - test_server_->waitForCounterEq("access_logs.grpc_access_log.logs_written", 1); - - // Create a new config with HTTP/1.0 proxying. The goal is to trigger a listener update. - ConfigHelper new_config_helper( - version_, *api_, MessageUtil::getJsonStringFromMessageOrDie(config_helper_.bootstrap())); - new_config_helper.addConfigModifier( - [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& - hcm) { - hcm.mutable_http_protocol_options()->set_accept_http_10(true); - hcm.mutable_http_protocol_options()->set_default_host_for_http_10("default.com"); - }); - - // Create an LDS response with the new config, and reload config. - new_config_helper.setLds("1"); - test_server_->waitForCounterGe("listener_manager.listener_in_place_updated", 1); - test_server_->waitForCounterEq("listener_manager.lds.update_success", 2); - - // Wait until the http 1.1 connection is destroyed due to the listener update. It indicates the - // listener starts draining. - test_server_->waitForGaugeEq("listener.listener_0.downstream_cx_active", 0); - // Wait until all the draining filter chain is gone. It indicates the old listener and filter - // chains are destroyed. - test_server_->waitForGaugeEq("listener_manager.total_filter_chains_draining", 0); - - // Verify that the new listener config is applied. - std::string response2; - sendRawHttpAndWaitForResponse(lookupPort("http"), "GET / HTTP/1.0\r\n\r\n", &response2, true); - EXPECT_THAT(response2, HasSubstr("HTTP/1.0 200 OK\r\n")); - - // Verify that the grpc access logger is available after the listener update. - test_server_->waitForCounterEq("access_logs.grpc_access_log.logs_written", 2); -} - // Sample test making sure our config framework informs on listener failure. TEST_P(LdsIntegrationTest, FailConfigLoad) { config_helper_.addConfigModifier([&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { diff --git a/test/integration/xfcc_integration_test.cc b/test/integration/xfcc_integration_test.cc index 19186660db21..0ce4d4a94213 100644 --- a/test/integration/xfcc_integration_test.cc +++ b/test/integration/xfcc_integration_test.cc @@ -6,6 +6,7 @@ #include "envoy/config/bootstrap/v3/bootstrap.pb.h" #include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h" #include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h" +#include "envoy/extensions/transport_sockets/tls/v3/common.pb.h" #include "envoy/stats/scope.h" #include "source/common/event/dispatcher_impl.h" @@ -45,10 +46,16 @@ Network::TransportSocketFactoryPtr XfccIntegrationTest::createClientSslContext(b validation_context: trusted_ca: filename: {{ test_rundir }}/test/config/integration/certs/cacert.pem - match_subject_alt_names: - exact: "spiffe://lyft.com/backend-team" - exact: "lyft.com" - exact: "www.lyft.com" + match_typed_subject_alt_names: + - san_type: URI + matcher: + exact: "spiffe://lyft.com/backend-team" + - san_type: DNS + matcher: + exact: "lyft.com" + - san_type: DNS + matcher: + exact: "www.lyft.com" )EOF"; const std::string yaml_mtls = R"EOF( @@ -56,10 +63,16 @@ Network::TransportSocketFactoryPtr XfccIntegrationTest::createClientSslContext(b validation_context: trusted_ca: filename: {{ test_rundir }}/test/config/integration/certs/cacert.pem - match_subject_alt_names: - exact: "spiffe://lyft.com/backend-team" - exact: "lyft.com" - exact: "www.lyft.com" + match_typed_subject_alt_names: + - san_type: URI + matcher: + exact: "spiffe://lyft.com/backend-team" + - san_type: DNS + matcher: + exact: "lyft.com" + - san_type: DNS + matcher: + exact: "www.lyft.com" tls_certificates: certificate_chain: filename: {{ test_rundir }}/test/config/integration/certs/clientcert.pem @@ -135,7 +148,10 @@ void XfccIntegrationTest::initialize() { auto* validation_context = context.mutable_common_tls_context()->mutable_validation_context(); validation_context->mutable_trusted_ca()->set_filename( TestEnvironment::runfilesPath("test/config/integration/certs/upstreamcacert.pem")); - validation_context->add_match_subject_alt_names()->set_suffix("lyft.com"); + auto* san_matcher = validation_context->add_match_typed_subject_alt_names(); + san_matcher->mutable_matcher()->set_suffix("lyft.com"); + san_matcher->set_san_type( + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::DNS); transport_socket->set_name("envoy.transport_sockets.tls"); transport_socket->mutable_typed_config()->PackFrom(context); }); diff --git a/test/mocks/api/mocks.cc b/test/mocks/api/mocks.cc index c3eeed40d0c5..6a396cd4e064 100644 --- a/test/mocks/api/mocks.cc +++ b/test/mocks/api/mocks.cc @@ -60,14 +60,14 @@ MockOsSysCalls::~MockOsSysCalls() = default; SysCallIntResult MockOsSysCalls::setsockopt(os_fd_t sockfd, int level, int optname, const void* optval, socklen_t optlen) { - ASSERT(optlen == sizeof(int)); - // Allow mocking system call failure. if (setsockopt_(sockfd, level, optname, optval, optlen) != 0) { return SysCallIntResult{-1, 0}; } - boolsockopts_[SockOptKey(sockfd, level, optname)] = !!*reinterpret_cast(optval); + if (optlen >= sizeof(int)) { + boolsockopts_[SockOptKey(sockfd, level, optname)] = !!*reinterpret_cast(optval); + } return SysCallIntResult{0, 0}; }; diff --git a/test/mocks/api/mocks.h b/test/mocks/api/mocks.h index d2d54c13ff3f..5736dbd28e69 100644 --- a/test/mocks/api/mocks.h +++ b/test/mocks/api/mocks.h @@ -110,6 +110,10 @@ class MockOsSysCalls : public OsSysCallsImpl { MOCK_METHOD(bool, supportsMmsg, (), (const)); MOCK_METHOD(bool, supportsUdpGro, (), (const)); MOCK_METHOD(bool, supportsIpTransparent, (), (const)); + MOCK_METHOD(bool, supportsMptcp, (), (const)); + MOCK_METHOD(bool, supportsGetifaddrs, (), (const)); + MOCK_METHOD(void, setAlternateGetifaddrs, (AlternateGetifaddrs alternate_getifaddrs)); + MOCK_METHOD(SysCallIntResult, getifaddrs, (InterfaceAddressVector & interfaces)); // Map from (sockfd,level,optname) to boolean socket option. using SockOptKey = std::tuple; diff --git a/test/mocks/event/mocks.cc b/test/mocks/event/mocks.cc index 9a8f04cc23d1..16468bf697cb 100644 --- a/test/mocks/event/mocks.cc +++ b/test/mocks/event/mocks.cc @@ -70,10 +70,15 @@ MockTimer::~MockTimer() { } } -MockSchedulableCallback::~MockSchedulableCallback() = default; +MockSchedulableCallback::~MockSchedulableCallback() { + if (destroy_cb_) { + destroy_cb_->Call(); + } +} -MockSchedulableCallback::MockSchedulableCallback(MockDispatcher* dispatcher) - : dispatcher_(dispatcher) { +MockSchedulableCallback::MockSchedulableCallback(MockDispatcher* dispatcher, + testing::MockFunction* destroy_cb) + : dispatcher_(dispatcher), destroy_cb_(destroy_cb) { EXPECT_CALL(*dispatcher, createSchedulableCallback_(_)) .WillOnce(DoAll(SaveArg<0>(&callback_), Return(this))) .RetiresOnSaturation(); diff --git a/test/mocks/event/mocks.h b/test/mocks/event/mocks.h index 8d91e3558291..c4ade93b6d6c 100644 --- a/test/mocks/event/mocks.h +++ b/test/mocks/event/mocks.h @@ -70,9 +70,10 @@ class MockDispatcher : public Dispatcher { } Network::ListenerPtr createListener(Network::SocketSharedPtr&& socket, - Network::TcpListenerCallbacks& cb, - bool bind_to_port) override { - return Network::ListenerPtr{createListener_(std::move(socket), cb, bind_to_port)}; + Network::TcpListenerCallbacks& cb, bool bind_to_port, + bool ignore_global_conn_limit) override { + return Network::ListenerPtr{ + createListener_(std::move(socket), cb, bind_to_port, ignore_global_conn_limit)}; } Network::UdpListenerPtr @@ -138,7 +139,7 @@ class MockDispatcher : public Dispatcher { MOCK_METHOD(Filesystem::Watcher*, createFilesystemWatcher_, ()); MOCK_METHOD(Network::Listener*, createListener_, (Network::SocketSharedPtr && socket, Network::TcpListenerCallbacks& cb, - bool bind_to_port)); + bool bind_to_port, bool ignore_global_conn_limit)); MOCK_METHOD(Network::UdpListener*, createUdpListener_, (Network::SocketSharedPtr socket, Network::UdpListenerCallbacks& cb, const envoy::config::core::v3::UdpSocketConfig& config)); @@ -224,7 +225,8 @@ class MockScaledRangeTimerManager : public ScaledRangeTimerManager { class MockSchedulableCallback : public SchedulableCallback { public: - MockSchedulableCallback(MockDispatcher* dispatcher); + MockSchedulableCallback(MockDispatcher* dispatcher, + testing::MockFunction* destroy_cb = nullptr); ~MockSchedulableCallback() override; void invokeCallback() { @@ -244,6 +246,7 @@ class MockSchedulableCallback : public SchedulableCallback { private: std::function callback_; + testing::MockFunction* destroy_cb_{nullptr}; }; class MockSignalEvent : public SignalEvent { diff --git a/test/mocks/event/wrapped_dispatcher.h b/test/mocks/event/wrapped_dispatcher.h index c36705cd457b..634235cbbd63 100644 --- a/test/mocks/event/wrapped_dispatcher.h +++ b/test/mocks/event/wrapped_dispatcher.h @@ -60,9 +60,9 @@ class WrappedDispatcher : public Dispatcher { } Network::ListenerPtr createListener(Network::SocketSharedPtr&& socket, - Network::TcpListenerCallbacks& cb, - bool bind_to_port) override { - return impl_.createListener(std::move(socket), cb, bind_to_port); + Network::TcpListenerCallbacks& cb, bool bind_to_port, + bool ignore_global_conn_limit) override { + return impl_.createListener(std::move(socket), cb, bind_to_port, ignore_global_conn_limit); } Network::UdpListenerPtr diff --git a/test/mocks/filesystem/mocks.cc b/test/mocks/filesystem/mocks.cc index bbc1e17413b7..4a8df380e47d 100644 --- a/test/mocks/filesystem/mocks.cc +++ b/test/mocks/filesystem/mocks.cc @@ -23,7 +23,7 @@ Api::IoCallBoolResult MockFile::open(FlagSet flag) { Api::IoCallSizeResult MockFile::write(absl::string_view buffer) { Thread::LockGuard lock(write_mutex_); if (!is_open_) { - return {-1, Api::IoErrorPtr(nullptr, [](Api::IoError*) { NOT_REACHED_GCOVR_EXCL_LINE; })}; + return {-1, Api::IoErrorPtr(nullptr, [](Api::IoError*) { PANIC("reached unexpected code"); })}; } Api::IoCallSizeResult result = write_(buffer); diff --git a/test/mocks/http/mocks.cc b/test/mocks/http/mocks.cc index ae55011dd211..959e1bb07890 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 11db5041411a..58b20487a421 100644 --- a/test/mocks/http/mocks.h +++ b/test/mocks/http/mocks.h @@ -59,16 +59,16 @@ 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&)); 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_; }; @@ -232,10 +232,8 @@ class MockStreamDecoderFilterCallbacks : public StreamDecoderFilterCallbacks, const absl::optional grpc_status, absl::string_view details); - void encode100ContinueHeaders(ResponseHeaderMapPtr&& headers) override { - encode100ContinueHeaders_(*headers); - } - MOCK_METHOD(ResponseHeaderMapOptRef, continueHeaders, (), (const)); + void encode1xxHeaders(ResponseHeaderMapPtr&& headers) override { encode1xxHeaders_(*headers); } + MOCK_METHOD(ResponseHeaderMapOptRef, informationalHeaders, (), (const)); void encodeHeaders(ResponseHeaderMapPtr&& headers, bool end_stream, absl::string_view details) override { stream_info_.setResponseCodeDetails(details); @@ -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,8 +401,8 @@ class MockStreamFilter : public StreamFilter { MOCK_METHOD(void, decodeComplete, ()); // Http::MockStreamEncoderFilter - MOCK_METHOD(FilterHeadersStatus, encode100ContinueHeaders, (ResponseHeaderMap & headers)); - MOCK_METHOD(ResponseHeaderMapOptRef, continueHeaders, (), (const)); + MOCK_METHOD(FilterHeadersStatus, encode1xxHeaders, (ResponseHeaderMap & headers)); + 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)); diff --git a/test/mocks/http/stream.cc b/test/mocks/http/stream.cc index 19181d8c26ed..9410afdf2774 100644 --- a/test/mocks/http/stream.cc +++ b/test/mocks/http/stream.cc @@ -13,12 +13,21 @@ MockStream::MockStream() { })); ON_CALL(*this, removeCallbacks(_)) - .WillByDefault( - Invoke([this](StreamCallbacks& callbacks) -> void { callbacks_.remove(&callbacks); })); + .WillByDefault(Invoke([this](StreamCallbacks& callbacks) -> void { + for (auto& callback : callbacks_) { + if (callback == &callbacks) { + callback = nullptr; + return; + } + } + })); ON_CALL(*this, resetStream(_)).WillByDefault(Invoke([this](StreamResetReason reason) -> void { - for (StreamCallbacks* callbacks : callbacks_) { - callbacks->onResetStream(reason, absl::string_view()); + for (auto& callback : callbacks_) { + if (callback) { + callback->onResetStream(reason, absl::string_view()); + callback = nullptr; + } } })); diff --git a/test/mocks/http/stream.h b/test/mocks/http/stream.h index 9a0abc4c58df..a9d27e58ddef 100644 --- a/test/mocks/http/stream.h +++ b/test/mocks/http/stream.h @@ -23,19 +23,25 @@ class MockStream : public Stream { MOCK_METHOD(void, setFlushTimeout, (std::chrono::milliseconds timeout)); MOCK_METHOD(void, setAccount, (Buffer::BufferMemoryAccountSharedPtr)); - std::list callbacks_{}; + // Use the same underlying structure as StreamCallbackHelper to insure iteration stability + // if we remove callbacks during iteration. + absl::InlinedVector callbacks_; Network::Address::InstanceConstSharedPtr connection_local_address_; Buffer::BufferMemoryAccountSharedPtr account_; void runHighWatermarkCallbacks() { for (auto* callback : callbacks_) { - callback->onAboveWriteBufferHighWatermark(); + if (callback) { + callback->onAboveWriteBufferHighWatermark(); + } } } void runLowWatermarkCallbacks() { for (auto* callback : callbacks_) { - callback->onBelowWriteBufferLowWatermark(); + if (callback) { + callback->onBelowWriteBufferLowWatermark(); + } } } diff --git a/test/mocks/http/stream_decoder.h b/test/mocks/http/stream_decoder.h index 3e930a629cce..8bf5e5305d4f 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 8f783e69b283..c15da88f170d 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/network/BUILD b/test/mocks/network/BUILD index 483043b645ed..304cb8d8c44f 100644 --- a/test/mocks/network/BUILD +++ b/test/mocks/network/BUILD @@ -59,7 +59,7 @@ envoy_cc_mock( "//source/common/network:address_lib", "//source/common/network:socket_interface_lib", "//source/common/network:utility_lib", - "//source/common/network/dns_resolver:dns_factory_lib", + "//source/common/network/dns_resolver:dns_factory_util_lib", "//source/common/stats:isolated_store_lib", "//test/mocks/event:event_mocks", "//test/mocks/stream_info:stream_info_mocks", diff --git a/test/mocks/network/connection.h b/test/mocks/network/connection.h index 949b6e2b0956..8d044ff2b33a 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 e12ffbc89b89..40e5c43edd2a 100644 --- a/test/mocks/network/mocks.h +++ b/test/mocks/network/mocks.h @@ -18,7 +18,7 @@ #include "envoy/network/transport_socket.h" #include "envoy/stats/scope.h" -#include "source/common/network/dns_resolver/dns_factory.h" +#include "source/common/network/dns_resolver/dns_factory_util.h" #include "source/common/network/filter_manager_impl.h" #include "source/common/network/socket_interface.h" #include "source/common/network/socket_interface_impl.h" @@ -306,6 +306,7 @@ class MockSocketOption : public Socket::Option { MOCK_METHOD(void, hashKey, (std::vector&), (const)); MOCK_METHOD(absl::optional, getOptionDetails, (const Socket&, envoy::config::core::v3::SocketOption::SocketState state), (const)); + MOCK_METHOD(bool, isSupported, (), (const)); }; class MockConnectionSocket : public ConnectionSocket { @@ -329,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)); @@ -356,7 +359,6 @@ class MockConnectionSocket : public ConnectionSocket { IoHandlePtr io_handle_; std::shared_ptr connection_info_provider_; - bool is_closed_; }; class MockListenerFilterCallbacks : public ListenerFilterCallbacks { @@ -427,10 +429,12 @@ 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)); MOCK_METHOD(Init::Manager&, initManager, ()); + MOCK_METHOD(bool, ignoreGlobalConnLimit, (), (const)); envoy::config::core::v3::TrafficDirection direction() const override { return envoy::config::core::v3::UNSPECIFIED; @@ -641,9 +645,12 @@ class MockSocketInterface : public SocketInterfaceImpl { public: explicit MockSocketInterface(const std::vector& versions) : versions_(versions.begin(), versions.end()) {} - MOCK_METHOD(IoHandlePtr, socket, (Socket::Type, Address::Type, Address::IpVersion, bool), + MOCK_METHOD(IoHandlePtr, socket, + (Socket::Type, Address::Type, Address::IpVersion, bool, const SocketCreationOptions&), + (const)); + MOCK_METHOD(IoHandlePtr, socket, + (Socket::Type, const Address::InstanceConstSharedPtr, const SocketCreationOptions&), (const)); - MOCK_METHOD(IoHandlePtr, socket, (Socket::Type, const Address::InstanceConstSharedPtr), (const)); bool ipFamilySupported(int domain) override { const auto to_version = domain == AF_INET ? Address::IpVersion::v4 : Address::IpVersion::v6; return std::any_of(versions_.begin(), versions_.end(), diff --git a/test/mocks/router/mocks.h b/test/mocks/router/mocks.h index e9ccd1de05a2..9942d0d93699 100644 --- a/test/mocks/router/mocks.h +++ b/test/mocks/router/mocks.h @@ -263,9 +263,11 @@ class MockShadowWriter : public ShadowWriter { class TestVirtualCluster : public VirtualCluster { public: // Router::VirtualCluster + const absl::optional& name() const override { return name_; } Stats::StatName statName() const override { return stat_name_.statName(); } VirtualClusterStats& stats() const override { return stats_; } + const absl::optional name_ = "fake_virtual_cluster"; Stats::TestUtil::TestSymbolTable symbol_table_; Stats::StatNameManagedStorage stat_name_{"fake_virtual_cluster", *symbol_table_}; Stats::IsolatedStoreImpl stats_store_; @@ -390,6 +392,7 @@ class MockRouteEntry : public RouteEntry { MOCK_METHOD(const VirtualCluster*, virtualCluster, (const Http::HeaderMap& headers), (const)); MOCK_METHOD(const VirtualHost&, virtualHost, (), (const)); MOCK_METHOD(bool, autoHostRewrite, (), (const)); + MOCK_METHOD(bool, appendXfh, (), (const)); MOCK_METHOD((const std::multimap&), opaqueConfig, (), (const)); MOCK_METHOD(bool, includeVirtualHostRateLimits, (), (const)); MOCK_METHOD(const CorsPolicy*, corsPolicy, (), (const)); @@ -506,7 +509,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)); @@ -573,7 +575,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 d1349a4103ce..3116cf251d0a 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, diff --git a/test/mocks/server/factory_context.h b/test/mocks/server/factory_context.h index c5b104a1f5d7..79797209d211 100644 --- a/test/mocks/server/factory_context.h +++ b/test/mocks/server/factory_context.h @@ -42,6 +42,7 @@ class MockFactoryContext : public virtual FactoryContext { MOCK_METHOD(bool, isQuicListener, (), (const)); MOCK_METHOD(const LocalInfo::LocalInfo&, localInfo, (), (const)); MOCK_METHOD(const envoy::config::core::v3::Metadata&, listenerMetadata, (), (const)); + MOCK_METHOD(const Envoy::Config::TypedMetadata&, listenerTypedMetadata, (), (const)); MOCK_METHOD(envoy::config::core::v3::TrafficDirection, direction, (), (const)); MOCK_METHOD(TimeSource&, timeSource, ()); Event::TestTimeSystem& timeSystem() { return time_system_; } diff --git a/test/mocks/server/instance.h b/test/mocks/server/instance.h index de4f51099823..c20861421872 100644 --- a/test/mocks/server/instance.h +++ b/test/mocks/server/instance.h @@ -87,6 +87,7 @@ class MockInstance : public Instance { MOCK_METHOD(Configuration::ServerFactoryContext&, serverFactoryContext, ()); MOCK_METHOD(Configuration::TransportSocketFactoryContext&, transportSocketFactoryContext, ()); MOCK_METHOD(bool, enableReusePortDefault, ()); + MOCK_METHOD(void, setSinkPredicates, (std::unique_ptr &&)); void setDefaultTracingConfig(const envoy::config::trace::v3::Tracing& tracing_config) override { http_context_.setDefaultTracingConfig(tracing_config); @@ -141,6 +142,7 @@ class MockStatsConfig : public virtual StatsConfig { MOCK_METHOD(const std::list&, sinks, (), (const)); MOCK_METHOD(std::chrono::milliseconds, flushInterval, (), (const)); MOCK_METHOD(bool, flushOnAdmin, (), (const)); + MOCK_METHOD(const Stats::SinkPredicates*, sinkPredicates, (), (const)); }; class MockServerFactoryContext : public virtual ServerFactoryContext { diff --git a/test/mocks/server/listener_component_factory.cc b/test/mocks/server/listener_component_factory.cc index 540754339eb3..64eabf2a522c 100644 --- a/test/mocks/server/listener_component_factory.cc +++ b/test/mocks/server/listener_component_factory.cc @@ -13,17 +13,17 @@ using ::testing::Invoke; MockListenerComponentFactory::MockListenerComponentFactory() : socket_(std::make_shared>()) { - ON_CALL(*this, createListenSocket(_, _, _, _, _)) - .WillByDefault(Invoke([&](Network::Address::InstanceConstSharedPtr, Network::Socket::Type, - const Network::Socket::OptionsSharedPtr& options, - ListenerComponentFactory::BindType, - uint32_t) -> Network::SocketSharedPtr { - if (!Network::Socket::applyOptions(options, *socket_, - envoy::config::core::v3::SocketOption::STATE_PREBIND)) { - throw EnvoyException("MockListenerComponentFactory: Setting socket options failed"); - } - return socket_; - })); + ON_CALL(*this, createListenSocket(_, _, _, _, _, _)) + .WillByDefault(Invoke( + [&](Network::Address::InstanceConstSharedPtr, Network::Socket::Type, + const Network::Socket::OptionsSharedPtr& options, ListenerComponentFactory::BindType, + const Network::SocketCreationOptions&, uint32_t) -> Network::SocketSharedPtr { + if (!Network::Socket::applyOptions( + options, *socket_, envoy::config::core::v3::SocketOption::STATE_PREBIND)) { + throw EnvoyException("MockListenerComponentFactory: Setting socket options failed"); + } + return socket_; + })); } MockListenerComponentFactory::~MockListenerComponentFactory() = default; diff --git a/test/mocks/server/listener_component_factory.h b/test/mocks/server/listener_component_factory.h index 1234437ee02b..8711e0bdf70f 100644 --- a/test/mocks/server/listener_component_factory.h +++ b/test/mocks/server/listener_component_factory.h @@ -39,7 +39,7 @@ class MockListenerComponentFactory : public ListenerComponentFactory { MOCK_METHOD(Network::SocketSharedPtr, createListenSocket, (Network::Address::InstanceConstSharedPtr address, Network::Socket::Type socket_type, const Network::Socket::OptionsSharedPtr& options, BindType bind_type, - uint32_t worker_index)); + const Network::SocketCreationOptions& creation_options, uint32_t worker_index)); MOCK_METHOD(DrainManager*, createDrainManager_, (envoy::config::listener::v3::Listener::DrainType drain_type)); MOCK_METHOD(uint64_t, nextListenerTag, ()); diff --git a/test/mocks/server/listener_factory_context.h b/test/mocks/server/listener_factory_context.h index 0fbaddf2bd3a..0047446ba774 100644 --- a/test/mocks/server/listener_factory_context.h +++ b/test/mocks/server/listener_factory_context.h @@ -44,6 +44,7 @@ class MockListenerFactoryContext : public ListenerFactoryContext { MOCK_METHOD(bool, isQuicListener, (), (const)); MOCK_METHOD(const LocalInfo::LocalInfo&, localInfo, (), (const)); MOCK_METHOD(const envoy::config::core::v3::Metadata&, listenerMetadata, (), (const)); + MOCK_METHOD(const Envoy::Config::TypedMetadata&, listenerTypedMetadata, (), (const)); MOCK_METHOD(envoy::config::core::v3::TrafficDirection, direction, (), (const)); MOCK_METHOD(TimeSource&, timeSource, ()); Event::TestTimeSystem& timeSystem() { return time_system_; } diff --git a/test/mocks/server/options.cc b/test/mocks/server/options.cc index 8c48e026f81d..d947cccf1c9e 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 35bc8ff5c26a..fb64f4f1b664 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/mocks/server/overload_manager.cc b/test/mocks/server/overload_manager.cc index 4ebf19f032b8..9ca2c36a8755 100644 --- a/test/mocks/server/overload_manager.cc +++ b/test/mocks/server/overload_manager.cc @@ -10,11 +10,15 @@ namespace Envoy { namespace Server { +using ::testing::Return; using ::testing::ReturnRef; MockThreadLocalOverloadState::MockThreadLocalOverloadState() : disabled_state_(OverloadActionState::inactive()) { ON_CALL(*this, getState).WillByDefault(ReturnRef(disabled_state_)); + ON_CALL(*this, tryAllocateResource).WillByDefault(Return(true)); + ON_CALL(*this, tryDeallocateResource).WillByDefault(Return(true)); + ON_CALL(*this, isResourceMonitorEnabled).WillByDefault(Return(false)); } MockOverloadManager::MockOverloadManager() { diff --git a/test/mocks/server/overload_manager.h b/test/mocks/server/overload_manager.h index e5ab03992836..c3c08c8895eb 100644 --- a/test/mocks/server/overload_manager.h +++ b/test/mocks/server/overload_manager.h @@ -14,6 +14,9 @@ class MockThreadLocalOverloadState : public ThreadLocalOverloadState { public: MockThreadLocalOverloadState(); MOCK_METHOD(const OverloadActionState&, getState, (const std::string&), (override)); + MOCK_METHOD(bool, tryAllocateResource, (OverloadProactiveResourceName, int64_t)); + MOCK_METHOD(bool, tryDeallocateResource, (OverloadProactiveResourceName, int64_t)); + MOCK_METHOD(bool, isResourceMonitorEnabled, (OverloadProactiveResourceName)); private: const OverloadActionState disabled_state_; diff --git a/test/mocks/ssl/mocks.h b/test/mocks/ssl/mocks.h index 43ad275fce27..104242ce0c92 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 { @@ -141,6 +142,8 @@ class MockTlsCertificateConfig : public TlsCertificateConfig { MOCK_METHOD(const std::string&, certificateChain, (), (const)); MOCK_METHOD(const std::string&, certificateChainPath, (), (const)); + MOCK_METHOD(const std::string&, pkcs12, (), (const)); + MOCK_METHOD(const std::string&, pkcs12Path, (), (const)); MOCK_METHOD(const std::string&, privateKey, (), (const)); MOCK_METHOD(const std::string&, privateKeyPath, (), (const)); MOCK_METHOD(const std::vector&, ocspStaple, (), (const)); @@ -156,8 +159,9 @@ class MockCertificateValidationContextConfig : public CertificateValidationConte MOCK_METHOD(const std::string&, caCertPath, (), (const)); MOCK_METHOD(const std::string&, certificateRevocationList, (), (const)); MOCK_METHOD(const std::string&, certificateRevocationListPath, (), (const)); - MOCK_METHOD(const std::vector&, subjectAltNameMatchers, - (), (const)); + MOCK_METHOD( + const std::vector&, + subjectAltNameMatchers, (), (const)); MOCK_METHOD(const std::vector&, verifyCertificateHashList, (), (const)); MOCK_METHOD(const std::vector&, verifyCertificateSpkiList, (), (const)); MOCK_METHOD(bool, allowExpiredCertificate, (), (const)); @@ -167,6 +171,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 { diff --git a/test/mocks/stats/mocks.cc b/test/mocks/stats/mocks.cc index 66ed44834fc1..6ed277eed2fd 100644 --- a/test/mocks/stats/mocks.cc +++ b/test/mocks/stats/mocks.cc @@ -70,8 +70,12 @@ MockMetricSnapshot::~MockMetricSnapshot() = default; MockSink::MockSink() = default; MockSink::~MockSink() = default; +MockSinkPredicates::MockSinkPredicates() = default; +MockSinkPredicates::~MockSinkPredicates() = default; + MockStore::MockStore() { ON_CALL(*this, counter(_)).WillByDefault(ReturnRef(counter_)); + ON_CALL(*this, gauge(_, _)).WillByDefault(ReturnRef(gauge_)); ON_CALL(*this, histogram(_, _)) .WillByDefault(Invoke([this](const std::string& name, Histogram::Unit unit) -> Histogram& { auto* histogram = new NiceMock(); // symbol_table_); diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index ba81b5922f30..fd3d24539551 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -264,6 +264,15 @@ class MockSink : public Sink { MOCK_METHOD(void, onHistogramComplete, (const Histogram& histogram, uint64_t value)); }; +class MockSinkPredicates : public SinkPredicates { +public: + MockSinkPredicates(); + ~MockSinkPredicates() override; + MOCK_METHOD(bool, includeCounter, (const Counter&)); + MOCK_METHOD(bool, includeGauge, (const Gauge&)); + MOCK_METHOD(bool, includeTextReadout, (const TextReadout&)); +}; + class MockStore : public TestUtil::TestStore { public: MockStore(); @@ -286,13 +295,9 @@ class MockStore : public TestUtil::TestStore { MOCK_METHOD(Histogram&, histogramFromString, (const std::string& name, Histogram::Unit unit)); MOCK_METHOD(TextReadout&, textReadout, (const std::string&)); MOCK_METHOD(std::vector, text_readouts, (), (const)); - MOCK_METHOD(void, forEachCounter, - (std::function, std::function), (const)); - MOCK_METHOD(void, forEachGauge, - (std::function, std::function), (const)); - MOCK_METHOD(void, forEachTextReadout, - (std::function, std::function), - (const)); + MOCK_METHOD(void, forEachCounter, (SizeFn, StatFn), (const)); + MOCK_METHOD(void, forEachGauge, (SizeFn, StatFn), (const)); + MOCK_METHOD(void, forEachTextReadout, (SizeFn, StatFn), (const)); MOCK_METHOD(CounterOptConstRef, findCounter, (StatName), (const)); MOCK_METHOD(GaugeOptConstRef, findGauge, (StatName), (const)); @@ -321,6 +326,7 @@ class MockStore : public TestUtil::TestStore { TestUtil::TestSymbolTable symbol_table_; testing::NiceMock counter_; + testing::NiceMock gauge_; std::vector> histograms_; }; diff --git a/test/mocks/stream_info/mocks.cc b/test/mocks/stream_info/mocks.cc index 2eaad512dcd7..528ec13a3c89 100644 --- a/test/mocks/stream_info/mocks.cc +++ b/test/mocks/stream_info/mocks.cc @@ -21,6 +21,10 @@ MockStreamInfo::MockStreamInfo() downstream_connection_info_provider_(std::make_shared( std::make_shared("127.0.0.2"), std::make_shared("127.0.0.1"))) { + upstream_info_ = std::make_unique(); + Upstream::HostDescriptionConstSharedPtr host{ + new testing::NiceMock()}; + upstream_info_->setUpstreamHost(host); ON_CALL(*this, setResponseFlag(_)).WillByDefault(Invoke([this](ResponseFlag response_flag) { response_flags_ |= response_flag; })); @@ -36,40 +40,24 @@ MockStreamInfo::MockStreamInfo() })); ON_CALL(*this, startTime()).WillByDefault(ReturnPointee(&start_time_)); ON_CALL(*this, startTimeMonotonic()).WillByDefault(ReturnPointee(&start_time_monotonic_)); - ON_CALL(*this, lastDownstreamRxByteReceived()) - .WillByDefault(ReturnPointee(&last_downstream_rx_byte_received_)); - ON_CALL(*this, firstUpstreamTxByteSent()) - .WillByDefault(ReturnPointee(&first_upstream_tx_byte_sent_)); - ON_CALL(*this, lastUpstreamTxByteSent()) - .WillByDefault(ReturnPointee(&last_upstream_tx_byte_sent_)); - ON_CALL(*this, firstUpstreamRxByteReceived()) - .WillByDefault(ReturnPointee(&first_upstream_rx_byte_received_)); - ON_CALL(*this, lastUpstreamRxByteReceived()) - .WillByDefault(ReturnPointee(&last_upstream_rx_byte_received_)); - ON_CALL(*this, firstDownstreamTxByteSent()) - .WillByDefault(ReturnPointee(&first_downstream_tx_byte_sent_)); - ON_CALL(*this, lastDownstreamTxByteSent()) - .WillByDefault(ReturnPointee(&last_downstream_tx_byte_sent_)); ON_CALL(*this, requestComplete()).WillByDefault(ReturnPointee(&end_time_)); ON_CALL(*this, onRequestComplete()).WillByDefault(Invoke([this]() { end_time_ = absl::make_optional( std::chrono::duration_cast(ts_.systemTime() - start_time_) .count()); })); - ON_CALL(*this, setUpstreamLocalAddress(_)) + ON_CALL(*this, downstreamTiming()).WillByDefault(Invoke([this]() -> DownstreamTiming& { + return downstream_timing_; + })); + ON_CALL(Const(*this), downstreamTiming()) .WillByDefault( - Invoke([this](const Network::Address::InstanceConstSharedPtr& upstream_local_address) { - upstream_local_address_ = upstream_local_address; - })); - ON_CALL(*this, upstreamLocalAddress()).WillByDefault(ReturnRef(upstream_local_address_)); + Invoke([this]() -> OptRef { return downstream_timing_; })); + ON_CALL(*this, upstreamInfo()).WillByDefault(Invoke([this]() { return upstream_info_; })); + ON_CALL(testing::Const(*this), upstreamInfo()).WillByDefault(Invoke([this]() { + return OptRef(*upstream_info_); + })); ON_CALL(*this, downstreamAddressProvider()) .WillByDefault(ReturnPointee(downstream_connection_info_provider_)); - ON_CALL(*this, setUpstreamSslConnection(_)) - .WillByDefault(Invoke( - [this](const auto& connection_info) { upstream_connection_info_ = connection_info; })); - ON_CALL(*this, upstreamSslConnection()).WillByDefault(Invoke([this]() { - return upstream_connection_info_; - })); ON_CALL(*this, protocol()).WillByDefault(ReturnPointee(&protocol_)); ON_CALL(*this, responseCode()).WillByDefault(ReturnPointee(&response_code_)); ON_CALL(*this, responseCodeDetails()).WillByDefault(ReturnPointee(&response_code_details_)); @@ -93,39 +81,28 @@ MockStreamInfo::MockStreamInfo() return response_flags_ != 0; })); ON_CALL(*this, responseFlags()).WillByDefault(Return(response_flags_)); - ON_CALL(*this, upstreamHost()).WillByDefault(ReturnPointee(&host_)); - ON_CALL(*this, dynamicMetadata()).WillByDefault(ReturnRef(metadata_)); ON_CALL(Const(*this), dynamicMetadata()).WillByDefault(ReturnRef(metadata_)); ON_CALL(*this, filterState()).WillByDefault(ReturnRef(filter_state_)); ON_CALL(Const(*this), filterState()).WillByDefault(Invoke([this]() -> const FilterState& { return *filter_state_; })); - ON_CALL(*this, upstreamFilterState()).WillByDefault(ReturnRef(upstream_filter_state_)); - ON_CALL(*this, setUpstreamFilterState(_)) - .WillByDefault(Invoke([this](const FilterStateSharedPtr& filter_state) { - upstream_filter_state_ = filter_state; - })); ON_CALL(*this, setRouteName(_)).WillByDefault(Invoke([this](const absl::string_view route_name) { route_name_ = std::string(route_name); })); + ON_CALL(*this, setVirtualClusterName(_)) + .WillByDefault(Invoke([this](const absl::optional& virtual_cluster_name) { + virtual_cluster_name_ = virtual_cluster_name; + })); ON_CALL(*this, getRouteName()).WillByDefault(ReturnRef(route_name_)); - ON_CALL(*this, upstreamTransportFailureReason()) - .WillByDefault(ReturnRef(upstream_transport_failure_reason_)); - ON_CALL(*this, setConnectionID(_)).WillByDefault(Invoke([this](uint64_t id) { - connection_id_ = id; - })); + ON_CALL(*this, setUpstreamInfo(_)) + .WillByDefault(Invoke([this](std::shared_ptr info) { upstream_info_ = info; })); + ON_CALL(*this, virtualClusterName()).WillByDefault(ReturnRef(virtual_cluster_name_)); ON_CALL(*this, setFilterChainName(_)) .WillByDefault(Invoke([this](const absl::string_view filter_chain_name) { filter_chain_name_ = std::string(filter_chain_name); })); ON_CALL(*this, filterChainName()).WillByDefault(ReturnRef(filter_chain_name_)); - ON_CALL(*this, setUpstreamConnectionId(_)).WillByDefault(Invoke([this](uint64_t id) { - upstream_connection_id_ = id; - })); - ON_CALL(*this, upstreamConnectionId()).WillByDefault(Invoke([this]() { - return upstream_connection_id_; - })); ON_CALL(*this, setAttemptCount(_)).WillByDefault(Invoke([this](uint32_t attempt_count) { attempt_count_ = attempt_count; })); diff --git a/test/mocks/stream_info/mocks.h b/test/mocks/stream_info/mocks.h index be4c47a77ead..bd3603ea2564 100644 --- a/test/mocks/stream_info/mocks.h +++ b/test/mocks/stream_info/mocks.h @@ -6,6 +6,7 @@ #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 "test/mocks/upstream/host.h" #include "test/test_common/simulated_time_system.h" @@ -29,29 +30,22 @@ class MockStreamInfo : public StreamInfo { MOCK_METHOD(void, onUpstreamHostSelected, (Upstream::HostDescriptionConstSharedPtr host)); 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, ()); - MOCK_METHOD(absl::optional, lastUpstreamTxByteSent, (), (const)); - MOCK_METHOD(void, onLastUpstreamTxByteSent, ()); - MOCK_METHOD(absl::optional, firstUpstreamRxByteReceived, (), (const)); - MOCK_METHOD(void, onFirstUpstreamRxByteReceived, ()); - 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, setUpstreamInfo, (std::shared_ptr)); + MOCK_METHOD(std::shared_ptr, upstreamInfo, ()); + MOCK_METHOD(OptRef, upstreamInfo, (), (const)); MOCK_METHOD(void, onRequestComplete, ()); MOCK_METHOD(absl::optional, requestComplete, (), (const)); + MOCK_METHOD(DownstreamTiming&, downstreamTiming, ()); + MOCK_METHOD(OptRef, downstreamTiming, (), (const)); MOCK_METHOD(void, addBytesReceived, (uint64_t)); MOCK_METHOD(uint64_t, bytesReceived, (), (const)); MOCK_METHOD(void, addWireBytesReceived, (uint64_t)); MOCK_METHOD(uint64_t, wireBytesReceived, (), (const)); MOCK_METHOD(void, setRouteName, (absl::string_view route_name)); + MOCK_METHOD(void, setVirtualClusterName, + (const absl::optional& virtual_cluster_name)); MOCK_METHOD(const std::string&, getRouteName, (), (const)); + MOCK_METHOD(const absl::optional&, virtualClusterName, (), (const)); MOCK_METHOD(absl::optional, protocol, (), (const)); MOCK_METHOD(void, protocol, (Http::Protocol protocol)); MOCK_METHOD(absl::optional, responseCode, (), (const)); @@ -64,14 +58,9 @@ class MockStreamInfo : public StreamInfo { MOCK_METHOD(bool, hasResponseFlag, (ResponseFlag), (const)); MOCK_METHOD(bool, hasAnyResponseFlag, (), (const)); MOCK_METHOD(uint64_t, responseFlags, (), (const)); - MOCK_METHOD(Upstream::HostDescriptionConstSharedPtr, upstreamHost, (), (const)); - MOCK_METHOD(void, setUpstreamLocalAddress, (const Network::Address::InstanceConstSharedPtr&)); - MOCK_METHOD(const Network::Address::InstanceConstSharedPtr&, upstreamLocalAddress, (), (const)); MOCK_METHOD(bool, healthCheck, (), (const)); MOCK_METHOD(void, healthCheck, (bool is_health_check)); MOCK_METHOD(const Network::ConnectionInfoProvider&, downstreamAddressProvider, (), (const)); - MOCK_METHOD(void, setUpstreamSslConnection, (const Ssl::ConnectionInfoConstSharedPtr&)); - MOCK_METHOD(Ssl::ConnectionInfoConstSharedPtr, upstreamSslConnection, (), (const)); MOCK_METHOD(Router::RouteConstSharedPtr, route, (), (const)); MOCK_METHOD(envoy::config::core::v3::Metadata&, dynamicMetadata, ()); MOCK_METHOD(const envoy::config::core::v3::Metadata&, dynamicMetadata, (), (const)); @@ -80,10 +69,6 @@ class MockStreamInfo : public StreamInfo { (const std::string&, const std::string&, const std::string&)); MOCK_METHOD(const FilterStateSharedPtr&, filterState, ()); MOCK_METHOD(const FilterState&, filterState, (), (const)); - MOCK_METHOD(const FilterStateSharedPtr&, upstreamFilterState, (), (const)); - MOCK_METHOD(void, setUpstreamFilterState, (const FilterStateSharedPtr&)); - MOCK_METHOD(void, setUpstreamTransportFailureReason, (absl::string_view)); - MOCK_METHOD(const std::string&, upstreamTransportFailureReason, (), (const)); MOCK_METHOD(void, setRequestHeaders, (const Http::RequestHeaderMap&)); MOCK_METHOD(const Http::RequestHeaderMap*, getRequestHeaders, (), (const)); MOCK_METHOD(void, setUpstreamClusterInfo, (const Upstream::ClusterInfoConstSharedPtr&)); @@ -98,49 +83,35 @@ class MockStreamInfo : public StreamInfo { MOCK_METHOD(void, setConnectionID, (uint64_t)); MOCK_METHOD(void, setFilterChainName, (const absl::string_view)); MOCK_METHOD(const std::string&, filterChainName, (), (const)); - MOCK_METHOD(void, setUpstreamConnectionId, (uint64_t)); - MOCK_METHOD(absl::optional, upstreamConnectionId, (), (const)); MOCK_METHOD(void, setAttemptCount, (uint32_t), ()); MOCK_METHOD(absl::optional, attemptCount, (), (const)); MOCK_METHOD(const BytesMeterSharedPtr&, getUpstreamBytesMeter, (), (const)); MOCK_METHOD(const BytesMeterSharedPtr&, getDownstreamBytesMeter, (), (const)); MOCK_METHOD(void, setUpstreamBytesMeter, (const BytesMeterSharedPtr&)); MOCK_METHOD(void, setDownstreamBytesMeter, (const BytesMeterSharedPtr&)); - std::shared_ptr> host_{ - new testing::NiceMock()}; Envoy::Event::SimulatedTimeSystem ts_; SystemTime start_time_; MonotonicTime start_time_monotonic_; - absl::optional last_downstream_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 connection_id_; - 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_; absl::optional response_code_; absl::optional response_code_details_; absl::optional connection_termination_details_; + std::shared_ptr upstream_info_; uint64_t response_flags_{}; envoy::config::core::v3::Metadata metadata_; - FilterStateSharedPtr upstream_filter_state_; FilterStateSharedPtr filter_state_; uint64_t bytes_received_{}; uint64_t bytes_sent_{}; - Network::Address::InstanceConstSharedPtr upstream_local_address_; std::shared_ptr downstream_connection_info_provider_; BytesMeterSharedPtr upstream_bytes_meter_; BytesMeterSharedPtr downstream_bytes_meter_; Ssl::ConnectionInfoConstSharedPtr downstream_connection_info_; - Ssl::ConnectionInfoConstSharedPtr upstream_connection_info_; std::string route_name_; - std::string upstream_transport_failure_reason_; std::string filter_chain_name_; - absl::optional upstream_connection_id_; absl::optional attempt_count_; + absl::optional virtual_cluster_name_; + DownstreamTiming downstream_timing_; }; } // namespace StreamInfo diff --git a/test/mocks/upstream/BUILD b/test/mocks/upstream/BUILD index f30aa2683c4d..624a336837ac 100644 --- a/test/mocks/upstream/BUILD +++ b/test/mocks/upstream/BUILD @@ -97,6 +97,7 @@ envoy_cc_mock( ":thread_aware_load_balancer_mocks", ":thread_local_cluster_mocks", ":transport_socket_match_mocks", + ":typed_load_balancer_factory_mocks", "//envoy/http:async_client_interface", "//envoy/upstream:cluster_factory_interface", "//envoy/upstream:cluster_manager_interface", @@ -208,6 +209,15 @@ envoy_cc_mock( ], ) +envoy_cc_mock( + name = "typed_load_balancer_factory_mocks", + srcs = ["typed_load_balancer_factory.cc"], + hdrs = ["typed_load_balancer_factory.h"], + deps = [ + "//envoy/upstream:load_balancer_interface", + ], +) + envoy_cc_mock( name = "thread_local_cluster_mocks", srcs = ["thread_local_cluster.cc"], diff --git a/test/mocks/upstream/load_balancer.h b/test/mocks/upstream/load_balancer.h index f9283aceabcb..5edbd0d06b73 100644 --- a/test/mocks/upstream/load_balancer.h +++ b/test/mocks/upstream/load_balancer.h @@ -17,7 +17,7 @@ class MockLoadBalancer : public LoadBalancer { // Upstream::LoadBalancer MOCK_METHOD(HostConstSharedPtr, chooseHost, (LoadBalancerContext * context)); MOCK_METHOD(HostConstSharedPtr, peekAnotherHost, (LoadBalancerContext * context)); - MOCK_METHOD(absl::optional, selectPool, + MOCK_METHOD(absl::optional, selectExistingConnection, (Upstream::LoadBalancerContext * context, const Upstream::Host& host, std::vector& hash_key)); MOCK_METHOD(OptRef, lifetimeCallbacks, diff --git a/test/mocks/upstream/typed_load_balancer_factory.cc b/test/mocks/upstream/typed_load_balancer_factory.cc new file mode 100644 index 000000000000..7fff528012b0 --- /dev/null +++ b/test/mocks/upstream/typed_load_balancer_factory.cc @@ -0,0 +1,10 @@ +#include "typed_load_balancer_factory.h" + +namespace Envoy { +namespace Upstream { +MockTypedLoadBalancerFactory::MockTypedLoadBalancerFactory() = default; + +MockTypedLoadBalancerFactory::~MockTypedLoadBalancerFactory() = default; + +} // namespace Upstream +} // namespace Envoy diff --git a/test/mocks/upstream/typed_load_balancer_factory.h b/test/mocks/upstream/typed_load_balancer_factory.h new file mode 100644 index 000000000000..33f6849597af --- /dev/null +++ b/test/mocks/upstream/typed_load_balancer_factory.h @@ -0,0 +1,23 @@ +#pragma once + +#include "envoy/upstream/load_balancer.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace Envoy { +namespace Upstream { +class MockTypedLoadBalancerFactory : public TypedLoadBalancerFactory { +public: + MockTypedLoadBalancerFactory(); + ~MockTypedLoadBalancerFactory() override; + + // Upstream::TypedLoadBalancerFactory + MOCK_METHOD(std::string, name, (), (const)); + MOCK_METHOD(ThreadAwareLoadBalancerPtr, create, + (const PrioritySet& priority_set, ClusterStats& stats, Stats::Scope& stats_scope, + Runtime::Loader& runtime, Random::RandomGenerator& random, + const ::envoy::config::cluster::v3::LoadBalancingPolicy_Policy& lb_policy)); +}; +} // namespace Upstream +} // namespace Envoy diff --git a/test/per_file_coverage.sh b/test/per_file_coverage.sh index 0d8e6470668c..93ba06fd0c3f 100755 --- a/test/per_file_coverage.sh +++ b/test/per_file_coverage.sh @@ -4,8 +4,8 @@ # for existing directories with low coverage. declare -a KNOWN_LOW_COVERAGE=( "source/common:95.9" # Raise when QUIC coverage goes up -"source/common/api:79.8" -"source/common/api/posix:78.5" +"source/common/api:76.5" +"source/common/api/posix:75.0" "source/common/common/posix:92.7" "source/common/config:96.5" "source/common/crypto:0.0" @@ -13,10 +13,10 @@ 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.8" # A few lines of MacOS code not tested in linux scripts. Tested in MacOS scripts +"source/common/network/dns_resolver:90.7" # A few lines of MacOS code not tested in linux scripts. Tested in MacOS scripts "source/common/protobuf:95.3" "source/common/quic:91.8" "source/common/router:96.5" @@ -31,7 +31,7 @@ declare -a KNOWN_LOW_COVERAGE=( "source/exe:92.6" "source/extensions/common:95.8" "source/extensions/common/tap:94.2" -"source/extensions/common/wasm:95.3" # flaky: be careful adjusting +"source/extensions/common/wasm:95.2" # flaky: be careful adjusting "source/extensions/common/wasm/ext:92.0" "source/extensions/filters/common:96.1" "source/extensions/filters/common/expr:96.2" @@ -45,10 +45,10 @@ declare -a KNOWN_LOW_COVERAGE=( "source/extensions/filters/http/kill_request:95.3" # Death tests don't report LCOV "source/extensions/filters/http/lua:96.4" "source/extensions/filters/http/wasm:95.8" -"source/extensions/filters/listener:96.2" -"source/extensions/filters/listener/http_inspector:95.9" +"source/extensions/filters/listener:95.9" +"source/extensions/filters/listener/http_inspector:95.8" "source/extensions/filters/listener/original_dst:93.3" -"source/extensions/filters/listener/tls_inspector:93.5" +"source/extensions/filters/listener/tls_inspector:92.3" "source/extensions/filters/network/common:96.0" "source/extensions/filters/network/common/redis:96.2" "source/extensions/filters/network/mongo_proxy:95.5" @@ -71,7 +71,7 @@ declare -a KNOWN_LOW_COVERAGE=( "source/extensions/tracers/zipkin:96.1" "source/extensions/transport_sockets:95.3" "source/extensions/transport_sockets/tls:94.5" -"source/extensions/transport_sockets/tls/cert_validator:95.8" +"source/extensions/transport_sockets/tls/cert_validator:95.7" "source/extensions/transport_sockets/tls/ocsp:96.5" "source/extensions/transport_sockets/tls/private_key:77.8" "source/extensions/wasm_runtime/wamr:0.0" # Not enabled in coverage build diff --git a/test/proto/bookstore.proto b/test/proto/bookstore.proto index 6855e6444369..2ecbd0abd441 100644 --- a/test/proto/bookstore.proto +++ b/test/proto/bookstore.proto @@ -158,6 +158,11 @@ service Bookstore { post: "/wildcard/{arg=**}" }; } + rpc PostCustomVerb(EchoBodyRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/foo/bar:verb" + }; + } } service ServiceWithResponseBody { diff --git a/test/server/BUILD b/test/server/BUILD index 3195047b65ac..3060862c1ef7 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", ], ) @@ -427,8 +436,6 @@ envoy_cc_test( "//source/extensions/filters/http/health_check:config", "//source/extensions/filters/http/router:config", "//source/extensions/filters/network/http_connection_manager:config", - "//source/extensions/filters/network/redis_proxy:config", - "//source/extensions/stat_sinks/statsd:config", "//source/extensions/tracers/zipkin:config", "//source/server:process_context_lib", "//source/server:server_lib", diff --git a/test/server/active_internal_listener_test.cc b/test/server/active_internal_listener_test.cc new file mode 100644 index 000000000000..a2e60f3bc8c8 --- /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 2e6e2b807b03..2217c3f64020 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/admin/admin_instance.cc b/test/server/admin/admin_instance.cc index d63b0887c746..e05656d9b336 100644 --- a/test/server/admin/admin_instance.cc +++ b/test/server/admin/admin_instance.cc @@ -9,7 +9,7 @@ namespace Server { AdminInstanceTest::AdminInstanceTest() : address_out_path_(TestEnvironment::temporaryPath("admin.address")), cpu_profile_path_(TestEnvironment::temporaryPath("envoy.prof")), - admin_(cpu_profile_path_, server_), request_headers_{{":path", "/"}}, + admin_(cpu_profile_path_, server_, false), request_headers_{{":path", "/"}}, admin_filter_(admin_.createCallbackFunction()) { std::list access_logs; Filesystem::FilePathAndType file_info{Filesystem::DestinationType::File, "/dev/null"}; diff --git a/test/server/admin/admin_test.cc b/test/server/admin/admin_test.cc index 8a0fce046499..99b93afda1d4 100644 --- a/test/server/admin/admin_test.cc +++ b/test/server/admin/admin_test.cc @@ -67,7 +67,7 @@ TEST_P(AdminInstanceTest, WriteAddressToFile) { TEST_P(AdminInstanceTest, AdminAddress) { std::string address_out_path = TestEnvironment::temporaryPath("admin.address"); - AdminImpl admin_address_out_path(cpu_profile_path_, server_); + AdminImpl admin_address_out_path(cpu_profile_path_, server_, false); std::list access_logs; Filesystem::FilePathAndType file_info{Filesystem::DestinationType::File, "/dev/null"}; access_logs.emplace_back(new Extensions::AccessLoggers::File::FileAccessLog( @@ -82,7 +82,7 @@ TEST_P(AdminInstanceTest, AdminAddress) { TEST_P(AdminInstanceTest, AdminBadAddressOutPath) { std::string bad_path = TestEnvironment::temporaryPath("some/unlikely/bad/path/admin.address"); - AdminImpl admin_bad_address_out_path(cpu_profile_path_, server_); + AdminImpl admin_bad_address_out_path(cpu_profile_path_, server_, false); std::list access_logs; Filesystem::FilePathAndType file_info{Filesystem::DestinationType::File, "/dev/null"}; access_logs.emplace_back(new Extensions::AccessLoggers::File::FileAccessLog( diff --git a/test/server/admin/config_dump_handler_test.cc b/test/server/admin/config_dump_handler_test.cc index 92e8d56fa586..cbfea6af2b80 100644 --- a/test/server/admin/config_dump_handler_test.cc +++ b/test/server/admin/config_dump_handler_test.cc @@ -719,8 +719,11 @@ TEST_P(AdminInstanceTest, InvalidFieldMaskWithResourceDoesNotCrash) { getCallback( "/config_dump?resource=static_clusters&mask=cluster.transport_socket_matches.name", header_map, response)); - EXPECT_EQ("FieldMask paths: \"cluster.transport_socket_matches.name\"\n could not be " - "successfully used.", + std::string expected_mask_text = R"pb(paths: "cluster.transport_socket_matches.name")pb"; + Protobuf::FieldMask expected_mask_proto; + Protobuf::TextFormat::ParseFromString(expected_mask_text, &expected_mask_proto); + EXPECT_EQ(fmt::format("FieldMask {} could not be successfully used.", + expected_mask_proto.DebugString()), response.toString()); EXPECT_EQ(header_map.ContentType()->value().getStringView(), Http::Headers::get().ContentTypeValues.Text); diff --git a/test/server/admin/profiling_handler_test.cc b/test/server/admin/profiling_handler_test.cc index 709675e00a11..aefa0a6b10d7 100644 --- a/test/server/admin/profiling_handler_test.cc +++ b/test/server/admin/profiling_handler_test.cc @@ -67,7 +67,7 @@ TEST_P(AdminInstanceTest, AdminHeapProfiler) { TEST_P(AdminInstanceTest, AdminBadProfiler) { Buffer::OwnedImpl data; AdminImpl admin_bad_profile_path(TestEnvironment::temporaryPath("some/unlikely/bad/path.prof"), - server_); + server_, false); Http::TestResponseHeaderMapImpl header_map; const absl::string_view post = Http::Headers::get().MethodValues.Post; request_headers_.setMethod(post); diff --git a/test/server/admin/prometheus_stats_test.cc b/test/server/admin/prometheus_stats_test.cc index 0c86261c5983..2fb19f94e417 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); } diff --git a/test/server/api_listener_test.cc b/test/server/api_listener_test.cc index ee51447e4e40..4e4064ade789 100644 --- a/test/server/api_listener_test.cc +++ b/test/server/api_listener_test.cc @@ -109,7 +109,7 @@ name: test_api_listener port_value: 1234 api_listener: api_listener: - "@type": type.googleapis.com/envoy.api.v2.Cluster + "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster name: cluster1 type: EDS eds_cluster_config: @@ -123,7 +123,8 @@ name: test_api_listener HttpApiListener(config, *listener_manager_, config.name()), EnvoyException, "Unable to unpack as " "envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager: " - "[type.googleapis.com/envoy.api.v2.Cluster] {\n name: \"cluster1\"\n type: EDS\n " + "[type.googleapis.com/envoy.config.cluster.v3.Cluster] {\n name: \"cluster1\"\n type: " + "EDS\n " "eds_cluster_config {\n eds_config {\n path: \"eds path\"\n }\n }\n}\n"); } diff --git a/test/server/config_validation/server_test.cc b/test/server/config_validation/server_test.cc index 14611beda5a6..d65e7354fa65 100644 --- a/test/server/config_validation/server_test.cc +++ b/test/server/config_validation/server_test.cc @@ -1,3 +1,4 @@ +#include #include #include "envoy/server/filter_config.h" @@ -129,6 +130,7 @@ TEST_P(ValidationServerTest, NoopLifecycleNotifier) { server.registerCallback(ServerLifecycleNotifier::Stage::ShutdownExit, [] { FAIL(); }); server.registerCallback(ServerLifecycleNotifier::Stage::ShutdownExit, [](Event::PostCb) { FAIL(); }); + server.setSinkPredicates(std::make_unique>()); server.shutdown(); } diff --git a/test/server/config_validation/xds_fuzz.cc b/test/server/config_validation/xds_fuzz.cc index fb5535c6de08..28f239d89325 100644 --- a/test/server/config_validation/xds_fuzz.cc +++ b/test/server/config_validation/xds_fuzz.cc @@ -347,7 +347,7 @@ void XdsFuzzTest::verifyListeners() { FUZZ_ASSERT(listener_dump->has_active_state()); break; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } } } diff --git a/test/server/connection_handler_test.cc b/test/server/connection_handler_test.cc index 672f24e90164..d2b733072283 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" @@ -67,7 +70,8 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggable access_log, std::shared_ptr> filter_chain_manager = nullptr, uint32_t tcp_backlog_size = ENVOY_TCP_BACKLOG_SIZE, - Network::ConnectionBalancerSharedPtr connection_balancer = nullptr) + Network::ConnectionBalancerSharedPtr connection_balancer = nullptr, + bool ignore_global_conn_limit = false) : parent_(parent), socket_(std::make_shared>()), tag_(tag), bind_to_port_(bind_to_port), tcp_backlog_size_(tcp_backlog_size), hand_off_restored_destination_connections_(hand_off_restored_destination_connections), @@ -77,7 +81,7 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggable() : connection_balancer), access_logs_({access_log}), inline_filter_chain_manager_(filter_chain_manager), - init_manager_(nullptr) { + init_manager_(nullptr), ignore_global_conn_limit_(ignore_global_conn_limit) { envoy::config::listener::v3::UdpListenerConfig udp_config; udp_listener_config_ = std::make_unique(udp_config); udp_listener_config_->listener_factory_ = @@ -104,6 +108,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_; std::shared_ptr> inline_filter_chain_manager_; std::unique_ptr init_manager_; + const bool ignore_global_conn_limit_; envoy::config::core::v3::TrafficDirection direction_; Network::UdpListenerCallbacks* udp_listener_callbacks_{}; }; @@ -211,11 +227,12 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggable> overridden_filter_chain_manager = nullptr, - uint32_t tcp_backlog_size = ENVOY_TCP_BACKLOG_SIZE) { + uint32_t tcp_backlog_size = ENVOY_TCP_BACKLOG_SIZE, bool ignore_global_conn_limit = false) { listeners_.emplace_back(std::make_unique( *this, tag, bind_to_port, hand_off_restored_destination_connections, name, socket_type, listener_filters_timeout, continue_on_listener_filters_timeout, access_log_, - overridden_filter_chain_manager, tcp_backlog_size, connection_balancer)); + overridden_filter_chain_manager, tcp_backlog_size, connection_balancer, + ignore_global_conn_limit)); if (listener == nullptr) { // Expecting listener config in place update. @@ -226,9 +243,9 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggablesocket_factory_, getListenSocket(_)) .WillOnce(Return(listeners_.back()->socket_)); if (socket_type == Network::Socket::Type::Stream) { - EXPECT_CALL(dispatcher_, createListener_(_, _, _)) + EXPECT_CALL(dispatcher_, createListener_(_, _, _, _)) .WillOnce(Invoke([listener, listener_callbacks](Network::SocketSharedPtr&&, - Network::TcpListenerCallbacks& cb, + Network::TcpListenerCallbacks& cb, bool, bool) -> Network::Listener* { if (listener_callbacks != nullptr) { *listener_callbacks = &cb; @@ -256,6 +273,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 +333,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 +1579,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/filter_chain_benchmark_test.cc b/test/server/filter_chain_benchmark_test.cc index 919085fdcb94..e2d4a9b891e1 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{ diff --git a/test/server/listener_manager_impl_quic_only_test.cc b/test/server/listener_manager_impl_quic_only_test.cc index 84e1a3a200c9..c60143bada35 100644 --- a/test/server/listener_manager_impl_quic_only_test.cc +++ b/test/server/listener_manager_impl_quic_only_test.cc @@ -60,9 +60,13 @@ TEST_F(ListenerManagerImplQuicOnlyTest, QuicListenerFactoryAndSslContext) { validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" - match_subject_alt_names: - - exact: localhost - - exact: 127.0.0.1 + match_typed_subject_alt_names: + - matcher: + exact: localhost + san_type: URI + - matcher: + exact: 127.0.0.1 + san_type: IP_ADDRESS udp_listener_config: quic_options: {} )EOF", @@ -134,6 +138,18 @@ TEST_F(ListenerManagerImplQuicOnlyTest, QuicListenerFactoryAndSslContext) { filter_chain->transportSocketFactory()); EXPECT_TRUE(quic_socket_factory.implementsSecureTransport()); EXPECT_FALSE(quic_socket_factory.getTlsCertificates().empty()); + EXPECT_TRUE(listener_factory_.socket_->socket_is_open_); + + // Stop listening shouldn't close the socket. + EXPECT_CALL(server_.dispatcher_, post(_)).WillOnce(Invoke([](std::function callback) { + callback(); + })); + EXPECT_CALL(*worker_, stopListener(_, _)) + .WillOnce( + Invoke([](Network::ListenerConfig&, std::function completion) { completion(); })); + manager_->stopListeners(ListenerManager::StopListenersType::All); + EXPECT_CALL(*listener_factory_.socket_, close()).Times(0u); + EXPECT_TRUE(listener_factory_.socket_->socket_is_open_); } #endif @@ -161,9 +177,13 @@ TEST_F(ListenerManagerImplQuicOnlyTest, QuicListenerFactoryWithWrongTransportSoc validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" - match_subject_alt_names: - - exact: localhost - - exact: 127.0.0.1 + match_typed_subject_alt_names: + - matcher: + exact: localhost + san_type: URI + - matcher: + exact: 127.0.0.1 + san_type: IP_ADDRESS udp_listener_config: quic_options: {} )EOF", @@ -205,9 +225,13 @@ TEST_F(ListenerManagerImplQuicOnlyTest, QuicListenerFactoryWithWrongCodec) { validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" - match_subject_alt_names: - - exact: localhost - - exact: 127.0.0.1 + match_typed_subject_alt_names: + - matcher: + exact: localhost + san_type: URI + - matcher: + exact: 127.0.0.1 + san_type: IP_ADDRESS udp_listener_config: quic_options: {} )EOF", @@ -259,9 +283,13 @@ TEST_F(ListenerManagerImplQuicOnlyTest, QuicListenerFactoryWithConnectionBalence validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" - match_subject_alt_names: - - exact: localhost - - exact: 127.0.0.1 + match_typed_subject_alt_names: + - matcher: + exact: localhost + san_type: URI + - matcher: + exact: 127.0.0.1 + san_type: IP_ADDRESS udp_listener_config: quic_options: {} connection_balance_config: diff --git a/test/server/listener_manager_impl_test.cc b/test/server/listener_manager_impl_test.cc index 7cd7784a5ae1..69f7166ef129 100644 --- a/test/server/listener_manager_impl_test.cc +++ b/test/server/listener_manager_impl_test.cc @@ -115,7 +115,7 @@ class ListenerManagerImplForInPlaceFilterChainUpdateTest : public Event::Simulat void expectAddListener(const envoy::config::listener::v3::Listener& listener_proto, ListenerHandle*) { - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(*worker_, addListener(_, _, _)); manager_->addOrUpdateListener(listener_proto, "", true); worker_->callAddCompletion(); @@ -135,7 +135,7 @@ class ListenerManagerImplForInPlaceFilterChainUpdateTest : public Event::Simulat .WillOnce(Return(ByMove(std::unique_ptr(new_socket)))); } else { new_socket = listener_factory_.socket_.get(); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, bind_type, _, 0)); } EXPECT_CALL(*worker_, addListener(_, _, _)); EXPECT_CALL(*worker_, stopListener(_, _)); @@ -183,7 +183,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, EmptyFilter) { )EOF"; EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(&manager_->httpContext(), &server_.httpContext()); EXPECT_EQ(1U, manager_->listeners().size()); @@ -201,11 +201,39 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, DefaultListenerPerConnectionBuffe - filters: [] )EOF"; - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1024 * 1024U, manager_->listeners().back().get().perConnectionBufferLimitBytes()); } +TEST_F(ListenerManagerImplWithRealFiltersTest, DuplicatePortNotAllowed) { + const std::string yaml1 = R"EOF( +name: foo +address: + socket_address: + address: 127.0.0.1 + port_value: 1234 +filter_chains: +- filters: [] + )EOF"; + + const std::string yaml2 = R"EOF( +name: bar +address: + socket_address: + address: 127.0.0.1 + port_value: 1234 +filter_chains: +- filters: [] + )EOF"; + + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); + manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml1), "", true); + EXPECT_THROW_WITH_MESSAGE( + manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml2), "", true), EnvoyException, + "error adding listener: 'bar' has duplicate address '127.0.0.1:1234' as existing listener"); +} + TEST_F(ListenerManagerImplWithRealFiltersTest, SetListenerPerConnectionBufferLimit) { const std::string yaml = R"EOF( address: @@ -217,7 +245,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SetListenerPerConnectionBufferLim per_connection_buffer_limit_bytes: 8192 )EOF"; - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(8192U, manager_->listeners().back().get().perConnectionBufferLimitBytes()); } @@ -249,7 +277,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, TlsTransportSocket) { )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -269,7 +297,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, TransportSocketConnectTimeout) { transport_socket_connect_timeout: 3s )EOF"; - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); auto filter_chain = findFilterChain(1234, "127.0.0.1", "", "", {}, "8.8.8.8", 111); ASSERT_NE(filter_chain, nullptr); @@ -305,10 +333,11 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, UdpAddress) { EXPECT_CALL(*worker_, addListener(_, _, _)); EXPECT_CALL(listener_factory_, createListenSocket(_, Network::Socket::Type::Datagram, _, - ListenerComponentFactory::BindType::ReusePort, 0)) + ListenerComponentFactory::BindType::ReusePort, _, 0)) .WillOnce(Invoke( [this](const Network::Address::InstanceConstSharedPtr&, Network::Socket::Type, const Network::Socket::OptionsSharedPtr&, ListenerComponentFactory::BindType, + const Network::SocketCreationOptions&, uint32_t) -> Network::SocketSharedPtr { return listener_factory_.socket_; })); EXPECT_CALL(*listener_factory_.socket_, setSocketOption(_, _, _, _)).Times(testing::AtLeast(1)); EXPECT_CALL(os_sys_calls_, close(_)).WillRepeatedly(Return(Api::SysCallIntResult{0, errno})); @@ -509,7 +538,7 @@ bind_to_port: false )EOF"; EXPECT_CALL(listener_factory_, - createListenSocket(_, _, _, ListenerComponentFactory::BindType::NoBind, 0)); + createListenSocket(_, _, _, ListenerComponentFactory::BindType::NoBind, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); manager_->listeners().front().get().listenerScope().counterFromString("foo").inc(); @@ -518,15 +547,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"; + + 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"; - ASSERT_DEATH(manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true), ""); + 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) { @@ -539,7 +646,7 @@ TEST_F(ListenerManagerImplTest, NotDefaultListenerFiltersTimeout) { listener_filters_timeout: 0s )EOF"; - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true)); EXPECT_EQ(std::chrono::milliseconds(), manager_->listeners().front().get().listenerFiltersTimeout()); @@ -560,7 +667,7 @@ TEST_F(ListenerManagerImplTest, ModifyOnlyDrainType) { ListenerHandle* listener_foo = expectListenerCreate(false, true, envoy::config::listener::v3::Listener::MODIFY_ONLY); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); checkStats(__LINE__, 1, 0, 0, 0, 1, 0, 0); @@ -588,7 +695,7 @@ filter_chains: {} )EOF"; ListenerHandle* listener_foo = expectListenerCreate(false, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_TRUE( manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "version1", true)); checkStats(__LINE__, 1, 0, 0, 0, 1, 0, 0); @@ -627,7 +734,7 @@ filter_chains: {} ListenerHandle* listener_foo_different_address = expectListenerCreate(false, true); // Another socket should be created. - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(*listener_foo, onDestroy()); EXPECT_TRUE(manager_->addOrUpdateListener( parseListenerFromV3Yaml(listener_foo_different_address_yaml), "version2", true)); @@ -671,7 +778,7 @@ filter_chains: {} )EOF"; ListenerHandle* listener_baz = expectListenerCreate(true, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(listener_baz->target_, initialize()); EXPECT_TRUE( manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_baz_yaml), "version3", true)); @@ -724,7 +831,7 @@ filter_chains: {} // Modify the address of a warming listener. ListenerHandle* listener_baz_different_address = expectListenerCreate(true, true); // Another socket should be created. - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(*listener_baz, onDestroy()).WillOnce(Invoke([listener_baz]() -> void { listener_baz->target_.ready(); })); @@ -799,7 +906,7 @@ drain_type: default .WillByDefault(Return(Api::SysCallSocketResult{INVALID_SOCKET, 0})); ON_CALL(os_sys_calls_, close(_)).WillByDefault(Return(Api::SysCallIntResult{0, 0})); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); checkStats(__LINE__, 1, 0, 0, 0, 1, 0, 0); @@ -831,7 +938,7 @@ drain_type: default .WillByDefault(Return(Api::SysCallSocketResult{5, 0})); ON_CALL(os_sys_calls_, close(_)).WillByDefault(Return(Api::SysCallIntResult{0, 0})); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); checkStats(__LINE__, 1, 0, 0, 0, 1, 0, 0); @@ -856,7 +963,7 @@ name: foo )EOF"; ListenerHandle* listener_foo = expectListenerCreate(false, false); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", false)); checkStats(__LINE__, 1, 0, 0, 0, 1, 0, 0); checkConfigDump(R"EOF( @@ -936,7 +1043,7 @@ filter_chains: {} { // Add and remove a listener before starting workers. ListenerHandle* listener_foo = expectListenerCreate(true, true); EXPECT_CALL(server_, initManager()).WillOnce(ReturnRef(server_init_mgr)); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "version1", true)); checkStats(__LINE__, 1, 0, 0, 0, 1, 0, 0); @@ -987,7 +1094,7 @@ version_info: version1 server_init_watcher.expectReady().Times(0); { ListenerHandle* listener_foo2 = expectListenerCreate(true, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); // Version 2 listener will be initialized by listener manager directly. EXPECT_CALL(listener_foo2->target_, initialize()); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), @@ -1041,7 +1148,7 @@ filter_chains: {} )EOF"; ListenerHandle* listener_foo = expectListenerCreate(false, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_TRUE( manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "version1", true)); checkStats(__LINE__, 1, 0, 0, 0, 1, 0, 0); @@ -1122,7 +1229,7 @@ filter_chains: {} )EOF"; ListenerHandle* listener_foo = expectListenerCreate(false, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_TRUE( manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "version1", true)); checkStats(__LINE__, 1, 0, 0, 0, 1, 0, 0); @@ -1311,7 +1418,7 @@ filter_chains: {} )EOF"; ListenerHandle* listener_bar = expectListenerCreate(false, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(*worker_, addListener(_, _, _)); EXPECT_TRUE( manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_bar_yaml), "version4", true)); @@ -1332,7 +1439,7 @@ filter_chains: {} )EOF"; ListenerHandle* listener_baz = expectListenerCreate(true, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(listener_baz->target_, initialize()); EXPECT_TRUE( manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_baz_yaml), "version5", true)); @@ -1452,7 +1559,7 @@ name: foo )EOF"; ListenerHandle* listener_foo = expectListenerCreate(true, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(listener_foo->target_, initialize()); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); checkStats(__LINE__, 1, 0, 0, 1, 0, 0, 0); @@ -1518,7 +1625,7 @@ name: foo listener_factory_.socket_->connection_info_provider_->setLocalAddress(local_address); ListenerHandle* listener_foo = expectListenerCreate(false, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(*worker_, addListener(_, _, _)); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); worker_->callAddCompletion(); @@ -1578,7 +1685,7 @@ name: foo listener_factory_.socket_->connection_info_provider_->setLocalAddress(local_address); ListenerHandle* listener_foo = expectListenerCreate(false, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(*worker_, addListener(_, _, _)); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); worker_->callAddCompletion(); @@ -1596,7 +1703,7 @@ name: foo // Add foo again. We should use the socket from draining. ListenerHandle* listener_foo2 = expectListenerCreate(false, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(*worker_, addListener(_, _, _)); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); worker_->callAddCompletion(); @@ -1631,17 +1738,18 @@ bind_to_port: false ListenerHandle* listener_foo = expectListenerCreate(true, true); EXPECT_CALL(listener_factory_, - createListenSocket(_, _, _, ListenerComponentFactory::BindType::NoBind, 0)) + createListenSocket(_, _, _, ListenerComponentFactory::BindType::NoBind, _, 0)) .WillOnce(Invoke( [this, &real_listener_factory](const Network::Address::InstanceConstSharedPtr& address, Network::Socket::Type socket_type, const Network::Socket::OptionsSharedPtr& options, ListenerComponentFactory::BindType bind_type, + const Network::SocketCreationOptions& creation_options, uint32_t worker_index) -> Network::SocketSharedPtr { // When bind_to_port is equal to false, the BSD socket is not created at main thread. EXPECT_CALL(os_sys_calls_, socket(AF_INET, _, 0)).Times(0); - return real_listener_factory.createListenSocket(address, socket_type, options, - bind_type, worker_index); + return real_listener_factory.createListenSocket( + address, socket_type, options, bind_type, creation_options, worker_index); })); EXPECT_CALL(listener_foo->target_, initialize()); EXPECT_CALL(*listener_foo, onDestroy()); @@ -1670,17 +1778,18 @@ bind_to_port: false ListenerHandle* listener_foo = expectListenerCreate(false, true); EXPECT_CALL(listener_factory_, - createListenSocket(_, _, _, ListenerComponentFactory::BindType::NoBind, 0)) + createListenSocket(_, _, _, ListenerComponentFactory::BindType::NoBind, _, 0)) .WillOnce(Invoke( [this, &real_listener_factory](const Network::Address::InstanceConstSharedPtr& address, Network::Socket::Type socket_type, const Network::Socket::OptionsSharedPtr& options, ListenerComponentFactory::BindType bind_type, + const Network::SocketCreationOptions& creation_options, uint32_t worker_index) -> Network::SocketSharedPtr { // When bind_to_port is equal to false, the BSD socket is not created at main thread. EXPECT_CALL(os_sys_calls_, socket(AF_INET, _, 0)).Times(0); - return real_listener_factory.createListenSocket(address, socket_type, options, - bind_type, worker_index); + return real_listener_factory.createListenSocket( + address, socket_type, options, bind_type, creation_options, worker_index); })); EXPECT_CALL(*worker_, addListener(_, _, _)); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); @@ -1725,19 +1834,20 @@ name: foo ListenerHandle* listener_foo = expectListenerCreate(true, true); EXPECT_CALL(listener_factory_, - createListenSocket(_, _, _, ListenerComponentFactory::BindType::NoBind, 0)) + createListenSocket(_, _, _, ListenerComponentFactory::BindType::NoBind, _, 0)) .WillOnce(Invoke([this, &syscall_result, &real_listener_factory]( const Network::Address::InstanceConstSharedPtr& address, Network::Socket::Type socket_type, const Network::Socket::OptionsSharedPtr& options, ListenerComponentFactory::BindType bind_type, + const Network::SocketCreationOptions& creation_options, uint32_t worker_index) -> Network::SocketSharedPtr { EXPECT_CALL(server_, hotRestart).Times(0); // When bind_to_port is equal to false, create socket fd directly, and do not get socket // fd through hot restart. ON_CALL(os_sys_calls_, socket(AF_INET, _, 0)).WillByDefault(Return(syscall_result)); return real_listener_factory.createListenSocket(address, socket_type, options, bind_type, - worker_index); + creation_options, worker_index); })); EXPECT_CALL(listener_foo->target_, initialize()); EXPECT_CALL(*listener_foo, onDestroy()); @@ -1773,16 +1883,17 @@ name: foo return result; })); ListenerHandle* listener_foo = expectListenerCreate(true, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)) + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)) .WillOnce(Invoke([this, &syscall_result, &real_listener_factory]( const Network::Address::InstanceConstSharedPtr& address, Network::Socket::Type socket_type, const Network::Socket::OptionsSharedPtr& options, ListenerComponentFactory::BindType bind_type, + const Network::SocketCreationOptions& creation_options, uint32_t worker_index) -> Network::SocketSharedPtr { ON_CALL(os_sys_calls_, socket(AF_INET, _, 0)).WillByDefault(Return(syscall_result)); return real_listener_factory.createListenSocket(address, socket_type, options, bind_type, - worker_index); + creation_options, worker_index); })); EXPECT_CALL(listener_foo->target_, initialize()); EXPECT_CALL(*listener_foo, onDestroy()); @@ -1793,7 +1904,7 @@ TEST_F(ListenerManagerImplTest, NotSupportedDatagramUds) { ProdListenerComponentFactory real_listener_factory(server_); EXPECT_THROW_WITH_MESSAGE(real_listener_factory.createListenSocket( std::make_shared("/foo"), - Network::Socket::Type::Datagram, nullptr, default_bind_type, 0), + Network::Socket::Type::Datagram, nullptr, default_bind_type, {}, 0), EnvoyException, "socket type SocketType::Datagram not supported for pipes"); } @@ -1815,7 +1926,7 @@ name: foo )EOF"; ListenerHandle* listener_foo = expectListenerCreate(true, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(listener_foo->target_, initialize()); manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true); @@ -1849,7 +1960,7 @@ enable_reuse_port: false ListenerHandle* listener_foo = expectListenerCreate(true, true); EXPECT_CALL(listener_factory_, - createListenSocket(_, _, _, ListenerComponentFactory::BindType::NoReusePort, 0)) + createListenSocket(_, _, _, ListenerComponentFactory::BindType::NoReusePort, _, 0)) .WillOnce(Throw(EnvoyException("can't bind"))); EXPECT_CALL(*listener_foo, onDestroy()); EXPECT_THROW(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true), @@ -1942,7 +2053,7 @@ name: foo )EOF"; ListenerHandle* listener_foo = expectListenerCreate(false, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(*worker_, addListener(_, _, _)); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); worker_->callAddCompletion(); @@ -1996,7 +2107,7 @@ name: foo )EOF"; ListenerHandle* listener_foo = expectListenerCreate(true, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(listener_foo->target_, initialize()); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); EXPECT_EQ(0UL, manager_->listeners().size()); @@ -2010,7 +2121,7 @@ name: foo // Add foo again and initialize it. listener_foo = expectListenerCreate(true, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(listener_foo->target_, initialize()); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); checkStats(__LINE__, 2, 0, 1, 1, 0, 0, 0); @@ -2077,7 +2188,7 @@ traffic_direction: INBOUND )EOF"; ListenerHandle* listener_foo = expectListenerCreate(true, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(listener_foo->target_, initialize()); auto foo_inbound_proto = parseListenerFromV3Yaml(listener_foo_yaml); EXPECT_TRUE(manager_->addOrUpdateListener(foo_inbound_proto, "", true)); @@ -2101,7 +2212,7 @@ traffic_direction: OUTBOUND )EOF"; ListenerHandle* listener_foo_outbound = expectListenerCreate(true, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(listener_foo_outbound->target_, initialize()); EXPECT_TRUE( manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_outbound_yaml), "", true)); @@ -2129,7 +2240,7 @@ traffic_direction: OUTBOUND )EOF"; ListenerHandle* listener_bar_outbound = expectListenerCreate(false, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(*worker_, addListener(_, _, _)); EXPECT_TRUE( manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_bar_outbound_yaml), "", true)); @@ -2181,7 +2292,7 @@ name: foo )EOF"; ListenerHandle* listener_foo = expectListenerCreate(true, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(listener_foo->target_, initialize()); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); checkStats(__LINE__, 1, 0, 0, 1, 0, 0, 0); @@ -2230,7 +2341,7 @@ traffic_direction: INBOUND )EOF"; ListenerHandle* listener_foo = expectListenerCreate(true, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(listener_foo->target_, initialize()); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); checkStats(__LINE__, 1, 0, 0, 1, 0, 0, 0); @@ -2322,7 +2433,7 @@ bind_to_port: false ListenerHandle* listener_foo = expectListenerCreate(true, true); EXPECT_CALL(listener_factory_, - createListenSocket(_, _, _, ListenerComponentFactory::BindType::NoBind, 0)); + createListenSocket(_, _, _, ListenerComponentFactory::BindType::NoBind, _, 0)); EXPECT_CALL(listener_foo->target_, initialize()); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); @@ -2389,7 +2500,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SingleFilterChainWithDestinationP Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2435,7 +2546,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SingleFilterChainWithDirectSource Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2480,7 +2591,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SingleFilterChainWithDestinationI Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2525,7 +2636,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SingleFilterChainWithServerNamesM Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2571,7 +2682,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SingleFilterChainWithTransportPro Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2613,7 +2724,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SingleFilterChainWithApplicationP Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2658,7 +2769,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SingleFilterChainWithSourceTypeMa Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2718,7 +2829,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SingleFilterChainWithSourceIpMatc Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2777,7 +2888,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SingleFilterChainWithSourceIpv6Ma Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2815,7 +2926,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, SingleFilterChainWithSourcePortMa Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2881,7 +2992,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, MultipleFilterChainWithSourceType Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -2967,7 +3078,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, MultipleFilterChainsWithDestinati Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3052,7 +3163,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, MultipleFilterChainsWithDestinati Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3147,7 +3258,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, MultipleFilterChainsWithDirectSou Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3241,7 +3352,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, MultipleFilterChainsWithServerNam Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3312,7 +3423,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, MultipleFilterChainsWithTransport Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3356,7 +3467,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, MultipleFilterChainsWithApplicati Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3405,7 +3516,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, MultipleFilterChainsWithMultipleR Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3479,7 +3590,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, MultipleFilterChainsWithDifferent Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); } @@ -3519,7 +3630,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); } @@ -3650,7 +3761,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, TlsCertificateInline) { )EOF"); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); } @@ -3675,7 +3786,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, TlsCertificateChainInlinePrivateK Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); } @@ -3907,7 +4018,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, OriginalDstFilter) { )EOF", Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -3991,7 +4102,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, OriginalDstTestFilterOutbound) { Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -4046,7 +4157,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, OriginalDstFilterStopsIteration) Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -4096,7 +4207,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, OriginalDstTestFilterInbound) { Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -4177,7 +4288,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, OriginalDstTestFilterIPv6) { Network::Address::IpVersion::v6); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); @@ -4222,11 +4333,11 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, TransparentFreebindListenerDisabl )EOF", Network::Address::IpVersion::v4); EXPECT_CALL(listener_factory_, - createListenSocket(_, _, _, ListenerComponentFactory::BindType::NoReusePort, 0)) - .WillOnce( - Invoke([&](Network::Address::InstanceConstSharedPtr, Network::Socket::Type, - const Network::Socket::OptionsSharedPtr& options, - ListenerComponentFactory::BindType, uint32_t) -> Network::SocketSharedPtr { + createListenSocket(_, _, _, ListenerComponentFactory::BindType::NoReusePort, _, 0)) + .WillOnce(Invoke( + [&](Network::Address::InstanceConstSharedPtr, Network::Socket::Type, + const Network::Socket::OptionsSharedPtr& options, ListenerComponentFactory::BindType, + const Network::SocketCreationOptions&, uint32_t) -> Network::SocketSharedPtr { EXPECT_EQ(options, nullptr); return listener_factory_.socket_; })); @@ -4341,6 +4452,105 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, LiteralSockoptListenerEnabled) { EXPECT_EQ(1U, manager_->listeners().size()); } +// This test relies on linux-only code, and a linux-only name IPPROTO_MPTCP +#if defined(__linux__) +TEST_F(ListenerManagerImplWithRealFiltersTest, Mptcp) { + envoy::config::listener::v3::Listener listener = parseListenerFromV3Yaml(R"EOF( + name: mptcp-udp + enable_mptcp: true + address: + socket_address: + address: 127.0.0.1 + port_value: 1111 + filter_chains: + - filters: + )EOF"); + + ListenerHandle* listener_foo = expectListenerCreate(false, true); + + EXPECT_CALL(os_sys_calls_, supportsMptcp()).WillOnce(Return(true)); + + // Filter chain check for supported IP family. + EXPECT_CALL(os_sys_calls_, socket(_, _, 0)) + .WillRepeatedly(Return(Api::SysCallSocketResult{5, 0})); + + // Actual creation of the listening socket. + EXPECT_CALL(os_sys_calls_, socket(AF_INET, _, IPPROTO_MPTCP)) + .WillOnce(Return(Api::SysCallSocketResult{5, 0})); + + ON_CALL(os_sys_calls_, close(_)).WillByDefault(Return(Api::SysCallIntResult{0, 0})); + + ProdListenerComponentFactory real_listener_factory(server_); + + Network::SocketCreationOptions creation_options; + creation_options.mptcp_enabled_ = true; + + EXPECT_CALL(listener_factory_, + createListenSocket(_, _, _, default_bind_type, creation_options, 0)) + .WillOnce( + Invoke([&real_listener_factory](const Network::Address::InstanceConstSharedPtr& address, + Network::Socket::Type socket_type, + const Network::Socket::OptionsSharedPtr& options, + ListenerComponentFactory::BindType bind_type, + const Network::SocketCreationOptions& creation_options, + uint32_t worker_index) -> Network::SocketSharedPtr { + return real_listener_factory.createListenSocket( + address, socket_type, options, bind_type, creation_options, worker_index); + })); + + EXPECT_TRUE(manager_->addOrUpdateListener(listener, "", true)); + checkStats(__LINE__, 1, 0, 0, 0, 1, 0, 0); + EXPECT_CALL(*listener_foo, onDestroy()); +} +#endif + +TEST_F(ListenerManagerImplWithRealFiltersTest, MptcpOnUdp) { + envoy::config::listener::v3::Listener listener = parseListenerFromV3Yaml(R"EOF( + name: mptcp-udp + enable_mptcp: true + address: + socket_address: + address: 127.0.0.1 + port_value: 1111 + protocol: UDP + filter_chains: + - filters: + )EOF"); + EXPECT_THROW_WITH_MESSAGE(manager_->addOrUpdateListener(listener, "", true), EnvoyException, + "listener mptcp-udp: enable_mptcp can only be used with TCP listeners"); +} + +TEST_F(ListenerManagerImplWithRealFiltersTest, MptcpOnUnixDomainSocket) { + envoy::config::listener::v3::Listener listener = parseListenerFromV3Yaml(R"EOF( + name: mptcp-udp + enable_mptcp: true + address: + pipe: + path: /path + filter_chains: + - filters: + )EOF"); + EXPECT_THROW_WITH_MESSAGE(manager_->addOrUpdateListener(listener, "", true), EnvoyException, + "listener mptcp-udp: enable_mptcp can only be used with IP addresses"); +} + +TEST_F(ListenerManagerImplWithRealFiltersTest, MptcpNotSupported) { + envoy::config::listener::v3::Listener listener = parseListenerFromV3Yaml(R"EOF( + name: mptcp-udp + enable_mptcp: true + address: + socket_address: + address: 127.0.0.1 + port_value: 1111 + filter_chains: + - filters: + )EOF"); + EXPECT_CALL(os_sys_calls_, supportsMptcp()).WillOnce(Return(false)); + EXPECT_THROW_WITH_MESSAGE( + manager_->addOrUpdateListener(listener, "", true), EnvoyException, + "listener mptcp-udp: enable_mptcp is set but MPTCP is not supported by the operating system"); +} + // Set the resolver to the default IP resolver. The address resolver logic is unit tested in // resolver_impl_test.cc. TEST_F(ListenerManagerImplWithRealFiltersTest, AddressResolver) { @@ -4359,7 +4569,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, AddressResolver) { .WillRepeatedly(Return(Network::Utility::parseInternetAddress("127.0.0.1", 1111, false))); Registry::InjectFactory register_resolver(mock_resolver); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); } @@ -4384,7 +4594,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, CRLFilename) { Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); } @@ -4412,7 +4622,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, CRLInline) { Network::Address::IpVersion::v4); EXPECT_CALL(server_.api_.random_, uuid()); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); } @@ -4664,6 +4874,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; @@ -4683,7 +5190,7 @@ traffic_direction: INBOUND )EOF"; ListenerHandle* listener_foo = expectListenerCreate(true, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(listener_foo->target_, initialize()); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); EXPECT_EQ(0, server_.stats_store_.counter("listener_manager.listener_in_place_updated").value()); @@ -4746,7 +5253,7 @@ traffic_direction: INBOUND )EOF"; ListenerHandle* listener_foo = expectListenerCreate(true, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(listener_foo->target_, initialize()); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); EXPECT_EQ(0, server_.stats_store_.counter("listener_manager.listener_in_place_updated").value()); @@ -4816,7 +5323,7 @@ traffic_direction: INBOUND )EOF"; ListenerHandle* listener_foo = expectListenerCreate(true, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(listener_foo->target_, initialize()); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); EXPECT_EQ(0, server_.stats_store_.counter("listener_manager.listener_in_place_updated").value()); @@ -4880,7 +5387,7 @@ traffic_direction: INBOUND )EOF"; ListenerHandle* listener_foo = expectListenerCreate(true, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(listener_foo->target_, initialize()); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); checkStats(__LINE__, 1, 0, 0, 1, 0, 0, 0); @@ -4966,7 +5473,7 @@ traffic_direction: INBOUND )EOF"; ListenerHandle* listener_foo = expectListenerCreate(true, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(listener_foo->target_, initialize()); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); checkStats(__LINE__, 1, 0, 0, 1, 0, 0, 0); @@ -5037,7 +5544,7 @@ traffic_direction: INBOUND )EOF"; ListenerHandle* listener_foo = expectListenerCreate(true, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); EXPECT_CALL(listener_foo->target_, initialize()); EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); checkStats(__LINE__, 1, 0, 0, 1, 0, 0, 0); @@ -5103,7 +5610,7 @@ traffic_direction: INBOUND // The next step is to add a listener on the same socket address and the listen socket of // listener_foo will be duplicated. auto listener_foo_expect_reuse_socket = expectListenerCreate(true, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, _, _)).Times(0); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, _, _, _)).Times(0); EXPECT_CALL(*listener_factory_.socket_, duplicate()); EXPECT_CALL(listener_foo_expect_reuse_socket->target_, initialize()); EXPECT_TRUE( @@ -5177,7 +5684,7 @@ TEST_F(ListenerManagerImplForInPlaceFilterChainUpdateTest, TraditionalUpdateIfWo // Worker is not started yet. auto listener_proto = createDefaultListener(); ListenerHandle* listener_foo = expectListenerCreate(false, true); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, 0)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)); manager_->addOrUpdateListener(listener_proto, "", true); EXPECT_EQ(1u, manager_->listeners().size()); @@ -5328,7 +5835,7 @@ TEST_F(ListenerManagerImplTest, TcpBacklogCustomConfig) { )EOF", Network::Address::IpVersion::v4); - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, _, _)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, _, _, _)); manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); EXPECT_EQ(1U, manager_->listeners().size()); EXPECT_EQ(100U, manager_->listeners().back().get().tcpBacklogSize()); diff --git a/test/server/listener_manager_impl_test.h b/test/server/listener_manager_impl_test.h index e3869f80d5f7..6febd935efa3 100644 --- a/test/server/listener_manager_impl_test.h +++ b/test/server/listener_manager_impl_test.h @@ -232,12 +232,13 @@ class ListenerManagerImplTest : public testing::Test { Network::Socket::Options::size_type expected_num_options, ListenerComponentFactory::BindType bind_type = default_bind_type, uint32_t worker_index = 0) { - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, bind_type, worker_index)) + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, bind_type, _, worker_index)) .WillOnce( Invoke([this, expected_num_options, &expected_state]( const Network::Address::InstanceConstSharedPtr&, Network::Socket::Type, const Network::Socket::OptionsSharedPtr& options, - ListenerComponentFactory::BindType, uint32_t) -> Network::SocketSharedPtr { + ListenerComponentFactory::BindType, const Network::SocketCreationOptions&, + uint32_t) -> Network::SocketSharedPtr { EXPECT_NE(options.get(), nullptr); EXPECT_EQ(options->size(), expected_num_options); EXPECT_TRUE(Network::Socket::applyOptions(options, *listener_factory_.socket_, diff --git a/test/server/options_impl_test.cc b/test/server/options_impl_test.cc index 916b68b19f37..06b4b4a02f7d 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")); diff --git a/test/server/overload_manager_impl_test.cc b/test/server/overload_manager_impl_test.cc index 0886e0181d5f..6fa231cfeb71 100644 --- a/test/server/overload_manager_impl_test.cc +++ b/test/server/overload_manager_impl_test.cc @@ -57,7 +57,7 @@ class FakeResourceMonitor : public ResourceMonitor { update_async_ = new_update_async; } - void updateResourceUsage(ResourceMonitor::Callbacks& callbacks) override { + void updateResourceUsage(ResourceUpdateCallbacks& callbacks) override { if (update_async_) { callbacks_.emplace(callbacks); } else { @@ -74,7 +74,7 @@ class FakeResourceMonitor : public ResourceMonitor { } private: - void publishUpdate(ResourceMonitor::Callbacks& callbacks) { + void publishUpdate(ResourceUpdateCallbacks& callbacks) { if (absl::holds_alternative(response_)) { Server::ResourceUsage usage; usage.resource_pressure_ = absl::get(response_); @@ -88,7 +88,39 @@ class FakeResourceMonitor : public ResourceMonitor { Event::Dispatcher& dispatcher_; absl::variant response_; bool update_async_ = false; - absl::optional> callbacks_; + absl::optional> callbacks_; +}; + +class FakeProactiveResourceMonitor : public ProactiveResourceMonitor { +public: + FakeProactiveResourceMonitor(uint64_t max) : max_(max), current_(0){}; + + bool tryAllocateResource(int64_t increment) override { + int64_t new_val = (current_ += increment); + if (new_val > static_cast(max_) || new_val < 0) { + current_ -= increment; + return false; + } + return true; + } + + bool tryDeallocateResource(int64_t decrement) override { + RELEASE_ASSERT(decrement <= current_, + "Cannot deallocate resource, current resource usage is lower than decrement"); + int64_t new_val = (current_ -= decrement); + if (new_val < 0) { + current_ += decrement; + return false; + } + return true; + } + + int64_t currentResourceUsage() const override { return current_.load(); } + int64_t maxResourceUsage() const override { return max_; } + +private: + int64_t max_; + std::atomic current_; }; template @@ -114,6 +146,30 @@ class FakeResourceMonitorFactory : public Server::Configuration::ResourceMonitor const std::string name_; }; +template +class FakeProactiveResourceMonitorFactory + : public Server::Configuration::ProactiveResourceMonitorFactory { +public: + FakeProactiveResourceMonitorFactory(const std::string& name) : monitor_(nullptr), name_(name) {} + + Server::ProactiveResourceMonitorPtr + createProactiveResourceMonitor(const Protobuf::Message&, + Server::Configuration::ResourceMonitorFactoryContext&) override { + auto monitor = std::make_unique(3); + monitor_ = monitor.get(); + return monitor; + } + + ProtobufTypes::MessagePtr createEmptyConfigProto() override { + return ProtobufTypes::MessagePtr{new ConfigType()}; + } + + std::string name() const override { return name_; } + + FakeProactiveResourceMonitor* monitor_; // not owned + const std::string name_; +}; + class TestOverloadManager : public OverloadManagerImpl { public: TestOverloadManager(Event::Dispatcher& dispatcher, Stats::Scope& stats_scope, @@ -146,8 +202,10 @@ class OverloadManagerImplTest : public testing::Test { : factory1_("envoy.resource_monitors.fake_resource1"), factory2_("envoy.resource_monitors.fake_resource2"), factory3_("envoy.resource_monitors.fake_resource3"), - factory4_("envoy.resource_monitors.fake_resource4"), register_factory1_(factory1_), - register_factory2_(factory2_), register_factory3_(factory3_), register_factory4_(factory4_), + factory4_("envoy.resource_monitors.fake_resource4"), + factory5_("envoy.resource_monitors.global_downstream_max_connections"), + register_factory1_(factory1_), register_factory2_(factory2_), register_factory3_(factory3_), + register_factory4_(factory4_), register_factory5_(factory5_), api_(Api::createApiForTest(stats_)) {} void setDispatcherExpectation() { @@ -174,10 +232,12 @@ class OverloadManagerImplTest : public testing::Test { FakeResourceMonitorFactory factory2_; FakeResourceMonitorFactory factory3_; FakeResourceMonitorFactory factory4_; + FakeProactiveResourceMonitorFactory factory5_; Registry::InjectFactory register_factory1_; Registry::InjectFactory register_factory2_; Registry::InjectFactory register_factory3_; Registry::InjectFactory register_factory4_; + Registry::InjectFactory register_factory5_; NiceMock dispatcher_; NiceMock* timer_; // not owned Stats::TestUtil::TestStore stats_; @@ -215,6 +275,20 @@ constexpr char kRegularStateConfig[] = R"YAML( saturation_threshold: 0.8 )YAML"; +constexpr char proactiveResourceConfig[] = R"YAML( + refresh_interval: + seconds: 1 + resource_monitors: + - name: envoy.resource_monitors.fake_resource1 + - name: envoy.resource_monitors.global_downstream_max_connections + actions: + - name: envoy.overload_actions.dummy_action + triggers: + - name: envoy.resource_monitors.fake_resource1 + threshold: + value: 0.9 +)YAML"; + TEST_F(OverloadManagerImplTest, CallbackOnlyFiresWhenStateChanges) { setDispatcherExpectation(); @@ -230,6 +304,9 @@ TEST_F(OverloadManagerImplTest, CallbackOnlyFiresWhenStateChanges) { [&](OverloadActionState) { EXPECT_TRUE(false); }); manager->start(); + EXPECT_FALSE(manager->getThreadLocalOverloadState().isResourceMonitorEnabled( + OverloadProactiveResourceName::GlobalDownstreamMaxConnections)); + Stats::Gauge& active_gauge = stats_.gauge("overload.envoy.overload_actions.dummy_action.active", Stats::Gauge::ImportMode::Accumulate); Stats::Gauge& scale_percent_gauge = @@ -566,6 +643,17 @@ TEST_F(OverloadManagerImplTest, DuplicateResourceMonitor) { "Duplicate resource monitor .*"); } +TEST_F(OverloadManagerImplTest, DuplicateProactiveResourceMonitor) { + const std::string config = R"EOF( + resource_monitors: + - name: "envoy.resource_monitors.global_downstream_max_connections" + - name: "envoy.resource_monitors.global_downstream_max_connections" + )EOF"; + + EXPECT_THROW_WITH_REGEX(createOverloadManager(config), EnvoyException, + "Duplicate resource monitor .*"); +} + TEST_F(OverloadManagerImplTest, DuplicateOverloadAction) { const std::string config = R"EOF( actions: @@ -711,6 +799,32 @@ TEST_F(OverloadManagerImplTest, Shutdown) { manager->stop(); } +TEST_F(OverloadManagerImplTest, ProactiveResourceAllocateAndDeallocateResourceTest) { + setDispatcherExpectation(); + auto manager(createOverloadManager(proactiveResourceConfig)); + Stats::Counter& failed_updates = + stats_.counter("overload.envoy.resource_monitors.global_downstream_max_connections." + "failed_updates"); + manager->start(); + EXPECT_TRUE(manager->getThreadLocalOverloadState().isResourceMonitorEnabled( + OverloadProactiveResourceName::GlobalDownstreamMaxConnections)); + bool resource_allocated = manager->getThreadLocalOverloadState().tryAllocateResource( + Server::OverloadProactiveResourceName::GlobalDownstreamMaxConnections, 1); + EXPECT_TRUE(resource_allocated); + resource_allocated = manager->getThreadLocalOverloadState().tryAllocateResource( + Server::OverloadProactiveResourceName::GlobalDownstreamMaxConnections, 3); + EXPECT_FALSE(resource_allocated); + EXPECT_EQ(1, failed_updates.value()); + + bool resource_deallocated = manager->getThreadLocalOverloadState().tryDeallocateResource( + Server::OverloadProactiveResourceName::GlobalDownstreamMaxConnections, 1); + EXPECT_TRUE(resource_deallocated); + EXPECT_DEATH(manager->getThreadLocalOverloadState().tryDeallocateResource( + Server::OverloadProactiveResourceName::GlobalDownstreamMaxConnections, 1), + ".*Cannot deallocate resource, current resource usage is lower than decrement.*"); + manager->stop(); +} + } // namespace } // namespace Server } // namespace Envoy diff --git a/test/server/server_stats_flush_benchmark_test.cc b/test/server/server_stats_flush_benchmark_test.cc index 98790c5c65d5..895967523c62 100644 --- a/test/server/server_stats_flush_benchmark_test.cc +++ b/test/server/server_stats_flush_benchmark_test.cc @@ -19,10 +19,28 @@ namespace Envoy { +class TestSinkPredicates : public Stats::SinkPredicates { +public: + bool includeCounter(const Stats::Counter&) override { return (++num_counters_) % 10 == 0; } + bool includeGauge(const Stats::Gauge&) override { return (++num_gauges_) % 10 == 0; } + bool includeTextReadout(const Stats::TextReadout&) override { + return (++num_text_readouts_) % 10 == 0; + } + +private: + size_t num_counters_ = 0; + size_t num_gauges_ = 0; + size_t num_text_readouts_ = 0; +}; + class StatsSinkFlushSpeedTest { public: - StatsSinkFlushSpeedTest(size_t const num_stats) + StatsSinkFlushSpeedTest(size_t const num_stats, bool set_sink_predicates = false) : pool_(symbol_table_), stats_allocator_(symbol_table_), stats_store_(stats_allocator_) { + if (set_sink_predicates) { + stats_store_.setSinkPredicates( + std::unique_ptr{std::make_unique()}); + } // Create counters for (uint64_t idx = 0; idx < num_stats; ++idx) { @@ -40,6 +58,12 @@ class StatsSinkFlushSpeedTest { auto stat_name = pool_.add(absl::StrCat("text_readout.", idx)); stats_store_.textReadoutFromStatName(stat_name).set(absl::StrCat("text_readout.", idx)); } + + // Create histograms + for (uint64_t idx = 0; idx < num_stats; ++idx) { + std::string stat_name(absl::StrCat("histogram.", idx)); + stats_store_.histogramFromString(stat_name, Stats::Histogram::Unit::Unspecified); + } } void test(::benchmark::State& state) { @@ -69,6 +93,22 @@ static void bmFlushToSinks(::benchmark::State& state) { StatsSinkFlushSpeedTest speed_test(state.range(0)); speed_test.test(state); } + +static void bmFlushToSinksWithPredicatesSet(::benchmark::State& state) { + // Skip expensive benchmarks for unit tests. + if (benchmark::skipExpensiveBenchmarks() && state.range(0) > 100) { + state.SkipWithError("Skipping expensive benchmark"); + return; + } + + StatsSinkFlushSpeedTest speed_test(state.range(0), true); + speed_test.test(state); +} + BENCHMARK(bmFlushToSinks)->Unit(::benchmark::kMillisecond)->RangeMultiplier(10)->Range(10, 1000000); +BENCHMARK(bmFlushToSinksWithPredicatesSet) + ->Unit(::benchmark::kMillisecond) + ->RangeMultiplier(10) + ->Range(10, 1000000); } // namespace Envoy diff --git a/test/test_common/environment.cc b/test/test_common/environment.cc index e19a5ebb8af2..37ee5679ec3a 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) { diff --git a/test/test_common/logging.cc b/test/test_common/logging.cc index 3663feb0c83d..ecfe04dbf9cf 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 2d6252b2c68e..09cb5cdb5fa3 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 { diff --git a/test/test_common/network_utility.cc b/test/test_common/network_utility.cc index 637d238438c7..dec1dceca3ee 100644 --- a/test/test_common/network_utility.cc +++ b/test/test_common/network_utility.cc @@ -28,7 +28,7 @@ Address::InstanceConstSharedPtr findOrCheckFreePort(Address::InstanceConstShared << (addr_port == nullptr ? "nullptr" : addr_port->asString()); return nullptr; } - SocketImpl sock(type, addr_port, nullptr); + SocketImpl sock(type, addr_port, nullptr, {}); // Not setting REUSEADDR, therefore if the address has been recently used we won't reuse it here. // However, because we're going to use the address while checking if it is available, we'll need // to set REUSEADDR on listener sockets created by tests using an address validated by this means. @@ -158,13 +158,13 @@ std::string ipVersionToDnsFamily(Network::Address::IpVersion version) { } // This seems to be needed on the coverage build for some reason. - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } std::pair bindFreeLoopbackPort(Address::IpVersion version, Socket::Type type, bool reuse_port) { Address::InstanceConstSharedPtr addr = getCanonicalLoopbackAddress(version); - SocketPtr sock = std::make_unique(type, addr, nullptr); + SocketPtr sock = std::make_unique(type, addr, nullptr, SocketCreationOptions{}); if (reuse_port) { sock->addOptions(SocketOptionFactory::buildReusePortOptions()); Socket::applyOptions(sock->options(), *sock, diff --git a/test/test_common/network_utility.h b/test/test_common/network_utility.h index c7a9b7b85b67..58e40374b031 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). diff --git a/test/test_common/utility.cc b/test/test_common/utility.cc index 74d009a42189..0fa076fe35d0 100644 --- a/test/test_common/utility.cc +++ b/test/test_common/utility.cc @@ -74,7 +74,9 @@ bool TestUtility::headerMapEqualIgnoreOrder(const Http::HeaderMap& lhs, lhs_keys.insert(key); return Http::HeaderMap::Iterate::Continue; }); - rhs.iterate([&lhs, &rhs, &rhs_keys](const Http::HeaderEntry& header) -> Http::HeaderMap::Iterate { + bool values_match = true; + rhs.iterate([&values_match, &lhs, &rhs, + &rhs_keys](const Http::HeaderEntry& header) -> Http::HeaderMap::Iterate { const std::string key{header.key().getStringView()}; // Compare with canonicalized multi-value headers. This ensures we respect order within // a header. @@ -84,12 +86,13 @@ bool TestUtility::headerMapEqualIgnoreOrder(const Http::HeaderMap& lhs, Http::HeaderUtility::getAllOfHeaderAsString(rhs, Http::LowerCaseString(key)); ASSERT(rhs_entry.result()); if (lhs_entry.result() != rhs_entry.result()) { + values_match = false; return Http::HeaderMap::Iterate::Break; } rhs_keys.insert(key); return Http::HeaderMap::Iterate::Continue; }); - return lhs_keys.size() == rhs_keys.size(); + return values_match && lhs_keys.size() == rhs_keys.size(); } bool TestUtility::buffersEqual(const Buffer::Instance& lhs, const Buffer::Instance& rhs) { @@ -168,6 +171,11 @@ Stats::TextReadoutSharedPtr TestUtility::findTextReadout(Stats::Store& store, return findByName(store.textReadouts(), name); } +Stats::ParentHistogramSharedPtr TestUtility::findHistogram(Stats::Store& store, + const std::string& name) { + return findByName(store.histograms(), name); +} + AssertionResult TestUtility::waitForCounterEq(Stats::Store& store, const std::string& name, uint64_t value, Event::TestTimeSystem& time_system, std::chrono::milliseconds timeout, diff --git a/test/test_common/utility.h b/test/test_common/utility.h index 0b3b0c4a6bb8..545fcbe17edb 100644 --- a/test/test_common/utility.h +++ b/test/test_common/utility.h @@ -215,6 +215,15 @@ class TestUtility { */ static Stats::GaugeSharedPtr findGauge(Stats::Store& store, const std::string& name); + /** + * Find a histogram in a stats store. + * @param store supplies the stats store. + * @param name supplies the name to search for. + * @return Stats::ParentHistogramSharedPtr the histogram or nullptr if there is none. + */ + static Stats::ParentHistogramSharedPtr findHistogram(Stats::Store& store, + const std::string& name); + /** * Wait for a counter to == a given value. * @param store supplies the stats store. @@ -764,7 +773,7 @@ class TestUtility { case envoy::config::core::v3::ApiVersion::V3: return "V3"; default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } } }; diff --git a/test/test_runner.cc b/test/test_runner.cc index 68c0b2dc6d4b..c910d4fb505a 100644 --- a/test/test_runner.cc +++ b/test/test_runner.cc @@ -73,8 +73,6 @@ class RuntimeManagingListener : public ::testing::EmptyTestEventListener { } // namespace int TestRunner::RunTests(int argc, char** argv) { - Thread::TestThread test_thread; - ::testing::InitGoogleMock(&argc, argv); // We hold on to process_wide to provide RAII cleanup of process-wide // state. diff --git a/test/tools/router_check/coverage.cc b/test/tools/router_check/coverage.cc index 687e7ae3aa75..32ffb51f9ba5 100644 --- a/test/tools/router_check/coverage.cc +++ b/test/tools/router_check/coverage.cc @@ -129,6 +129,6 @@ RouteCoverage& Coverage::coveredRoute(const Envoy::Router::Route& route) { covered_routes_.push_back(std::move(new_coverage)); return coveredRoute(route); } - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); }; } // namespace Envoy diff --git a/test/tools/router_check/router.cc b/test/tools/router_check/router.cc index 3dedd6238e57..27cfb9aace34 100644 --- a/test/tools/router_check/router.cc +++ b/test/tools/router_check/router.cc @@ -35,7 +35,7 @@ const std::string toString(envoy::type::matcher::v3::StringMatcher::MatchPattern case envoy::type::matcher::v3::StringMatcher::MatchPatternCase::MATCH_PATTERN_NOT_SET: return "match_pattern_not_set"; } - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } const std::string toString(const envoy::config::route::v3::HeaderMatcher& header) { @@ -67,7 +67,7 @@ const std::string toString(const envoy::config::route::v3::HeaderMatcher& header return "string_match." + ::toString(header.string_match().match_pattern_case()); break; } - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } const std::string toString(const Envoy::Http::HeaderMap::GetResult& entry) { diff --git a/test/tools/router_check/router_check.cc b/test/tools/router_check/router_check.cc index 8ac19702134c..24ff036f4530 100644 --- a/test/tools/router_check/router_check.cc +++ b/test/tools/router_check/router_check.cc @@ -9,7 +9,6 @@ #include "test/tools/router_check/router.h" int main(int argc, char* argv[]) { - Envoy::Thread::TestThread test_thread; Envoy::Options options(argc, argv); const bool enforce_coverage = options.failUnder() != 0.0; diff --git a/test/tools/schema_validator/validator.cc b/test/tools/schema_validator/validator.cc index 28ab9316a8a1..f86484fb45f5 100644 --- a/test/tools/schema_validator/validator.cc +++ b/test/tools/schema_validator/validator.cc @@ -22,7 +22,7 @@ const std::string& Schema::toString(Type type) { return ROUTE; } - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } Options::Options(int argc, char** argv) { @@ -69,7 +69,7 @@ void Validator::validate(const std::string& config_path, Schema::Type schema_typ break; } default: - NOT_REACHED_GCOVR_EXCL_LINE; + PANIC("reached unexpected code"); } } diff --git a/tools/base/requirements.in b/tools/base/requirements.in index e76c666f3f84..8041aa7fb3d7 100644 --- a/tools/base/requirements.in +++ b/tools/base/requirements.in @@ -6,8 +6,9 @@ coloredlogs coverage envoy.base.checker envoy.base.runner -envoy.base.utils +envoy.base.utils>=0.0.10 envoy.code_format.python_check>=0.0.4 +envoy.dependency.cve_scan envoy.dependency.pip_check>=0.0.4 envoy.distribution.release envoy.distribution.verify diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index 38e518d3be85..7a0102f1eac1 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -1,5 +1,5 @@ # -# 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 @@ -11,6 +11,7 @@ abstracts==0.0.12 \ # envoy.abstract.command # envoy.base.utils # envoy.code-format.python-check + # envoy.dependency.cve-scan # envoy.dependency.pip-check # envoy.github.abstract # envoy.github.release @@ -19,6 +20,7 @@ aio.functional==0.0.9 \ # via # -r requirements.in # aio.tasks + # envoy.dependency.cve-scan # envoy.github.abstract # envoy.github.release aio.stream==0.0.2 \ @@ -34,6 +36,7 @@ aio.tasks==0.0.4 \ # via # -r requirements.in # envoy.code-format.python-check + # envoy.dependency.cve-scan # envoy.github.abstract # envoy.github.release aiodocker==0.21.0 \ @@ -87,6 +90,7 @@ aiohttp==3.7.4.post0 \ # via # aio.stream # aiodocker + # envoy.dependency.cve-scan # envoy.github.abstract # envoy.github.release # slackclient @@ -181,7 +185,7 @@ coloredlogs==15.0.1 \ # via # -r requirements.in # envoy.base.runner -coverage==6.0 \ +coverage[toml]==6.0 \ --hash=sha256:08fd55d2e00dac4c18a2fa26281076035ec86e764acdc198b9185ce749ada58f \ --hash=sha256:11ce082eb0f7c2bbfe96f6c8bcc3a339daac57de4dc0f3186069ec5c58da911c \ --hash=sha256:17983f6ccc47f4864fd16d20ff677782b23d1207bf222d10e4d676e4636b0872 \ @@ -261,6 +265,7 @@ envoy.base.checker==0.0.2 \ # via # -r requirements.in # envoy.code-format.python-check + # envoy.dependency.cve-scan # envoy.dependency.pip-check # envoy.distribution.distrotest # envoy.distribution.verify @@ -273,11 +278,13 @@ envoy.base.runner==0.0.4 \ # envoy.docs.sphinx-runner # envoy.github.abstract # envoy.gpg.sign -envoy.base.utils==0.0.8 \ - --hash=sha256:b82e18ab0535207b7136d6980239c9350f7113fa5da7dda781bcb6ad1e05b3ab +envoy.base.utils==0.0.10 \ + --hash=sha256:252f47a17c6346b2496c24c239e7ff7b1312a62f0605877c5315176d3387aa61 \ + --hash=sha256:a3e9fe045c2f117e4b5754c0f412a46ff1e058796e42aabda416f52029c7cd53 # via # -r requirements.in # envoy.code-format.python-check + # envoy.dependency.cve-scan # envoy.dependency.pip-check # envoy.distribution.distrotest # envoy.docs.sphinx-runner @@ -286,6 +293,10 @@ envoy.base.utils==0.0.8 \ envoy.code-format.python-check==0.0.4 \ --hash=sha256:5e166102d1f873f0c14640bcef87b46147cbad1cb68888c977acfde7fce96e04 # via -r requirements.in +envoy.dependency.cve-scan==0.0.1 \ + --hash=sha256:438973e6258deb271d60a9ad688c13ebf9c5360ccb9b6b0d4af3b3228235b153 \ + --hash=sha256:733fa5c6bdbe91da4afe1d46bca75279f717e410693866825d92208fa0d3418f + # via -r requirements.in envoy.dependency.pip-check==0.0.4 \ --hash=sha256:3213d77959f65c3c97e9b5d74cb14c02bc02dae64bac2e7c3cb829a2f4e5e40e # via -r requirements.in @@ -319,9 +330,9 @@ envoy.gpg.identity==0.0.2 \ envoy.gpg.sign==0.0.3 \ --hash=sha256:31667931f5d7ff05fd809b89748f277511486311c777652af4cb8889bd641049 # via -r requirements.in -flake8==3.9.2 \ - --hash=sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b \ - --hash=sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907 +flake8==4.0.1 \ + --hash=sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d \ + --hash=sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d # via # -r requirements.in # envoy.code-format.python-check @@ -331,9 +342,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.1.1 \ + --hash=sha256:655b879217dd445a2023e16154cc231febef802b5c812d5c2e822280ad69e1dc \ + --hash=sha256:bbad8ef1a428f5e6f7e195742fd739f56a5a602f9cf9bbe86f55a4848b1fabd6 # via # -r requirements.in # envoy.base.runner @@ -369,11 +380,12 @@ 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 + # envoy.dependency.cve-scan # sphinx markupsafe==2.0.1 \ --hash=sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298 \ @@ -480,6 +492,7 @@ packaging==21.0 \ --hash=sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7 \ --hash=sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14 # via + # envoy.dependency.cve-scan # envoy.github.release # pytest # sphinx @@ -501,17 +514,17 @@ py==1.10.0 \ --hash=sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3 \ --hash=sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a # via pytest -pycodestyle==2.7.0 \ - --hash=sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068 \ - --hash=sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef +pycodestyle==2.8.0 \ + --hash=sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20 \ + --hash=sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f # via flake8 pycparser==2.20 \ --hash=sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0 \ --hash=sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705 # via cffi -pyflakes==2.3.1 \ - --hash=sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3 \ - --hash=sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db +pyflakes==2.4.0 \ + --hash=sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c \ + --hash=sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e # via flake8 pygithub==1.55 \ --hash=sha256:1bbfff9372047ff3f21d5cd8e07720f3dbfdaf6462fcaed9d815f528f1ba7283 \ @@ -564,9 +577,9 @@ pytest==6.2.5 \ # pytest-asyncio # pytest-cov # pytest-patches -pytest-asyncio==0.15.1 \ - --hash=sha256:2564ceb9612bbd560d19ca4b41347b54e7835c2f792c504f698e05395ed63f6f \ - --hash=sha256:3042bcdf1c5d978f6b74d96a151c4cfb9dcece65006198389ccd7e6c60eb1eea +pytest-asyncio==0.16.0 \ + --hash=sha256:5f2a21273c47b331ae6aa5b36087047b4899e40f03f18397c0e65fa5cca54e9b \ + --hash=sha256:7496c5977ce88c34379df64a66459fe395cd05543f0a2f837016e7144391fcfb # via -r requirements.in pytest-cov==3.0.0 \ --hash=sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6 \ @@ -583,36 +596,40 @@ pytz==2021.1 \ --hash=sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da \ --hash=sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798 # via babel -pyyaml==5.4.1 \ - --hash=sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf \ - --hash=sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696 \ - --hash=sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393 \ - --hash=sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77 \ - --hash=sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922 \ - --hash=sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5 \ - --hash=sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8 \ - --hash=sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10 \ - --hash=sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc \ - --hash=sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018 \ - --hash=sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e \ - --hash=sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253 \ - --hash=sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347 \ - --hash=sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183 \ - --hash=sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541 \ - --hash=sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb \ - --hash=sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185 \ - --hash=sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc \ - --hash=sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db \ - --hash=sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa \ - --hash=sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46 \ - --hash=sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122 \ - --hash=sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b \ - --hash=sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63 \ - --hash=sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df \ - --hash=sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc \ - --hash=sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247 \ - --hash=sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6 \ - --hash=sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0 +pyyaml==6.0 \ + --hash=sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293 \ + --hash=sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b \ + --hash=sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57 \ + --hash=sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b \ + --hash=sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4 \ + --hash=sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07 \ + --hash=sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba \ + --hash=sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9 \ + --hash=sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287 \ + --hash=sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513 \ + --hash=sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0 \ + --hash=sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0 \ + --hash=sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92 \ + --hash=sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f \ + --hash=sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2 \ + --hash=sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc \ + --hash=sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c \ + --hash=sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86 \ + --hash=sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4 \ + --hash=sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c \ + --hash=sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34 \ + --hash=sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b \ + --hash=sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c \ + --hash=sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb \ + --hash=sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737 \ + --hash=sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3 \ + --hash=sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d \ + --hash=sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53 \ + --hash=sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78 \ + --hash=sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803 \ + --hash=sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a \ + --hash=sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174 \ + --hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5 # via # -r requirements.in # envoy.base.utils @@ -640,9 +657,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.1 \ + --hash=sha256:048dac56039a5713f47a554589dc98a442b39226a2b9ed7f82797fcb2fe9253f \ + --hash=sha256:32a5b3e9a1b176cc25ed048557d4d3d01af635e6b76c5bc7a43b0a34447fbd45 # via # -r requirements.in # envoy.docs.sphinx-runner @@ -711,9 +728,11 @@ sphinxext-rediraffe==0.2.7 \ toml==0.10.2 \ --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f - # via - # coverage - # pytest + # via pytest +tomli==1.2.1 \ + --hash=sha256:8dd0e9524d6f386271a36b41dbf6c57d8e32fd96fd22b6584679dc569d20899f \ + --hash=sha256:a5b75cb6f3968abb47af1b40c1819dc519ea82bcc065776a866e8d74c5ca9442 + # via coverage trycast==0.3.0 \ --hash=sha256:1b7b4c0d4b0d674770a53f34a762e52a6cd6879eb251ab21625602699920080d \ --hash=sha256:687185b812e8d1c45f2ba841e8de7bdcdee0695dcf3464f206800505d4c65f26 @@ -725,7 +744,6 @@ typing-extensions==3.10.0.2 \ # via # aiodocker # aiohttp - # gitpython uritemplate==3.0.1 \ --hash=sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f \ --hash=sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae @@ -792,9 +810,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==59.6.0 \ + --hash=sha256:22c7348c6d2976a52632c67f7ab0cdf40147db7789f9aed18734643fe9cf3373 \ + --hash=sha256:4ce92f1e1f8f01233ee9952c04f6b81d1e02939d6e1b488428154974a4d0783e # via # -r requirements.in # sphinx diff --git a/tools/code_format/check_format.py b/tools/code_format/check_format.py index a7f9e43eabee..ac6f2d3e9f89 100755 --- a/tools/code_format/check_format.py +++ b/tools/code_format/check_format.py @@ -86,9 +86,9 @@ # Histogram names which are allowed to be suffixed with the unit symbol, all of the pre-existing # ones were grandfathered as part of PR #8484 for backwards compatibility. HISTOGRAM_WITH_SI_SUFFIX_ALLOWLIST = ( - "downstream_cx_length_ms", "downstream_cx_length_ms", "initialization_time_ms", - "loop_duration_us", "poll_delay_us", "request_time_ms", "upstream_cx_connect_ms", - "upstream_cx_length_ms") + "cx_rtt_us", "cx_rtt_variance_us", "downstream_cx_length_ms", "downstream_cx_length_ms", + "initialization_time_ms", "loop_duration_us", "poll_delay_us", "request_time_ms", + "upstream_cx_connect_ms", "upstream_cx_length_ms") # Files in these paths can use std::regex STD_REGEX_ALLOWLIST = ( @@ -267,8 +267,7 @@ 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.header_map_correctly_coalesce_cookies", + "envoy.reloadable_features.sanitize_http_header_referer", } # yapf: enable @@ -1174,7 +1173,7 @@ def owned_directories(error_messages): owned = [] maintainers = [ '@mattklein123', '@htuch', '@alyssawilk', '@zuercher', '@lizan', '@snowp', '@asraa', - '@yanavlasov', '@junr03', '@dio', '@jmarantz', '@antoniovicente' + '@yanavlasov', '@junr03', '@dio', '@jmarantz', '@antoniovicente', '@ggreenway' ] try: diff --git a/tools/code_format/requirements.txt b/tools/code_format/requirements.txt index bb703224ca3f..a383645319d7 100644 --- a/tools/code_format/requirements.txt +++ b/tools/code_format/requirements.txt @@ -8,9 +8,9 @@ flake8-polyfill==1.0.2 \ --hash=sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9 \ --hash=sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda # via pep8-naming -flake8==3.9.2 \ - --hash=sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b \ - --hash=sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907 +flake8==4.0.1 \ + --hash=sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d \ + --hash=sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d # via # -r tools/code_format/requirements.txt # flake8-polyfill @@ -22,13 +22,13 @@ pep8-naming==0.12.1 \ --hash=sha256:4a8daeaeb33cfcde779309fc0c9c0a68a3bbe2ad8a8308b763c5068f86eb9f37 \ --hash=sha256:bb2455947757d162aa4cad55dba4ce029005cd1692f2899a21d51d8630ca7841 # via -r tools/code_format/requirements.txt -pycodestyle==2.7.0 \ - --hash=sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068 \ - --hash=sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef +pycodestyle==2.8.0 \ + --hash=sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20 \ + --hash=sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f # via flake8 -pyflakes==2.3.1 \ - --hash=sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3 \ - --hash=sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db +pyflakes==2.4.0 \ + --hash=sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e \ + --hash=sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c # via flake8 yapf==0.31.0 \ --hash=sha256:408fb9a2b254c302f49db83c59f9aa0b4b0fd0ec25be3a5c51181327922ff63d \ diff --git a/tools/dependency/BUILD b/tools/dependency/BUILD index b1e0af18c2ae..394ad20c0a34 100644 --- a/tools/dependency/BUILD +++ b/tools/dependency/BUILD @@ -24,20 +24,12 @@ py_library( py_binary( name = "cve_scan", - srcs = [ - "cve_scan.py", - "utils.py", - ], - data = [ - ":exports", - ], -) - -py_binary( - name = "cve_scan_test", - srcs = ["cve_scan_test.py"], - data = [ - ":cve_scan", + srcs = ["cve_scan.py"], + args = ["$(location :cve.yaml)"], + data = [":cve.yaml"], + deps = [ + ":utils", + requirement("envoy.dependency.cve_scan"), ], ) diff --git a/tools/dependency/cve.yaml b/tools/dependency/cve.yaml new file mode 100644 index 000000000000..45fc35106e3e --- /dev/null +++ b/tools/dependency/cve.yaml @@ -0,0 +1,51 @@ + +# We only look back a few years, since we shouldn't have any ancient deps. +start_year: 2018 + +# These CVEs are false positives for the match heuristics. An explanation is +# required when adding a new entry to this list as a comment. +ignored_cves: +# 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 +# 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 +# Node.js issue unrelated to http-parser, see +# https://github.com/mhart/StringStream/issues/7. +- CVE-2018-21270 +# 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 +# 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 diff --git a/tools/dependency/cve_scan.py b/tools/dependency/cve_scan.py index e1d048ed93dc..fdd0d42b4c8b 100755 --- a/tools/dependency/cve_scan.py +++ b/tools/dependency/cve_scan.py @@ -1,342 +1,88 @@ #!/usr/bin/env python3 -# Scan for any external dependencies that were last updated before known CVEs -# (and near relatives). We also try a fuzzy match on version information. +# usage +# +# with bazel: +# +# $ bazel run //tools/dependency:cve_scan -- -h +# +# $ bazel run //tools/dependency:cve_scan +# +# +# The upstream lib is maintained here: +# +# https://github.com/envoyproxy/pytooling/tree/main/envoy.dependency.cve_scan +# +# Please submit issues/PRs to the pytooling repo: +# +# https://github.com/envoyproxy/pytooling +# -from collections import defaultdict, namedtuple -import datetime as dt -import gzip -import json -import re import sys -import textwrap -import urllib.request +from functools import cached_property +from typing import Type -import utils as dep_utils +import abstracts -# 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-22931', - 'CVE-2021-22939', - 'CVE-2021-22940', - # This cve only affects versions of kafka < 2.8.1, but scanner - # does not support version matching atm. - # Tracking issue to fix versioning: - # https://github.com/envoyproxy/envoy/issues/18354 - 'CVE-2021-38153', -]) +from envoy.dependency import cve_scan -# Subset of CVE fields that are useful below. -Cve = namedtuple( - 'Cve', - ['id', 'description', 'cpes', 'score', 'severity', 'published_date', 'last_modified_date']) +import tools.dependency.utils as dep_utils -class Cpe(namedtuple('CPE', ['part', 'vendor', 'product', 'version'])): - """Model a subset of CPE fields that are used in CPE matching.""" +@abstracts.implementer(cve_scan.ACVE) +class EnvoyCVE: - @classmethod - def from_string(cls, cpe_str): - assert (cpe_str.startswith('cpe:2.3:')) - components = cpe_str.split(':') - assert (len(components) >= 6) - return cls(*components[2:6]) + @property + def cpe_class(self): + return EnvoyCPE - def __str__(self): - return f'cpe:2.3:{self.part}:{self.vendor}:{self.product}:{self.version}' + @property + def version_matcher_class(self) -> Type[cve_scan.ACVEVersionMatcher]: + return EnvoyCVEVersionMatcher - def vendor_normalized(self): - """Return a normalized CPE where only part and vendor are significant.""" - return Cpe(self.part, self.vendor, '*', '*') +@abstracts.implementer(cve_scan.ACPE) +class EnvoyCPE: + pass -def parse_cve_json(cve_json, cves, cpe_revmap): - """Parse CVE JSON dictionary. - Args: - cve_json: a NIST CVE JSON dictionary. - cves: dictionary mapping CVE ID string to Cve object (output). - cpe_revmap: a reverse map from vendor normalized CPE to CVE ID string. - """ +@abstracts.implementer(cve_scan.ADependency) +class EnvoyDependency: + pass - # This provides an over-approximation of possible CPEs affected by CVE nodes - # metadata; it traverses the entire AND-OR tree and just gathers every CPE - # observed. Generally we expect that most of Envoy's CVE-CPE matches to be - # simple, plus it's interesting to consumers of this data to understand when a - # CPE pops up, even in a conditional setting. - def gather_cpes(nodes, cpe_set): - for node in nodes: - for cpe_match in node.get('cpe_match', []): - cpe_set.add(Cpe.from_string(cpe_match['cpe23Uri'])) - gather_cpes(node.get('children', []), cpe_set) - for cve in cve_json['CVE_Items']: - cve_id = cve['cve']['CVE_data_meta']['ID'] - description = cve['cve']['description']['description_data'][0]['value'] - cpe_set = set() - gather_cpes(cve['configurations']['nodes'], cpe_set) - if len(cpe_set) == 0: - continue +@abstracts.implementer(cve_scan.ACVEChecker) +class EnvoyCVEChecker: - if not "baseMetricV3" in cve['impact']: - print(f"WARNING: ignoring v2 metric for {cve['cve']['CVE_data_meta']['ID']}") - continue + @property + def cpe_class(self): + return EnvoyCPE - cvss_v3_score = cve['impact']['baseMetricV3']['cvssV3']['baseScore'] - cvss_v3_severity = cve['impact']['baseMetricV3']['cvssV3']['baseSeverity'] + @property + def cve_class(self): + return EnvoyCVE - def parse_cve_date(date_str): - assert (date_str.endswith('Z')) - return dt.date.fromisoformat(date_str.split('T')[0]) + @property + def dependency_class(self): + return EnvoyDependency - published_date = parse_cve_date(cve['publishedDate']) - last_modified_date = parse_cve_date(cve['lastModifiedDate']) - cves[cve_id] = Cve( - cve_id, description, cpe_set, cvss_v3_score, cvss_v3_severity, published_date, - last_modified_date) - for cpe in cpe_set: - cpe_revmap[str(cpe.vendor_normalized())].add(cve_id) - return cves, cpe_revmap + @cached_property + def dependency_metadata(self): + return dep_utils.repository_locations() + @cached_property + def ignored_cves(self): + return super().ignored_cves -def download_cve_data(urls): - """Download NIST CVE JSON databases from given URLs and parse. - Args: - urls: a list of URLs. - Returns: - cves: dictionary mapping CVE ID string to Cve object (output). - cpe_revmap: a reverse map from vendor normalized CPE to CVE ID string. - """ - cves = {} - cpe_revmap = defaultdict(set) - for url in urls: - print(f'Loading NIST CVE database from {url}...') - with urllib.request.urlopen(url) as request: - with gzip.GzipFile(fileobj=request) as json_data: - parse_cve_json(json.loads(json_data.read()), cves, cpe_revmap) - return cves, cpe_revmap +@abstracts.implementer(cve_scan.ACVEVersionMatcher) +class EnvoyCVEVersionMatcher: + pass -def format_cve_details(cve, deps): - formatted_deps = ', '.join(sorted(deps)) - wrapped_description = '\n '.join(textwrap.wrap(cve.description)) - return f""" - CVE ID: {cve.id} - CVSS v3 score: {cve.score} - Severity: {cve.severity} - Published date: {cve.published_date} - Last modified date: {cve.last_modified_date} - Dependencies: {formatted_deps} - Description: {wrapped_description} - Affected CPEs: - """ + '\n '.join(f'- {cpe}' for cpe in cve.cpes) +def main(*args) -> int: + return EnvoyCVEChecker(*args)() -FUZZY_DATE_RE = re.compile('(\d{4}).?(\d{2}).?(\d{2})') -FUZZY_SEMVER_RE = re.compile('(\d+)[:\.\-_](\d+)[:\.\-_](\d+)') - - -def regex_groups_match(regex, lhs, rhs): - """Do two strings match modulo a regular expression? - - Args: - regex: regular expression - lhs: LHS string - rhs: RHS string - Returns: - A boolean indicating match. - """ - lhs_match = regex.search(lhs) - if lhs_match: - rhs_match = regex.search(rhs) - if rhs_match and lhs_match.groups() == rhs_match.groups(): - return True - return False - - -def cpe_match(cpe, dep_metadata): - """Heuristically match dependency metadata against CPE. - - We have a number of rules below that should are easy to compute without having - to look at the dependency metadata. In the future, with additional access to - repository information we could do the following: - - For dependencies at a non-release version, walk back through git history to - the last known release version and attempt a match with this. - - For dependencies at a non-release version, use the commit date to look for a - version match where version is YYYY-MM-DD. - - Args: - cpe: Cpe object to match against. - dep_metadata: dependency metadata dictionary. - Returns: - A boolean indicating a match. - """ - dep_cpe = Cpe.from_string(dep_metadata['cpe']) - dep_version = dep_metadata['version'] - # The 'part' and 'vendor' must be an exact match. - if cpe.part != dep_cpe.part: - return False - if cpe.vendor != dep_cpe.vendor: - return False - # We allow Envoy dependency CPEs to wildcard the 'product', this is useful for - # LLVM where multiple product need to be covered. - if dep_cpe.product != '*' and cpe.product != dep_cpe.product: - return False - # Wildcard versions always match. - if cpe.version == '*': - return True - # An exact version match is a hit. - if cpe.version == dep_version: - return True - # Allow the 'release_date' dependency metadata to substitute for date. - # TODO(htuch): Consider fuzzier date ranges. - if cpe.version == dep_metadata['release_date']: - return True - # Try a fuzzy date match to deal with versions like fips-20190304 in dependency version. - if regex_groups_match(FUZZY_DATE_RE, dep_version, cpe.version): - return True - # Try a fuzzy semver match to deal with things like 2.1.0-beta3. - if regex_groups_match(FUZZY_SEMVER_RE, dep_version, cpe.version): - return True - # Fall-thru. - return False - - -def cve_match(cve, dep_metadata): - """Heuristically match dependency metadata against CVE. - - In general, we allow false positives but want to keep the noise low, to avoid - the toil around having to populate IGNORES_CVES. - - Args: - cve: Cve object to match against. - dep_metadata: dependency metadata dictionary. - Returns: - A boolean indicating a match. - """ - wildcard_version_match = False - # Consider each CPE attached to the CVE for a match against the dependency CPE. - for cpe in cve.cpes: - if cpe_match(cpe, dep_metadata): - # Wildcard version matches need additional heuristics unrelated to CPE to - # qualify, e.g. last updated date. - if cpe.version == '*': - wildcard_version_match = True - else: - return True - if wildcard_version_match: - # If the CVE was published after the dependency was last updated, it's a - # potential match. - last_dep_update = dt.date.fromisoformat(dep_metadata['release_date']) - if last_dep_update <= cve.published_date: - return True - return False - - -def cve_scan(cves, cpe_revmap, cve_allowlist, repository_locations): - """Scan for CVEs in a parsed NIST CVE database. - - Args: - cves: CVE dictionary as provided by download_cve_data(). - cve_revmap: CPE-CVE reverse map as provided by download_cve_data(). - cve_allowlist: an allowlist of CVE IDs to ignore. - repository_locations: a dictionary of dependency metadata in the format - described in api/bazel/external_deps.bzl. - Returns: - possible_cves: a dictionary mapping CVE IDs to Cve objects. - cve_deps: a dictionary mapping CVE IDs to dependency names. - """ - possible_cves = {} - cve_deps = defaultdict(list) - for dep, metadata in repository_locations.items(): - cpe = metadata.get('cpe', 'N/A') - if cpe == 'N/A': - continue - candidate_cve_ids = cpe_revmap.get(str(Cpe.from_string(cpe).vendor_normalized()), []) - for cve_id in candidate_cve_ids: - cve = cves[cve_id] - if cve.id in cve_allowlist: - continue - if cve_match(cve, metadata): - possible_cves[cve_id] = cve - cve_deps[cve_id].append(dep) - return possible_cves, cve_deps - - -if __name__ == '__main__': - # Allow local overrides for NIST CVE database URLs via args. - urls = sys.argv[1:] - 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 - ] - cves, cpe_revmap = download_cve_data(urls) - possible_cves, cve_deps = cve_scan( - cves, cpe_revmap, IGNORES_CVES, dep_utils.repository_locations()) - if possible_cves: - print( - '\nBased on heuristic matching with the NIST CVE database, Envoy may be vulnerable to:') - for cve_id in sorted(possible_cves): - print(f'{format_cve_details(possible_cves[cve_id], cve_deps[cve_id])}') - sys.exit(1) +if __name__ == "__main__": + sys.exit(main(*sys.argv[1:])) diff --git a/tools/dependency/cve_scan_test.py b/tools/dependency/cve_scan_test.py deleted file mode 100755 index 28ff3224c2f2..000000000000 --- a/tools/dependency/cve_scan_test.py +++ /dev/null @@ -1,295 +0,0 @@ -#!/usr/bin/env python3 -"""Tests for cve_scan.""" - -from collections import defaultdict -import datetime as dt -import unittest - -import cve_scan - - -class CveScanTest(unittest.TestCase): - - def test_parse_cve_json(self): - cve_json = { - 'CVE_Items': [ - { - 'cve': { - 'CVE_data_meta': { - 'ID': 'CVE-2020-1234' - }, - 'description': { - 'description_data': [{ - 'value': 'foo' - }] - } - }, - 'configurations': { - 'nodes': [{ - 'cpe_match': [{ - 'cpe23Uri': 'cpe:2.3:a:foo:bar:1.2.3' - }], - }], - }, - 'impact': { - 'baseMetricV3': { - 'cvssV3': { - 'baseScore': 3.4, - 'baseSeverity': 'LOW' - } - } - }, - 'publishedDate': '2020-03-17T00:59Z', - 'lastModifiedDate': '2020-04-17T00:59Z' - }, - { - 'cve': { - 'CVE_data_meta': { - 'ID': 'CVE-2020-1235' - }, - 'description': { - 'description_data': [{ - 'value': 'bar' - }] - } - }, - 'configurations': { - 'nodes': [{ - 'cpe_match': [{ - 'cpe23Uri': 'cpe:2.3:a:foo:bar:1.2.3' - }], - 'children': [ - { - 'cpe_match': [{ - 'cpe23Uri': 'cpe:2.3:a:foo:baz:3.2.3' - }] - }, - { - 'cpe_match': [{ - 'cpe23Uri': 'cpe:2.3:a:foo:*:*' - }, { - 'cpe23Uri': 'cpe:2.3:a:wat:bar:1.2.3' - }] - }, - ], - }], - }, - 'impact': { - 'baseMetricV3': { - 'cvssV3': { - 'baseScore': 9.9, - 'baseSeverity': 'HIGH' - } - } - }, - 'publishedDate': '2020-03-18T00:59Z', - 'lastModifiedDate': '2020-04-18T00:59Z' - }, - ] - } - cves = {} - cpe_revmap = defaultdict(set) - cve_scan.parse_cve_json(cve_json, cves, cpe_revmap) - self.maxDiff = None - self.assertDictEqual( - cves, { - 'CVE-2020-1234': - cve_scan.Cve( - id='CVE-2020-1234', - description='foo', - cpes=set([self.build_cpe('cpe:2.3:a:foo:bar:1.2.3')]), - score=3.4, - severity='LOW', - published_date=dt.date(2020, 3, 17), - last_modified_date=dt.date(2020, 4, 17)), - 'CVE-2020-1235': - cve_scan.Cve( - id='CVE-2020-1235', - description='bar', - cpes=set( - map( - self.build_cpe, [ - 'cpe:2.3:a:foo:bar:1.2.3', 'cpe:2.3:a:foo:baz:3.2.3', - 'cpe:2.3:a:foo:*:*', 'cpe:2.3:a:wat:bar:1.2.3' - ])), - score=9.9, - severity='HIGH', - published_date=dt.date(2020, 3, 18), - last_modified_date=dt.date(2020, 4, 18)) - }) - self.assertDictEqual( - cpe_revmap, { - 'cpe:2.3:a:foo:*:*': {'CVE-2020-1234', 'CVE-2020-1235'}, - 'cpe:2.3:a:wat:*:*': {'CVE-2020-1235'} - }) - - def build_cpe(self, cpe_str): - return cve_scan.Cpe.from_string(cpe_str) - - def build_dep(self, cpe_str, version=None, release_date=None): - return {'cpe': cpe_str, 'version': version, 'release_date': release_date} - - def cpe_match(self, cpe_str, dep_cpe_str, version=None, release_date=None): - return cve_scan.cpe_match( - self.build_cpe(cpe_str), - self.build_dep(dep_cpe_str, version=version, release_date=release_date)) - - def test_cpe_match(self): - # Mismatched part - self.assertFalse(self.cpe_match('cpe:2.3:o:foo:bar:*', 'cpe:2.3:a:foo:bar:*')) - # Mismatched vendor - self.assertFalse(self.cpe_match('cpe:2.3:a:foo:bar:*', 'cpe:2.3:a:foz:bar:*')) - # Mismatched product - self.assertFalse(self.cpe_match('cpe:2.3:a:foo:bar:*', 'cpe:2.3:a:foo:baz:*')) - # Wildcard product - self.assertTrue(self.cpe_match('cpe:2.3:a:foo:bar:*', 'cpe:2.3:a:foo:*:*')) - # Wildcard version match - self.assertTrue(self.cpe_match('cpe:2.3:a:foo:bar:*', 'cpe:2.3:a:foo:bar:*')) - # Exact version match - self.assertTrue( - self.cpe_match('cpe:2.3:a:foo:bar:1.2.3', 'cpe:2.3:a:foo:bar:*', version='1.2.3')) - # Date version match - self.assertTrue( - self.cpe_match( - 'cpe:2.3:a:foo:bar:2020-03-05', 'cpe:2.3:a:foo:bar:*', release_date='2020-03-05')) - fuzzy_version_matches = [ - ('2020-03-05', '2020-03-05'), - ('2020-03-05', '20200305'), - ('2020-03-05', 'foo-20200305-bar'), - ('2020-03-05', 'foo-2020_03_05-bar'), - ('2020-03-05', 'foo-2020-03-05-bar'), - ('1.2.3', '1.2.3'), - ('1.2.3', '1-2-3'), - ('1.2.3', '1_2_3'), - ('1.2.3', '1:2:3'), - ('1.2.3', 'foo-1-2-3-bar'), - ] - for cpe_version, dep_version in fuzzy_version_matches: - self.assertTrue( - self.cpe_match( - f'cpe:2.3:a:foo:bar:{cpe_version}', 'cpe:2.3:a:foo:bar:*', version=dep_version)) - fuzzy_version_no_matches = [ - ('2020-03-05', '2020-3.5'), - ('2020-03-05', '2020--03-05'), - ('1.2.3', '1@2@3'), - ('1.2.3', '1..2.3'), - ] - for cpe_version, dep_version in fuzzy_version_no_matches: - self.assertFalse( - self.cpe_match( - f'cpe:2.3:a:foo:bar:{cpe_version}', 'cpe:2.3:a:foo:bar:*', version=dep_version)) - - def build_cve(self, cve_id, cpes, published_date): - return cve_scan.Cve( - cve_id, - description=None, - cpes=cpes, - score=None, - severity=None, - published_date=dt.date.fromisoformat(published_date), - last_modified_date=None) - - def cve_match(self, cve_id, cpes, published_date, dep_cpe_str, version=None, release_date=None): - return cve_scan.cve_match( - self.build_cve(cve_id, cpes=cpes, published_date=published_date), - self.build_dep(dep_cpe_str, version=version, release_date=release_date)) - - def test_cve_match(self): - # Empty CPEs, no match - self.assertFalse(self.cve_match('CVE-2020-123', set(), '2020-05-03', 'cpe:2.3:a:foo:bar:*')) - # Wildcard version, stale dependency match - self.assertTrue( - self.cve_match( - 'CVE-2020-123', - set([self.build_cpe('cpe:2.3:a:foo:bar:*')]), - '2020-05-03', - 'cpe:2.3:a:foo:bar:*', - release_date='2020-05-02')) - self.assertTrue( - self.cve_match( - 'CVE-2020-123', - set([self.build_cpe('cpe:2.3:a:foo:bar:*')]), - '2020-05-03', - 'cpe:2.3:a:foo:bar:*', - release_date='2020-05-03')) - # Wildcard version, recently updated - self.assertFalse( - self.cve_match( - 'CVE-2020-123', - set([self.build_cpe('cpe:2.3:a:foo:bar:*')]), - '2020-05-03', - 'cpe:2.3:a:foo:bar:*', - release_date='2020-05-04')) - # Version match - self.assertTrue( - self.cve_match( - 'CVE-2020-123', - set([self.build_cpe('cpe:2.3:a:foo:bar:1.2.3')]), - '2020-05-03', - 'cpe:2.3:a:foo:bar:*', - version='1.2.3')) - # Version mismatch - self.assertFalse( - self.cve_match( - 'CVE-2020-123', - set([self.build_cpe('cpe:2.3:a:foo:bar:1.2.3')]), - '2020-05-03', - 'cpe:2.3:a:foo:bar:*', - version='1.2.4', - release_date='2020-05-02')) - # Multiple CPEs, match first, don't match later. - self.assertTrue( - self.cve_match( - 'CVE-2020-123', - set([ - self.build_cpe('cpe:2.3:a:foo:bar:1.2.3'), - self.build_cpe('cpe:2.3:a:foo:baz:3.2.1') - ]), - '2020-05-03', - 'cpe:2.3:a:foo:bar:*', - version='1.2.3')) - - def test_cve_scan(self): - cves = { - 'CVE-2020-1234': - self.build_cve( - 'CVE-2020-1234', - set([ - self.build_cpe('cpe:2.3:a:foo:bar:1.2.3'), - self.build_cpe('cpe:2.3:a:foo:baz:3.2.1') - ]), '2020-05-03'), - 'CVE-2020-1235': - self.build_cve( - 'CVE-2020-1235', - set([ - self.build_cpe('cpe:2.3:a:foo:bar:1.2.3'), - self.build_cpe('cpe:2.3:a:foo:baz:3.2.1') - ]), '2020-05-03'), - 'CVE-2020-1236': - self.build_cve( - 'CVE-2020-1236', set([ - self.build_cpe('cpe:2.3:a:foo:wat:1.2.3'), - ]), '2020-05-03'), - } - cpe_revmap = { - 'cpe:2.3:a:foo:*:*': ['CVE-2020-1234', 'CVE-2020-1235', 'CVE-2020-1236'], - } - cve_allowlist = ['CVE-2020-1235'] - repository_locations = { - 'bar': self.build_dep('cpe:2.3:a:foo:bar:*', version='1.2.3'), - 'baz': self.build_dep('cpe:2.3:a:foo:baz:*', version='3.2.1'), - 'foo': self.build_dep('cpe:2.3:a:foo:*:*', version='1.2.3'), - 'blah': self.build_dep('N/A'), - } - possible_cves, cve_deps = cve_scan.cve_scan( - cves, cpe_revmap, cve_allowlist, repository_locations) - self.assertListEqual(sorted(possible_cves.keys()), ['CVE-2020-1234', 'CVE-2020-1236']) - self.assertDictEqual( - cve_deps, { - 'CVE-2020-1234': ['bar', 'baz', 'foo'], - 'CVE-2020-1236': ['foo'] - }) - - -if __name__ == '__main__': - unittest.main() diff --git a/tools/dependency/release_dates.py b/tools/dependency/release_dates.py index aed102107189..a0d1c5484a94 100644 --- a/tools/dependency/release_dates.py +++ b/tools/dependency/release_dates.py @@ -24,6 +24,7 @@ import utils from colorama import Fore, Style from packaging import version +from packaging.version import parse as parse_version # Tag issues created with these labels. LABELS = ['dependencies', 'area/build', 'no stalebot'] @@ -60,16 +61,33 @@ def format_utc_date(date): return date.date().isoformat() +# Get the chronologically latest release from a github repo +def get_latest_release(repo, version_min): + current_version = parse_version(version_min) + latest_version = current_version + latest_release = None + for release in repo.get_releases(): + if release.prerelease: + continue + version = parse_version(release.tag_name) + if not version: + continue + if version >= latest_version: + latest_release = release + latest_version = version + return latest_release + + # Obtain latest release version and compare against metadata version, warn on # mismatch. def verify_and_print_latest_release(dep, repo, metadata_version, release_date, create_issue=False): try: - latest_release = repo.get_latest_release() + latest_release = get_latest_release(repo, metadata_version) except github.GithubException as err: # Repositories can not have releases or if they have releases may not publish a latest releases. Return print(f'GithubException {repo.name}: {err.data} {err.status} while getting latest release.') return - if latest_release.created_at > release_date and latest_release.tag_name != metadata_version: + if latest_release and latest_release.created_at > release_date and latest_release.tag_name != metadata_version: print( f'{Fore.YELLOW}*WARNING* {dep} has a newer release than {metadata_version}@<{release_date}>: ' f'{latest_release.tag_name}@<{latest_release.created_at}>{Style.RESET_ALL}') @@ -203,29 +221,27 @@ def verify_and_print_release_date(dep, github_release_date, metadata_release_dat # Extract release date from GitHub API for tagged releases. def get_tagged_release_date(repo, metadata_version, github_release): - try: - latest = repo.get_latest_release() + latest = get_latest_release(repo, github_release.version) + if latest: + release = repo.get_release(github_release.version) + return release.published_at except github.GithubException as err: # Repositories can not have releases or if they have releases may not publish a latest releases. If this is the case we keep going latest = '' print(f'GithubException {repo.name}: {err.data} {err.status} while getting latest release.') - if latest and github_release.version <= latest.tag_name: - release = repo.get_release(github_release.version) - return release.published_at - else: - tags = repo.get_tags() - current_metadata_tag_commit_date = '' - for tag in tags.reversed: - if tag.name == github_release.version: - current_metadata_tag_commit_date = tag.commit.commit.committer.date - if not version.parse(tag.name).is_prerelease and version.parse( - tag.name) > version.parse(github_release.version): - print( - f'{Fore.YELLOW}*WARNING* {repo.name} has a newer release than {github_release.version}@<{current_metadata_tag_commit_date}>: ' - f'{tag.name}@<{tag.commit.commit.committer.date}>{Style.RESET_ALL}') - return current_metadata_tag_commit_date + tags = repo.get_tags() + current_metadata_tag_commit_date = '' + for tag in tags.reversed: + if tag.name == github_release.version: + current_metadata_tag_commit_date = tag.commit.commit.committer.date + if not version.parse(tag.name).is_prerelease and version.parse(tag.name) > version.parse( + github_release.version): + print( + f'{Fore.YELLOW}*WARNING* {repo.name} has a newer release than {github_release.version}@<{current_metadata_tag_commit_date}>: ' + f'{tag.name}@<{tag.commit.commit.committer.date}>{Style.RESET_ALL}') + return current_metadata_tag_commit_date # Extract release date from GitHub API for untagged releases. diff --git a/tools/dependency/requirements.txt b/tools/dependency/requirements.txt index 218ffecf8af5..3ce2f7049151 100644 --- a/tools/dependency/requirements.txt +++ b/tools/dependency/requirements.txt @@ -1,55 +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.5.30 \ - --hash=sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee \ - --hash=sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8 +certifi==2021.10.8 \ + --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 \ + --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569 # via requests -cffi==1.14.5 \ - --hash=sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813 \ - --hash=sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06 \ - --hash=sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea \ - --hash=sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee \ - --hash=sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396 \ - --hash=sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73 \ - --hash=sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315 \ - --hash=sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1 \ - --hash=sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49 \ - --hash=sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892 \ - --hash=sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482 \ - --hash=sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058 \ - --hash=sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5 \ - --hash=sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53 \ - --hash=sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045 \ - --hash=sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3 \ - --hash=sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5 \ - --hash=sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e \ - --hash=sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c \ - --hash=sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369 \ - --hash=sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827 \ - --hash=sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053 \ - --hash=sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa \ - --hash=sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4 \ - --hash=sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322 \ - --hash=sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132 \ - --hash=sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62 \ - --hash=sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa \ - --hash=sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0 \ - --hash=sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396 \ - --hash=sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e \ - --hash=sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991 \ - --hash=sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6 \ - --hash=sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1 \ - --hash=sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406 \ - --hash=sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d \ - --hash=sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c +cffi==1.15.0 \ + --hash=sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3 \ + --hash=sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2 \ + --hash=sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636 \ + --hash=sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20 \ + --hash=sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728 \ + --hash=sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27 \ + --hash=sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66 \ + --hash=sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443 \ + --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: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:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382 \ + --hash=sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7 \ + --hash=sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc \ + --hash=sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997 \ + --hash=sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796 # via pynacl -charset-normalizer==2.0.6 \ - --hash=sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6 \ - --hash=sha256:5ec46d183433dcbd0ab716f2d7f29d8dee50505b3fdb40c6b985c7c4f5a3591f +charset-normalizer==2.0.9 \ + --hash=sha256:1eecaa09422db5be9e29d7fc65664e6c33bd06f9ced7838578ba40d58bdf3721 \ + --hash=sha256:b0b883e8e874edfdece9c28f314e3dd5badf067342e42fb162203335ae61aa2c # via requests colorama==0.4.4 \ --hash=sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b \ @@ -59,25 +72,25 @@ deprecated==1.2.13 \ --hash=sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d \ --hash=sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d # via pygithub -idna==2.10 \ - --hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 \ - --hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0 +idna==3.3 \ + --hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \ + --hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d # via requests -packaging==21.0 \ - --hash=sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7 \ - --hash=sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14 +packaging==21.3 \ + --hash=sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522 \ + --hash=sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb # 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 \ --hash=sha256:2caf0054ea079b71e539741ae56c5a95e073b81fa472ce222e81667381b9601b # via -r requirements.in -pyjwt==2.1.0 \ - --hash=sha256:934d73fbba91b0483d3857d1aff50e96b2a892384ee2c17417ed3203f173fca1 \ - --hash=sha256:fba44e7898bbca160a2b2b501f492824fc8382485d3a6f11ba5d0c1937ce6130 +pyjwt==2.3.0 \ + --hash=sha256:b888b4d56f06f6dcd777210c334e69c737be74755d3e5e9ee3fe67dc18a0ee41 \ + --hash=sha256:e0c4bb8d9f0af0c7f5b1ec4c5036309617d03d56932877f2f7a0beeb5318322f # via pygithub pynacl==1.4.0 \ --hash=sha256:06cbb4d9b2c4bd3c8dc0d267416aaed79906e7b33f114ddbf0911969794b1cc4 \ @@ -102,7 +115,9 @@ pynacl==1.4.0 \ pyparsing==2.4.7 \ --hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1 \ --hash=sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b - # via packaging + # via + # -r requirements.in + # packaging pytz==2021.3 \ --hash=sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c \ --hash=sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326 @@ -116,53 +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.1 \ - --hash=sha256:97f016514ceac524832e7d1bd41cf928b992ebe0324d59736f84ad5f4bbe0632 \ - --hash=sha256:0b2cbe418beeff3aadb3afc39a67d3f5f6a3eb020ceb5f2bcf56bef14b33629a \ - --hash=sha256:95c9fcfc326fdd3e2fd264e808f6474ca7ffd253feb3a505ee5ceb4d78216ef7 \ - --hash=sha256:db0daf2afca9f3b3a76e96ecb5f55ba82615ec584471d7aa27c1bdeb9e3888bb \ - --hash=sha256:1b46e4fe0f9efbfaf1ee82fc79f9cb044c69b67b181c58370440d396fe40736e \ - --hash=sha256:b0eed9b295039a619f64667f27cffbffcfc0559073d562700912ca6266bc8b28 \ - --hash=sha256:8a6ba1b00d07f5a90a2d2eb1804a42e2067a6145b7745a8297664a75a8a232ba \ - --hash=sha256:947a8d9d7829364e11eca88af18394713c8f98571cbc672b12545977d837f054 \ - --hash=sha256:6aa687da5565674c9696fafd2b8d44a04fb697ec2431af21c3def9cbedc4082a \ - --hash=sha256:7929ce97be2f7c49f454a6f8e014225e53cc3767fe48cce94b188de2225232ac \ - --hash=sha256:2d18618440df6bc072762625e9c843d32a7328347c321b89f8df3a7c4a72ce6c \ - --hash=sha256:cb0b12b365b054bee2a53078a67df81781be0686cc3f3ab8bbdd16b2e188570a \ - --hash=sha256:3816922f0941f1637869a04e25d1e5261dfa55cc6b39c73872cbf192ea562443 \ - --hash=sha256:b41ce8ee3825634e67883dd4dab336f95d0cc9d223fb7e224dcd36d66af93694 \ - --hash=sha256:d0ae90fd60c7473e437b0dd48ae323c11f631fe47c243056f9e7505d26e8e2f6 \ - --hash=sha256:f4377eda306b488255ea4336662cd9015a902d6dc2ed77a3e4c1e3b42387453a \ - --hash=sha256:bc42803987eb46b5fc67ec9a072df15a72ee9db61e3b7dd955d82581bf141f60 \ - --hash=sha256:04a00cef5d1b9e0e8db997816437b436e859106283c4771a40c4de4759344765 \ - --hash=sha256:836c73f53a0cefc7ba10c6f4a0d78894cb4876f56035fe500b029e0a1ae0ffe9 \ - --hash=sha256:6c241b4ef0744590ae0ee89305743977e478200cff961bdcc6b3d0530aea3377 \ - --hash=sha256:19b2c992668c9ca764899bae52987a04041ebc21859d2646db0b27e089c2fd6b \ - --hash=sha256:9d200716eb4bb1d73f47f3ccc4f98fdf979dcc82d752183828f1be2e332b6874 \ - --hash=sha256:77fef0bfdc612f5f30e43392a9f67dddaf4f48f299421bf25f910d0f47173f3d \ - --hash=sha256:b1137e6aef3ac267c2af7d3af0266ef3f8dd1e5cde67b8eac9fa3b94e7fa0ada \ - --hash=sha256:972099fa9cf4e43c255701c78ec5098c2fec4d6ea669a110b3414a158e772b0a \ - --hash=sha256:5dc6c8cfaf4ff2a4632f8f97d29f555d6951eb0f905d3d47b3fd69bddb653214 \ - --hash=sha256:f1e2cea943192e24070b65bda862901c02bdf7c6abcd66ef5381ad6511921067 \ - --hash=sha256:8a184c655bb41295a9b0c28745a1b762c0c86025e43808b7e814f9cedc6c563d \ - --hash=sha256:6b81913fdba96e286f0c6007eb61f0158e64a1941bfc72fee61b34a4f8f9877f \ - --hash=sha256:aa637733f1d599077522f6a1f0c6c40389aa90a44cba37afcefef26f8e53d28f \ - --hash=sha256:ec803c9d6e4ce037201132d903ff8b0dd26c9688be50ce4c77c420c076e78ff7 \ - --hash=sha256:8055f8cc9a80dc1db01f31af6399b83f597ec164f07b7251d2a1bf1c6c025190 \ - --hash=sha256:3658ae9c704906cab5865a00c1aa9e1fd3555074d1a4462fa1742d7fea8260ae \ - --hash=sha256:9f839c47698052ef5c2c094e21f8a06d0828aebe52d20cdb505faa318c62e886 \ - --hash=sha256:fd5320bf61a2e8d3b46d9e183323293c9a695df8f38c98d17c45e1846758f9a9 \ - --hash=sha256:e2eb4f38441b56698b4d40d48fd331e4e8a0477264785d08cbced63813d4bd29 \ - --hash=sha256:2f6fbea8936ba862425664fc689182a8ef50a6d88cd49f3cd073eccd3e78c930 \ - --hash=sha256:4f3f99bb8eed5d394bbb898c5191ed91ebf21187d52b2c45895733ae2798f373 \ - --hash=sha256:21c1710f61aa95b4be83a32b6d6facbb0efdfac22dee65e1caa72a83deed7cda \ - --hash=sha256:40fd2cebad4010787de4221ec27a650635eed3e49e4bbfa8244fc34836cc2457 \ - --hash=sha256:c803526c0d3fa426e06de379b4eb56102234f2dc3c3a24a500d7962a83ca6166 \ - --hash=sha256:e5a0727ea56de6e9a17693589bcf913d6bf1ec49f12d4671993321f3325fda4f \ - --hash=sha256:04312fbf51e9dd15261228e6b4bed0c0ed5723ccf986645d2c7308511dccba35 \ - --hash=sha256:bd705e341baccc3d1ef20e790b1f6383bd4ae92a77ba87a86ece8189fab8793c \ - --hash=sha256:909a80ce028821c7ad01bdcaa588126825931d177cdccd00b3545818d4a195ce +wrapt==1.13.3 \ + --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:2ebdde19cd3c8cdf8df3fc165bc7827334bc4e353465048b36f7deeae8ee0918 \ + --hash=sha256:43e69ffe47e3609a6aec0fe723001c60c65305784d964f5007d5b4fb1bc6bf33 \ + --hash=sha256:46f7f3af321a573fc0c3586612db4decb7eb37172af1bc6173d81f5b66c2e068 \ + --hash=sha256:47f0a183743e7f71f29e4e21574ad3fa95676136f45b91afcf83f6a050914829 \ + --hash=sha256:498e6217523111d07cd67e87a791f5e9ee769f9241fcf8a379696e25806965af \ + --hash=sha256:4b9c458732450ec42578b5642ac53e312092acf8c0bfce140ada5ca1ac556f79 \ + --hash=sha256:51799ca950cfee9396a87f4a1240622ac38973b6df5ef7a41e7f0b98797099ce \ + --hash=sha256:5601f44a0f38fed36cc07db004f0eedeaadbdcec90e4e90509480e7e6060a5bc \ + --hash=sha256:5f223101f21cfd41deec8ce3889dc59f88a59b409db028c469c9b20cfeefbe36 \ + --hash=sha256:610f5f83dd1e0ad40254c306f4764fcdc846641f120c3cf424ff57a19d5f7ade \ + --hash=sha256:6a03d9917aee887690aa3f1747ce634e610f6db6f6b332b35c2dd89412912bca \ + --hash=sha256:705e2af1f7be4707e49ced9153f8d72131090e52be9278b5dbb1498c749a1e32 \ + --hash=sha256:766b32c762e07e26f50d8a3468e3b4228b3736c805018e4b0ec8cc01ecd88125 \ + --hash=sha256:77416e6b17926d953b5c666a3cb718d5945df63ecf922af0ee576206d7033b5e \ + --hash=sha256:778fd096ee96890c10ce96187c76b3e99b2da44e08c9e24d5652f356873f6709 \ + --hash=sha256:78dea98c81915bbf510eb6a3c9c24915e4660302937b9ae05a0947164248020f \ + --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:96b81ae75591a795d8c90edc0bfaab44d3d41ffc1aae4d994c5aa21d9b8e19a2 \ + --hash=sha256:981da26722bebb9247a0601e2922cedf8bb7a600e89c852d063313102de6f2cb \ + --hash=sha256:ae9de71eb60940e58207f8e71fe113c639da42adb02fb2bcbcaccc1ccecd092b \ + --hash=sha256:b73d4b78807bd299b38e4598b8e7bd34ed55d480160d2e7fdaabd9931afa65f9 \ + --hash=sha256:d4a5f6146cfa5c7ba0134249665acd322a70d1ea61732723c7d3e8cc0fa80755 \ + --hash=sha256:dd91006848eb55af2159375134d724032a2d1d13bcc6f81cd8d3ed9f2b8e846c \ + --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 diff --git a/tools/extensions/extensions_check.py b/tools/extensions/extensions_check.py index c9c204050f38..313206572d12 100644 --- a/tools/extensions/extensions_check.py +++ b/tools/extensions/extensions_check.py @@ -55,7 +55,7 @@ "envoy.stats_sinks", "envoy.thrift_proxy.filters", "envoy.tracers", "envoy.sip_proxy.filters", "envoy.transport_sockets.downstream", "envoy.transport_sockets.upstream", "envoy.tls.cert_validator", "envoy.upstreams", "envoy.wasm.runtime", "envoy.common.key_value", - "envoy.network.dns_resolver", "envoy.rbac.matchers") + "envoy.network.dns_resolver", "envoy.rbac.matchers", "envoy.access_loggers.extension_filters") EXTENSION_STATUS_VALUES = ( # This extension is stable and is expected to be production usable. diff --git a/tools/gen_compilation_database.py b/tools/gen_compilation_database.py index 46d23fdef933..983d489936dd 100755 --- a/tools/gen_compilation_database.py +++ b/tools/gen_compilation_database.py @@ -8,7 +8,7 @@ from pathlib import Path -# This method is equivalent to https://github.com/grailbio/bazel-compilation-database/blob/master/generate.sh +# This method is equivalent to https://github.com/grailbio/bazel-compilation-database/blob/master/generate.py def generate_compilation_database(args): # We need to download all remote outputs for generated source code. This option lives here to override those # specified in bazelrc. @@ -25,11 +25,19 @@ def generate_compilation_database(args): execroot = subprocess.check_output(["bazel", "info", "execution_root"] + bazel_options).decode().strip() - compdb = [] - for compdb_file in Path(execroot).glob("**/*.compile_commands.json"): - compdb.extend( - json.loads("[" + compdb_file.read_text().replace("__EXEC_ROOT__", execroot) + "]")) - return compdb + db_entries = [] + for db in Path(execroot).glob('**/*.compile_commands.json'): + db_entries.extend(json.loads(db.read_text())) + + def replace_execroot_marker(db_entry): + if 'directory' in db_entry and db_entry['directory'] == '__EXEC_ROOT__': + db_entry['directory'] = execroot + if 'command' in db_entry: + db_entry['command'] = ( + db_entry['command'].replace('-isysroot __BAZEL_XCODE_SDKROOT__', '')) + return db_entry + + return list(map(replace_execroot_marker, db_entries)) def is_header(filename): diff --git a/tools/proto_format/proto_format.sh b/tools/proto_format/proto_format.sh index d6ae826618ef..cf5f36ed9ef5 100755 --- a/tools/proto_format/proto_format.sh +++ b/tools/proto_format/proto_format.sh @@ -35,8 +35,8 @@ if [[ "$1" == "freeze" ]]; then fi # Invoke protoxform aspect. -bazel build "${BAZEL_BUILD_OPTIONS[@]}" --//tools/api_proto_plugin:default_type_db_target=@envoy_api//versioning:active_protos ${FREEZE_ARG} \ - @envoy_api//versioning:active_protos --aspects //tools/protoxform:protoxform.bzl%protoxform_aspect --output_groups=proto +bazel build "${BAZEL_BUILD_OPTIONS[@]}" --//tools/api_proto_plugin:default_type_db_target=@envoy_api//:all_protos ${FREEZE_ARG} \ + @envoy_api//versioning:active_protos @envoy_api//versioning:frozen_protos --aspects //tools/protoxform:protoxform.bzl%protoxform_aspect --output_groups=proto # Find all source protos. PROTO_TARGETS=() diff --git a/tools/proto_format/proto_sync.py b/tools/proto_format/proto_sync.py index 8d878309c698..4d7c0fda000c 100755 --- a/tools/proto_format/proto_sync.py +++ b/tools/proto_format/proto_sync.py @@ -56,78 +56,6 @@ api_proto_package($fields) """) -IGNORED_V2_PROTOS = [ - "envoy/config/accesslog/v2", - "envoy/config/cluster/aggregate/v2alpha", - "envoy/config/cluster/dynamic_forward_proxy/v2alpha", - "envoy/config/cluster/redis", - "envoy/config/common/dynamic_forward_proxy/v2alpha", - "envoy/config/common/tap/v2alpha", - "envoy/config/filter/dubbo/router/v2alpha1", - "envoy/config/filter/http/adaptive_concurrency/v2alpha", - "envoy/config/filter/http/aws_lambda/v2alpha", - "envoy/config/filter/http/aws_request_signing/v2alpha", - "envoy/config/filter/http/buffer/v2", - "envoy/config/filter/http/cache/v2alpha", - "envoy/config/filter/http/compressor/v2", - "envoy/config/filter/http/cors/v2", - "envoy/config/filter/http/csrf/v2", - "envoy/config/filter/http/dynamic_forward_proxy/v2alpha", - "envoy/config/filter/http/dynamo/v2", - "envoy/config/filter/http/ext_authz/v2", - "envoy/config/filter/http/fault/v2", - "envoy/config/filter/http/grpc_http1_bridge/v2", - "envoy/config/filter/http/grpc_http1_reverse_bridge/v2alpha1", - "envoy/config/filter/http/grpc_stats/v2alpha", - "envoy/config/filter/http/grpc_web/v2", - "envoy/config/filter/http/gzip/v2", - "envoy/config/filter/http/header_to_metadata/v2", - "envoy/config/filter/http/health_check/v2", - "envoy/config/filter/http/ip_tagging/v2", - "envoy/config/filter/http/jwt_authn/v2alpha", - "envoy/config/filter/http/lua/v2", - "envoy/config/filter/http/on_demand/v2", - "envoy/config/filter/http/original_src/v2alpha1", - "envoy/config/filter/http/rate_limit/v2", - "envoy/config/filter/http/rbac/v2", - "envoy/config/filter/http/router/v2", - "envoy/config/filter/http/squash/v2", - "envoy/config/filter/http/tap/v2alpha", - "envoy/config/filter/http/transcoder/v2", - "envoy/config/filter/listener/http_inspector/v2", - "envoy/config/filter/listener/original_dst/v2", - "envoy/config/filter/listener/original_src/v2alpha1", - "envoy/config/filter/listener/proxy_protocol/v2", - "envoy/config/filter/listener/tls_inspector/v2", - "envoy/config/filter/network/client_ssl_auth/v2", - "envoy/config/filter/network/direct_response/v2", - "envoy/config/filter/network/dubbo_proxy/v2alpha1", - "envoy/config/filter/network/echo/v2", - "envoy/config/filter/network/ext_authz/v2", - "envoy/config/filter/network/kafka_broker/v2alpha1", - "envoy/config/filter/network/local_rate_limit/v2alpha", - "envoy/config/filter/network/mongo_proxy/v2", - "envoy/config/filter/network/mysql_proxy/v1alpha1", - "envoy/config/filter/network/rate_limit/v2", - "envoy/config/filter/network/rbac/v2", - "envoy/config/filter/network/sni_cluster/v2", - "envoy/config/filter/network/zookeeper_proxy/v1alpha1", - "envoy/config/filter/thrift/rate_limit/v2alpha1", - "envoy/config/filter/udp/udp_proxy/v2alpha", - "envoy/config/grpc_credential/v2alpha", - "envoy/config/ratelimit/v2", - "envoy/config/rbac/v2", - "envoy/config/retry/omit_host_metadata/v2", - "envoy/config/retry/previous_priorities", - "envoy/config/transport_socket/raw_buffer/v2", - "envoy/config/transport_socket/tap/v2alpha", - "envoy/data/cluster/v2alpha", - "envoy/data/dns/v2alpha", - "envoy/data/core/v2alpha", - "envoy/service/event_reporting/v2alpha", - "envoy/service/trace/v2", -] - IMPORT_REGEX = re.compile('import "(.*)";') SERVICE_REGEX = re.compile('service \w+ {') PACKAGE_REGEX = re.compile('\npackage: "([^"]*)"') @@ -182,7 +110,7 @@ def get_destination_path(src): dst_path = pathlib.Path('contrib').joinpath(dst_path) # Non-contrib can not use alpha. if not 'contrib' in src: - if not 'v2alpha' in package and 'alpha' in package: + if (not 'v2alpha' in package and not 'v1alpha1' in package) and 'alpha' in package: raise ProtoSyncError( "package '{}' uses an alpha namespace. This is not allowed. Instead mark with " "(xds.annotations.v3.file_status).work_in_progress or related annotation.".format( @@ -294,28 +222,6 @@ def get_import_deps(proto_path): return imports -def get_previous_message_type_deps(proto_path): - """Obtain the Bazel dependencies for the previous version of messages in a .proto file. - - We need to link in earlier proto descriptors to support Envoy reflection upgrades. - - Args: - proto_path: path to .proto. - - Returns: - A list of Bazel targets reflecting the previous message types in the .proto at proto_path. - """ - contents = pathlib.Path(proto_path).read_text(encoding='utf8') - matches = re.findall(PREVIOUS_MESSAGE_TYPE_REGEX, contents) - deps = [] - for m in matches: - pkg = get_directory_from_package(m) - if pkg in IGNORED_V2_PROTOS: - continue - deps.append('//%s:pkg' % pkg) - return deps - - def has_services(proto_path): """Does a .proto file have any service definitions? @@ -347,10 +253,7 @@ def build_file_contents(root, files): Returns: A string containing the canonical BUILD file content for root. """ - import_deps = set(sum([get_import_deps(os.path.join(root, f)) for f in files], [])) - history_deps = set( - sum([get_previous_message_type_deps(os.path.join(root, f)) for f in files], [])) - deps = import_deps.union(history_deps) + deps = set(sum([get_import_deps(os.path.join(root, f)) for f in files], [])) _has_services = any(has_services(os.path.join(root, f)) for f in files) fields = [] if _has_services: @@ -396,14 +299,12 @@ def generate_current_api_dir(api_dir, dst_dir): dst = dst_dir.joinpath("envoy") shutil.copytree(str(api_dir.joinpath("envoy")), str(dst)) - for p in dst.glob('**/*.md'): - p.unlink() # envoy.service.auth.v2alpha exist for compatibility while we don't run in protoxform # so we ignore it here. shutil.rmtree(str(dst.joinpath("service", "auth", "v2alpha"))) - for proto in IGNORED_V2_PROTOS: - shutil.rmtree(str(dst.joinpath(proto[6:]))) + for p in dst.glob('**/*.md'): + p.unlink() def git_status(path): diff --git a/tools/protoxform/protoprint.py b/tools/protoxform/protoprint.py index 5bcefd650702..cbb05dda5b07 100755 --- a/tools/protoxform/protoprint.py +++ b/tools/protoxform/protoprint.py @@ -214,6 +214,17 @@ def camel_case(s): options.java_multiple_files = True options.java_package = 'io.envoyproxy.' + file_proto.package + # Workaround packages in generated go code conflicting by transforming: + # foo/bar/v2 to use barv2 as the package in the generated code + golang_package_name = "" + if file_proto.package.split(".")[-1] in ("v2", "v3"): + name = "".join(file_proto.package.split(".")[-2:]) + golang_package_name = ";" + name + options.go_package = "".join([ + "github.com/envoyproxy/go-control-plane/", + file_proto.package.replace(".", "/"), golang_package_name + ]) + # This is a workaround for C#/Ruby namespace conflicts between packages and # objects, see https://github.com/envoyproxy/envoy/pull/3854. # TODO(htuch): remove once v3 fixes this naming issue in diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index 3690e69896a1..5e45a3f034b5 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -11,6 +11,7 @@ ALS AMZ APC API +ARRAYSIZE ARN ASAN ASCII @@ -148,7 +149,6 @@ GSS GTEST GURL Grabbit -Hashable HC HCM HDS @@ -206,6 +206,7 @@ MD MERCHANTABILITY Merkle MGET +MPTCP MQ MSDN MSET @@ -257,6 +258,7 @@ PEM PERF PGV PID +PKCS12 PKTINFO PNG Pointwise @@ -402,6 +404,7 @@ absl accesslog accessor accessors +acked ackless acks acls @@ -579,6 +582,7 @@ dedup dedupe deduplicate deduplicates +deduplication deflater deletable deleter @@ -641,6 +645,7 @@ evbuffer evbuffers evconnlistener evented +eventfd evwatch exe execlp @@ -705,6 +710,7 @@ handshaker hardcoded hardcodes hardcoding +hashable hasher hashtagging hd @@ -772,6 +778,7 @@ keyder kqueue kubernetes kv +kvlist kvs lala latencies @@ -821,6 +828,7 @@ megamiss mem memcmp memcpy +memset memoize mergeable messagename @@ -864,6 +872,7 @@ nan nanos natively ndk +negative netblock netblocks netfilter @@ -1009,6 +1018,7 @@ rebalancer rebalancing rebuffer rebuilder +receival reconnection recurse recv @@ -1104,6 +1114,7 @@ siginfo signalstack siloed sim +sinked sizeof smatch snapshotted @@ -1312,3 +1323,4 @@ ep suri transid routable +vhosts diff --git a/tools/testdata/protoxform/envoy/v2/discovery_service.proto.active_or_frozen.gold b/tools/testdata/protoxform/envoy/v2/discovery_service.proto.active_or_frozen.gold index 40d597ad8cd2..4164044f84aa 100644 --- a/tools/testdata/protoxform/envoy/v2/discovery_service.proto.active_or_frozen.gold +++ b/tools/testdata/protoxform/envoy/v2/discovery_service.proto.active_or_frozen.gold @@ -13,6 +13,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.v2"; option java_outer_classname = "DiscoveryServiceProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/v2;envoyv2"; option java_generic_services = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/tools/testdata/protoxform/envoy/v2/fully_qualified_names.proto.active_or_frozen.gold b/tools/testdata/protoxform/envoy/v2/fully_qualified_names.proto.active_or_frozen.gold index 55d7af193dcf..83597051599a 100644 --- a/tools/testdata/protoxform/envoy/v2/fully_qualified_names.proto.active_or_frozen.gold +++ b/tools/testdata/protoxform/envoy/v2/fully_qualified_names.proto.active_or_frozen.gold @@ -15,6 +15,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.v2"; option java_outer_classname = "FullyQualifiedNamesProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/v2;envoyv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.external.v3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/tools/testdata/protoxform/envoy/v2/oneof.proto.active_or_frozen.gold b/tools/testdata/protoxform/envoy/v2/oneof.proto.active_or_frozen.gold index 19d79b993af8..03d345a51a84 100644 --- a/tools/testdata/protoxform/envoy/v2/oneof.proto.active_or_frozen.gold +++ b/tools/testdata/protoxform/envoy/v2/oneof.proto.active_or_frozen.gold @@ -8,6 +8,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.v2"; option java_outer_classname = "OneofProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/v2;envoyv2"; option (udpa.annotations.file_status).package_version_status = ACTIVE; message OneofExample { diff --git a/tools/testdata/protoxform/envoy/v2/package_move.proto.active_or_frozen.gold b/tools/testdata/protoxform/envoy/v2/package_move.proto.active_or_frozen.gold index a8e7a9fce768..250dbbea8b92 100644 --- a/tools/testdata/protoxform/envoy/v2/package_move.proto.active_or_frozen.gold +++ b/tools/testdata/protoxform/envoy/v2/package_move.proto.active_or_frozen.gold @@ -8,6 +8,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.v2"; option java_outer_classname = "PackageMoveProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/v2;envoyv2"; option (udpa.annotations.file_migrate).move_to_package = "envoy.foo.v3"; option (udpa.annotations.file_status).package_version_status = ACTIVE; diff --git a/tools/testdata/protoxform/envoy/v2/sample.proto.active_or_frozen.gold b/tools/testdata/protoxform/envoy/v2/sample.proto.active_or_frozen.gold index c20df5a51637..d6d8e9491a84 100644 --- a/tools/testdata/protoxform/envoy/v2/sample.proto.active_or_frozen.gold +++ b/tools/testdata/protoxform/envoy/v2/sample.proto.active_or_frozen.gold @@ -9,6 +9,7 @@ import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.v2"; option java_outer_classname = "SampleProto"; option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/v2;envoyv2"; option (udpa.annotations.file_status).package_version_status = ACTIVE; enum SomeEnum { diff --git a/tools/type_whisperer/BUILD b/tools/type_whisperer/BUILD index 37eead8dc906..f83eecb2e3fc 100644 --- a/tools/type_whisperer/BUILD +++ b/tools/type_whisperer/BUILD @@ -56,7 +56,7 @@ py_binary( label_flag( name = "api_type_db_target", - build_setting_default = "@envoy_api//versioning:active_protos", + build_setting_default = "@envoy_api//:all_protos", visibility = ["//visibility:public"], )