diff --git a/.bazelrc b/.bazelrc index 36a2766787ef..51490a2493fb 100644 --- a/.bazelrc +++ b/.bazelrc @@ -359,7 +359,7 @@ build:compile-time-options --@envoy//source/extensions/filters/http/kill_request # Docker sandbox # NOTE: Update this from https://github.com/envoyproxy/envoy-build-tools/blob/main/toolchains/rbe_toolchains_config.bzl#L8 -build:docker-sandbox --experimental_docker_image=envoyproxy/envoy-build-ubuntu:0ca52447572ee105a4730da5e76fe47c9c5a7c64@sha256:d736c58f06f36848e7966752cc7e01519cc1b5101a178d5c6634807e8ac3deab +build:docker-sandbox --experimental_docker_image=envoyproxy/envoy-build-ubuntu:f94a38f62220a2b017878b790b6ea98a0f6c5f9c@sha256:2dd96b6f43c08ccabd5f4747fce5854f5f96af509b32e5cf6493f136e9833649 build:docker-sandbox --spawn_strategy=docker build:docker-sandbox --strategy=Javac=docker build:docker-sandbox --strategy=Closure=docker @@ -523,7 +523,7 @@ build:bes-envoy-engflow --bes_upload_mode=fully_async build:rbe-envoy-engflow --config=cache-envoy-engflow build:rbe-envoy-engflow --config=bes-envoy-engflow build:rbe-envoy-engflow --remote_executor=grpcs://morganite.cluster.engflow.com -build:rbe-envoy-engflow --remote_default_exec_properties=container-image=docker://docker.io/envoyproxy/envoy-build-ubuntu:0ca52447572ee105a4730da5e76fe47c9c5a7c64@sha256:d736c58f06f36848e7966752cc7e01519cc1b5101a178d5c6634807e8ac3deab +build:rbe-envoy-engflow --remote_default_exec_properties=container-image=docker://docker.io/envoyproxy/envoy-build-ubuntu:f94a38f62220a2b017878b790b6ea98a0f6c5f9c@sha256:2dd96b6f43c08ccabd5f4747fce5854f5f96af509b32e5cf6493f136e9833649 ############################################################################# # debug: Various Bazel debugging flags diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index f5687d40bd6d..e62b0b56fd5f 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/envoy-ci/envoy-build:0ca52447572ee105a4730da5e76fe47c9c5a7c64@sha256:0434d2f6a65895a00f8936fba83b7ff14d21e149dc15c9ac25a31a6fa018234f +FROM gcr.io/envoy-ci/envoy-build:f94a38f62220a2b017878b790b6ea98a0f6c5f9c@sha256:7adc40c09508f957624c4d2e0f5aeecb73a59207ee6ded53b107eac828c091b2 ARG USERNAME=vscode ARG USER_UID=501 diff --git a/.github/config.yml b/.github/config.yml index b3bdcd3985c7..18e83d891fee 100644 --- a/.github/config.yml +++ b/.github/config.yml @@ -2,11 +2,11 @@ agent-ubuntu: ubuntu-22.04 build-image: # Authoritative configuration for build image/s repo: envoyproxy/envoy-build-ubuntu - sha: d736c58f06f36848e7966752cc7e01519cc1b5101a178d5c6634807e8ac3deab - mobile-sha: 13674edd989ada8b94257f410e38e5e8e4c6a0a9c2fdf8bda9261047a7f843e4 + sha: 2dd96b6f43c08ccabd5f4747fce5854f5f96af509b32e5cf6493f136e9833649 + mobile-sha: c9b6001d0c24170ae77b0ad39e1b0fcda49580c72a8578755dc65aec9be85be3 # this is authoritative, but is not currently used in github ci - gcr-sha: 0434d2f6a65895a00f8936fba83b7ff14d21e149dc15c9ac25a31a6fa018234f - tag: 0ca52447572ee105a4730da5e76fe47c9c5a7c64 + gcr-sha: 7adc40c09508f957624c4d2e0f5aeecb73a59207ee6ded53b107eac828c091b2 + tag: f94a38f62220a2b017878b790b6ea98a0f6c5f9c config: envoy: @@ -150,27 +150,8 @@ checks: - publish - verify required: true - windows: - name: Envoy/Windows - required: true - on-run: - - build-windows run: - build-windows: - paths: - - .bazelrc - - .bazelversion - - .github/config.yml - - api/**/* - - bazel/**/* - - ci/**/* - - configs/**/* - - contrib/**/* - - envoy/**/* - - source/**/* - - test/**/* - - VERSION.txt build-macos: paths: - .bazelrc @@ -191,6 +172,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-android-all: @@ -200,6 +182,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py push: never @@ -210,6 +193,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-asan: @@ -219,6 +203,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-cc: @@ -228,6 +213,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-compile-time-cc: @@ -240,6 +226,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-coverage: @@ -249,12 +236,14 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-core: paths: - "**/*" - "*" + - mobile/.bazelrc mobile-format: paths: - .bazelrc @@ -262,6 +251,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-ios: @@ -271,6 +261,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-ios-all: @@ -280,6 +271,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py push: never @@ -290,6 +282,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-perf: @@ -299,6 +292,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-release-validation: @@ -308,6 +302,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py mobile-tsan: @@ -317,6 +312,7 @@ run: - .github/config.yml - bazel/external/quiche.BUILD - bazel/repository_locations.bzl + - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py precheck-deps: diff --git a/.github/workflows/_precheck_deps.yml b/.github/workflows/_precheck_deps.yml index 8864b7beada4..f1ac65e0a3e1 100644 --- a/.github/workflows/_precheck_deps.yml +++ b/.github/workflows/_precheck_deps.yml @@ -50,9 +50,9 @@ jobs: if: ${{ inputs.dependency-review }} steps: - name: Checkout Repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 with: ref: ${{ fromJSON(inputs.request).request.sha }} persist-credentials: false - name: Dependency Review - uses: actions/dependency-review-action@9129d7d40b8c12c1ed0f60400d00c92d437adcce # v4.1.3 + uses: actions/dependency-review-action@0fa40c3c10055986a88de3baa0d6ec17c5a894b3 # v4.2.3 diff --git a/.github/workflows/codeql-daily.yml b/.github/workflows/codeql-daily.yml index 0217ac0c4462..6f2a31997585 100644 --- a/.github/workflows/codeql-daily.yml +++ b/.github/workflows/codeql-daily.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - name: Free disk space uses: envoyproxy/toolshed/gh-actions/diskspace@actions-v0.2.27 diff --git a/.github/workflows/codeql-push.yml b/.github/workflows/codeql-push.yml index 91cfb8b2df1f..06e1f29a7260 100644 --- a/.github/workflows/codeql-push.yml +++ b/.github/workflows/codeql-push.yml @@ -32,7 +32,7 @@ jobs: if: github.repository == 'envoyproxy/envoy' steps: - name: Checkout repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 with: fetch-depth: 2 diff --git a/.github/workflows/envoy-dependency.yml b/.github/workflows/envoy-dependency.yml index 1aae9d60d58d..286b9f248ea1 100644 --- a/.github/workflows/envoy-dependency.yml +++ b/.github/workflows/envoy-dependency.yml @@ -143,7 +143,7 @@ jobs: path: envoy fetch-depth: 0 token: ${{ steps.appauth.outputs.token }} - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 name: Checkout Envoy build tools repository with: repository: envoyproxy/envoy-build-tools @@ -235,7 +235,7 @@ jobs: issues: write steps: - name: Checkout repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - name: Run dependency checker run: | TODAY_DATE=$(date -u -I"date") diff --git a/.github/workflows/envoy-macos.yml b/.github/workflows/envoy-macos.yml index 087aa8a3433d..f3d41dad8268 100644 --- a/.github/workflows/envoy-macos.yml +++ b/.github/workflows/envoy-macos.yml @@ -51,6 +51,7 @@ jobs: steps-post: steps-pre: ${{ matrix.steps-pre }} target: ${{ matrix.target }} + timeout-minutes: 90 trusted: ${{ fromJSON(needs.load.outputs.trusted) }} strategy: fail-fast: false diff --git a/.github/workflows/envoy-windows.yml b/.github/workflows/envoy-windows.yml deleted file mode 100644 index fbd01b992a95..000000000000 --- a/.github/workflows/envoy-windows.yml +++ /dev/null @@ -1,140 +0,0 @@ -name: Envoy/Windows - -permissions: - contents: read - -on: - workflow_run: - workflows: - - Request - types: - - completed - -concurrency: - group: ${{ github.head_ref || github.run_id }}-${{ github.workflow }} - cancel-in-progress: true - - -jobs: - load: - secrets: - app-key: ${{ secrets.ENVOY_CI_APP_KEY }} - app-id: ${{ secrets.ENVOY_CI_APP_ID }} - lock-app-key: ${{ secrets.ENVOY_CI_MUTEX_APP_KEY }} - lock-app-id: ${{ secrets.ENVOY_CI_MUTEX_APP_ID }} - permissions: - actions: read - contents: read - packages: read - pull-requests: read - if: ${{ github.event.workflow_run.conclusion == 'success' }} - uses: ./.github/workflows/_load.yml - with: - cache-docker: false - check-name: windows - - windows: - permissions: - contents: read - packages: read - secrets: - rbe-key: ${{ secrets.GCP_SERVICE_ACCOUNT_KEY }} - if: ${{ fromJSON(needs.load.outputs.request).run.build-windows }} - needs: - - load - uses: ./.github/workflows/_run.yml - name: CI ${{ matrix.name || matrix.target }} - with: - command: - request: ${{ needs.load.outputs.request }} - runs-on: envoy-win19-small - source: | - export ENVOY_SHARED_TMP_DIR="C:\Users\runner\AppData\Local\Temp\bazel-shared" - export ENVOY_DOCKER_BUILD_DIR="C:\Users\runner\AppData\Local\Temp" - mkdir -p "$ENVOY_SHARED_TMP_DIR" - GCP_SERVICE_ACCOUNT_KEY_PATH=$(mktemp -p "${ENVOY_SHARED_TMP_DIR}" -t gcp_service_account.XXXXXX.json) - bash -c "echo \"${RBE_KEY}\" | base64 --decode > \"${GCP_SERVICE_ACCOUNT_KEY_PATH}\"" - _BAZEL_BUILD_EXTRA_OPTIONS=( - --config=remote-ci - --config=rbe-google - --config=remote-msvc-cl - --google_credentials=${GCP_SERVICE_ACCOUNT_KEY_PATH} - --jobs=75 - --flaky_test_attempts=2) - export BAZEL_BUILD_EXTRA_OPTIONS=${_BAZEL_BUILD_EXTRA_OPTIONS[*]} - steps-post: - target: ${{ matrix.target }} - temp-dir: 'C:\Users\runner\AppData\Local\Temp\bazel-shared' - timeout-minutes: 120 - trusted: ${{ fromJSON(needs.load.outputs.trusted) }} - upload-name: windows.release - upload-path: 'C:\Users\runner\AppData\Local\Temp\envoy' - strategy: - fail-fast: false - matrix: - include: - - target: ci/windows_ci_steps.sh - name: Windows 2019 - - docker: - needs: - - load - - windows - strategy: - fail-fast: false - matrix: - include: - - target: windows2019 - name: Windows 2019 - runs-on: envoy-win19-small - build-type: windows - image-base: mcr.microsoft.com/windows/servercore - image-tag: ltsc2019 - - target: windows2022 - name: Windows 2022 - runs-on: envoy-win22-small - build-type: windows-ltsc2022 - image-base: mcr.microsoft.com/windows/nanoserver - image-tag: ltsc2022 - runs-on: ${{ matrix.runs-on }} - steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - ref: ${{ needs.load.outputs.repo_ref }} - - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 - with: - name: windows.release - - run: | - # Convert to Unix-style path so tar doesn't think drive letter is a hostname - STAGING_DIR="$(echo $PWD | tr -d ':' | tr '\\' '/')" - mkdir -p windows/amd64 && tar zxf "${STAGING_DIR}/envoy_binary.tar.gz" -C ./windows/amd64 - CI_SHA1=$(git rev-parse head) - export CI_SHA1 - ci/docker_ci.sh - shell: bash - env: - CI_BRANCH: ${{ github.ref }} - DOCKERHUB_USERNAME: ${{ fromJSON(needs.load.outputs.trusted) && secrets.DOCKERHUB_USERNAME || '' }} - DOCKERHUB_PASSWORD: ${{ fromJSON(needs.load.outputs.trusted) && secrets.DOCKERHUB_PASSWORD || '' }} - WINDOWS_BUILD_TYPE: ${{ matrix.build-type }} - WINDOWS_IMAGE_BASE: ${{ matrix.image-base }} - WINDOWS_IMAGE_TAG: ${{ matrix.image-tag }} - request: - permissions: - actions: read - contents: read - pull-requests: read - secrets: - app-id: ${{ secrets.ENVOY_CI_APP_ID }} - app-key: ${{ secrets.ENVOY_CI_APP_KEY }} - if: >- - ${{ always() - && github.event.workflow_run.conclusion == 'success' - && fromJSON(needs.load.outputs.request).run.build-windows }} - needs: - - load - - windows - - docker - uses: ./.github/workflows/_finish.yml - with: - needs: ${{ toJSON(needs) }} diff --git a/.github/workflows/mobile-ios_build.yml b/.github/workflows/mobile-ios_build.yml index f64d6a42f5d3..c31edc2ef7e8 100644 --- a/.github/workflows/mobile-ios_build.yml +++ b/.github/workflows/mobile-ios_build.yml @@ -106,7 +106,7 @@ jobs: app: //examples/swift/hello_world:app expected-status: 200 target: swift-hello-world - timeout-minutes: 50 + timeout-minutes: 90 apps: permissions: @@ -141,7 +141,7 @@ jobs: ANDROID_NDK_HOME: ANDROID_HOME: target: ${{ matrix.target }} - timeout-minutes: 50 + timeout-minutes: 90 trusted: ${{ fromJSON(needs.load.outputs.trusted) }} working-directory: mobile strategy: diff --git a/.github/workflows/mobile-perf.yml b/.github/workflows/mobile-perf.yml index 807f1eda861b..241199383f08 100644 --- a/.github/workflows/mobile-perf.yml +++ b/.github/workflows/mobile-perf.yml @@ -70,6 +70,7 @@ jobs: source/server/guarddog_impl.h source/server/watchdog_impl.h source/server/options_impl.cc + source/extensions/access_loggers/common/file_access_log_impl.h target: size-current - name: Main size args: >- diff --git a/.github/workflows/mobile-release.yml b/.github/workflows/mobile-release.yml index d051821814f0..007ac2b6b819 100644 --- a/.github/workflows/mobile-release.yml +++ b/.github/workflows/mobile-release.yml @@ -95,12 +95,12 @@ jobs: - output: envoy - output: envoy_xds steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 with: fetch-depth: 0 - name: Add safe directory run: git config --global --add safe.directory /__w/envoy/envoy - - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4 with: name: ${{ matrix.output }}_android_aar_sources path: . diff --git a/.github/workflows/mobile-traffic_director.yml b/.github/workflows/mobile-traffic_director.yml index 4458469f0636..b4fa71cf30bd 100644 --- a/.github/workflows/mobile-traffic_director.yml +++ b/.github/workflows/mobile-traffic_director.yml @@ -30,7 +30,7 @@ jobs: timeout-minutes: 120 steps: - name: Checkout repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - name: Add safe directory run: git config --global --add safe.directory /__w/envoy/envoy - name: 'Run GcpTrafficDirectorIntegrationTest' diff --git a/.github/workflows/pr_notifier.yml b/.github/workflows/pr_notifier.yml index 95083e527889..06e8ff695cc7 100644 --- a/.github/workflows/pr_notifier.yml +++ b/.github/workflows/pr_notifier.yml @@ -22,7 +22,7 @@ jobs: || !contains(github.actor, '[bot]')) }} steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - name: Notify about PRs run: | ARGS=() diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 8c4bb04b2ba0..589daf48cffe 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -21,7 +21,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 with: persist-credentials: false diff --git a/api/BUILD b/api/BUILD index e0efeebce4f7..613bcf064591 100644 --- a/api/BUILD +++ b/api/BUILD @@ -73,6 +73,7 @@ proto_library( visibility = ["//visibility:public"], deps = [ "//contrib/envoy/extensions/compression/qatzip/compressor/v3alpha:pkg", + "//contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha:pkg", "//contrib/envoy/extensions/filters/http/checksum/v3alpha:pkg", "//contrib/envoy/extensions/filters/http/dynamo/v3:pkg", "//contrib/envoy/extensions/filters/http/golang/v3alpha:pkg", @@ -82,6 +83,7 @@ proto_library( "//contrib/envoy/extensions/filters/network/client_ssl_auth/v3:pkg", "//contrib/envoy/extensions/filters/network/generic_proxy/action/v3:pkg", "//contrib/envoy/extensions/filters/network/generic_proxy/codecs/dubbo/v3:pkg", + "//contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3:pkg", "//contrib/envoy/extensions/filters/network/generic_proxy/codecs/kafka/v3:pkg", "//contrib/envoy/extensions/filters/network/generic_proxy/matcher/v3:pkg", "//contrib/envoy/extensions/filters/network/generic_proxy/router/v3:pkg", diff --git a/api/bazel/cc_proto_descriptor_library/builddefs.bzl b/api/bazel/cc_proto_descriptor_library/builddefs.bzl index 2da95d00a063..c38c20bd8a1a 100644 --- a/api/bazel/cc_proto_descriptor_library/builddefs.bzl +++ b/api/bazel/cc_proto_descriptor_library/builddefs.bzl @@ -335,7 +335,6 @@ cc_proto_descriptor_library_aspect = aspect( ) cc_proto_descriptor_library = rule( - output_to_genfiles = True, implementation = _cc_proto_descriptor_rule_impl, attrs = { "deps": attr.label_list( diff --git a/api/bazel/repository_locations.bzl b/api/bazel/repository_locations.bzl index 4ba96d3a72f0..b6212e61e8ac 100644 --- a/api/bazel/repository_locations.bzl +++ b/api/bazel/repository_locations.bzl @@ -39,9 +39,9 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_desc = "xDS API Working Group (xDS-WG)", project_url = "https://github.com/cncf/xds", # During the UDPA -> xDS migration, we aren't working with releases. - version = "3a472e524827f72d1ad621c4983dd5af54c46776", - sha256 = "dc305e20c9fa80822322271b50aa2ffa917bf4fd3973bcec52bfc28dc32c5927", - release_date = "2023-11-16", + version = "ee0267137e252710af66562e0d54bcf8669b74b1", + sha256 = "0adcd7a74d5158f710612f38e5b9ec8bd4aabd2f53ff7905e0c198028ca68dfc", + release_date = "2024-03-12", strip_prefix = "xds-{version}", urls = ["https://github.com/cncf/xds/archive/{version}.tar.gz"], use_category = ["api"], @@ -131,11 +131,11 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "buf", project_desc = "A new way of working with Protocol Buffers.", # Used for breaking change detection in API protobufs project_url = "https://buf.build", - version = "1.29.0", - sha256 = "1033f26361e6fc30ffcfab9d4e4274ffd4af88d9c97de63d2e1721c4a07c1380", + version = "1.30.0", + sha256 = "219f48fb1bb190e0f761e35cac0821dfd9c1b0dfda80d7aaf522347755d829ab", strip_prefix = "buf", urls = ["https://github.com/bufbuild/buf/releases/download/v{version}/buf-Linux-x86_64.tar.gz"], - release_date = "2024-01-24", + release_date = "2024-03-07", use_category = ["api"], license = "Apache-2.0", license_url = "https://github.com/bufbuild/buf/blob/v{version}/LICENSE", @@ -166,12 +166,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "envoy_toolshed", project_desc = "Tooling, libraries, runners and checkers for Envoy proxy's CI", project_url = "https://github.com/envoyproxy/toolshed", - version = "0.1.1", - sha256 = "ee759b57270a2747f3f2a3d6ecaad63b834dd9887505a9f1c919d72429dbeffd", + version = "0.1.2", + sha256 = "d3139c43a0097d694e86cd61690b596528ad874ef7a11e42bed932a96bc44618", strip_prefix = "toolshed-bazel-v{version}/bazel", urls = ["https://github.com/envoyproxy/toolshed/archive/bazel-v{version}.tar.gz"], use_category = ["build"], - release_date = "2023-10-21", + release_date = "2024-03-21", cpe = "N/A", license = "Apache-2.0", license_url = "https://github.com/envoyproxy/envoy/blob/bazel-v{version}/LICENSE", diff --git a/api/contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha/BUILD b/api/contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha/BUILD new file mode 100644 index 000000000000..29ebf0741406 --- /dev/null +++ b/api/contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha/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_xds//udpa/annotations:pkg"], +) diff --git a/api/contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha/qatzstd.proto b/api/contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha/qatzstd.proto new file mode 100644 index 000000000000..182722d01e4f --- /dev/null +++ b/api/contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha/qatzstd.proto @@ -0,0 +1,69 @@ +syntax = "proto3"; + +package envoy.extensions.compression.qatzstd.compressor.v3alpha; + +import "google/protobuf/wrappers.proto"; + +import "udpa/annotations/status.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.compression.qatzstd.compressor.v3alpha"; +option java_outer_classname = "QatzstdProto"; +option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha"; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Qatzstd Compressor] +// Qatzstd :ref:`configuration overview `. +// [#extension: envoy.compression.qatzstd.compressor] + +// [#next-free-field: 8] +message Qatzstd { + // Reference to http://facebook.github.io/zstd/zstd_manual.html + enum Strategy { + DEFAULT = 0; + FAST = 1; + DFAST = 2; + GREEDY = 3; + LAZY = 4; + LAZY2 = 5; + BTLAZY2 = 6; + BTOPT = 7; + BTULTRA = 8; + BTULTRA2 = 9; + } + + // Set compression parameters according to pre-defined compression level table. + // Note that exact compression parameters are dynamically determined, + // depending on both compression level and source content size (when known). + // Value 0 means default, and default level is 3. + // + // Setting a level does not automatically set all other compression parameters + // to default. Setting this will however eventually dynamically impact the compression + // parameters which have not been manually set. The manually set + // ones will 'stick'. + google.protobuf.UInt32Value compression_level = 1 [(validate.rules).uint32 = {lte: 22 gte: 1}]; + + // A 32-bits checksum of content is written at end of frame. If not set, defaults to false. + bool enable_checksum = 2; + + // The higher the value of selected strategy, the more complex it is, + // resulting in stronger and slower compression. + // + // Special: value 0 means "use default strategy". + Strategy strategy = 3 [(validate.rules).enum = {defined_only: true}]; + + // Value for compressor's next output buffer. If not set, defaults to 4096. + google.protobuf.UInt32Value chunk_size = 5 [(validate.rules).uint32 = {lte: 65536 gte: 4096}]; + + // Enable QAT to accelerate Zstd compression or not. If not set, defaults to false. + // + // This is useful in the case that users want to enable QAT for a period of time and disable QAT for another period of time, + // they don't have to change the config too much or prepare for another config that has software zstd compressor and just changing the value of this filed. + bool enable_qat_zstd = 6; + + // Fallback to software for Qatzstd when input size is less than this value. + // Valid only ``enable_qat_zstd`` is ``true``. 0 means no fallback at all. If not set, defaults to 4000. + google.protobuf.UInt32Value qat_zstd_fallback_threshold = 7 + [(validate.rules).uint32 = {lte: 65536 gte: 0}]; +} diff --git a/api/contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3/BUILD b/api/contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3/BUILD new file mode 100644 index 000000000000..d49202b74ab4 --- /dev/null +++ b/api/contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/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_xds//udpa/annotations:pkg", + "@com_github_cncf_xds//xds/annotations/v3:pkg", + ], +) diff --git a/api/contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3/http1.proto b/api/contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3/http1.proto new file mode 100644 index 000000000000..973a190d8138 --- /dev/null +++ b/api/contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3/http1.proto @@ -0,0 +1,49 @@ +syntax = "proto3"; + +package envoy.extensions.filters.network.generic_proxy.codecs.http1.v3; + +import "google/protobuf/wrappers.proto"; + +import "xds/annotations/v3/status.proto"; + +import "udpa/annotations/status.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.network.generic_proxy.codecs.http1.v3"; +option java_outer_classname = "Http1Proto"; +option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3;http1v3"; +option (udpa.annotations.file_status).package_version_status = ACTIVE; +option (xds.annotations.v3.file_status).work_in_progress = true; + +// [#protodoc-title: HTTP1 codec configuration for Generic Proxy] +// [#extension: envoy.generic_proxy.codecs.http1] + +// Configuration for HTTP codec. This HTTP1 codec is used to parse and serialize HTTP1 messages +// for the generic proxy filter. +// Any decoding error will result in the generic proxy closing the connection. +// +// .. note:: +// This codec only supports HTTP1.1 messages and does not support HTTP1.0 messages. And it limits +// part of the HTTP1.1 features, such as upgrade, connect, etc. +// This codec is mainly designed for the features evaluation of the generic proxy filter. Please +// be cautious when using it in production. +message Http1CodecConfig { + // If true, the codec will parse and serialize HTTP1 messages in a single frame per message. + // + // A frame is a minimal unit of data that can be processed by the generic proxy. If false, the + // codec will parse and serialize HTTP1 messages in a streaming way. In this case, the codec + // will output multiple frames for a single HTTP1 message to the generic proxy. + // If true, the codec will buffer the entire HTTP1 message body before sending it to the generic + // proxy. This may have better performance in small message scenarios and is more friendly to + // handle the HTTP1 message body. This also may result in higher memory usage and latency if + // the message body is large. + // + // Default is true. + google.protobuf.BoolValue single_frame_mode = 1; + + // The maximum size of the HTTP1 message body in bytes. If not set, 8*1024*1024 (8MB) is used. + // This only makes sense when single_frame_mode is true. + // If the HTTP1 message body size exceeds this value, this will result in a decoding error + // and the generic proxy will close the connection. + google.protobuf.UInt32Value max_buffer_size = 2; +} diff --git a/api/envoy/config/core/v3/health_check.proto b/api/envoy/config/core/v3/health_check.proto index 2ec258d8ac09..739d5db1e108 100644 --- a/api/envoy/config/core/v3/health_check.proto +++ b/api/envoy/config/core/v3/health_check.proto @@ -62,7 +62,7 @@ message HealthStatusSet { [(validate.rules).repeated = {items {enum {defined_only: true}}}]; } -// [#next-free-field: 26] +// [#next-free-field: 27] message HealthCheck { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.core.HealthCheck"; @@ -392,6 +392,11 @@ message HealthCheck { // The default value is false. bool always_log_health_check_failures = 19; + // If set to true, health check success events will always be logged. If set to false, only host addition event will be logged + // if it is the first successful health check, or if the healthy threshold is reached. + // The default value is false. + bool always_log_health_check_success = 26; + // This allows overriding the cluster TLS settings, just for health check connections. TlsOptions tls_options = 21; diff --git a/api/envoy/config/core/v3/protocol.proto b/api/envoy/config/core/v3/protocol.proto index 8a4e292af508..70af4851b8e7 100644 --- a/api/envoy/config/core/v3/protocol.proto +++ b/api/envoy/config/core/v3/protocol.proto @@ -486,10 +486,10 @@ message Http2ProtocolOptions { // Allows proxying Websocket and other upgrades over H2 connect. bool allow_connect = 5; - // [#not-implemented-hide:] Hiding until envoy has full metadata support. + // [#not-implemented-hide:] Hiding until Envoy has full metadata support. // Still under implementation. DO NOT USE. // - // Allows metadata. See [metadata + // Allows sending and receiving HTTP/2 METADATA frames. See [metadata // docs](https://github.com/envoyproxy/envoy/blob/main/source/docs/h2_metadata.md) for more // information. bool allow_metadata = 6; @@ -618,7 +618,7 @@ message GrpcProtocolOptions { } // A message which allows using HTTP/3. -// [#next-free-field: 6] +// [#next-free-field: 7] message Http3ProtocolOptions { QuicProtocolOptions quic_protocol_options = 1; @@ -637,6 +637,14 @@ message Http3ProtocolOptions { // `_ // Note that HTTP/3 CONNECT is not yet an RFC. bool allow_extended_connect = 5 [(xds.annotations.v3.field_status).work_in_progress = true]; + + // [#not-implemented-hide:] Hiding until Envoy has full metadata support. + // Still under implementation. DO NOT USE. + // + // Allows sending and receiving HTTP/3 METADATA frames. See [metadata + // docs](https://github.com/envoyproxy/envoy/blob/main/source/docs/h2_metadata.md) for more + // information. + bool allow_metadata = 6; } // A message to control transformations to the :scheme header diff --git a/api/envoy/data/core/v3/health_check_event.proto b/api/envoy/data/core/v3/health_check_event.proto index 17e78ea5ecb1..2f8c32d92caf 100644 --- a/api/envoy/data/core/v3/health_check_event.proto +++ b/api/envoy/data/core/v3/health_check_event.proto @@ -35,7 +35,7 @@ enum HealthCheckerType { THRIFT = 4; } -// [#next-free-field: 12] +// [#next-free-field: 13] message HealthCheckEvent { option (udpa.annotations.versioning).previous_message_type = "envoy.data.core.v2alpha.HealthCheckEvent"; @@ -55,6 +55,12 @@ message HealthCheckEvent { // Host addition. HealthCheckAddHealthy add_healthy_event = 5; + // A health check was successful. Note: a host will be considered healthy either if it is + // the first ever health check, or if the healthy threshold is reached. This kind of event + // indicate that a health check was successful, but does not indicates that the host is + // considered healthy. A host is considered healthy if HealthCheckAddHealthy kind of event is sent. + HealthCheckSuccessful successful_health_check_event = 12; + // Host failure. HealthCheckFailure health_check_failure_event = 7; @@ -93,6 +99,9 @@ message HealthCheckAddHealthy { bool first_check = 1; } +message HealthCheckSuccessful { +} + message HealthCheckFailure { option (udpa.annotations.versioning).previous_message_type = "envoy.data.core.v2alpha.HealthCheckFailure"; diff --git a/api/envoy/extensions/access_loggers/fluentd/v3/BUILD b/api/envoy/extensions/access_loggers/fluentd/v3/BUILD index 29ebf0741406..09a37ad16b83 100644 --- a/api/envoy/extensions/access_loggers/fluentd/v3/BUILD +++ b/api/envoy/extensions/access_loggers/fluentd/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_xds//udpa/annotations:pkg"], + deps = [ + "//envoy/config/core/v3:pkg", + "@com_github_cncf_xds//udpa/annotations:pkg", + ], ) diff --git a/api/envoy/extensions/access_loggers/fluentd/v3/fluentd.proto b/api/envoy/extensions/access_loggers/fluentd/v3/fluentd.proto index e6b2adfdc9c0..ce68c79e9d91 100644 --- a/api/envoy/extensions/access_loggers/fluentd/v3/fluentd.proto +++ b/api/envoy/extensions/access_loggers/fluentd/v3/fluentd.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package envoy.extensions.access_loggers.fluentd.v3; +import "envoy/config/core/v3/backoff.proto"; + import "google/protobuf/duration.proto"; import "google/protobuf/struct.proto"; import "google/protobuf/wrappers.proto"; @@ -22,8 +24,19 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // the Fluentd Forward Protocol as described in: `Fluentd Forward Protocol Specification // `_. // [#extension: envoy.access_loggers.fluentd] -// [#next-free-field: 7] +// [#next-free-field: 8] message FluentdAccessLogConfig { + message RetryOptions { + // The number of times the logger will attempt to connect to the upstream during reconnects. + // By default, there is no limit. The logger will attempt to reconnect to the upstream each time + // connecting to the upstream failed or the upstream connection had been closed for any reason. + google.protobuf.UInt32Value max_connect_attempts = 1; + + // Sets the backoff strategy. If this value is not set, the default base backoff interval is 500 + // milliseconds and the default max backoff interval is 5 seconds (10 times the base interval). + config.core.v3.BackoffStrategy backoff_options = 2; + } + // The upstream cluster to connect to for streaming the Fluentd messages. string cluster = 1 [(validate.rules).string = {min_len: 1}]; @@ -67,4 +80,9 @@ message FluentdAccessLogConfig { // "message": "My error message" // } google.protobuf.Struct record = 6 [(validate.rules).message = {required: true}]; + + // Optional retry, in case upstream connection has failed. If this field is not set, the default values will be applied, + // as specified in the :ref:`RetryOptions ` + // configuration. + RetryOptions retry_options = 7; } diff --git a/api/envoy/extensions/filters/http/json_to_metadata/v3/BUILD b/api/envoy/extensions/filters/http/json_to_metadata/v3/BUILD index 29ebf0741406..bfc486330911 100644 --- a/api/envoy/extensions/filters/http/json_to_metadata/v3/BUILD +++ b/api/envoy/extensions/filters/http/json_to_metadata/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_xds//udpa/annotations:pkg"], + deps = [ + "//envoy/type/matcher/v3:pkg", + "@com_github_cncf_xds//udpa/annotations:pkg", + ], ) diff --git a/api/envoy/extensions/filters/http/json_to_metadata/v3/json_to_metadata.proto b/api/envoy/extensions/filters/http/json_to_metadata/v3/json_to_metadata.proto index 8d7b53d1c841..97f59330161d 100644 --- a/api/envoy/extensions/filters/http/json_to_metadata/v3/json_to_metadata.proto +++ b/api/envoy/extensions/filters/http/json_to_metadata/v3/json_to_metadata.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package envoy.extensions.filters.http.json_to_metadata.v3; +import "envoy/type/matcher/v3/regex.proto"; + import "google/protobuf/struct.proto"; import "udpa/annotations/status.proto"; @@ -108,6 +110,10 @@ message JsonToMetadata { // Allowed empty content-type for json to metadata transformation. // Default to false. bool allow_empty_content_type = 3; + + // Allowed content-type by regex match for json to metadata transformation. + // This can be used in parallel with ``allow_content_types``. + type.matcher.v3.RegexMatcher allow_content_types_regex = 4; } // At least one of request_rules and response_rules must be provided. diff --git a/api/envoy/extensions/filters/http/jwt_authn/v3/BUILD b/api/envoy/extensions/filters/http/jwt_authn/v3/BUILD index cea648f6d0ec..fd0f6d5f15c4 100644 --- a/api/envoy/extensions/filters/http/jwt_authn/v3/BUILD +++ b/api/envoy/extensions/filters/http/jwt_authn/v3/BUILD @@ -8,6 +8,7 @@ api_proto_package( deps = [ "//envoy/config/core/v3:pkg", "//envoy/config/route/v3:pkg", + "//envoy/type/matcher/v3:pkg", "@com_github_cncf_xds//udpa/annotations:pkg", ], ) 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 d8bfd7d16e7e..3db921a94818 100644 --- a/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto +++ b/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto @@ -5,6 +5,7 @@ package envoy.extensions.filters.http.jwt_authn.v3; import "envoy/config/core/v3/base.proto"; import "envoy/config/core/v3/http_uri.proto"; import "envoy/config/route/v3/route_components.proto"; +import "envoy/type/matcher/v3/string.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/empty.proto"; @@ -53,7 +54,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // cache_duration: // seconds: 300 // -// [#next-free-field: 19] +// [#next-free-field: 22] message JwtProvider { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.http.jwt_authn.v2alpha.JwtProvider"; @@ -104,6 +105,48 @@ message JwtProvider { // repeated string audiences = 2; + // Restrict the `subjects `_ + // that the JwtProvider can assert. For instance, this could implement JWT-SVID + // `subject restrictions `_. + // If not specified, will not check subjects in the token. + // + // Example: + // + // .. code-block:: yaml + // + // subjects: + // prefix: spiffe://spiffe.example.com/ + // + type.matcher.v3.StringMatcher subjects = 19; + + // Requires that the credential contains an `expiration `_. + // For instance, this could implement JWT-SVID + // `expiration restrictions `_. + // Unlike ``max_lifetime``, this only requires that expiration is present, where ``max_lifetime`` also checks the value. + // + // Example: + // + // .. code-block:: yaml + // + // require_expiration: true + // + bool require_expiration = 20; + + // Restrict the maximum remaining lifetime of a credential from the JwtProvider. Credential lifetime + // is the difference between the current time and the expiration of the credential. For instance, + // the following example will reject credentials that have a lifetime longer than 24 hours. If not set, + // expiration checking still occurs, but there is no limit on credential lifetime. If set, takes precedence + // over ``require_expiration``. + // + // Example: + // + // .. code-block:: yaml + // + // max_lifetime: + // seconds: 86400 + // + google.protobuf.Duration max_lifetime = 21; + // `JSON Web Key Set (JWKS) `_ is needed to // validate signature of a JWT. This field specifies where to fetch JWKS. oneof jwks_source_specifier { diff --git a/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto b/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto index 48524521cf84..aa5f70b2897a 100644 --- a/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto +++ b/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto @@ -74,7 +74,7 @@ message OAuth2Credentials { // OAuth config // -// [#next-free-field: 15] +// [#next-free-field: 16] message OAuth2Config { enum AuthType { // The ``client_id`` and ``client_secret`` will be sent in the URL encoded request body. @@ -142,6 +142,13 @@ message OAuth2Config { // Automatic access token refresh will be performed for these requests, if enabled. // This behavior can be useful for AJAX requests. repeated config.route.v3.HeaderMatcher deny_redirect_matcher = 14; + + // The default lifetime in seconds of the refresh token, if the exp (expiration time) claim is omitted in the refresh token or the refresh token is not JWT. + // + // If this value is not set, it will default to ``604800s``. In this case, the cookie with the refresh token will be expired + // in a week. + // This setting is only considered if ``use_refresh_token`` is set to true, otherwise the authorization server expiration or ``defaul_expires_in`` is used. + google.protobuf.Duration default_refresh_token_expires_in = 15; } // Filter config. diff --git a/api/envoy/extensions/filters/http/rbac/v3/rbac.proto b/api/envoy/extensions/filters/http/rbac/v3/rbac.proto index eeb505a17fb7..b14478b70e8f 100644 --- a/api/envoy/extensions/filters/http/rbac/v3/rbac.proto +++ b/api/envoy/extensions/filters/http/rbac/v3/rbac.proto @@ -22,7 +22,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#extension: envoy.filters.http.rbac] // RBAC filter config. -// [#next-free-field: 6] +// [#next-free-field: 7] message RBAC { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.http.rbac.v2.RBAC"; @@ -34,6 +34,11 @@ message RBAC { config.rbac.v3.RBAC rules = 1 [(udpa.annotations.field_migrate).oneof_promotion = "rules_specifier"]; + // If specified, rules will emit stats with the given prefix. + // This is useful to distinguish the stat when there are more than 1 RBAC filter configured with + // rules. + string rules_stat_prefix = 6; + // The match tree to use when resolving RBAC action for incoming requests. Requests do not // match any matcher will be denied. // If absent, no enforcing RBAC matcher will be applied. diff --git a/api/envoy/extensions/tracers/opentelemetry/samplers/v3/dynatrace_sampler.proto b/api/envoy/extensions/tracers/opentelemetry/samplers/v3/dynatrace_sampler.proto index b74e96a6a416..e4d64e24eb07 100644 --- a/api/envoy/extensions/tracers/opentelemetry/samplers/v3/dynatrace_sampler.proto +++ b/api/envoy/extensions/tracers/opentelemetry/samplers/v3/dynatrace_sampler.proto @@ -33,7 +33,7 @@ message DynatraceSamplerConfig { // .. code-block:: yaml // // http_uri: - // uri: .dev.dynatracelabs.com/api/v2/otlp/v1/traces + // uri: .dev.dynatracelabs.com/api/v2/samplingConfiguration // cluster: dynatrace // timeout: 10s // diff --git a/api/envoy/extensions/wasm/v3/wasm.proto b/api/envoy/extensions/wasm/v3/wasm.proto index 54ef437cbaf7..58b7b57c489d 100644 --- a/api/envoy/extensions/wasm/v3/wasm.proto +++ b/api/envoy/extensions/wasm/v3/wasm.proto @@ -50,7 +50,7 @@ message VmConfig { string vm_id = 1; // The Wasm runtime type, defaults to the first available Wasm engine used at Envoy build-time. - // The priority to search for the available engine is: v8 -> wasmtime -> wamr -> wavm. + // The priority to search for the available engine is: v8 -> wasmtime -> wamr. // Available Wasm runtime types are registered as extensions. The following runtimes are included // in Envoy code base: // @@ -68,11 +68,6 @@ message VmConfig { // **envoy.wasm.runtime.wamr**: `WAMR `_-based WebAssembly runtime. // This runtime is not enabled in the official build. // - // .. _extension_envoy.wasm.runtime.wavm: - // - // **envoy.wasm.runtime.wavm**: `WAVM `_-based WebAssembly runtime. - // This runtime is not enabled in the official build. - // // .. _extension_envoy.wasm.runtime.wasmtime: // // **envoy.wasm.runtime.wasmtime**: `Wasmtime `_-based WebAssembly runtime. diff --git a/api/versioning/BUILD b/api/versioning/BUILD index eb4569353709..56952b6ff2ad 100644 --- a/api/versioning/BUILD +++ b/api/versioning/BUILD @@ -10,6 +10,7 @@ proto_library( visibility = ["//visibility:public"], deps = [ "//contrib/envoy/extensions/compression/qatzip/compressor/v3alpha:pkg", + "//contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha:pkg", "//contrib/envoy/extensions/config/v3alpha:pkg", "//contrib/envoy/extensions/filters/http/checksum/v3alpha:pkg", "//contrib/envoy/extensions/filters/http/dynamo/v3:pkg", @@ -20,6 +21,7 @@ proto_library( "//contrib/envoy/extensions/filters/network/client_ssl_auth/v3:pkg", "//contrib/envoy/extensions/filters/network/generic_proxy/action/v3:pkg", "//contrib/envoy/extensions/filters/network/generic_proxy/codecs/dubbo/v3:pkg", + "//contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3:pkg", "//contrib/envoy/extensions/filters/network/generic_proxy/codecs/kafka/v3:pkg", "//contrib/envoy/extensions/filters/network/generic_proxy/matcher/v3:pkg", "//contrib/envoy/extensions/filters/network/generic_proxy/router/v3:pkg", diff --git a/bazel/BUILD b/bazel/BUILD index 19578ec3c59e..c4749d3140d3 100644 --- a/bazel/BUILD +++ b/bazel/BUILD @@ -497,12 +497,6 @@ config_setting( values = {"define": "zlib=ng"}, ) -# TODO: consider converting WAVM VM support to an extension (https://github.com/envoyproxy/envoy/issues/12574) -config_setting( - name = "wasm_wavm", - values = {"define": "wasm=wavm"}, -) - config_setting( name = "wasm_v8", values = {"define": "wasm=v8"}, diff --git a/bazel/README.md b/bazel/README.md index 03a8008bcf05..02ad261e62b6 100644 --- a/bazel/README.md +++ b/bazel/README.md @@ -738,7 +738,6 @@ To enable a specific WebAssembly (Wasm) engine, you'll need to pass `--define wa * `v8` (the default included engine) * `wamr` * `wasmtime` -* `wavm` If you're building from a custom build repository, the parameters need to prefixed with `@envoy`, for example `--@envoy//source/extensions/filters/http/kill_request:enabled`. diff --git a/bazel/envoy_build_system.bzl b/bazel/envoy_build_system.bzl index 90fd023733b2..604e9f86bc61 100644 --- a/bazel/envoy_build_system.bzl +++ b/bazel/envoy_build_system.bzl @@ -38,7 +38,6 @@ load( _envoy_select_wasm_v8 = "envoy_select_wasm_v8", _envoy_select_wasm_wamr = "envoy_select_wasm_wamr", _envoy_select_wasm_wasmtime = "envoy_select_wasm_wasmtime", - _envoy_select_wasm_wavm = "envoy_select_wasm_wavm", ) load( ":envoy_test.bzl", @@ -252,7 +251,6 @@ envoy_select_wasm_cpp_tests = _envoy_select_wasm_cpp_tests envoy_select_wasm_rust_tests = _envoy_select_wasm_rust_tests envoy_select_wasm_v8 = _envoy_select_wasm_v8 envoy_select_wasm_wamr = _envoy_select_wasm_wamr -envoy_select_wasm_wavm = _envoy_select_wasm_wavm envoy_select_wasm_wasmtime = _envoy_select_wasm_wasmtime envoy_select_linkstatic = _envoy_linkstatic diff --git a/bazel/envoy_select.bzl b/bazel/envoy_select.bzl index 87d5c71eefa2..8caee534fab4 100644 --- a/bazel/envoy_select.bzl +++ b/bazel/envoy_select.bzl @@ -154,7 +154,6 @@ def envoy_select_wasm_v8(xs): "@envoy//bazel:wasm_v8": xs, "@envoy//bazel:wasm_wamr": [], "@envoy//bazel:wasm_wasmtime": [], - "@envoy//bazel:wasm_wavm": [], "@envoy//bazel:wasm_disabled": [], # TODO(phlax): re-enable once issues with llvm profiler are resolved # (see https://github.com/envoyproxy/envoy/issues/24164) @@ -168,7 +167,6 @@ def envoy_select_wasm_v8_bool(): "@envoy//bazel:wasm_v8": True, "@envoy//bazel:wasm_wamr": False, "@envoy//bazel:wasm_wasmtime": False, - "@envoy//bazel:wasm_wavm": False, "@envoy//bazel:wasm_disabled": False, # TODO(phlax): re-enable once issues with llvm profiler are resolved # (see https://github.com/envoyproxy/envoy/issues/24164) @@ -183,13 +181,6 @@ def envoy_select_wasm_wamr(xs): "//conditions:default": [], }) -# Selects the given values depending on the Wasm runtimes enabled in the current build. -def envoy_select_wasm_wavm(xs): - return select({ - "@envoy//bazel:wasm_wavm": xs, - "//conditions:default": [], - }) - # Selects the given values depending on the Wasm runtimes enabled in the current build. def envoy_select_wasm_wasmtime(xs): return select({ diff --git a/bazel/external/boringssl_fips.genrule_cmd b/bazel/external/boringssl_fips.genrule_cmd index 46526a9a84de..44f21fc9c515 100755 --- a/bazel/external/boringssl_fips.genrule_cmd +++ b/bazel/external/boringssl_fips.genrule_cmd @@ -119,7 +119,9 @@ rm -rf boringssl/build # Build BoringSSL. cd boringssl -mkdir build && cd build && cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=${HOME}/toolchain -DFIPS=1 -DCMAKE_BUILD_TYPE=Release .. +# Setting -fPIC only affects the compilation of the non-module code in libcrypto.a, +# because the FIPS module itself is already built with -fPIC. +mkdir build && cd build && cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=${HOME}/toolchain -DFIPS=1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-fPIC" -DCMAKE_CXX_FLAGS="-fPIC" .. ninja ninja run_tests ./crypto/crypto_test diff --git a/bazel/external/quiche.BUILD b/bazel/external/quiche.BUILD index 03f9b143274a..fed3bfc9f04e 100644 --- a/bazel/external/quiche.BUILD +++ b/bazel/external/quiche.BUILD @@ -3126,6 +3126,18 @@ envoy_quic_cc_library( ], ) +envoy_quic_cc_library( + name = "quic_core_http_metadata_decoder_lib", + srcs = ["quiche/quic/core/http/metadata_decoder.cc"], + hdrs = ["quiche/quic/core/http/metadata_decoder.h"], + deps = [ + ":quic_core_error_codes_lib", + ":quic_core_http_header_list_lib", + ":quic_core_qpack_qpack_decoded_headers_accumulator_lib", + ":quic_core_qpack_qpack_decoder_lib", + ], +) + envoy_quic_cc_library( name = "quic_core_http_server_initiated_spdy_stream_lib", srcs = ["quiche/quic/core/http/quic_server_initiated_spdy_stream.cc"], @@ -3167,6 +3179,7 @@ envoy_quic_cc_library( ":quic_core_http_http_constants_lib", ":quic_core_http_http_decoder_lib", ":quic_core_http_http_encoder_lib", + ":quic_core_http_metadata_decoder_lib", ":quic_core_http_spdy_stream_body_manager_lib", ":quic_core_http_spdy_utils_lib", ":quic_core_packets_lib", diff --git a/bazel/flatbuffers.patch b/bazel/flatbuffers.patch new file mode 100644 index 000000000000..67a7d5f5d2b8 --- /dev/null +++ b/bazel/flatbuffers.patch @@ -0,0 +1,25 @@ +# Pending resolution of https://github.com/google/flatbuffers/issues/7988 +diff --git a/BUILD.bazel b/BUILD.bazel +index b4f015a0..8cf7a55e 100644 +--- a/BUILD.bazel ++++ b/BUILD.bazel +@@ -1,5 +1,3 @@ +-load("@aspect_rules_js//npm:defs.bzl", "npm_link_package") +-load("@npm//:defs.bzl", "npm_link_all_packages") + load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") + + licenses(["notice"]) +@@ -8,13 +6,6 @@ package( + default_visibility = ["//visibility:public"], + ) + +-npm_link_all_packages(name = "node_modules") +- +-npm_link_package( +- name = "node_modules/flatbuffers", +- src = "//ts:flatbuffers", +-) +- + exports_files([ + "LICENSE", + "tsconfig.json", diff --git a/bazel/foreign_cc/BUILD b/bazel/foreign_cc/BUILD index d80b6b218293..b863192e58a7 100644 --- a/bazel/foreign_cc/BUILD +++ b/bazel/foreign_cc/BUILD @@ -349,133 +349,6 @@ envoy_cmake( }), ) -envoy_cmake( - name = "llvm", - cache_entries = { - # Disable both: BUILD and INCLUDE, since some of the INCLUDE - # targets build code instead of only generating build files. - "LLVM_BUILD_BENCHMARKS": "off", - "LLVM_INCLUDE_BENCHMARKS": "off", - "LLVM_BUILD_DOCS": "off", - "LLVM_INCLUDE_DOCS": "off", - "LLVM_BUILD_EXAMPLES": "off", - "LLVM_INCLUDE_EXAMPLES": "off", - "LLVM_BUILD_RUNTIME": "off", - "LLVM_BUILD_RUNTIMES": "off", - "LLVM_INCLUDE_RUNTIMES": "off", - "LLVM_BUILD_TESTS": "off", - "LLVM_INCLUDE_TESTS": "off", - "LLVM_BUILD_TOOLS": "off", - "LLVM_INCLUDE_TOOLS": "off", - "LLVM_BUILD_UTILS": "off", - "LLVM_INCLUDE_UTILS": "off", - "LLVM_ENABLE_IDE": "off", - "LLVM_ENABLE_LIBEDIT": "off", - "LLVM_ENABLE_LIBXML2": "off", - "LLVM_ENABLE_TERMINFO": "off", - "LLVM_ENABLE_ZLIB": "off", - "LLVM_TARGETS_TO_BUILD": "X86", - "CMAKE_CXX_COMPILER_FORCED": "on", - # Workaround for the issue with statically linked libstdc++ - # using -l:libstdc++.a. - "CMAKE_CXX_FLAGS": "-lstdc++", - }, - env = { - # Workaround for the -DDEBUG flag added in fastbuild on macOS, - # which conflicts with DEBUG macro used in LLVM. - "CFLAGS": "-UDEBUG", - "CXXFLAGS": "-UDEBUG", - "ASMFLAGS": "-UDEBUG", - }, - lib_source = "@org_llvm_llvm//:all", - out_static_libs = select({ - "//conditions:default": [ - # 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", - "libLLVMMCJIT.a", - "libLLVMJITLink.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", - "libLLVMVectorize.a", - "libLLVMLinker.a", - "libLLVMInstrumentation.a", - "libLLVMFrontendOpenMP.a", - "libLLVMFrontendOpenACC.a", - "libLLVMExtensions.a", - "libLLVMDWARFLinker.a", - "libLLVMGlobalISel.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", - "libLLVMAnalysis.a", - "libLLVMProfileData.a", - "libLLVMObject.a", - "libLLVMTextAPI.a", - "libLLVMMCParser.a", - "libLLVMMC.a", - "libLLVMDebugInfoCodeView.a", - "libLLVMDebugInfoMSF.a", - "libLLVMBitReader.a", - "libLLVMCore.a", - "libLLVMRemarks.a", - "libLLVMBitstreamReader.a", - "libLLVMBinaryFormat.a", - "libLLVMSupport.a", - "libLLVMDemangle.a", - ], - }), - tags = ["skip_on_windows"], - alwayslink = True, -) - envoy_cmake( name = "nghttp2", cache_entries = { @@ -522,36 +395,6 @@ envoy_cmake( tags = ["skip_on_windows"], ) -envoy_cmake( - name = "wavm", - cache_entries = { - "LLVM_DIR": "$EXT_BUILD_DEPS/copy_llvm/llvm/lib/cmake/llvm", - "WAVM_ENABLE_STATIC_LINKING": "on", - "WAVM_ENABLE_RELEASE_ASSERTS": "on", - "WAVM_ENABLE_UNWIND": "on", - # Workaround for the issue with statically linked libstdc++ - # using -l:libstdc++.a. - "CMAKE_CXX_FLAGS": "-lstdc++ -Wno-unused-command-line-argument", - }, - env = { - # Workaround for the -DDEBUG flag added in fastbuild on macOS, - # which conflicts with DEBUG macro used in LLVM. - "CFLAGS": "-UDEBUG", - "CXXFLAGS": "-UDEBUG -Wno-error=unused-but-set-variable", - "ASMFLAGS": "-UDEBUG", - }, - lib_source = "@com_github_wavm_wavm//:all", - out_binaries = ["wavm"], - out_static_libs = select({ - "//conditions:default": [ - "libWAVM.a", - "libWAVMUnwind.a", - ], - }), - tags = ["skip_on_windows"], - deps = [":llvm"], -) - envoy_cmake( name = "zlib", cache_entries = { diff --git a/bazel/foreign_cc/ipp-crypto-bn2lebinpad.patch b/bazel/foreign_cc/ipp-crypto-bn2lebinpad.patch deleted file mode 100644 index da1546a1afcd..000000000000 --- a/bazel/foreign_cc/ipp-crypto-bn2lebinpad.patch +++ /dev/null @@ -1,17 +0,0 @@ -diff --git a/sources/ippcp/crypto_mb/src/common/ifma_cvt52.c b/sources/ippcp/crypto_mb/src/common/ifma_cvt52.c -index 1099518..7526fdc 100644 ---- a/sources/ippcp/crypto_mb/src/common/ifma_cvt52.c -+++ b/sources/ippcp/crypto_mb/src/common/ifma_cvt52.c -@@ -168,12 +168,6 @@ __INLINE void transform_8sb_to_mb8(U64 out_mb8[], int bitLen, int8u *inp[8], int - } - } - --#ifdef OPENSSL_IS_BORINGSSL --static int BN_bn2lebinpad(const BIGNUM *a, unsigned char *to, int tolen) { -- return BN_bn2le_padded(to, tolen, a); --} --#endif -- - #ifndef BN_OPENSSL_DISABLE - // Convert BIGNUM into MB8(Radix=2^52) format - // Returns bitmask of succesfully converted values diff --git a/bazel/foreign_cc/ipp-crypto-skip-dynamic-lib.patch b/bazel/foreign_cc/ipp-crypto-skip-dynamic-lib.patch deleted file mode 100644 index acc43511f2a2..000000000000 --- a/bazel/foreign_cc/ipp-crypto-skip-dynamic-lib.patch +++ /dev/null @@ -1,46 +0,0 @@ -diff --git a/sources/ippcp/crypto_mb/src/CMakeLists.txt b/sources/ippcp/crypto_mb/src/CMakeLists.txt -index f75f448..043a0a2 100644 ---- a/sources/ippcp/crypto_mb/src/CMakeLists.txt -+++ b/sources/ippcp/crypto_mb/src/CMakeLists.txt -@@ -90,41 +90,6 @@ if(CMAKE_C_COMPILER_VERSION VERSION_LESS 20.2.3) - COMPILE_FLAGS "${AVX512_CFLAGS} ${CMAKE_C_FLAGS_SECURITY}") - endif() - --# Create shared library --if(DYNAMIC_LIB OR MB_STANDALONE) -- if(WIN32) -- add_library(${MB_DYN_LIB_TARGET} SHARED ${CRYPTO_MB_HEADERS} ${CRYPTO_MB_SOURCES} ${CPU_FEATURES_FILE} ${WIN_RESOURCE_FILE}) -- else() -- add_library(${MB_DYN_LIB_TARGET} SHARED ${CRYPTO_MB_HEADERS} ${CRYPTO_MB_SOURCES} ${CPU_FEATURES_FILE}) -- endif() -- -- set_target_properties(${MB_DYN_LIB_TARGET} PROPERTIES C_VISIBILITY_PRESET hidden -- VISIBILITY_INLINES_HIDDEN ON -- LINK_FLAGS "${LINK_FLAGS_DYNAMIC} ${LINK_FLAG_SECURITY}" -- PUBLIC_HEADER "${PUBLIC_HEADERS}" -- ) -- -- if(UNIX) -- set_target_properties(${MB_DYN_LIB_TARGET} PROPERTIES VERSION ${MBX_INTERFACE_VERSION} -- SOVERSION ${MBX_INTERFACE_VERSION_MAJOR}) -- endif() -- -- target_link_libraries(${MB_DYN_LIB_TARGET} OpenSSL::Crypto) --endif(DYNAMIC_LIB OR MB_STANDALONE) -- --# Installation of the shared library --if (MB_STANDALONE) # standalone crypto_mb's cmake run -- install(TARGETS ${MB_DYN_LIB_TARGET} -- LIBRARY DESTINATION "lib" -- RUNTIME DESTINATION "lib" -- PUBLIC_HEADER DESTINATION "include/crypto_mb") --elseif (DYNAMIC_LIB) # build from ippcp's cmake -- install(TARGETS ${MB_DYN_LIB_TARGET} -- LIBRARY DESTINATION "lib/intel64" -- RUNTIME DESTINATION "lib/intel64" -- PUBLIC_HEADER DESTINATION "include/crypto_mb") --endif() -- - # Static library - if(WIN32) - add_library(${MB_STATIC_LIB_TARGET} STATIC ${CRYPTO_MB_HEADERS} ${CRYPTO_MB_SOURCES} ${CPU_FEATURES_FILE} ${WIN_RESOURCE_FILE}) diff --git a/bazel/foreign_cc/llvm.patch b/bazel/foreign_cc/llvm.patch deleted file mode 100644 index cd02f2842401..000000000000 --- a/bazel/foreign_cc/llvm.patch +++ /dev/null @@ -1,25 +0,0 @@ -# Workaround for Envoy's CMAKE_BUILD_TYPE=Bazel. ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -247,7 +247,7 @@ - string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE) - - if (CMAKE_BUILD_TYPE AND -- NOT uppercase_CMAKE_BUILD_TYPE MATCHES "^(DEBUG|RELEASE|RELWITHDEBINFO|MINSIZEREL)$") -+ NOT uppercase_CMAKE_BUILD_TYPE MATCHES "^(DEBUG|RELEASE|RELWITHDEBINFO|MINSIZEREL|BAZEL)$") - message(FATAL_ERROR "Invalid value for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") - endif() - -# Workaround for a missing -fuse-ld flag in CXXFLAGS, which results in -# different linkers being used during configure and compilation phases. ---- a/cmake/modules/HandleLLVMOptions.cmake -+++ b/cmake/modules/HandleLLVMOptions.cmake -@@ -718,8 +718,6 @@ endif() - if (UNIX AND CMAKE_GENERATOR STREQUAL "Ninja") - include(CheckLinkerFlag) - check_linker_flag("-Wl,--color-diagnostics" LINKER_SUPPORTS_COLOR_DIAGNOSTICS) -- append_if(LINKER_SUPPORTS_COLOR_DIAGNOSTICS "-Wl,--color-diagnostics" -- CMAKE_EXE_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS) - endif() - - # Add flags for add_dead_strip(). diff --git a/bazel/foreign_cc/luajit.patch b/bazel/foreign_cc/luajit.patch index f7781067b4ab..ab525a950f69 100644 --- a/bazel/foreign_cc/luajit.patch +++ b/bazel/foreign_cc/luajit.patch @@ -39,96 +39,18 @@ index 30d64be2..ae7ec875 100644 TARGET_AR= $(CROSS)ar rcus -TARGET_STRIP= $(CROSS)strip +TARGET_STRIP?= $(CROSS)strip - + TARGET_LIBPATH= $(or $(PREFIX),/usr/local)/$(or $(MULTILIB),lib) TARGET_SONAME= libluajit-$(ABIVER).so.$(MAJVER) @@ -598,7 +598,7 @@ endif - + Q= @ E= @echo -#Q= +Q= #E= @: - + ############################################################################## -diff --git a/src/msvcbuild.bat b/src/msvcbuild.bat -index d323d8d4..2e08a3a1 100644 ---- a/src/msvcbuild.bat -+++ b/src/msvcbuild.bat -@@ -13,9 +13,7 @@ - @if not defined INCLUDE goto :FAIL - - @setlocal --@rem Add more debug flags here, e.g. DEBUGCFLAGS=/DLUA_USE_APICHECK --@set DEBUGCFLAGS= --@set LJCOMPILE=cl /nologo /c /O2 /W3 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_STDIO_INLINE=__declspec(dllexport)__inline -+@set LJCOMPILE=cl /nologo /c /W3 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_STDIO_INLINE=__declspec(dllexport)__inline /DLUAJIT_ENABLE_LUA52COMPAT - @set LJLINK=link /nologo - @set LJMT=mt /nologo - @set LJLIB=lib /nologo /nodefaultlib -@@ -24,10 +22,9 @@ - @set DASC=vm_x64.dasc - @set LJDLLNAME=lua51.dll - @set LJLIBNAME=lua51.lib --@set BUILDTYPE=release - @set ALL_LIB=lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c lib_buffer.c - --%LJCOMPILE% host\minilua.c -+%LJCOMPILE% /O2 host\minilua.c - @if errorlevel 1 goto :BAD - %LJLINK% /out:minilua.exe minilua.obj - @if errorlevel 1 goto :BAD -@@ -51,7 +48,7 @@ if exist minilua.exe.manifest^ - minilua %DASM% -LN %DASMFLAGS% -o host\buildvm_arch.h %DASC% - @if errorlevel 1 goto :BAD - --%LJCOMPILE% /I "." /I %DASMDIR% host\buildvm*.c -+%LJCOMPILE% /O2 /I "." /I %DASMDIR% host\buildvm*.c - @if errorlevel 1 goto :BAD - %LJLINK% /out:buildvm.exe buildvm*.obj - @if errorlevel 1 goto :BAD -@@ -75,26 +72,35 @@ buildvm -m folddef -o lj_folddef.h lj_opt_fold.c - - @if "%1" neq "debug" goto :NODEBUG - @shift --@set BUILDTYPE=debug --@set LJCOMPILE=%LJCOMPILE% /Zi %DEBUGCFLAGS% --@set LJLINK=%LJLINK% /opt:ref /opt:icf /incremental:no -+@set LJCOMPILE=%LJCOMPILE% /O0 /Z7 -+@set LJLINK=%LJLINK% /debug /opt:ref /opt:icf /incremental:no -+@set LJCRTDBG=d -+@goto :ENDDEBUG - :NODEBUG --@set LJLINK=%LJLINK% /%BUILDTYPE% -+@set LJCOMPILE=%LJCOMPILE% /O2 /Z7 -+@set LJLINK=%LJLINK% /release /incremental:no -+@set LJCRTDBG= -+:ENDDEBUG - @if "%1"=="amalg" goto :AMALGDLL - @if "%1"=="static" goto :STATIC --%LJCOMPILE% /MD /DLUA_BUILD_AS_DLL lj_*.c lib_*.c -+@set LJCOMPILE=%LJCOMPILE% /MD%LJCRTDBG% -+%LJCOMPILE% /DLUA_BUILD_AS_DLL lj_*.c lib_*.c - @if errorlevel 1 goto :BAD - %LJLINK% /DLL /out:%LJDLLNAME% lj_*.obj lib_*.obj - @if errorlevel 1 goto :BAD - @goto :MTDLL - :STATIC -+@shift -+@set LJCOMPILE=%LJCOMPILE% /MT%LJCRTDBG% - %LJCOMPILE% lj_*.c lib_*.c - @if errorlevel 1 goto :BAD - %LJLIB% /OUT:%LJLIBNAME% lj_*.obj lib_*.obj - @if errorlevel 1 goto :BAD - @goto :MTDLL - :AMALGDLL --%LJCOMPILE% /MD /DLUA_BUILD_AS_DLL ljamalg.c -+@shift -+@set LJCOMPILE=%LJCOMPILE% /MD%LJCRTDBG% -+%LJCOMPILE% /DLUA_BUILD_AS_DLL ljamalg.c - @if errorlevel 1 goto :BAD - %LJLINK% /DLL /out:%LJDLLNAME% ljamalg.obj lj_vm.obj - @if errorlevel 1 goto :BAD diff --git a/build.py b/build.py new file mode 100755 index 00000000..1201542c diff --git a/bazel/foreign_cc/qatzstd.patch b/bazel/foreign_cc/qatzstd.patch new file mode 100644 index 000000000000..84bd231db5e9 --- /dev/null +++ b/bazel/foreign_cc/qatzstd.patch @@ -0,0 +1,13 @@ +diff --git a/src/Makefile b/src/Makefile +index 1abf10d..c5fa3a6 100644 +--- a/src/Makefile ++++ b/src/Makefile +@@ -54,7 +54,7 @@ ifneq ($(ICP_ROOT), ) + endif + else + QATFLAGS = -DINTREE +- LDFLAGS = -lqat ++ LDFLAGS += -lqat + ifneq ($(ENABLE_USDM_DRV), 0) + QATFLAGS += -DENABLE_USDM_DRV + LDFLAGS += -lusdm diff --git a/bazel/foreign_cc/vpp_vcl.patch b/bazel/foreign_cc/vpp_vcl.patch index 07916e8a4167..2ce6b455ed04 100644 --- a/bazel/foreign_cc/vpp_vcl.patch +++ b/bazel/foreign_cc/vpp_vcl.patch @@ -1,8 +1,8 @@ diff --git src/CMakeLists.txt src/CMakeLists.txt -index fb4463e33..de1ef8990 100644 +index 68d0a4fe6..958918ef1 100644 --- src/CMakeLists.txt +++ src/CMakeLists.txt -@@ -31,12 +31,8 @@ include(cmake/ccache.cmake) +@@ -50,13 +50,8 @@ include(cmake/ccache.cmake) ############################################################################## # VPP Version ############################################################################## @@ -12,12 +12,12 @@ index fb4463e33..de1ef8990 100644 - OUTPUT_VARIABLE VPP_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE -) -+ -+set(VPP_VERSION 23.06-release) - string(REPLACE "-" ";" VPP_LIB_VERSION ${VPP_VERSION}) - list(GET VPP_LIB_VERSION 0 VPP_LIB_VERSION) - -@@ -215,8 +211,7 @@ elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + ++set(VPP_VERSION 24.03-dev) + if (VPP_PLATFORM) + set(VPP_VERSION ${VPP_VERSION}-${VPP_PLATFORM_NAME}) + endif() +@@ -277,8 +272,7 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD") find_package(OpenSSL) set(SUBDIRS vppinfra svm vlib vlibmemory vlibapi vnet vpp vat vat2 vcl vpp-api @@ -47,11 +47,11 @@ index 45b3944eb..b1dcc56e1 100644 @@ -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 src/tools/vppapigen/CMakeLists.txt src/tools/vppapigen/CMakeLists.txt index 04ebed548..bfabc3a67 100644 @@ -60,7 +60,7 @@ index 04ebed548..bfabc3a67 100644 @@ -11,22 +11,6 @@ # See the License for the specific language governing permissions and # limitations under the License. - + -find_package( - Python3 - REQUIRED @@ -97,7 +97,7 @@ index 2b0ce9999..f28a17302 100755 + import ply.lex as lex import ply.yacc as yacc - + diff --git src/vcl/CMakeLists.txt src/vcl/CMakeLists.txt index 610b422d1..c5e6f8ca8 100644 --- src/vcl/CMakeLists.txt @@ -105,7 +105,7 @@ index 610b422d1..c5e6f8ca8 100644 @@ -35,6 +35,8 @@ if (LDP_HAS_GNU_SOURCE) add_compile_definitions(HAVE_GNU_SOURCE) endif(LDP_HAS_GNU_SOURCE) - + +file(COPY vppcom.h DESTINATION ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) + add_vpp_library(vcl_ldpreload diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index bec5940badf3..d91aa712bdee 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -310,9 +310,9 @@ def envoy_dependencies(skip_targets = []): _com_github_rules_proto_grpc() _com_github_unicode_org_icu() _com_github_intel_ipp_crypto_crypto_mb() - _com_github_intel_ipp_crypto_crypto_mb_fips() _com_github_intel_qatlib() _com_github_intel_qatzip() + _com_github_qat_zstd() _com_github_lz4_lz4() _com_github_jbeder_yaml_cpp() _com_github_libevent_libevent() @@ -358,7 +358,11 @@ def envoy_dependencies(skip_targets = []): _com_github_google_perfetto() _utf8_range() _rules_ruby() - external_http_archive("com_github_google_flatbuffers") + external_http_archive( + "com_github_google_flatbuffers", + patch_args = ["-p1"], + patches = ["@envoy//bazel:flatbuffers.patch"], + ) external_http_archive("bazel_toolchains") external_http_archive("bazel_compdb") external_http_archive("envoy_build_tools") @@ -382,9 +386,7 @@ def envoy_dependencies(skip_targets = []): _rust_deps() _kafka_deps() - _org_llvm_llvm() _com_github_wamr() - _com_github_wavm_wavm() _com_github_wasmtime() _com_github_wasm_c_api() @@ -544,35 +546,9 @@ def _com_github_unicode_org_icu(): def _com_github_intel_ipp_crypto_crypto_mb(): external_http_archive( name = "com_github_intel_ipp_crypto_crypto_mb", - # Patch removes from CMakeLists.txt instructions to - # to create dynamic *.so library target. Linker fails when linking - # with boringssl_fips library. Envoy uses only static library - # anyways, so created dynamic library would not be used anyways. - patches = [ - "@envoy//bazel/foreign_cc:ipp-crypto-skip-dynamic-lib.patch", - "@envoy//bazel/foreign_cc:ipp-crypto-bn2lebinpad.patch", - ], - patch_args = ["-p1"], build_file_content = BUILD_ALL_CONTENT, ) -def _com_github_intel_ipp_crypto_crypto_mb_fips(): - # Temporary fix for building ipp-crypto when boringssl-fips is used. - # Build will fail if bn2lebinpad patch is applied. Remove this archive - # when upstream dependency fixes this issue. - external_http_archive( - name = "com_github_intel_ipp_crypto_crypto_mb_fips", - # Patch removes from CMakeLists.txt instructions to - # to create dynamic *.so library target. Linker fails when linking - # with boringssl_fips library. Envoy uses only static library - # anyways, so created dynamic library would not be used anyways. - patches = ["@envoy//bazel/foreign_cc:ipp-crypto-skip-dynamic-lib.patch"], - patch_args = ["-p1"], - build_file_content = BUILD_ALL_CONTENT, - # Use existing ipp-crypto repository location name to avoid redefinition. - location_name = "com_github_intel_ipp_crypto_crypto_mb", - ) - def _com_github_intel_qatlib(): external_http_archive( name = "com_github_intel_qatlib", @@ -585,6 +561,14 @@ def _com_github_intel_qatzip(): build_file_content = BUILD_ALL_CONTENT, ) +def _com_github_qat_zstd(): + external_http_archive( + name = "com_github_qat_zstd", + build_file_content = BUILD_ALL_CONTENT, + patch_args = ["-p1"], + patches = ["@envoy//bazel/foreign_cc:qatzstd.patch"], + ) + def _com_github_lz4_lz4(): external_http_archive( name = "com_github_lz4_lz4", @@ -1359,18 +1343,6 @@ def _com_github_gperftools_gperftools(): actual = "@envoy//bazel/foreign_cc:gperftools", ) -def _org_llvm_llvm(): - external_http_archive( - name = "org_llvm_llvm", - build_file_content = BUILD_ALL_CONTENT, - patch_args = ["-p1"], - patches = ["@envoy//bazel/foreign_cc:llvm.patch"], - ) - native.bind( - name = "llvm", - actual = "@envoy//bazel/foreign_cc:llvm", - ) - def _com_github_wamr(): external_http_archive( name = "com_github_wamr", @@ -1381,16 +1353,6 @@ def _com_github_wamr(): actual = "@envoy//bazel/foreign_cc:wamr", ) -def _com_github_wavm_wavm(): - external_http_archive( - name = "com_github_wavm_wavm", - build_file_content = BUILD_ALL_CONTENT, - ) - native.bind( - name = "wavm", - actual = "@envoy//bazel/foreign_cc:wavm", - ) - def _com_github_wasmtime(): external_http_archive( name = "com_github_wasmtime", diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index e4483c6063c1..d1aecef16519 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -102,11 +102,11 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "envoy-build-tools", project_desc = "Common build tools shared by the Envoy/UDPA ecosystem", project_url = "https://github.com/envoyproxy/envoy-build-tools", - version = "7cd964feb564419532ec8a825031e4e1b7b6974f", - sha256 = "74182edc4c40a4d3b775598cdcf7f88000a96cc8753ffbd6d94083fb403f5e78", + version = "32570f74e1e36d2711f5f0a156b9623cdfa4b384", + sha256 = "962e398567306e3230eb2457c5eabec26616667379bfba566894a7bd334416d4", strip_prefix = "envoy-build-tools-{version}", urls = ["https://github.com/envoyproxy/envoy-build-tools/archive/{version}.tar.gz"], - release_date = "2024-01-08", + release_date = "2024-03-11", use_category = ["build"], license = "Apache-2.0", license_url = "https://github.com/envoyproxy/envoy-build-tools/blob/{version}/LICENSE", @@ -148,12 +148,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "Aspect Bazel helpers", project_desc = "Base Starlark libraries and basic Bazel rules which are useful for constructing rulesets and BUILD files", project_url = "https://github.com/aspect-build/bazel-lib", - version = "2.4.2", - sha256 = "f75d03783588e054899eb0729a97fb5b8973c1a26f30373fafd485c90bf207d1", + version = "2.5.3", + sha256 = "6c25c59581041ede31e117693047f972cc4700c89acf913658dc89d04c338f8d", strip_prefix = "bazel-lib-{version}", urls = ["https://github.com/aspect-build/bazel-lib/archive/v{version}.tar.gz"], use_category = ["build"], - release_date = "2024-02-21", + release_date = "2024-03-08", cpe = "N/A", license = "Apache-2.0", license_url = "https://github.com/aspect-build/bazel-lib/blob/v{version}/LICENSE", @@ -190,12 +190,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "liburing", project_desc = "C helpers to set up and tear down io_uring instances", project_url = "https://github.com/axboe/liburing", - version = "2.3", - sha256 = "60b367dbdc6f2b0418a6e0cd203ee0049d9d629a36706fcf91dfb9428bae23c8", + version = "2.5", + sha256 = "456f5f882165630f0dc7b75e8fd53bd01a955d5d4720729b4323097e6e9f2a98", strip_prefix = "liburing-liburing-{version}", urls = ["https://github.com/axboe/liburing/archive/liburing-{version}.tar.gz"], use_category = ["dataplane_core", "controlplane"], - release_date = "2022-10-26", + release_date = "2023-11-29", cpe = "N/A", ), # This dependency is built only when performance tracing is enabled with the @@ -425,11 +425,11 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "libipp-crypto", project_desc = "Intel® Integrated Performance Primitives Cryptography", project_url = "https://github.com/intel/ipp-crypto", - version = "2021.6", - sha256 = "632cc5ba54413eeab575682619c05d247e9b7f2fc58ea3e5f4a02bdcab3e6b78", + version = "2021.11.1", + sha256 = "d785fd8d5245ada79068588e5cc4d721d35c50e7d26fc268306f4aaae28ec6d6", strip_prefix = "ipp-crypto-ippcp_{version}", urls = ["https://github.com/intel/ipp-crypto/archive/ippcp_{version}.tar.gz"], - release_date = "2022-08-09", + release_date = "2024-02-28", use_category = ["dataplane_ext"], extensions = ["envoy.tls.key_providers.cryptomb"], cpe = "cpe:2.3:a:intel:cryptography_for_intel_integrated_performance_primitives:*", @@ -446,7 +446,7 @@ REPOSITORY_LOCATIONS_SPEC = dict( urls = ["https://github.com/intel/qatlib/archive/refs/tags/{version}.tar.gz"], use_category = ["dataplane_ext"], release_date = "2024-02-20", - extensions = ["envoy.tls.key_providers.qat", "envoy.compression.qatzip.compressor"], + extensions = ["envoy.tls.key_providers.qat", "envoy.compression.qatzip.compressor", "envoy.compression.qatzstd.compressor"], cpe = "N/A", license = "BSD-3-Clause", license_url = "https://github.com/intel/qatlib/blob/{version}/LICENSE", @@ -466,16 +466,31 @@ REPOSITORY_LOCATIONS_SPEC = dict( license = "BSD-3-Clause", license_url = "https://github.com/intel/QATzip/blob/v{version}/LICENSE", ), + com_github_qat_zstd = dict( + project_name = "QAT-ZSTD-Plugin", + project_desc = "Intel® QuickAssist Technology ZSTD Plugin (QAT ZSTD Plugin)", + project_url = "https://github.com/intel/QAT-ZSTD-Plugin/", + version = "0.1.0", + sha256 = "74c5bfbb3b0c6f1334e128ee0b43958d1d34751a4762e54e8f970c443e445f33", + strip_prefix = "QAT-ZSTD-Plugin-{version}", + urls = ["https://github.com/intel/QAT-ZSTD-Plugin/archive/refs/tags/v{version}.tar.gz"], + use_category = ["dataplane_ext"], + extensions = [ + "envoy.compression.qatzstd.compressor", + ], + release_date = "2023-09-08", + cpe = "N/A", + ), com_github_luajit_luajit = dict( project_name = "LuaJIT", project_desc = "Just-In-Time compiler for Lua", project_url = "https://luajit.org", # LuaJIT only provides rolling releases - version = "1c279127050e86e99970100e9c42e0f09cd54ab7", - sha256 = "c62f6e6d5bff89e4718709841cd6be71ad419ac9fa780c91abf1635cda923f8f", + version = "d06beb0480c5d1eb53b3343e78063950275aa281", + sha256 = "6abd146a1dfa240a965748f63221633446affa2a715e3eb03879136e3efb95f4", strip_prefix = "LuaJIT-{version}", urls = ["https://github.com/LuaJIT/LuaJIT/archive/{version}.tar.gz"], - release_date = "2023-04-16", + release_date = "2024-03-10", use_category = ["dataplane_ext"], extensions = [ "envoy.filters.http.lua", @@ -822,7 +837,7 @@ REPOSITORY_LOCATIONS_SPEC = dict( strip_prefix = "jwt_verify_lib-{version}", urls = ["https://github.com/google/jwt_verify_lib/archive/{version}.tar.gz"], use_category = ["dataplane_ext"], - extensions = ["envoy.filters.http.jwt_authn", "envoy.filters.http.gcp_authn"], + extensions = ["envoy.filters.http.jwt_authn", "envoy.filters.http.gcp_authn", "envoy.filters.http.oauth2"], release_date = "2023-05-17", cpe = "N/A", license = "Apache-2.0", @@ -1059,26 +1074,6 @@ REPOSITORY_LOCATIONS_SPEC = dict( license = "Apache-2.0", license_url = "https://github.com/bazelbuild/rules_pkg/blob/{version}/LICENSE", ), - 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 = "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 = "2021-07-09", - use_category = ["dataplane_ext"], - extensions = [ - "envoy.wasm.runtime.wamr", - "envoy.wasm.runtime.wavm", - ], - cpe = "cpe:2.3:a:llvm:*:*", - license = "Apache-2.0", - license_url = "https://github.com/llvm/llvm-project/blob/llvmorg-{version}/llvm/LICENSE.TXT", - ), com_github_wamr = dict( project_name = "Webassembly Micro Runtime", project_desc = "A standalone runtime with a small footprint for WebAssembly", @@ -1094,19 +1089,6 @@ REPOSITORY_LOCATIONS_SPEC = dict( license = "Apache-2.0", license_url = "https://github.com/bytecodealliance/wasm-micro-runtime/blob/{version}/LICENSE", ), - com_github_wavm_wavm = dict( - project_name = "WAVM", - project_desc = "WebAssembly Virtual Machine", - project_url = "https://wavm.github.io", - version = "3f9a150cac7faf28eab357a2c5b83d2ec740c7d9", - sha256 = "82e05ade03fdac60cf863972d3e7420a771ef4a18afad26ac442554ab0be1207", - strip_prefix = "WAVM-{version}", - urls = ["https://github.com/WAVM/WAVM/archive/{version}.tar.gz"], - release_date = "2022-05-14", - use_category = ["dataplane_ext"], - extensions = ["envoy.wasm.runtime.wavm"], - cpe = "cpe:2.3:a:webassembly_virtual_machine_project:webassembly_virtual_machine:*", - ), com_github_wasmtime = dict( project_name = "wasmtime", project_desc = "A standalone runtime for WebAssembly", @@ -1210,12 +1192,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "QUICHE", project_desc = "QUICHE (QUIC, HTTP/2, Etc) is Google‘s implementation of QUIC and related protocols", project_url = "https://github.com/google/quiche", - version = "6f0b1d204da73155e21c683650dbebe05a36d781", - sha256 = "9023e5fa8c830543c04124dd994f3f8c60d8377e82e116cf1145cd846e686f90", + version = "10053f8f199aa6d6ad7727b3112db9e62884bb8c", + sha256 = "ab6f41a72fda9f731b157840293d1a22a0b7a495ac208a8c8f4c727ded9c7e1d", urls = ["https://github.com/google/quiche/archive/{version}.tar.gz"], strip_prefix = "quiche-{version}", use_category = ["controlplane", "dataplane_core"], - release_date = "2024-03-05", + release_date = "2024-03-19", cpe = "N/A", license = "BSD-3-Clause", license_url = "https://github.com/google/quiche/blob/{version}/LICENSE", @@ -1268,8 +1250,8 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "FlatBuffers", project_desc = "FlatBuffers is a cross platform serialization library architected for maximum memory efficiency", project_url = "https://github.com/google/flatbuffers", - version = "23.3.3", - sha256 = "8aff985da30aaab37edf8e5b02fda33ed4cbdd962699a8e2af98fdef306f4e4d", + version = "24.3.7", + sha256 = "bfff9d2150fcff88f844e8c608b02b2a0e94c92aea39b04c0624783464304784", strip_prefix = "flatbuffers-{version}", urls = ["https://github.com/google/flatbuffers/archive/v{version}.tar.gz"], use_category = ["dataplane_ext"], @@ -1290,7 +1272,7 @@ REPOSITORY_LOCATIONS_SPEC = dict( "envoy.matching.inputs.cel_data_input", "envoy.matching.matchers.cel_matcher", ], - release_date = "2023-03-03", + release_date = "2024-03-07", cpe = "cpe:2.3:a:google:flatbuffers:*", license = "Apache-2.0", license_url = "https://github.com/google/flatbuffers/blob/v{version}/LICENSE", @@ -1417,7 +1399,6 @@ REPOSITORY_LOCATIONS_SPEC = dict( "envoy.wasm.runtime.null", "envoy.wasm.runtime.v8", "envoy.wasm.runtime.wamr", - "envoy.wasm.runtime.wavm", "envoy.wasm.runtime.wasmtime", ], release_date = "2023-05-01", @@ -1443,7 +1424,6 @@ REPOSITORY_LOCATIONS_SPEC = dict( "envoy.wasm.runtime.null", "envoy.wasm.runtime.v8", "envoy.wasm.runtime.wamr", - "envoy.wasm.runtime.wavm", "envoy.wasm.runtime.wasmtime", ], release_date = "2023-12-19", @@ -1503,13 +1483,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 = "493b8990d1185f818890560101e13e1b69f54b1d", - sha256 = "94a515b9396822911271a8f72ba8ffd9965992241754a88625f4cf2a3883a4ac", + version = "39e7f2e650a65bac596a6fc968c9860a1496a5bf", + sha256 = "63742d09ac223b30d71d9fe2da5afb75253fde31e3cf986a3f9ce89e264e801a", strip_prefix = "vpp-{version}", urls = ["https://github.com/FDio/vpp/archive/{version}.tar.gz"], use_category = ["other"], extensions = ["envoy.bootstrap.vcl"], - release_date = "2023-06-28", + release_date = "2024-03-13", cpe = "N/A", license = "Apache-2.0", license_url = "https://github.com/FDio/vpp/blob/{version}/LICENSE", diff --git a/changelogs/1.29.2.yaml b/changelogs/1.29.2.yaml new file mode 100644 index 000000000000..7e9eaf3c2ed8 --- /dev/null +++ b/changelogs/1.29.2.yaml @@ -0,0 +1,20 @@ +date: March 7, 2024 + +behavior_changes: +- area: http2 + change: | + Changes the default value of ``envoy.reloadable_features.http2_use_oghttp2`` to ``false``. This changes the codec used for HTTP/2 + requests and responses. A number of users have reported issues with oghttp2 including issue 32611 and issue 32401 This behavior + can be reverted by setting the feature to ``true``. + +bug_fixes: +- area: jwt_authn + change: | + Fixed JWT extractor, which concatenated headers with a comma, resultig in invalid tokens. + +new_features: +- area: google_grpc + change: | + Added an off-by-default runtime flag + ``envoy.reloadable_features.google_grpc_disable_tls_13`` to disable TLSv1.3 + usage by gRPC SDK for ``google_grpc`` services. diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 45a31ea33600..8adcf56d4a08 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -65,6 +65,16 @@ minor_behavior_changes: change: | Enable obsolete line folding in BalsaParser (for behavior parity with http-parser, the previously used HTTP/1 parser). +- area: http + change: | + When the HTTP CONNECT request method is enabled using :ref:`RouteAction.UpgradeConfig + `, CONNECT requests will now be + proxied to the upstream, unless the :ref:`connect_config + ` field is also + set. (Previously Envoy would terminate CONNECT requests even when the ``connect_config`` field + was unset.) The updated behavior should now be consistent with the existing documentation. This + change can be reverted by setting + ``envoy.reloadable_features.http_route_connect_proxy_by_default`` to ``false``. - area: proxy status change: | Add more conversion in the proxy status utility. It can be disabled by the runtime guard @@ -73,6 +83,17 @@ minor_behavior_changes: change: | Simplifies integration with the codec by removing translation between nghttp2 callbacks and Http2VisitorInterface events. Guarded by ``envoy.reloadable_features.http2_skip_callback_visitor``. +- area: http3 + change: | + Use GRO (Generic Receive Offload) for reading packets from a client QUIC UDP socket. See + https://www.kernel.org/doc/html/next/networking/segmentation-offloads.html for a description of + GRO. This behavior change can be reverted by setting + ``envoy.reloadable_features.prefer_quic_client_udp_gro`` to ``false``. +- area: http3 + change: | + Disables recvmmsg (multi-message) for reading packets from a client QUIC UDP socket, if GRO + is not set or not supported. recvmsg will be used instead. This behavior change can be + reverted by setting ``envoy.reloadable_features.disallow_quic_client_udp_mmsg`` to ``false``. bug_fixes: # *Changes expected to improve the state of the world and are unlikely to have negative effects* @@ -140,9 +161,20 @@ bug_fixes: to support half close semantics during TCP tunneling. This behavioral change can be temporarily reverted by setting runtime guard ``envoy.reloadable_features.tcp_tunneling_send_downstream_fin_on_upstream_trailers`` to false. +- area: eds-caching + change: | + Fixing an issue where EDS caching is used (protected by the ``envoy.restart_features.use_eds_cache_for_ads`` + runtime flag that is false by default), when multiple clusters use the same EDS resource, and that cached EDS + resource is used. - area: jwt_authn change: | Fixed JWT extractor, which concatenated headers with a comma, resultig in invalid tokens. +- area: jwt_authn + change: | + Added + :ref:`max_lifetime ` and + :ref:`require_expiration ` + to limit the maximum remaining lifetime of a token from a ``JwtProvider`` and implement restrictions for JWT-SVIDs. - area: router change: | Fix a timing issue when upstream requests are empty when decoding data and send local reply when that happens. This is @@ -153,6 +185,17 @@ bug_fixes: - area: tracing change: | Dynatrace resource detector: Only log warning message when no enrichment attributes are found. +- area: oauth + change: | + When performing a token refresh and forwarding tokens upstream, replace existing token cookies rather than appending as + another Cookie header. +- area: oauth + change: | + The refresh and access tokens are not expired simultaneously so the access token can be updated using + the refresh token. The expiration time of the refresh token is taken from the exp claim of jwt by default. + If the claim is ommited in the jwt then :ref:`default_refresh_token_expires_in + ` + specifies the lifetime of the refresh token. The default value is ``604800`` seconds (a week). removed_config_or_runtime: # *Normally occurs at the end of the* :ref:`deprecation period ` @@ -225,10 +268,16 @@ new_features: change: | added support for :ref:`%UPSTREAM_CONNECTION_ID% ` for the upstream connection identifier. +- area: compression + change: | + Added Qatzstd :ref:`compressor `. - area: aws_lambda change: | Added :ref:`host_rewrite ` config to be used during signature. +- area: http3 + change: | + Added experimental support for sending and receiving HTTP/3 METADATA frames. - area: ext_proc change: | added @@ -273,6 +322,10 @@ new_features: change: | Added load shed point ``envoy.load_shed_points.hcm_ondata_creating_codec`` that closes connections before creating codec if Envoy is under pressure, typically memory. +- area: health_checks + change: | + added a :ref:`configuration option ` that enables + health check logs on each successful health check. - area: string matcher change: | Added an :ref:`extension point for custom string matcher implementations `. @@ -282,6 +335,11 @@ new_features: added a :ref:`configuration option ` to add ``x-envoy-local-overloaded`` header when Overload Manager is triggered. +- area: access_loggers + change: | + Added :ref:`retry options + ` to Fluentd Access Logger to + support upstream reconnect options, backoff intervals. - area: tracing change: | Added support to configure a Dynatrace sampler for the OpenTelemetry tracer. @@ -298,11 +356,23 @@ new_features: change: | :ref:`deny_redirect_matcher ` to support disabling authorization redirects for specific requests, e.g. AJAX requests. +- area: jwt_authn + change: | + Added + :ref:`subjects ` to allow restrictions + of subjects a ``JwtProvider`` can assert. - area: aws_request_signing change: | Update ``aws_request_signing`` filter to support optionally sending the aws signature in query parameters rather than headers, by specifying the :ref:`query_string ` configuration section. +- area: rbac + change: | + Added :ref:`rules_stat_prefix ` + to allow adding custom prefix to the stats emitted by rules. +- area: tracing + change: | + Dynatrace sampler fetches configuration from Dynatrace API. deprecated: - area: listener diff --git a/ci/Dockerfile-envoy b/ci/Dockerfile-envoy index 07affa3502e3..00e2c7ca472d 100644 --- a/ci/Dockerfile-envoy +++ b/ci/Dockerfile-envoy @@ -58,7 +58,7 @@ COPY --chown=0:0 --chmod=755 \ # STAGE: envoy-distroless -FROM gcr.io/distroless/base-nossl-debian12:nonroot@sha256:0e777c69ba810353b9f3f2033280bbe7d029d81fa55760f6eec817ef595aa19c AS envoy-distroless +FROM gcr.io/distroless/base-nossl-debian12:nonroot@sha256:099c13463fdd2f52d31af8b61f5a991ed8e97bdac529f10b22c4f4ebf0c21c0d AS envoy-distroless EXPOSE 10000 ENTRYPOINT ["/usr/local/bin/envoy"] CMD ["-c", "/etc/envoy/envoy.yaml"] diff --git a/ci/do_ci.sh b/ci/do_ci.sh index 1c04c4250292..2cecbff3f793 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -427,21 +427,12 @@ case $CI_TARGET in "${TEST_TARGETS[@]}" \ --test_tag_filters=-nofips \ --build_tests_only - echo "Building and testing with wasm=wavm: ${TEST_TARGETS[*]}" - bazel_with_collection \ - test "${BAZEL_BUILD_OPTIONS[@]}" \ - --config=compile-time-options \ - --define wasm=wavm \ - -c fastbuild \ - "${TEST_TARGETS[@]}" \ - --test_tag_filters=-nofips \ - --build_tests_only # "--define log_debug_assert_in_release=enabled" must be tested with a release build, so run only # these tests under "-c opt" to save time in CI. bazel_with_collection \ test "${BAZEL_BUILD_OPTIONS[@]}" \ --config=compile-time-options \ - --define wasm=wavm \ + --define wasm=wasmtime \ -c opt \ @envoy//test/common/common:assert_test \ @envoy//test/server:server_test @@ -449,15 +440,15 @@ case $CI_TARGET in bazel_with_collection \ test "${BAZEL_BUILD_OPTIONS[@]}" \ --config=compile-time-options \ - --define wasm=wavm \ + --define wasm=wamtime \ -c opt \ @envoy//test/common/common:assert_test \ --define log_fast_debug_assert_in_release=enabled \ --define log_debug_assert_in_release=disabled - echo "Building binary with wasm=wavm... and logging disabled" + echo "Building binary with wasm=wasmtime... and logging disabled" bazel build "${BAZEL_BUILD_OPTIONS[@]}" \ --config=compile-time-options \ - --define wasm=wavm \ + --define wasm=wasmtime \ --define enable_logging=disabled \ -c fastbuild \ @envoy//source/exe:envoy-static \ diff --git a/contrib/all_contrib_extensions.bzl b/contrib/all_contrib_extensions.bzl index 6b7994a2623b..7caf371f6032 100644 --- a/contrib/all_contrib_extensions.bzl +++ b/contrib/all_contrib_extensions.bzl @@ -18,6 +18,7 @@ ARM64_SKIP_CONTRIB_TARGETS = [ "envoy.tls.key_providers.qat", "envoy.network.connection_balance.dlb", "envoy.compression.qatzip.compressor", + "envoy.compression.qatzstd.compressor", ] PPC_SKIP_CONTRIB_TARGETS = [ "envoy.tls.key_providers.cryptomb", @@ -26,6 +27,7 @@ PPC_SKIP_CONTRIB_TARGETS = [ "envoy.network.connection_balance.dlb", "envoy.regex_engines.hyperscan", "envoy.compression.qatzip.compressor", + "envoy.compression.qatzstd.compressor", ] FIPS_LINUX_X86_SKIP_CONTRIB_TARGETS = [ diff --git a/contrib/contrib_build_config.bzl b/contrib/contrib_build_config.bzl index b8e7811a168c..7be1cd02ffad 100644 --- a/contrib/contrib_build_config.bzl +++ b/contrib/contrib_build_config.bzl @@ -5,6 +5,7 @@ CONTRIB_EXTENSIONS = { # "envoy.compression.qatzip.compressor": "//contrib/qat/compression/qatzip/compressor/source:config", + "envoy.compression.qatzstd.compressor": "//contrib/qat/compression/qatzstd/compressor/source:config", # # HTTP filters @@ -72,6 +73,7 @@ CONTRIB_EXTENSIONS = { # "envoy.filters.generic.router": "//contrib/generic_proxy/filters/network/source/router:config", "envoy.generic_proxy.codecs.dubbo": "//contrib/generic_proxy/filters/network/source/codecs/dubbo:config", + "envoy.generic_proxy.codecs.http1": "//contrib/generic_proxy/filters/network/source/codecs/http1:config", "envoy.generic_proxy.codecs.kafka": "//contrib/generic_proxy/filters/network/source/codecs/kafka:config", # diff --git a/contrib/cryptomb/private_key_providers/source/BUILD b/contrib/cryptomb/private_key_providers/source/BUILD index 610b66513373..55381c6b5f80 100644 --- a/contrib/cryptomb/private_key_providers/source/BUILD +++ b/contrib/cryptomb/private_key_providers/source/BUILD @@ -18,14 +18,14 @@ envoy_cmake( name = "ipp-crypto", cache_entries = { "BORINGSSL": "on", + "DYNAMIC_LIB": "off", + "MB_STANDALONE": "off", }, defines = [ "OPENSSL_USE_STATIC_LIBS=TRUE", ], - lib_source = select({ - "//bazel:boringssl_fips": "@com_github_intel_ipp_crypto_crypto_mb_fips//:all", - "//conditions:default": "@com_github_intel_ipp_crypto_crypto_mb//:all", - }), + lib_source = "@com_github_intel_ipp_crypto_crypto_mb//:all", + out_lib_dir = "lib/intel64", out_static_libs = ["libcrypto_mb.a"], tags = ["skip_on_windows"], target_compatible_with = envoy_contrib_linux_x86_64_constraints(), diff --git a/contrib/extensions_metadata.yaml b/contrib/extensions_metadata.yaml index 8168b063da58..76218e5a2b85 100644 --- a/contrib/extensions_metadata.yaml +++ b/contrib/extensions_metadata.yaml @@ -18,6 +18,11 @@ envoy.compression.qatzip.compressor: - envoy.compression.compressor security_posture: robust_to_untrusted_downstream_and_upstream status: alpha +envoy.compression.qatzstd.compressor: + categories: + - envoy.compression.compressor + security_posture: robust_to_untrusted_downstream_and_upstream + status: alpha envoy.filters.http.squash: categories: - envoy.filters.http @@ -139,6 +144,13 @@ envoy.generic_proxy.codecs.kafka: status: wip type_urls: - envoy.extensions.filters.network.generic_proxy.codecs.kafka.v3.KafkaCodecConfig +envoy.generic_proxy.codecs.http1: + categories: + - envoy.generic_proxy.codecs + security_posture: requires_trusted_downstream_and_upstream + status: wip + type_urls: + - envoy.extensions.filters.network.generic_proxy.codecs.http1.v3.Http1CodecConfig envoy.router.cluster_specifier_plugin.golang: categories: - envoy.router.cluster_specifier_plugin diff --git a/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.cc b/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.cc index 4bbfa68e1b06..588a40191c1b 100644 --- a/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.cc +++ b/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.cc @@ -4,7 +4,7 @@ #include "envoy/registry/registry.h" -#include "source/extensions/common/dubbo/message_impl.h" +#include "source/extensions/common/dubbo/message.h" namespace Envoy { namespace Extensions { @@ -31,20 +31,9 @@ Common::Dubbo::ResponseStatus genericStatusToStatus(StatusCode code) { } // namespace void DubboRequest::forEach(IterateCallback callback) const { - const auto* typed_request = - dynamic_cast(&inner_metadata_->mutableRequest()); - ASSERT(typed_request != nullptr); - - for (const auto& pair : typed_request->attachment().attachment()) { - ASSERT(pair.first != nullptr && pair.second != nullptr); - - if (pair.first->type() == Hessian2::Object::Type::String && - pair.second->type() == Hessian2::Object::Type::String) { - ASSERT(pair.first->toString().has_value() && pair.second->toString().has_value()); - - if (!callback(pair.first->toString().value().get(), pair.second->toString().value().get())) { - break; - } + for (const auto& [key, val] : inner_metadata_->request().content().attachments()) { + if (!callback(key, val)) { + break; } } } @@ -53,27 +42,21 @@ absl::optional DubboRequest::get(absl::string_view key) const if (key == VERSION_KEY) { return inner_metadata_->request().serviceVersion(); } - const auto* typed_request = - dynamic_cast(&inner_metadata_->mutableRequest()); - ASSERT(typed_request != nullptr); - return typed_request->attachment().lookup(key); + auto it = inner_metadata_->request().content().attachments().find(key); + if (it == inner_metadata_->request().content().attachments().end()) { + return absl::nullopt; + } + + return absl::string_view{it->second}; } void DubboRequest::set(absl::string_view key, absl::string_view val) { - auto* typed_request = - dynamic_cast(&inner_metadata_->mutableRequest()); - ASSERT(typed_request != nullptr); - - typed_request->mutableAttachment()->insert(key, val); + inner_metadata_->request().content().setAttachment(key, val); } void DubboRequest::erase(absl::string_view key) { - auto* typed_request = - dynamic_cast(&inner_metadata_->mutableRequest()); - ASSERT(typed_request != nullptr); - - typed_request->mutableAttachment()->remove(key); + inner_metadata_->request().content().delAttachment(key); } void DubboResponse::refreshStatus() { @@ -85,19 +68,19 @@ void DubboResponse::refreshStatus() { const auto status = inner_metadata_->context().responseStatus(); const auto optional_type = inner_metadata_->response().responseType(); - // The final status is not ok if the response status is not ResponseStatus::Ok - // anyway. - bool response_ok = (status == Common::Dubbo::ResponseStatus::Ok); + if (status != Common::Dubbo::ResponseStatus::Ok) { + status_ = StreamStatus(static_cast(status), false); + return; + } + bool response_ok = true; // The final status is not ok if the response type is ResponseWithException or // ResponseWithExceptionWithAttachments even if the response status is Ok. - if (status == Common::Dubbo::ResponseStatus::Ok) { - ASSERT(optional_type.has_value()); - auto type = optional_type.value_or(RpcResponseType::ResponseWithException); - if (type == RpcResponseType::ResponseWithException || - type == RpcResponseType::ResponseWithExceptionWithAttachments) { - response_ok = false; - } + ASSERT(optional_type.has_value()); + auto type = optional_type.value_or(RpcResponseType::ResponseWithException); + if (type == RpcResponseType::ResponseWithException || + type == RpcResponseType::ResponseWithExceptionWithAttachments) { + response_ok = false; } status_ = StreamStatus(static_cast(status), response_ok); @@ -105,7 +88,7 @@ void DubboResponse::refreshStatus() { DubboCodecBase::DubboCodecBase(Common::Dubbo::DubboCodecPtr codec) : codec_(std::move(codec)) {} -ResponsePtr DubboServerCodec::respond(Status status, absl::string_view, +ResponsePtr DubboServerCodec::respond(Status status, absl::string_view data, const Request& origin_request) { const auto* typed_request = dynamic_cast(&origin_request); ASSERT(typed_request != nullptr); @@ -113,17 +96,20 @@ ResponsePtr DubboServerCodec::respond(Status status, absl::string_view, Common::Dubbo::ResponseStatus response_status = genericStatusToStatus(status.code()); absl::optional optional_type; - absl::string_view content; if (response_status == Common::Dubbo::ResponseStatus::Ok) { optional_type.emplace(Common::Dubbo::RpcResponseType::ResponseWithException); - content = "exception_via_proxy"; + } + auto response = Common::Dubbo::DirectResponseUtil::localResponse( + *typed_request->inner_metadata_, response_status, optional_type, data); + + if (!status.ok()) { + response->mutableResponse().content().setAttachment("reason", status.message()); } else { - content = status.message(); + response->mutableResponse().content().setAttachment("reason", "envoy_response"); } - return std::make_unique(Common::Dubbo::DirectResponseUtil::localResponse( - *typed_request->inner_metadata_, response_status, optional_type, content)); + return std::make_unique(std::move(response)); } CodecFactoryPtr diff --git a/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.h b/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.h index 6ef3932be244..840cb61a733c 100644 --- a/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.h +++ b/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.h @@ -35,9 +35,9 @@ class DubboRequest : public Request { void forEach(IterateCallback callback) const override; absl::optional get(absl::string_view key) const override; void set(absl::string_view key, absl::string_view val) override; - absl::string_view host() const override { return inner_metadata_->request().serviceName(); } - absl::string_view path() const override { return inner_metadata_->request().serviceName(); } - absl::string_view method() const override { return inner_metadata_->request().methodName(); } + absl::string_view host() const override { return inner_metadata_->request().service(); } + absl::string_view path() const override { return inner_metadata_->request().service(); } + absl::string_view method() const override { return inner_metadata_->request().method(); } void erase(absl::string_view key) override; // StreamFrame @@ -87,7 +87,7 @@ class DubboDecoderBase : public DubboCodecBase, public CodecType { void setCodecCallbacks(CallBackType& callback) override { callback_ = &callback; } - void decode(Buffer::Instance& buffer, bool) override { + Common::Dubbo::DecodeStatus decodeOne(Buffer::Instance& buffer) { if (metadata_ == nullptr) { metadata_ = std::make_shared(); } @@ -109,27 +109,59 @@ class DubboDecoderBase : public DubboCodecBase, public CodecType { ENVOY_LOG(warn, "Dubbo codec: unexpected decoding error"); metadata_.reset(); callback_->onDecodingFailure(); - return; + return Common::Dubbo::DecodeStatus::Failure; } if (decode_status == Common::Dubbo::DecodeStatus::Waiting) { ENVOY_LOG(debug, "Dubbo codec: waiting for more input data"); - return; + return Common::Dubbo::DecodeStatus::Waiting; } ASSERT(decode_status == Common::Dubbo::DecodeStatus::Success); + if (metadata_->context().heartbeat()) { + Buffer::OwnedImpl heartbeat_response; + + ENVOY_LOG(debug, "Dubbo codec: heartbeat from downstream/upstream"); + constexpr char first_four_bytes[] = {'\xda', '\xbb', '\x22', 20}; + heartbeat_response.add(first_four_bytes, 4); + + heartbeat_response.writeBEInt(metadata_->requestId()); + heartbeat_response.writeBEInt(1); + heartbeat_response.writeByte('N'); + + metadata_.reset(); + callback_->writeToConnection(heartbeat_response); + + return Common::Dubbo::DecodeStatus::Success; + } + auto message = std::make_unique(metadata_); message->stream_frame_flags_ = {{static_cast(metadata_->requestId()), !metadata_->context().isTwoWay(), false, metadata_->context().heartbeat()}, true}; - callback_->onDecodingSuccess(std::move(message)); metadata_.reset(); + callback_->onDecodingSuccess(std::move(message)); + + return Common::Dubbo::DecodeStatus::Success; } catch (const EnvoyException& error) { ENVOY_LOG(warn, "Dubbo codec: decoding error: {}", error.what()); + metadata_.reset(); callback_->onDecodingFailure(); + + return Common::Dubbo::DecodeStatus::Failure; + } + } + + void decode(Buffer::Instance& buffer, bool) override { + while (buffer.length() > 0) { + // Continue decoding if the buffer has more data and the previous decoding is + // successful. + if (decodeOne(buffer) != Common::Dubbo::DecodeStatus::Success) { + break; + } } } @@ -151,8 +183,7 @@ class DubboServerCodec public: using DubboDecoderBase::DubboDecoderBase; - ResponsePtr respond(absl::Status status, absl::string_view short_response_flags, - const Request& request) override; + ResponsePtr respond(absl::Status status, absl::string_view data, const Request& request) override; }; class DubboClientCodec diff --git a/contrib/generic_proxy/filters/network/source/codecs/http1/BUILD b/contrib/generic_proxy/filters/network/source/codecs/http1/BUILD new file mode 100644 index 000000000000..adbc830baa9d --- /dev/null +++ b/contrib/generic_proxy/filters/network/source/codecs/http1/BUILD @@ -0,0 +1,28 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_contrib_extension", + "envoy_contrib_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_contrib_package() + +envoy_cc_contrib_extension( + name = "config", + srcs = [ + "config.cc", + ], + hdrs = [ + "config.h", + ], + deps = [ + "//contrib/generic_proxy/filters/network/source/interface:codec_interface", + "//source/common/http:codes_lib", + "//source/common/http:header_utility_lib", + "//source/common/http:headers_lib", + "//source/common/http:utility_lib", + "//source/common/http/http1:balsa_parser_lib", + "@envoy_api//contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3:pkg_cc_proto", + ], +) diff --git a/contrib/generic_proxy/filters/network/source/codecs/http1/config.cc b/contrib/generic_proxy/filters/network/source/codecs/http1/config.cc new file mode 100644 index 000000000000..c16287b4db60 --- /dev/null +++ b/contrib/generic_proxy/filters/network/source/codecs/http1/config.cc @@ -0,0 +1,653 @@ +#include "contrib/generic_proxy/filters/network/source/codecs/http1/config.h" + +#include "source/common/http/codes.h" +#include "source/common/http/header_utility.h" +#include "source/common/http/utility.h" + +namespace Envoy { +namespace Extensions { +namespace NetworkFilters { +namespace GenericProxy { +namespace Codec { +namespace Http1 { + +static constexpr absl::string_view CRLF = "\r\n"; +// Last chunk as defined here https://tools.ietf.org/html/rfc7230#section-4.1 +static constexpr absl::string_view LAST_CHUNK = "0\r\n"; + +static constexpr absl::string_view SPACE = " "; +static constexpr absl::string_view COLON_SPACE = ": "; + +static constexpr absl::string_view REQUEST_POSTFIX = " HTTP/1.1\r\n"; +static constexpr absl::string_view RESPONSE_PREFIX = "HTTP/1.1 "; + +static constexpr absl::string_view HOST_HEADER_PREFIX = ":a"; + +static constexpr uint32_t DEFAULT_MAX_BUFFER_SIZE = 8 * 1024 * 1024; + +static constexpr absl::string_view _100_CONTINUE_RESPONSE = "HTTP/1.1 100 Continue\r\n" + "content-length: 0\r\n" + "\r\n"; + +void encodeNormalHeaders(Buffer::Instance& buffer, const Http::RequestOrResponseHeaderMap& headers, + bool chunk_encoding) { + headers.iterate([&buffer](const Http::HeaderEntry& header) { + absl::string_view key_to_use = header.key().getStringView(); + // Translate :authority -> host so that upper layers do not need to deal with this. + if (absl::StartsWith(key_to_use, HOST_HEADER_PREFIX)) { + key_to_use = Http::Headers::get().HostLegacy; + } + + // Skip all headers starting with ':' that make it here. + if (key_to_use[0] == ':') { + return Http::HeaderMap::Iterate::Continue; + } + + buffer.addFragments({key_to_use, COLON_SPACE, header.value().getStringView(), CRLF}); + return Http::HeaderMap::Iterate::Continue; + }); + + if (chunk_encoding) { + if (headers.TransferEncoding() == nullptr) { + buffer.add("Transfer-Encoding: chunked\r\n"); + } + } +} + +absl::Status validateCommonHeaders(const Http::RequestOrResponseHeaderMap& headers) { + // Both Transfer-Encoding and Content-Length are set. + if (headers.TransferEncoding() != nullptr && headers.ContentLength() != nullptr) { + return absl::InvalidArgumentError("Both transfer-encoding and content-length are set"); + } + + // Transfer-Encoding is not chunked. + if (headers.TransferEncoding() != nullptr) { + absl::string_view encoding = headers.TransferEncoding()->value().getStringView(); + if (!absl::EqualsIgnoreCase(encoding, Http::Headers::get().TransferEncodingValues.Chunked)) { + return absl::InvalidArgumentError("transfer-encoding is not chunked"); + } + } + + return absl::OkStatus(); +} + +bool Utility::isChunked(const Http::RequestOrResponseHeaderMap& headers, bool bodiless) { + // If the message has body and the content length is not set, then treat it as chunked. + // Note all upgrade and connect requests are rejected so this is safe. + return !bodiless && headers.ContentLength() == nullptr; +} + +bool Utility::hasBody(const Envoy::Http::Http1::Parser& parser, bool response, + bool response_for_head_request) { + // Response for HEAD request should not have body. + if (response_for_head_request) { + ASSERT(response); + return false; + } + + // 1xx, 204, 304 responses should not have body. + if (response) { + const Envoy::Http::Code code = parser.statusCode(); + if (code < Http::Code::OK || code == Http::Code::NoContent || code == Http::Code::NotModified) { + return false; + } + } + + // Check the transfer-encoding and content-length headers in other cases. + return parser.isChunked() || parser.contentLength().value_or(0) > 0; +} + +absl::Status Utility::validateRequestHeaders(Http::RequestHeaderMap& headers) { + // No upgrade and connect support for now. + // TODO(wbpcode): add support for upgrade and connect in the future. + if (Http::Utility::isUpgrade(headers) || + headers.getMethodValue() == Http::Headers::get().MethodValues.Connect) { + return absl::InvalidArgumentError("upgrade or connect are not supported"); + } + + if (auto status = validateCommonHeaders(headers); !status.ok()) { + return status; + } + + // One of method, path, host is missing. + if (headers.Method() == nullptr || headers.Path() == nullptr || headers.Host() == nullptr) { + return absl::InvalidArgumentError("missing required headers"); + } + + return absl::OkStatus(); +} + +absl::Status Utility::validateResponseHeaders(Http::ResponseHeaderMap& headers, + Envoy::Http::Code code) { + if (auto status = validateCommonHeaders(headers); !status.ok()) { + return status; + } + + ASSERT(headers.Status() != nullptr); + + if (code < Http::Code::OK || code == Http::Code::NoContent || code == Http::Code::NotModified) { + // There is no clear description in the RFC about the transfer-encoding behavior + // of NotModified response. But 1xx, 204 responses should not have transfer-encoding. + // See https://datatracker.ietf.org/doc/html/rfc9112#section-6.1-6 + if (code != Http::Code::NotModified) { + if (headers.TransferEncoding() != nullptr) { + return absl::InvalidArgumentError("transfer-encoding is set for 1xx, 204 response"); + } + } + + // 1xx, 204, 304 responses should not have body. + if (headers.ContentLength() != nullptr) { + if (headers.ContentLength()->value().getStringView() != "0") { + return absl::InvalidArgumentError( + "content-length (non-zero) is set for 1xx, 204, 304 response"); + } + } + } + + return absl::OkStatus(); +} + +absl::Status Utility::encodeRequestHeaders(Buffer::Instance& buffer, + const Http::RequestHeaderMap& headers, + bool chunk_encoding) { + const Http::HeaderEntry* method = headers.Method(); + const Http::HeaderEntry* path = headers.Path(); + const Http::HeaderEntry* host = headers.Host(); + + if (method == nullptr || path == nullptr || host == nullptr) { + return absl::InvalidArgumentError("missing required headers"); + } + + absl::string_view host_or_path_view = path->value().getStringView(); + + buffer.addFragments({method->value().getStringView(), SPACE, host_or_path_view, REQUEST_POSTFIX}); + encodeNormalHeaders(buffer, headers, chunk_encoding); + buffer.add(CRLF); + + return absl::OkStatus(); +} + +uint64_t Utility::statusToHttpStatus(absl::StatusCode status_code) { + switch (status_code) { + case absl::StatusCode::kOk: + return 200; + case absl::StatusCode::kCancelled: + return 499; + case absl::StatusCode::kUnknown: + // Internal server error. + return 500; + case absl::StatusCode::kInvalidArgument: + // Bad request. + return 400; + case absl::StatusCode::kDeadlineExceeded: + // Gateway Time-out. + return 504; + case absl::StatusCode::kNotFound: + // Not found. + return 404; + case absl::StatusCode::kAlreadyExists: + // Conflict. + return 409; + case absl::StatusCode::kPermissionDenied: + // Forbidden. + return 403; + case absl::StatusCode::kResourceExhausted: + // Too many requests. + return 429; + case absl::StatusCode::kFailedPrecondition: + // Bad request. + return 400; + case absl::StatusCode::kAborted: + // Conflict. + return 409; + case absl::StatusCode::kOutOfRange: + // Bad request. + return 400; + case absl::StatusCode::kUnimplemented: + // Not implemented. + return 501; + case absl::StatusCode::kInternal: + // Internal server error. + return 500; + case absl::StatusCode::kUnavailable: + // Service unavailable. + return 503; + case absl::StatusCode::kDataLoss: + // Internal server error. + return 500; + case absl::StatusCode::kUnauthenticated: + // Unauthorized. + return 401; + default: + // Internal server error. + return 500; + } +} + +absl::Status Utility::encodeResponseHeaders(Buffer::Instance& buffer, + const Http::ResponseHeaderMap& headers, + bool chunk_encoding) { + const Http::HeaderEntry* status = headers.Status(); + if (status == nullptr) { + return absl::InvalidArgumentError("missing required headers"); + } + uint64_t numeric_status = Http::Utility::getResponseStatus(headers); + + absl::string_view reason_phrase; + const char* status_string = Http::CodeUtility::toString(static_cast(numeric_status)); + uint32_t status_string_len = strlen(status_string); + reason_phrase = {status_string, status_string_len}; + + buffer.addFragments({RESPONSE_PREFIX, absl::StrCat(numeric_status), SPACE, reason_phrase, CRLF}); + encodeNormalHeaders(buffer, headers, chunk_encoding); + buffer.add(CRLF); + + return absl::OkStatus(); +} + +void Utility::encodeBody(Buffer::Instance& dst_buffer, Buffer::Instance& src_buffer, + bool chunk_encoding, bool end_stream) { + if (src_buffer.length() > 0) { + // Chunk header. + if (chunk_encoding) { + dst_buffer.add(absl::StrCat(absl::Hex(src_buffer.length()), CRLF)); + } + + // Body. + dst_buffer.move(src_buffer); + + // Chunk footer. + if (chunk_encoding) { + dst_buffer.add(CRLF); + } + } + + // Add additional LAST_CHUNK if this is the last frame and chunk encoding is enabled. + if (end_stream) { + if (chunk_encoding) { + dst_buffer.addFragments({LAST_CHUNK, CRLF}); + } + } +} + +bool Http1CodecBase::decodeBuffer(Buffer::Instance& buffer) { + decoding_buffer_.move(buffer); + + // Always resume before decoding. + parser_->resume(); + + while (decoding_buffer_.length() > 0) { + const auto slice = decoding_buffer_.frontSlice(); + const auto nread = parser_->execute(static_cast(slice.mem_), slice.len_); + decoding_buffer_.drain(nread); + const auto status = parser_->getStatus(); + + // Parser is paused by the callback. Do nothing and return. Don't handle the buffered body + // because parser is paused and no callback should be called. + if (status == Http::Http1::ParserStatus::Paused) { + return true; + } + // Parser has encountered an error. Return false to indicate decoding failure. Ignore the + // buffered body. + if (status == Http::Http1::ParserStatus::Error) { + // Decoding error. + return false; + } + // No more data to read and parser is not paused, break to avoid infinite loop. This is + // preventive check. The parser should not be in this state in normal cases. + if (nread == 0) { + return true; + } + } + // Try to dispatch any buffered body. If the message is complete then this will be a no-op. + dispatchBufferedBody(false); + return true; +} + +void Http1CodecBase::dispatchBufferedBody(bool end_stream) { + if (single_frame_mode_) { + // Do nothing until the onMessageComplete callback if we are in single frame mode. + + // Check if the buffered body is too large. + if (bufferedBodyOverflow()) { + // Pause the parser to avoid further parsing. + parser_->pause(); + // Tell the caller that the decoding failed. + onDecodingFailure(); + } + return; + } + + if (buffered_body_.length() > 0 || end_stream) { + ENVOY_LOG(debug, + "Generic proxy HTTP1 codec: decoding request/response body (end_stream={} size={})", + end_stream, buffered_body_.length()); + auto frame = std::make_unique(buffered_body_, end_stream); + onDecodingSuccess(std::move(frame)); + } +} + +bool Http1CodecBase::bufferedBodyOverflow() { + if (buffered_body_.length() < max_buffer_size_) { + return false; + } + + ENVOY_LOG(warn, "Generic proxy HTTP1 codec: body size exceeds max size({} vs {})", + buffered_body_.length(), max_buffer_size_); + return true; +} + +Http::Http1::CallbackResult Http1ServerCodec::onMessageBeginImpl() { + if (active_request_.has_value()) { + ENVOY_LOG(error, + "Generic proxy HTTP1 codec: multiple requests on the same connection at same time."); + return Http::Http1::CallbackResult::Error; + } + active_request_ = ActiveRequest{}; + + active_request_->request_headers_ = Http::RequestHeaderMapImpl::create(); + return Http::Http1::CallbackResult::Success; +} + +Http::Http1::CallbackResult Http1ServerCodec::onHeadersCompleteImpl() { + if (!parser_->isHttp11()) { + ENVOY_LOG(error, + "Generic proxy HTTP1 codec: unsupported HTTP version, only HTTP/1.1 is supported."); + return Http::Http1::CallbackResult::Error; + } + + active_request_->request_headers_->setMethod(parser_->methodName()); + + // Validate request headers. + const auto validate_headers_status = + Utility::validateRequestHeaders(*active_request_->request_headers_); + if (!validate_headers_status.ok()) { + ENVOY_LOG(error, "Generic proxy HTTP1 codec: failed to validate request headers: {}", + validate_headers_status.message()); + return Http::Http1::CallbackResult::Error; + } + + const bool non_end_stream = Utility::hasBody(*parser_, false, false); + ENVOY_LOG(debug, "decoding request headers complete (end_stream={}):\n{}", !non_end_stream, + *active_request_->request_headers_); + + // Handle the Expect header first. + if (active_request_->request_headers_->Expect() != nullptr) { + if (absl::EqualsIgnoreCase(active_request_->request_headers_->getExpectValue(), + Envoy::Http::Headers::get().ExpectValues._100Continue)) { + // Remove the expect header then the upstream server won't handle it again. + active_request_->request_headers_->removeExpect(); + + // Send 100 Continue response directly. We won't proxy the 100 Continue because + // the complexity in the generic proxy framework is too high. + Buffer::OwnedImpl buffer(_100_CONTINUE_RESPONSE); + callbacks_->writeToConnection(buffer); + } + } + + if (single_frame_mode_) { + // Do nothing until the onMessageComplete callback if we are in single frame mode. + return Http::Http1::CallbackResult::Success; + } else if (non_end_stream) { + auto request = + std::make_unique(std::move(active_request_->request_headers_), false); + onDecodingSuccess(std::move(request)); + } else { + deferred_end_stream_headers_ = true; + } + + return Http::Http1::CallbackResult::Success; +} + +Http::Http1::CallbackResult Http1ServerCodec::onMessageCompleteImpl() { + active_request_->request_complete_ = true; + + if (single_frame_mode_) { + // Check if the buffered body is too large. + if (bufferedBodyOverflow()) { + // Pause the parser to avoid further parsing. + parser_->pause(); + // Tell the caller that the decoding failed. + return Http::Http1::CallbackResult::Error; + } + + ASSERT(!deferred_end_stream_headers_); + auto request = + std::make_unique(std::move(active_request_->request_headers_), true); + request->optionalBuffer().move(buffered_body_); + + if (request->optionalBuffer().length() > 0) { + request->headerMap().removeTransferEncoding(); + request->headerMap().setContentLength(request->optionalBuffer().length()); + } + onDecodingSuccess(std::move(request)); + } else if (deferred_end_stream_headers_) { + deferred_end_stream_headers_ = false; + auto request = + std::make_unique(std::move(active_request_->request_headers_), true); + onDecodingSuccess(std::move(request)); + } else { + dispatchBufferedBody(true); + } + + parser_->pause(); + return Http::Http1::CallbackResult::Success; +} + +void Http1ServerCodec::encode(const StreamFrame& frame, EncodingCallbacks& callbacks) { + const bool response_end_stream = frame.frameFlags().endStream(); + + if (auto* headers = dynamic_cast(&frame); headers != nullptr) { + ENVOY_LOG(debug, "encoding response headers (end_stream={}):\n{}", response_end_stream, + *headers->response_); + + active_request_->response_chunk_encoding_ = + Utility::isChunked(*headers->response_, response_end_stream); + + auto status = Utility::encodeResponseHeaders(encoding_buffer_, *headers->response_, + active_request_->response_chunk_encoding_); + if (!status.ok()) { + ENVOY_LOG(error, "Generic proxy HTTP1 codec: failed to encode response headers: {}", + status.message()); + callbacks_->connection()->close(Network::ConnectionCloseType::FlushWrite); + return; + } + + // Encode the optional buffer if it exists. This is used for local response or for the + // request/responses in single frame mode. + if (headers->optionalBuffer().length() > 0) { + ASSERT(response_end_stream); + Utility::encodeBody(encoding_buffer_, headers->optionalBuffer(), + active_request_->response_chunk_encoding_, response_end_stream); + } + + } else if (auto* body = dynamic_cast(&frame); body != nullptr) { + ENVOY_LOG(debug, "encoding response body (end_stream={} size={})", response_end_stream, + body->buffer().length()); + Utility::encodeBody(encoding_buffer_, body->buffer(), active_request_->response_chunk_encoding_, + response_end_stream); + } + + callbacks.onEncodingSuccess(encoding_buffer_, response_end_stream); + + if (response_end_stream) { + if (active_request_->request_complete_) { + active_request_.reset(); + return; + } + ENVOY_LOG(debug, "Generic proxy HTTP1 server codec: response complete before request complete"); + if (callbacks_->connection().has_value()) { + callbacks_->connection()->close(Network::ConnectionCloseType::FlushWrite); + } + } +} + +void Http1ClientCodec::encode(const StreamFrame& frame, EncodingCallbacks& callbacks) { + const bool request_end_stream = frame.frameFlags().endStream(); + + if (auto* headers = dynamic_cast(&frame); headers != nullptr) { + ENVOY_LOG(debug, "encoding request headers (end_stream={}):\n{}", request_end_stream, + *headers->request_); + + ASSERT(!expect_response_.has_value()); + expect_response_ = ExpectResponse{}; + expect_response_->request_chunk_encoding_ = + Utility::isChunked(*headers->request_, request_end_stream); + expect_response_->head_request_ = + headers->request_->getMethodValue() == Http::Headers::get().MethodValues.Head; + + auto status = Utility::encodeRequestHeaders(encoding_buffer_, *headers->request_, + expect_response_->request_chunk_encoding_); + if (!status.ok()) { + ENVOY_LOG(error, "Generic proxy HTTP1 codec: failed to encode request headers: {}", + status.message()); + callbacks_->connection()->close(Network::ConnectionCloseType::FlushWrite); + return; + } + + // Encode the optional buffer if it exists. This is used for local response or for the + // request/responses in single frame mode. + if (headers->optionalBuffer().length() > 0) { + ASSERT(request_end_stream); + Utility::encodeBody(encoding_buffer_, headers->optionalBuffer(), + expect_response_->request_chunk_encoding_, request_end_stream); + } + + } else if (auto* body = dynamic_cast(&frame); body != nullptr) { + ENVOY_LOG(debug, "encoding request body (end_stream={} size={})", request_end_stream, + body->buffer().length()); + Utility::encodeBody(encoding_buffer_, body->buffer(), expect_response_->request_chunk_encoding_, + request_end_stream); + } + + if (request_end_stream) { + expect_response_->request_complete_ = true; + } + + callbacks.onEncodingSuccess(encoding_buffer_, request_end_stream); +} + +Http::Http1::CallbackResult Http1ClientCodec::onMessageBeginImpl() { + if (!expect_response_.has_value()) { + ENVOY_LOG(error, "Generic proxy HTTP1 codec: unexpected HTTP response from upstream"); + return Http::Http1::CallbackResult::Error; + } + expect_response_->response_headers_ = Http::ResponseHeaderMapImpl::create(); + return Http::Http1::CallbackResult::Success; +} + +Http::Http1::CallbackResult Http1ClientCodec::onHeadersCompleteImpl() { + if (!parser_->isHttp11()) { + ENVOY_LOG(error, + "Generic proxy HTTP1 codec: unsupported HTTP version, only HTTP/1.1 is supported."); + return Http::Http1::CallbackResult::Error; + } + + expect_response_->response_headers_->setStatus( + std::to_string(static_cast(parser_->statusCode()))); + + // Validate response headers. + const auto validate_headers_status = + Utility::validateResponseHeaders(*expect_response_->response_headers_, parser_->statusCode()); + if (!validate_headers_status.ok()) { + ENVOY_LOG(error, "Generic proxy HTTP1 codec: failed to validate response headers: {}", + validate_headers_status.message()); + return Http::Http1::CallbackResult::Error; + } + + const bool non_end_stream = Utility::hasBody(*parser_, true, expect_response_->head_request_); + + ENVOY_LOG(debug, "decoding response headers complete (end_stream={}):\n{}", !non_end_stream, + *expect_response_->response_headers_); + + if (single_frame_mode_) { + // Do nothing until the onMessageComplete callback if we are in single frame mode. + return Http::Http1::CallbackResult::Success; + } else if (non_end_stream) { + auto request = + std::make_unique(std::move(expect_response_->response_headers_), false); + onDecodingSuccess(std::move(request)); + } else { + deferred_end_stream_headers_ = true; + } + + return Http::Http1::CallbackResult::Success; +} + +Http::Http1::CallbackResult Http1ClientCodec::onMessageCompleteImpl() { + const auto status_code = parser_->statusCode(); + if (status_code < Envoy::Http::Code::OK) { + // There is no difference bewteen single frame mode and normal mode for 1xx responses + // because they are headers only responses. + + ASSERT(buffered_body_.length() == 0); + + expect_response_->response_headers_.reset(); + buffered_body_.drain(buffered_body_.length()); + + // 100 Continue response. Ignore it. + // 101 Switching Protocols response. Ignore it because we don't support upgrade for now. + // 102 Processing response. Ignore it. + // 103 Early Hints response. Ignore it. + + // Return success to continue parsing the actual response. + return Http::Http1::CallbackResult::Success; + } + + if (single_frame_mode_) { + // Check if the buffered body is too large. + if (bufferedBodyOverflow()) { + // Pause the parser to avoid further parsing. + parser_->pause(); + // Tell the caller that the decoding failed. + return Http::Http1::CallbackResult::Error; + } + + ASSERT(!deferred_end_stream_headers_); + auto response = + std::make_unique(std::move(expect_response_->response_headers_), true); + response->optionalBuffer().move(buffered_body_); + + if (response->optionalBuffer().length() > 0) { + response->headerMap().removeTransferEncoding(); + response->headerMap().setContentLength(response->optionalBuffer().length()); + } + + onDecodingSuccess(std::move(response)); + } else if (deferred_end_stream_headers_) { + deferred_end_stream_headers_ = false; + auto response = + std::make_unique(std::move(expect_response_->response_headers_), true); + onDecodingSuccess(std::move(response)); + } else { + dispatchBufferedBody(true); + } + // If both request and response is complete, reset the state and pause the parser. + if (expect_response_->request_complete_) { + parser_->pause(); + expect_response_.reset(); + return Http::Http1::CallbackResult::Success; + } else { + ENVOY_LOG(debug, "Generic proxy HTTP1 client codec: response complete before request complete"); + return Http::Http1::CallbackResult::Error; + } +} + +CodecFactoryPtr +Http1CodecFactoryConfig::createCodecFactory(const Protobuf::Message& config, + Envoy::Server::Configuration::FactoryContext&) { + const auto& typed_config = dynamic_cast(config); + + return std::make_unique( + PROTOBUF_GET_WRAPPED_OR_DEFAULT(typed_config, single_frame_mode, true), + PROTOBUF_GET_WRAPPED_OR_DEFAULT(typed_config, max_buffer_size, DEFAULT_MAX_BUFFER_SIZE)); +} + +REGISTER_FACTORY(Http1CodecFactoryConfig, CodecFactoryConfig); + +} // namespace Http1 +} // namespace Codec +} // namespace GenericProxy +} // namespace NetworkFilters +} // namespace Extensions +} // namespace Envoy diff --git a/contrib/generic_proxy/filters/network/source/codecs/http1/config.h b/contrib/generic_proxy/filters/network/source/codecs/http1/config.h new file mode 100644 index 000000000000..6a77b3329d64 --- /dev/null +++ b/contrib/generic_proxy/filters/network/source/codecs/http1/config.h @@ -0,0 +1,394 @@ +#pragma once + +#include "source/common/buffer/buffer_impl.h" +#include "source/common/http/header_map_impl.h" +#include "source/common/http/http1/balsa_parser.h" +#include "source/common/http/http1/parser.h" + +#include "contrib/envoy/extensions/filters/network/generic_proxy/codecs/http1/v3/http1.pb.h" +#include "contrib/generic_proxy/filters/network/source/interface/codec.h" + +namespace Envoy { +namespace Extensions { +namespace NetworkFilters { +namespace GenericProxy { +namespace Codec { +namespace Http1 { + +using ProtoConfig = + envoy::extensions::filters::network::generic_proxy::codecs::http1::v3::Http1CodecConfig; + +template class HttpHeaderFrame : public Interface { +public: + absl::string_view protocol() const override { return "http1"; } + void forEach(StreamBase::IterateCallback callback) const override { + headerMap().iterate([cb = std::move(callback)](const Http::HeaderEntry& entry) { + if (cb(entry.key().getStringView(), entry.value().getStringView())) { + return Http::HeaderMap::Iterate::Continue; + } + return Http::HeaderMap::Iterate::Break; + }); + }; + absl::optional get(absl::string_view key) const override { + const Http::LowerCaseString lower_key{key}; + const auto entry = headerMap().get(lower_key); + if (!entry.empty()) { + return entry[0]->value().getStringView(); + } + return absl::nullopt; + } + void set(absl::string_view key, absl::string_view val) override { + headerMap().setCopy(Http::LowerCaseString(key), std::string(val)); + } + void erase(absl::string_view key) override { headerMap().remove(Http::LowerCaseString(key)); } + + FrameFlags frameFlags() const override { return frame_flags_; } + + virtual Http::RequestOrResponseHeaderMap& headerMap() const PURE; + + // Optional buffer for the raw body. This is only make sense for local response and + // request/responses in single frame mode. + Buffer::Instance& optionalBuffer() const { return buffer_; } + +protected: + FrameFlags frame_flags_; + mutable Envoy::Buffer::OwnedImpl buffer_; +}; + +class HttpRequestFrame : public HttpHeaderFrame { +public: + HttpRequestFrame(Http::RequestHeaderMapPtr request, bool end_stream) + : request_(std::move(request)) { + ASSERT(request_ != nullptr); + frame_flags_ = {StreamFlags{}, end_stream}; + } + + absl::string_view host() const override { return request_->getHostValue(); } + absl::string_view path() const override { return request_->getPathValue(); } + absl::string_view method() const override { return request_->getMethodValue(); } + + Http::RequestOrResponseHeaderMap& headerMap() const override { return *request_; } + Http::RequestHeaderMapPtr request_; +}; + +class HttpResponseFrame : public HttpHeaderFrame { +public: + HttpResponseFrame(Http::ResponseHeaderMapPtr response, bool end_stream) + : response_(std::move(response)) { + ASSERT(response_ != nullptr); + + const bool drain_close = Envoy::StringUtil::caseFindToken( + response_->getConnectionValue(), ",", Http::Headers::get().ConnectionValues.Close); + + frame_flags_ = {StreamFlags{0, false, drain_close, false}, end_stream}; + } + + StreamStatus status() const override { + auto status_view = response_->getStatusValue(); + int32_t status = 0; + if (absl::SimpleAtoi(status_view, &status)) { + return {status, status < 500 && status > 99}; + } + // Unknown HTTP status. Return -1 and false. + return {-1, false}; + } + + Http::RequestOrResponseHeaderMap& headerMap() const override { return *response_; } + + Http::ResponseHeaderMapPtr response_; +}; + +class HttpRawBodyFrame : public StreamFrame { +public: + HttpRawBodyFrame(Envoy::Buffer::Instance& buffer, bool end_stream) + : frame_flags_({StreamFlags{}, end_stream}) { + buffer_.move(buffer); + } + FrameFlags frameFlags() const override { return frame_flags_; } + + Buffer::Instance& buffer() const { return buffer_; } + +private: + mutable Buffer::OwnedImpl buffer_; + const FrameFlags frame_flags_; +}; + +class Utility { +public: + static absl::Status encodeRequestHeaders(Buffer::Instance& buffer, + const Http::RequestHeaderMap& headers, + bool chunk_encoding); + static absl::Status encodeResponseHeaders(Buffer::Instance& buffer, + const Http::ResponseHeaderMap& headers, + bool chunk_encoding); + static void encodeBody(Buffer::Instance& dst_buffer, Buffer::Instance& src_buffer, + bool chunk_encoding, bool end_stream); + + static absl::Status validateRequestHeaders(Http::RequestHeaderMap& headers); + static absl::Status validateResponseHeaders(Http::ResponseHeaderMap& headers, + Envoy::Http::Code code); + + static bool isChunked(const Http::RequestOrResponseHeaderMap& headers, bool bodiless); + + static bool hasBody(const Envoy::Http::Http1::Parser& parser, bool response, + bool response_for_head_request); + + static uint64_t statusToHttpStatus(absl::StatusCode status_code); +}; + +struct ActiveRequest { + Http::RequestHeaderMapPtr request_headers_; + + bool request_complete_{}; + bool response_chunk_encoding_{}; +}; + +struct ExpectResponse { + Http::ResponseHeaderMapPtr response_headers_; + + bool request_complete_{}; + bool head_request_{}; + bool request_chunk_encoding_{}; +}; + +class Http1CodecBase : public Http::Http1::ParserCallbacks, + public Envoy::Logger::Loggable { +public: + Http1CodecBase(bool single_frame_mode, uint32_t max_buffer_size, bool server_codec) + : single_frame_mode_(single_frame_mode), max_buffer_size_(max_buffer_size) { + if (server_codec) { + parser_ = Http::Http1::ParserPtr{new Http::Http1::BalsaParser( + Http::Http1::MessageType::Request, this, 64 * 1024, false, false)}; + } else { + parser_ = Http::Http1::ParserPtr{new Http::Http1::BalsaParser( + Http::Http1::MessageType::Response, this, 64 * 1024, false, false)}; + } + } + + // ParserCallbacks. + Http::Http1::CallbackResult onMessageBegin() override { + header_parsing_state_ = HeaderParsingState::Field; + return onMessageBeginImpl(); + } + Http::Http1::CallbackResult onUrl(const char* data, size_t length) override { + onUrlImpl(data, length); + return Http::Http1::CallbackResult::Success; + } + Http::Http1::CallbackResult onStatus(const char* data, size_t length) override { + onStatusImpl(data, length); + return Http::Http1::CallbackResult::Success; + } + Http::Http1::CallbackResult onHeaderField(const char* data, size_t length) override { + if (header_parsing_state_ == HeaderParsingState::Done) { + // Ignore trailers for now. + return Http::Http1::CallbackResult::Success; + } + if (header_parsing_state_ == HeaderParsingState::Value) { + completeCurrentHeader(); + } + current_header_field_.append(data, length); + + header_parsing_state_ = HeaderParsingState::Field; + return Http::Http1::CallbackResult::Success; + } + Http::Http1::CallbackResult onHeaderValue(const char* data, size_t length) override { + if (header_parsing_state_ == HeaderParsingState::Done) { + // Ignore trailers for now. + return Http::Http1::CallbackResult::Success; + } + + absl::string_view value(data, length); + if (current_header_value_.empty()) { + value = StringUtil::ltrim(value); + } + + current_header_value_.append(value.data(), value.size()); + + header_parsing_state_ = HeaderParsingState::Value; + return Http::Http1::CallbackResult::Success; + } + Http::Http1::CallbackResult onHeadersComplete() override { + completeCurrentHeader(); + header_parsing_state_ = HeaderParsingState::Done; + return onHeadersCompleteImpl(); + } + void bufferBody(const char* data, size_t length) override { buffered_body_.add(data, length); } + Http::Http1::CallbackResult onMessageComplete() override { return onMessageCompleteImpl(); } + void onChunkHeader(bool is_final_chunk) override { + if (is_final_chunk) { + dispatchBufferedBody(false); + } + } + + virtual Http::Http1::CallbackResult onMessageBeginImpl() PURE; + virtual void onUrlImpl(const char* data, size_t length) PURE; + virtual void onStatusImpl(const char* data, size_t length) PURE; + virtual Http::Http1::CallbackResult onHeadersCompleteImpl() PURE; + virtual Http::Http1::CallbackResult onMessageCompleteImpl() PURE; + + void completeCurrentHeader() { + current_header_value_.rtrim(); + current_header_field_.inlineTransform([](char c) { return absl::ascii_tolower(c); }); + headerMap().addViaMove(std::move(current_header_field_), std::move(current_header_value_)); + + ASSERT(current_header_field_.empty()); + ASSERT(current_header_value_.empty()); + } + + bool decodeBuffer(Buffer::Instance& buffer); + + void dispatchBufferedBody(bool end_stream); + bool bufferedBodyOverflow(); + + virtual Http::HeaderMap& headerMap() PURE; + + virtual void onDecodingSuccess(StreamFramePtr&& frame) PURE; + virtual void onDecodingFailure() PURE; + +protected: + enum class HeaderParsingState { Field, Value, Done }; + + Envoy::Buffer::OwnedImpl decoding_buffer_; + Envoy::Buffer::OwnedImpl encoding_buffer_; + + Buffer::OwnedImpl buffered_body_; + + Http::Http1::ParserPtr parser_; + Http::HeaderString current_header_field_; + Http::HeaderString current_header_value_; + HeaderParsingState header_parsing_state_{HeaderParsingState::Field}; + + const bool single_frame_mode_{}; + const uint32_t max_buffer_size_{}; + + bool deferred_end_stream_headers_{}; +}; + +class Http1ServerCodec : public Http1CodecBase, public ServerCodec { +public: + Http1ServerCodec(bool single_frame_mode, uint32_t max_buffer_size) + : Http1CodecBase(single_frame_mode, max_buffer_size, true) {} + + Http::Http1::CallbackResult onMessageBeginImpl() override; + void onUrlImpl(const char* data, size_t length) override { + ASSERT(active_request_.has_value()); + ASSERT(active_request_->request_headers_ != nullptr); + active_request_->request_headers_->setPath(absl::string_view(data, length)); + } + void onStatusImpl(const char*, size_t) override {} + Http::Http1::CallbackResult onHeadersCompleteImpl() override; + Http::Http1::CallbackResult onMessageCompleteImpl() override; + + Http::HeaderMap& headerMap() override { return *active_request_->request_headers_; } + + void setCodecCallbacks(ServerCodecCallbacks& callbacks) override { callbacks_ = &callbacks; } + void decode(Envoy::Buffer::Instance& buffer, bool) override { + if (!decodeBuffer(buffer)) { + callbacks_->onDecodingFailure(); + } + } + void encode(const StreamFrame& frame, EncodingCallbacks& callbacks) override; + ResponsePtr respond(absl::Status status, absl::string_view data, const Request&) override { + auto response = Http::ResponseHeaderMapImpl::create(); + response->setStatus(std::to_string(Utility::statusToHttpStatus(status.code()))); + response->setContentLength(data.size()); + response->addCopy(Http::LowerCaseString("reason"), status.message()); + auto response_frame = std::make_unique(std::move(response), true); + response_frame->optionalBuffer().add(data.data(), data.size()); + return response_frame; + } + + void onDecodingSuccess(StreamFramePtr&& frame) override { + if (callbacks_->connection().has_value()) { + callbacks_->onDecodingSuccess(std::move(frame)); + } + + // Connection may have been closed by the callback. + if (!callbacks_->connection().has_value() || + callbacks_->connection()->state() != Network::Connection::State::Open) { + parser_->pause(); + } + } + + // TODO(wbpcode): send 400 bad request to client as response and then call the callback. + void onDecodingFailure() override { callbacks_->onDecodingFailure(); } + + absl::optional active_request_; + ServerCodecCallbacks* callbacks_{}; +}; + +class Http1ClientCodec : public Http1CodecBase, public ClientCodec { +public: + Http1ClientCodec(bool single_frame_mode, uint32_t max_buffer_size) + : Http1CodecBase(single_frame_mode, max_buffer_size, false) {} + + Http::Http1::CallbackResult onMessageBeginImpl() override; + void onUrlImpl(const char*, size_t) override {} + void onStatusImpl(const char*, size_t) override {} + Http::Http1::CallbackResult onHeadersCompleteImpl() override; + Http::Http1::CallbackResult onMessageCompleteImpl() override; + + Http::HeaderMap& headerMap() override { return *expect_response_->response_headers_; } + + void setCodecCallbacks(ClientCodecCallbacks& callbacks) override { callbacks_ = &callbacks; } + void decode(Envoy::Buffer::Instance& buffer, bool) override { + if (!decodeBuffer(buffer)) { + callbacks_->onDecodingFailure(); + } + } + void encode(const StreamFrame& frame, EncodingCallbacks& callbacks) override; + + void onDecodingSuccess(StreamFramePtr&& frame) override { + if (callbacks_->connection().has_value()) { + callbacks_->onDecodingSuccess(std::move(frame)); + } + + // Connection may have been closed by the callback. + if (!callbacks_->connection().has_value() || + callbacks_->connection()->state() != Network::Connection::State::Open) { + parser_->pause(); + } + } + void onDecodingFailure() override { callbacks_->onDecodingFailure(); } + + absl::optional expect_response_; + + ClientCodecCallbacks* callbacks_{}; +}; + +class Http1CodecFactory : public CodecFactory { +public: + Http1CodecFactory(bool single_frame_mode, uint32_t max_buffer_size) + : single_frame_mode_(single_frame_mode), max_buffer_size_(max_buffer_size) {} + + ClientCodecPtr createClientCodec() const override { + return std::make_unique(single_frame_mode_, max_buffer_size_); + } + + ServerCodecPtr createServerCodec() const override { + return std::make_unique(single_frame_mode_, max_buffer_size_); + } + +private: + const bool single_frame_mode_{}; + const uint32_t max_buffer_size_{}; +}; + +class Http1CodecFactoryConfig : public CodecFactoryConfig { +public: + // CodecFactoryConfig + CodecFactoryPtr + createCodecFactory(const Envoy::Protobuf::Message& config, + Envoy::Server::Configuration::FactoryContext& context) override; + std::string name() const override { return "envoy.generic_proxy.codecs.http1"; } + Envoy::ProtobufTypes::MessagePtr createEmptyConfigProto() override { + return std::make_unique(); + } +}; + +} // namespace Http1 +} // namespace Codec +} // namespace GenericProxy +} // namespace NetworkFilters +} // namespace Extensions +} // namespace Envoy diff --git a/contrib/generic_proxy/filters/network/source/router/router.cc b/contrib/generic_proxy/filters/network/source/router/router.cc index 2a7cbd69900b..47f3eb5fa5e0 100644 --- a/contrib/generic_proxy/filters/network/source/router/router.cc +++ b/contrib/generic_proxy/filters/network/source/router/router.cc @@ -31,21 +31,15 @@ constexpr absl::string_view RouterFilterName = "envoy.filters.generic.router"; } // namespace void GenericUpstream::writeToConnection(Buffer::Instance& buffer) { - if (is_cleaned_up_) { - return; - } - - if (owned_conn_data_ != nullptr) { - ASSERT(owned_conn_data_->connection().state() == Network::Connection::State::Open); + if (owned_conn_data_ != nullptr && + owned_conn_data_->connection().state() == Network::Connection::State::Open) { owned_conn_data_->connection().write(buffer, false); } } OptRef GenericUpstream::connection() { - if (is_cleaned_up_) { - return {}; - } - if (owned_conn_data_ != nullptr) { + if (owned_conn_data_ != nullptr && + owned_conn_data_->connection().state() == Network::Connection::State::Open) { return {owned_conn_data_->connection()}; } return {}; @@ -301,10 +295,10 @@ void UpstreamRequest::startStream() { } void UpstreamRequest::resetStream(StreamResetReason reason) { - if (stream_reset_) { + if (reset_or_response_complete_) { return; } - stream_reset_ = true; + reset_or_response_complete_ = true; ENVOY_LOG(debug, "generic proxy upstream request: reset upstream request"); @@ -329,9 +323,10 @@ void UpstreamRequest::resetStream(StreamResetReason reason) { void UpstreamRequest::clearStream(bool close_connection) { // Set the upstream response complete flag to true first to ensure the possible // connection close event will not be handled. - response_complete_ = true; + reset_or_response_complete_ = true; - ENVOY_LOG(debug, "generic proxy upstream request: complete upstream request"); + ENVOY_LOG(debug, "generic proxy upstream request: complete upstream request ()", + close_connection); if (span_ != nullptr) { TraceContextBridge trace_context{*parent_.request_stream_}; @@ -463,12 +458,22 @@ void UpstreamRequest::onDecodingSuccess(StreamFramePtr response) { } } -void UpstreamRequest::onDecodingFailure() { resetStream(StreamResetReason::ProtocolError); } +void UpstreamRequest::onDecodingFailure() { + // Decoding failure after the response is complete, close the connection. + // This should only happen when some special cases, for example: + // The HTTP response is complete but the request is not fully sent. + // The codec will throw an error after the response is complete. + if (reset_or_response_complete_) { + generic_upstream_->cleanUp(true); + return; + } + resetStream(StreamResetReason::ProtocolError); +} void UpstreamRequest::onConnectionClose(Network::ConnectionEvent event) { // If the upstream response is complete or the upstream request is reset then // ignore the connection close event. - if (response_complete_ || stream_reset_) { + if (reset_or_response_complete_) { return; } diff --git a/contrib/generic_proxy/filters/network/source/router/router.h b/contrib/generic_proxy/filters/network/source/router/router.h index 64f41bba5a51..205908b6cdf8 100644 --- a/contrib/generic_proxy/filters/network/source/router/router.h +++ b/contrib/generic_proxy/filters/network/source/router/router.h @@ -183,8 +183,7 @@ class UpstreamRequest : public LinkedObject, absl::optional connecting_start_time_; // One of these flags should be set to true when the request is complete. - bool stream_reset_{}; - bool response_complete_{}; + bool reset_or_response_complete_{}; bool expects_response_{}; diff --git a/contrib/generic_proxy/filters/network/source/upstream.cc b/contrib/generic_proxy/filters/network/source/upstream.cc index 3af4c0b712a1..8124e03f0738 100644 --- a/contrib/generic_proxy/filters/network/source/upstream.cc +++ b/contrib/generic_proxy/filters/network/source/upstream.cc @@ -6,11 +6,14 @@ namespace NetworkFilters { namespace GenericProxy { UpstreamConnection::~UpstreamConnection() { - // Do clean up here again to ensure the cleanUp is called. This is safe to call - // multiple times because of the is_cleand_up_ flag. - // TODO(wbpcode): Clarify/resolve bypassing of virtual dispatch - // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.VirtualCall) - this->cleanUp(true); + // In case we doesn't clean up the pending connecting request. + if (tcp_pool_handle_ != nullptr) { + // Clear the data first. + auto local_handle = tcp_pool_handle_; + tcp_pool_handle_ = nullptr; + + local_handle->cancel(Tcp::ConnectionPool::CancelPolicy::Default); + } } void UpstreamConnection::initialize() { @@ -21,34 +24,33 @@ void UpstreamConnection::initialize() { } void UpstreamConnection::cleanUp(bool close_connection) { - // If the cleanUp is called multiple times, just return. - if (is_cleaned_up_) { - return; - } - - ENVOY_LOG(debug, "generic proxy upstream manager: clean up upstream connection"); - // Set is_cleaned_up_ flag to true to avoid double clean up. - is_cleaned_up_ = true; + ENVOY_LOG(debug, "generic proxy upstream manager: clean up upstream (close: {})", + close_connection); if (close_connection && owned_conn_data_ != nullptr) { ENVOY_LOG(debug, "generic proxy upstream request: close upstream connection"); ASSERT(tcp_pool_handle_ == nullptr); - owned_conn_data_->connection().close(Network::ConnectionCloseType::FlushWrite); + + // Clear the data first to avoid re-entering this function in the close callback. + auto local_data = std::move(owned_conn_data_); + owned_conn_data_.reset(); + + local_data->connection().close(Network::ConnectionCloseType::FlushWrite); } - owned_conn_data_.reset(); if (tcp_pool_handle_ != nullptr) { ENVOY_LOG(debug, "generic proxy upstream manager: cacel upstream connection"); - ASSERT(owned_conn_data_ == nullptr); - tcp_pool_handle_->cancel(Tcp::ConnectionPool::CancelPolicy::Default); + + // Clear the data first. + auto local_handle = tcp_pool_handle_; tcp_pool_handle_ = nullptr; + + local_handle->cancel(Tcp::ConnectionPool::CancelPolicy::Default); } } void UpstreamConnection::onUpstreamData(Buffer::Instance& data, bool end_stream) { - ASSERT(!is_cleaned_up_); - if (data.length() == 0) { return; } diff --git a/contrib/generic_proxy/filters/network/source/upstream.h b/contrib/generic_proxy/filters/network/source/upstream.h index dc88b29d72cf..8e4e1f062dbf 100644 --- a/contrib/generic_proxy/filters/network/source/upstream.h +++ b/contrib/generic_proxy/filters/network/source/upstream.h @@ -50,7 +50,6 @@ class UpstreamConnection : public Envoy::Event::DeferredDeletable, Upstream::TcpPoolData tcp_pool_data_; ClientCodecPtr client_codec_; - bool is_cleaned_up_{}; // Whether the upstream connection is created. This will be set to true when the initialize() // is called. bool initialized_{}; diff --git a/contrib/generic_proxy/filters/network/test/codecs/dubbo/config_test.cc b/contrib/generic_proxy/filters/network/test/codecs/dubbo/config_test.cc index 6a4e3abb8614..f689f79024e2 100644 --- a/contrib/generic_proxy/filters/network/test/codecs/dubbo/config_test.cc +++ b/contrib/generic_proxy/filters/network/test/codecs/dubbo/config_test.cc @@ -1,7 +1,7 @@ #include #include -#include "source/extensions/common/dubbo/message_impl.h" +#include "source/extensions/common/dubbo/message.h" #include "test/extensions/common/dubbo/mocks.h" #include "test/mocks/server/factory_context.h" @@ -25,26 +25,15 @@ using testing::Return; using namespace Common::Dubbo; MessageMetadataSharedPtr createDubboRequst(bool one_way_request) { - auto request = std::make_unique(); - request->setServiceName("fake_service"); - request->setMethodName("fake_method"); - request->setServiceVersion("fake_version"); - request->setParametersLazyCallback([]() -> RpcRequestImpl::ParametersPtr { - return std::make_unique(); - }); - request->setAttachmentLazyCallback([]() -> RpcRequestImpl::AttachmentPtr { - auto map = std::make_unique(); - Hessian2::ObjectPtr key_o = std::make_unique("group"); - Hessian2::ObjectPtr val_o = std::make_unique("fake_group"); - - map->toMutableUntypedMap().value().get().emplace(std::move(key_o), std::move(val_o)); - return std::make_unique(std::move(map), 0); - }); + auto request = std::make_unique("fake_dubbo_version", "fake_service", "fake_version", + "fake_method"); + + request->content().initialize("", {}, {}); + request->content().setAttachment("group", "fake_group"); auto context = std::make_unique(); context->setMessageType(one_way_request ? MessageType::Oneway : MessageType::Request); context->setRequestId(123456); - context->setSerializeType(SerializeType::Hessian2); auto metadata = std::make_shared(); metadata->setContext(std::move(context)); @@ -245,7 +234,7 @@ TEST(DubboServerCodecTest, DubboServerCodecTest) { buffer.add("anything"); EXPECT_CALL(*raw_serializer, deserializeRpcRequest(_, _)) - .WillOnce(Return(ByMove(std::make_unique()))); + .WillOnce(Return(ByMove(std::make_unique("a", "b", "c", "d")))); EXPECT_CALL(callbacks, onDecodingSuccess(_)); server_codec.decode(buffer, false); @@ -269,42 +258,44 @@ TEST(DubboServerCodecTest, DubboServerCodecTest) { Status status = absl::OkStatus(); DubboRequest request(createDubboRequst(false)); - auto response = server_codec.respond(status, "", request); + auto response = server_codec.respond(status, "anything", request); auto* typed_response = static_cast(response.get()); - auto* typed_inner_response = - static_cast(&typed_response->inner_metadata_->mutableResponse()); + auto& typed_inner_response = typed_response->inner_metadata_->mutableResponse(); EXPECT_EQ(ResponseStatus::Ok, typed_response->inner_metadata_->responseStatus()); - EXPECT_EQ(RpcResponseType::ResponseWithException, typed_inner_response->responseType().value()); - EXPECT_EQ("exception_via_proxy", typed_inner_response->localRawMessage().value()); + EXPECT_EQ(RpcResponseType::ResponseWithException, typed_inner_response.responseType().value()); + EXPECT_EQ("anything", typed_inner_response.content().result()->toString().value().get()); + EXPECT_EQ("envoy_response", typed_inner_response.content().attachments().at("reason")); } { Status status(StatusCode::kInvalidArgument, "test_message"); DubboRequest request(createDubboRequst(false)); - auto response = server_codec.respond(status, "", request); + auto response = server_codec.respond(status, "anything", request); auto* typed_response = static_cast(response.get()); - auto* typed_inner_response = - static_cast(&typed_response->inner_metadata_->mutableResponse()); + auto& typed_inner_response = typed_response->inner_metadata_->mutableResponse(); EXPECT_EQ(ResponseStatus::BadRequest, typed_response->inner_metadata_->responseStatus()); - EXPECT_EQ(false, typed_inner_response->responseType().has_value()); - EXPECT_EQ("test_message", typed_inner_response->localRawMessage().value()); + EXPECT_EQ(false, typed_inner_response.responseType().has_value()); + + EXPECT_EQ("anything", typed_inner_response.content().result()->toString().value().get()); + EXPECT_EQ("test_message", typed_inner_response.content().attachments().at("reason")); } { Status status(StatusCode::kAborted, "test_message2"); DubboRequest request(createDubboRequst(false)); - auto response = server_codec.respond(status, "", request); + auto response = server_codec.respond(status, "anything", request); auto* typed_response = static_cast(response.get()); - auto* typed_inner_response = - static_cast(&typed_response->inner_metadata_->mutableResponse()); + auto& typed_inner_response = typed_response->inner_metadata_->mutableResponse(); EXPECT_EQ(ResponseStatus::ServerError, typed_response->inner_metadata_->responseStatus()); - EXPECT_EQ(false, typed_inner_response->responseType().has_value()); - EXPECT_EQ("test_message2", typed_inner_response->localRawMessage().value()); + EXPECT_EQ(false, typed_inner_response.responseType().has_value()); + + EXPECT_EQ("anything", typed_inner_response.content().result()->toString().value().get()); + EXPECT_EQ("test_message2", typed_inner_response.content().attachments().at("reason")); } } @@ -365,7 +356,7 @@ TEST(DubboClientCodecTest, DubboClientCodecTest) { buffer.writeBEInt(8); buffer.add("anything"); - auto response = std::make_unique(); + auto response = std::make_unique(); response->setResponseType(RpcResponseType::ResponseWithValue); EXPECT_CALL(*raw_serializer, deserializeRpcResponse(_, _)) diff --git a/contrib/generic_proxy/filters/network/test/codecs/http1/BUILD b/contrib/generic_proxy/filters/network/test/codecs/http1/BUILD new file mode 100644 index 000000000000..d13a77613895 --- /dev/null +++ b/contrib/generic_proxy/filters/network/test/codecs/http1/BUILD @@ -0,0 +1,21 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_test", + "envoy_contrib_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_contrib_package() + +envoy_cc_test( + name = "config_test", + srcs = [ + "config_test.cc", + ], + deps = [ + "//contrib/generic_proxy/filters/network/source/codecs/http1:config", + "//contrib/generic_proxy/filters/network/test/mocks:codec_mocks", + "//test/mocks/server:factory_context_mocks", + ], +) diff --git a/contrib/generic_proxy/filters/network/test/codecs/http1/config_test.cc b/contrib/generic_proxy/filters/network/test/codecs/http1/config_test.cc new file mode 100644 index 000000000000..118c48d760c0 --- /dev/null +++ b/contrib/generic_proxy/filters/network/test/codecs/http1/config_test.cc @@ -0,0 +1,1482 @@ +#include +#include + +#include "test/mocks/server/factory_context.h" + +#include "contrib/generic_proxy/filters/network/source/codecs/http1/config.h" +#include "contrib/generic_proxy/filters/network/test/mocks/codec.h" +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace NetworkFilters { +namespace GenericProxy { +namespace Codec { +namespace Http1 { +namespace { + +using testing::NiceMock; + +TEST(Http1MessageFrameTest, Http1MessageFrameTest) { + // Protocol. + { + auto headers = Http::RequestHeaderMapImpl::create(); + HttpRequestFrame frame(std::move(headers), true); + + EXPECT_EQ(frame.protocol(), "http1"); + } + + // ForEach. + { + auto headers = Http::RequestHeaderMapImpl::create(); + // Add some headers. + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + // Add some custom headers. + headers->addCopy(Http::LowerCaseString("custom"), "value"); + + HttpRequestFrame frame(std::move(headers), true); + + // Check that the headers are iterated correctly. + size_t count = 0; + frame.forEach([&count](absl::string_view, absl::string_view) -> bool { + count++; + return true; + }); + + EXPECT_EQ(count, 4); + } + + // Get. + { + auto headers = Http::RequestHeaderMapImpl::create(); + // Add some headers. + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + // Add some custom headers. + headers->addCopy(Http::LowerCaseString("custom"), "value"); + + HttpRequestFrame frame(std::move(headers), true); + + // Check that the headers are retrieved correctly. + EXPECT_EQ(frame.get("host").value(), "host"); + EXPECT_EQ(frame.get(":authority").value(), "host"); + EXPECT_EQ(frame.get(":path").value(), "/path"); + EXPECT_EQ(frame.get(":method").value(), "GET"); + EXPECT_EQ(frame.get("custom").value(), "value"); + } + + // Set. + { + auto headers = Http::RequestHeaderMapImpl::create(); + HttpRequestFrame frame(std::move(headers), true); + + // Set some headers. + frame.set("host", "host"); + frame.set(":path", "/path"); + frame.set(":method", "POST"); + frame.set("custom", "value"); + + // Check that the headers are set correctly. + EXPECT_EQ(frame.get("host").value(), "host"); + EXPECT_EQ(frame.get(":authority").value(), "host"); + EXPECT_EQ(frame.get(":path").value(), "/path"); + EXPECT_EQ(frame.get(":method").value(), "POST"); + EXPECT_EQ(frame.get("custom").value(), "value"); + } + + // Erase. + { + auto headers = Http::RequestHeaderMapImpl::create(); + // Add some headers. + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + // Add some custom headers. + headers->addCopy(Http::LowerCaseString("custom"), "value"); + + HttpRequestFrame frame(std::move(headers), true); + + // Erase some headers. + frame.erase("host"); + frame.erase(":path"); + frame.erase(":method"); + frame.erase("custom"); + + // Check that the headers are erased correctly. + EXPECT_EQ(frame.get("host"), absl::nullopt); + EXPECT_EQ(frame.get(":authority"), absl::nullopt); + EXPECT_EQ(frame.get(":path"), absl::nullopt); + EXPECT_EQ(frame.get(":method"), absl::nullopt); + EXPECT_EQ(frame.get("custom"), absl::nullopt); + } + + // Request FrameFlags. + { + auto headers = Http::RequestHeaderMapImpl::create(); + HttpRequestFrame frame(std::move(headers), true); + + EXPECT_EQ(true, frame.frameFlags().endStream()); + + auto headers2 = Http::RequestHeaderMapImpl::create(); + HttpRequestFrame frame2(std::move(headers2), false); + + EXPECT_EQ(false, frame2.frameFlags().endStream()); + } + + // Response FrameFlags. + { + auto headers = Http::ResponseHeaderMapImpl::create(); + HttpResponseFrame frame(std::move(headers), true); + + EXPECT_EQ(true, frame.frameFlags().endStream()); + EXPECT_EQ(false, frame.frameFlags().streamFlags().drainClose()); + + auto headers2 = Http::ResponseHeaderMapImpl::create(); + headers2->setConnection("close"); + HttpResponseFrame frame2(std::move(headers2), false); + + EXPECT_EQ(false, frame2.frameFlags().endStream()); + EXPECT_EQ(true, frame2.frameFlags().streamFlags().drainClose()); + } + + // Request FrameType. + { + auto headers = Http::RequestHeaderMapImpl::create(); + + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + + HttpRequestFrame frame(std::move(headers), true); + + EXPECT_EQ(frame.host(), "host"); + EXPECT_EQ(frame.path(), "/path"); + EXPECT_EQ(frame.method(), "GET"); + } + + // Response FrameType. + { + // 200 + auto headers = Http::ResponseHeaderMapImpl::create(); + headers->setStatus(200); + HttpResponseFrame frame(std::move(headers), true); + + EXPECT_EQ(frame.status().code(), 200); + EXPECT_TRUE(frame.status().ok()); + + // 404 + auto headers2 = Http::ResponseHeaderMapImpl::create(); + headers2->setStatus(404); + HttpResponseFrame frame2(std::move(headers2), true); + + EXPECT_EQ(frame2.status().code(), 404); + EXPECT_TRUE(frame2.status().ok()); + + // 500 + auto headers3 = Http::ResponseHeaderMapImpl::create(); + headers3->setStatus(500); + HttpResponseFrame frame3(std::move(headers3), true); + + EXPECT_EQ(frame3.status().code(), 500); + EXPECT_FALSE(frame3.status().ok()); + + // 503 + auto headers4 = Http::ResponseHeaderMapImpl::create(); + headers4->setStatus(503); + HttpResponseFrame frame4(std::move(headers4), true); + + EXPECT_EQ(frame4.status().code(), 503); + EXPECT_FALSE(frame4.status().ok()); + + // Invalid status code value. + auto headers5 = Http::ResponseHeaderMapImpl::create(); + headers5->addCopy(Http::Headers::get().Status, "xxx"); + HttpResponseFrame frame5(std::move(headers5), true); + + EXPECT_EQ(frame5.status().code(), -1); + EXPECT_FALSE(frame5.status().ok()); + } + + // Raw body frame. + { + Buffer::OwnedImpl buffer("body"); + + HttpRawBodyFrame frame(buffer, true); + + EXPECT_EQ(frame.frameFlags().endStream(), true); + + EXPECT_EQ(frame.buffer().length(), 4); + EXPECT_EQ(frame.buffer().toString(), "body"); + + Buffer::OwnedImpl new_buffer; + new_buffer.move(frame.buffer()); + + EXPECT_EQ(new_buffer.toString(), "body"); + + EXPECT_EQ(frame.buffer().length(), 0); + } +} + +class Http1ServerCodecTest : public testing::Test { +public: + Http1ServerCodecTest() { initializeCodec(); } + + void initializeCodec(bool single_frame_mode = false, uint32_t max_buffer_size = 8 * 1024 * 1024) { + codec_ = std::make_unique(single_frame_mode, max_buffer_size); + codec_->setCodecCallbacks(codec_callbacks_); + } + + NiceMock codec_callbacks_; + NiceMock mock_connection; + std::unique_ptr codec_; +}; + +TEST_F(Http1ServerCodecTest, HeaderOnlyRequestDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Content-Length: 0\r\n" + "custom: value\r\n" + "\r\n"); + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).WillOnce(Invoke([](StreamFramePtr frame) { + EXPECT_EQ(frame->frameFlags().endStream(), true); + + auto* request = dynamic_cast(frame.get()); + + EXPECT_EQ(request->host(), "host"); + EXPECT_EQ(request->path(), "/"); + EXPECT_EQ(request->method(), "GET"); + EXPECT_EQ(request->get("host").value(), "host"); + EXPECT_EQ(request->get(":authority").value(), "host"); + EXPECT_EQ(request->get(":path").value(), "/"); + EXPECT_EQ(request->get(":method").value(), "GET"); + EXPECT_EQ(request->get("custom").value(), "value"); + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ServerCodecTest, RequestDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Content-Length: 4\r\n" + "custom: value\r\n" + "\r\n" + "body"); + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)) + .Times(2) + .WillRepeatedly(Invoke([](StreamFramePtr frame) { + auto* request = dynamic_cast(frame.get()); + if (request != nullptr) { + EXPECT_EQ(frame->frameFlags().endStream(), false); + + EXPECT_EQ(request->host(), "host"); + EXPECT_EQ(request->path(), "/"); + EXPECT_EQ(request->method(), "GET"); + EXPECT_EQ(request->get("host").value(), "host"); + EXPECT_EQ(request->get(":authority").value(), "host"); + EXPECT_EQ(request->get(":path").value(), "/"); + EXPECT_EQ(request->get(":method").value(), "GET"); + EXPECT_EQ(request->get("custom").value(), "value"); + } + + auto* body = dynamic_cast(frame.get()); + if (body != nullptr) { + EXPECT_EQ(frame->frameFlags().endStream(), true); + + EXPECT_EQ(body->buffer().length(), 4); + EXPECT_EQ(body->buffer().toString(), "body"); + } + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ServerCodecTest, ChunkedRequestDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)) + .Times(3) // One for headers and one for body, and one for the last chunk. + .WillRepeatedly(Invoke([](StreamFramePtr frame) { + auto* request = dynamic_cast(frame.get()); + if (request != nullptr) { + EXPECT_EQ(frame->frameFlags().endStream(), false); + + EXPECT_EQ(request->host(), "host"); + EXPECT_EQ(request->path(), "/"); + EXPECT_EQ(request->method(), "GET"); + EXPECT_EQ(request->get("host").value(), "host"); + EXPECT_EQ(request->get(":authority").value(), "host"); + EXPECT_EQ(request->get(":path").value(), "/"); + EXPECT_EQ(request->get(":method").value(), "GET"); + EXPECT_EQ(request->get("custom").value(), "value"); + } + + auto* body = dynamic_cast(frame.get()); + if (body != nullptr) { + if (!frame->frameFlags().endStream()) { + EXPECT_EQ(body->buffer().length(), 4); + EXPECT_EQ(body->buffer().toString(), "body"); + } else { + EXPECT_EQ(body->buffer().length(), 0); + } + } + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ServerCodecTest, MultipleBufferChunkedRequestDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n"); // Chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)) + .Times(2) + .WillRepeatedly(Invoke([](StreamFramePtr frame) { + auto* request = dynamic_cast(frame.get()); + if (request != nullptr) { + EXPECT_EQ(frame->frameFlags().endStream(), false); + + EXPECT_EQ(request->host(), "host"); + EXPECT_EQ(request->path(), "/"); + EXPECT_EQ(request->method(), "GET"); + EXPECT_EQ(request->get("host").value(), "host"); + EXPECT_EQ(request->get(":authority").value(), "host"); + EXPECT_EQ(request->get(":path").value(), "/"); + EXPECT_EQ(request->get(":method").value(), "GET"); + EXPECT_EQ(request->get("custom").value(), "value"); + } else { + auto* body = dynamic_cast(frame.get()); + EXPECT_EQ(frame->frameFlags().endStream(), false); + EXPECT_EQ(body->buffer().length(), 4); + EXPECT_EQ(body->buffer().toString(), "body"); + } + })); + + codec_->decode(buffer, false); + + EXPECT_EQ(buffer.length(), 0); + + buffer.add("0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).WillOnce(Invoke([](StreamFramePtr frame) { + auto* body = dynamic_cast(frame.get()); + EXPECT_EQ(frame->frameFlags().endStream(), true); + EXPECT_EQ(body->buffer().length(), 0); + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ServerCodecTest, UnexpectedRequestTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + // Connect. + { + initializeCodec(); + + Buffer::OwnedImpl buffer; + + buffer.add("CONNECT host:443 HTTP/1.1\r\n" + "custom: value\r\n" + "\r\n"); + + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer, false); + } + + // Transfer-Encoding and Content-Length are set at same time. + { + initializeCodec(); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Transfer-Encoding: chunked\r\n" + "Content-Length: 4\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer, false); + } + + // Unknown Transfer-Encoding. + { + initializeCodec(); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Transfer-Encoding: gzip, chunked\r\n" // Only 'chunked' is supported. + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer, false); + } + + // Lost required request headers. + { + initializeCodec(); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "custom: value\r\n" + "\r\n"); + + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer, false); + } + + // HTTP1.0 request. + { + initializeCodec(); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.0\r\n" + "Host: host\r\n" + "custom: value\r\n" + "\r\n"); + + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer, false); + } +} + +TEST_F(Http1ServerCodecTest, RespondTest) { + // Create a request. + auto headers = Http::RequestHeaderMapImpl::create(); + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + headers->addCopy(Http::LowerCaseString("custom"), "value"); + + HttpRequestFrame request(std::move(headers), true); + + // Respond with a response. + { + for (int i = 0; i <= 16; i++) { + auto response = + codec_->respond(absl::Status(static_cast(i), ""), "", request); + + EXPECT_NE(response, nullptr); + EXPECT_NE(dynamic_cast(response.get())->response_, nullptr); + + auto* response_headers = dynamic_cast(response.get())->response_.get(); + EXPECT_EQ(response_headers->getStatusValue(), + std::to_string(Utility::statusToHttpStatus(static_cast(i)))); + } + } +} + +TEST_F(Http1ServerCodecTest, HeaderOnlyResponseEncodingTest) { + // Create a response. + auto headers = Http::ResponseHeaderMapImpl::create(); + headers->setStatus(200); + + HttpResponseFrame response(std::move(headers), true); + + NiceMock encoding_callbacks; + + // Encode the response. + { + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "HTTP/1.1 200 OK\r\n" + "\r\n"); + })); + + codec_->encode(response, encoding_callbacks); + } +} + +TEST_F(Http1ServerCodecTest, ResponseEncodingTest) { + // Create a response. + auto headers = Http::ResponseHeaderMapImpl::create(); + headers->setStatus(200); + headers->addCopy(Http::Headers::get().ContentLength, "4"); + + HttpResponseFrame response(std::move(headers), false); + + Buffer::OwnedImpl body_buffer("body"); + HttpRawBodyFrame body(body_buffer, true); + + NiceMock encoding_callbacks; + + // Encode the response. + { + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, false)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "HTTP/1.1 200 OK\r\n" + "content-length: 4\r\n" + "\r\n"); + buffer.drain(buffer.length()); + })); + + codec_->encode(response, encoding_callbacks); + + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "body"); + + buffer.drain(buffer.length()); + })); + + codec_->encode(body, encoding_callbacks); + } +} + +TEST_F(Http1ServerCodecTest, ChunkedResponseEncodingTest) { + // Create a response. + auto headers = Http::ResponseHeaderMapImpl::create(); + headers->setStatus(200); + headers->addCopy(Http::Headers::get().TransferEncoding, "chunked"); + + HttpResponseFrame response(std::move(headers), false); + + Buffer::OwnedImpl body_buffer("body"); + HttpRawBodyFrame body(body_buffer, true); + + NiceMock encoding_callbacks; + + // Encode the response. + { + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, false)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "HTTP/1.1 200 OK\r\n" + "transfer-encoding: chunked\r\n" + "\r\n"); + buffer.drain(buffer.length()); + })); + + codec_->encode(response, encoding_callbacks); + + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + buffer.drain(buffer.length()); + })); + + codec_->encode(body, encoding_callbacks); + } +} + +TEST_F(Http1ServerCodecTest, RequestAndResponseTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + // Do repeated request and response. + for (size_t i = 0; i < 100; i++) { + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).Times(3); + + codec_->decode(buffer, false); + + // Create a response. + auto headers = Http::ResponseHeaderMapImpl::create(); + headers->setStatus(200); + headers->addCopy(Http::Headers::get().TransferEncoding, "chunked"); + + HttpResponseFrame response(std::move(headers), false); + + Buffer::OwnedImpl body_buffer("body"); + HttpRawBodyFrame body(body_buffer, true); + + NiceMock encoding_callbacks; + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, _)).Times(2); + + // Encode the response. + codec_->encode(response, encoding_callbacks); + codec_->encode(body, encoding_callbacks); + } +} + +TEST_F(Http1ServerCodecTest, ResponseCompleteBeforeRequestCompleteTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n"); // Chunk footer. No last chunk header and footer, this is an incomplete + // request. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).Times(2); + + codec_->decode(buffer, false); + + // Create a response. + auto headers = Http::ResponseHeaderMapImpl::create(); + headers->setStatus(200); + headers->addCopy(Http::Headers::get().TransferEncoding, "chunked"); + + HttpResponseFrame response(std::move(headers), false); + + Buffer::OwnedImpl body_buffer("body"); + HttpRawBodyFrame body(body_buffer, true); + + NiceMock encoding_callbacks; + + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, false)); + // Encode the response. + codec_->encode(response, encoding_callbacks); + + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)); + // Response is complete, but request is not complete, so the codec should close the connection. + EXPECT_CALL(mock_connection, close(Network::ConnectionCloseType::FlushWrite)); + + codec_->encode(body, encoding_callbacks); +} + +TEST_F(Http1ServerCodecTest, NewRequestBeforeFirstRequestCompleteTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + Buffer::OwnedImpl buffer2; + buffer2.add(buffer); + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).Times(3); + + codec_->decode(buffer, false); + + // First request is not complete, so the codec should close the connection. + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer2, false); +} + +TEST_F(Http1ServerCodecTest, SingleFrameModeRequestDecodingTest) { + initializeCodec(true, 8 * 1024 * 1024); + + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Content-Length: 4\r\n" + "custom: value\r\n" + "\r\n" + "body"); + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).WillOnce(Invoke([](StreamFramePtr frame) { + auto* request = dynamic_cast(frame.get()); + + EXPECT_EQ(frame->frameFlags().endStream(), true); + + EXPECT_EQ(request->host(), "host"); + EXPECT_EQ(request->path(), "/"); + EXPECT_EQ(request->method(), "GET"); + EXPECT_EQ(request->get("host").value(), "host"); + EXPECT_EQ(request->get(":authority").value(), "host"); + EXPECT_EQ(request->get(":path").value(), "/"); + EXPECT_EQ(request->get(":method").value(), "GET"); + EXPECT_EQ(request->get("custom").value(), "value"); + + EXPECT_EQ(request->optionalBuffer().length(), 4); + EXPECT_EQ(request->optionalBuffer().toString(), "body"); + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ServerCodecTest, SingleFrameModeRequestTooLargeTest) { + initializeCodec(true, 4); + + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Content-Length: 4\r\n" + "custom: value\r\n" + "\r\n" + "body~"); + + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer, false); +} + +TEST_F(Http1ServerCodecTest, SingleFrameModeChunkedRequestDecodingTest) { + initializeCodec(true, 8 * 1024 * 1024); + + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).WillOnce(Invoke([](StreamFramePtr frame) { + auto* request = dynamic_cast(frame.get()); + + EXPECT_EQ(frame->frameFlags().endStream(), true); + + EXPECT_EQ(request->host(), "host"); + EXPECT_EQ(request->path(), "/"); + EXPECT_EQ(request->method(), "GET"); + EXPECT_EQ(request->get("host").value(), "host"); + EXPECT_EQ(request->get(":authority").value(), "host"); + EXPECT_EQ(request->get(":path").value(), "/"); + EXPECT_EQ(request->get(":method").value(), "GET"); + EXPECT_EQ(request->get("custom").value(), "value"); + + EXPECT_EQ(request->optionalBuffer().length(), 4); + EXPECT_EQ(request->optionalBuffer().toString(), "body"); + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ServerCodecTest, SingleFrameModeMultipleBufferChunkedRequestDecodingTest) { + initializeCodec(true, 8 * 1024 * 1024); + + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + Buffer::OwnedImpl buffer; + + buffer.add("GET / HTTP/1.1\r\n" + "Host: host\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n"); // Chunk footer. + + codec_->decode(buffer, false); + + EXPECT_EQ(buffer.length(), 0); + + buffer.add("0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).WillOnce(Invoke([](StreamFramePtr frame) { + auto* request = dynamic_cast(frame.get()); + + EXPECT_EQ(frame->frameFlags().endStream(), true); + + EXPECT_EQ(request->host(), "host"); + EXPECT_EQ(request->path(), "/"); + EXPECT_EQ(request->method(), "GET"); + EXPECT_EQ(request->get("host").value(), "host"); + EXPECT_EQ(request->get(":authority").value(), "host"); + EXPECT_EQ(request->get(":path").value(), "/"); + EXPECT_EQ(request->get(":method").value(), "GET"); + EXPECT_EQ(request->get("custom").value(), "value"); + + EXPECT_EQ(request->optionalBuffer().length(), 4); + EXPECT_EQ(request->optionalBuffer().toString(), "body"); + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ServerCodecTest, SingleFrameModeResponseEncodingTest) { + // Create a response. + auto headers = Http::ResponseHeaderMapImpl::create(); + headers->setStatus(200); + headers->setContentLength(4); + + HttpResponseFrame response(std::move(headers), true); + response.optionalBuffer().add("body"); + + NiceMock encoding_callbacks; + + // Encode the response. + { + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "HTTP/1.1 200 OK\r\n" + "content-length: 4\r\n" + "\r\n" + "body"); + buffer.drain(buffer.length()); + })); + codec_->encode(response, encoding_callbacks); + } +} + +class Http1ClientCodecTest : public testing::Test { +public: + Http1ClientCodecTest() { initializeCodec(); } + + void initializeCodec(bool single_frame_mode = false, uint32_t max_buffer_size = 8 * 1024 * 1024) { + codec_ = std::make_unique(single_frame_mode, max_buffer_size); + codec_->setCodecCallbacks(codec_callbacks_); + } + + void encodingOneRequest() { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + // Create a request. + auto headers = Http::RequestHeaderMapImpl::create(); + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + headers->addCopy(Http::Headers::get().ContentLength, "4"); + + HttpRequestFrame request(std::move(headers), false); + + Buffer::OwnedImpl body_buffer("body"); + HttpRawBodyFrame body(body_buffer, true); + + NiceMock encoding_callbacks; + + // Encode the request. + { + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, false)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "GET /path HTTP/1.1\r\n" + "host: host\r\n" + "content-length: 4\r\n" + "\r\n"); + buffer.drain(buffer.length()); + })); + + codec_->encode(request, encoding_callbacks); + + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "body"); + buffer.drain(buffer.length()); + })); + + codec_->encode(body, encoding_callbacks); + } + } + + NiceMock codec_callbacks_; + NiceMock mock_connection; + std::unique_ptr codec_; +}; + +TEST_F(Http1ClientCodecTest, HeaderOnlyResponseDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + encodingOneRequest(); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "content-length: 0\r\n" + "\r\n"); + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).WillOnce(Invoke([](StreamFramePtr frame) { + EXPECT_EQ(frame->frameFlags().endStream(), true); + + auto* response = dynamic_cast(frame.get()); + + EXPECT_EQ(response->response_->getStatusValue(), "200"); + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ClientCodecTest, ResponseDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + encodingOneRequest(); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Content-Length: 4\r\n" + "custom: value\r\n" + "\r\n" + "body"); + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)) + .Times(2) + .WillRepeatedly(Invoke([](StreamFramePtr frame) { + auto* response = dynamic_cast(frame.get()); + if (response != nullptr) { + EXPECT_EQ(frame->frameFlags().endStream(), false); + EXPECT_EQ(response->response_->getStatusValue(), "200"); + EXPECT_EQ(response->response_->getContentLengthValue(), "4"); + EXPECT_EQ(response->get("custom").value(), "value"); + } + + auto* body = dynamic_cast(frame.get()); + if (body != nullptr) { + EXPECT_EQ(frame->frameFlags().endStream(), true); + + EXPECT_EQ(body->buffer().length(), 4); + EXPECT_EQ(body->buffer().toString(), "body"); + } + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ClientCodecTest, ChunkedResponseDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + encodingOneRequest(); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)) + .Times(3) // One for headers and one for body, and one for the last chunk. + .WillRepeatedly(Invoke([](StreamFramePtr frame) { + auto* response = dynamic_cast(frame.get()); + if (response != nullptr) { + EXPECT_EQ(frame->frameFlags().endStream(), false); + + EXPECT_EQ(response->response_->getStatusValue(), "200"); + EXPECT_EQ(response->get("custom").value(), "value"); + } + + auto* body = dynamic_cast(frame.get()); + if (body != nullptr) { + if (!frame->frameFlags().endStream()) { + EXPECT_EQ(body->buffer().length(), 4); + EXPECT_EQ(body->buffer().toString(), "body"); + } else { + EXPECT_EQ(body->buffer().length(), 0); + } + } + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ClientCodecTest, MultipleBufferChunkedResponseDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + encodingOneRequest(); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n"); // Chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)) + .Times(2) + .WillRepeatedly(Invoke([](StreamFramePtr frame) { + auto* response = dynamic_cast(frame.get()); + if (response != nullptr) { + EXPECT_EQ(frame->frameFlags().endStream(), false); + + EXPECT_EQ(response->response_->getStatusValue(), "200"); + EXPECT_EQ(response->get("custom").value(), "value"); + } else { + auto* body = dynamic_cast(frame.get()); + EXPECT_EQ(frame->frameFlags().endStream(), false); + EXPECT_EQ(body->buffer().length(), 4); + EXPECT_EQ(body->buffer().toString(), "body"); + } + })); + + codec_->decode(buffer, false); + + EXPECT_EQ(buffer.length(), 0); + + buffer.add("0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).WillOnce(Invoke([](StreamFramePtr frame) { + auto* body = dynamic_cast(frame.get()); + EXPECT_EQ(frame->frameFlags().endStream(), true); + EXPECT_EQ(body->buffer().length(), 0); + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ClientCodecTest, UnexpectedResponseTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + // Transfer-Encoding and Content-Length are set at same time. + { + initializeCodec(); + + encodingOneRequest(); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "Content-Length: 4\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer, false); + } + + // Unknown Transfer-Encoding. + { + initializeCodec(); + + encodingOneRequest(); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: gzip, chunked\r\n" // Only 'chunked' is supported. + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer, false); + } +} + +TEST_F(Http1ClientCodecTest, HeaderOnlyRequestEncodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + // Create a request. + auto headers = Http::RequestHeaderMapImpl::create(); + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + headers->addCopy(Http::LowerCaseString("custom"), "value"); + + HttpRequestFrame request(std::move(headers), true); + + NiceMock encoding_callbacks; + + // Encode the request. + { + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "GET /path HTTP/1.1\r\n" + "host: host\r\n" + "custom: value\r\n" + "\r\n"); + })); + + codec_->encode(request, encoding_callbacks); + } +} + +TEST_F(Http1ClientCodecTest, RequestEncodingTest) { encodingOneRequest(); } + +TEST_F(Http1ClientCodecTest, ChunkedRequestEncodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + // Create a request. + auto headers = Http::RequestHeaderMapImpl::create(); + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + headers->addCopy(Http::Headers::get().TransferEncoding, "chunked"); + + HttpRequestFrame request(std::move(headers), false); + + Buffer::OwnedImpl body_buffer("body"); + HttpRawBodyFrame body(body_buffer, true); + + NiceMock encoding_callbacks; + + // Encode the request. + { + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, false)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "GET /path HTTP/1.1\r\n" + "host: host\r\n" + "transfer-encoding: chunked\r\n" + "\r\n"); + buffer.drain(buffer.length()); + })); + + codec_->encode(request, encoding_callbacks); + + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + buffer.drain(buffer.length()); + })); + + codec_->encode(body, encoding_callbacks); + } +} + +TEST_F(Http1ClientCodecTest, RequestAndResponseTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + // Do repeated request and response. + for (size_t i = 0; i < 100; i++) { + // Create a request. + auto headers = Http::RequestHeaderMapImpl::create(); + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + headers->addCopy(Http::Headers::get().TransferEncoding, "chunked"); + + HttpRequestFrame request(std::move(headers), false); + + Buffer::OwnedImpl body_buffer("body"); + HttpRawBodyFrame body(body_buffer, true); + + NiceMock encoding_callbacks; + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, _)).Times(2); + + // Encode the request. + codec_->encode(request, encoding_callbacks); + codec_->encode(body, encoding_callbacks); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + // Decode the response. + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).Times(3); + codec_->decode(buffer, false); + } +} + +TEST_F(Http1ClientCodecTest, ResponseCompleteBeforeRequestCompleteTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + // Create a request. + auto headers = Http::RequestHeaderMapImpl::create(); + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + headers->addCopy(Http::Headers::get().TransferEncoding, "chunked"); + + HttpRequestFrame request(std::move(headers), false); + + NiceMock encoding_callbacks; + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, _)); + + // Encode the request. Only the headers are encoded and the body is not encoded. + codec_->encode(request, encoding_callbacks); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + // Decode the response. + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).Times(3); + // Finally, the onDecodingFailure() is called because the request is not complete and the + // response is complete. + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer, false); +} + +TEST_F(Http1ClientCodecTest, SingleFrameModeRequestEncodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + initializeCodec(true, 8 * 1024 * 1024); + + // Create a request. + auto headers = Http::RequestHeaderMapImpl::create(); + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::Headers::get().Method, "GET"); + headers->setContentLength(4); + + HttpRequestFrame request(std::move(headers), true); + request.optionalBuffer().add("body"); + + NiceMock encoding_callbacks; + + // Encode the request. + { + EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) + .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_EQ(buffer.toString(), "GET /path HTTP/1.1\r\n" + "host: host\r\n" + "content-length: 4\r\n" + "\r\n" + "body"); + })); + codec_->encode(request, encoding_callbacks); + } +} + +TEST_F(Http1ClientCodecTest, SingleFrameModeResponseDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + initializeCodec(true, 8 * 1024 * 1024); + + encodingOneRequest(); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Content-Length: 4\r\n" + "custom: value\r\n" + "\r\n" + "body"); + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).WillOnce(Invoke([](StreamFramePtr frame) { + auto* response = dynamic_cast(frame.get()); + + EXPECT_EQ(frame->frameFlags().endStream(), true); + EXPECT_EQ(response->response_->getStatusValue(), "200"); + EXPECT_EQ(response->response_->getContentLengthValue(), "4"); + EXPECT_EQ(response->get("custom").value(), "value"); + + EXPECT_EQ(response->optionalBuffer().length(), 4); + EXPECT_EQ(response->optionalBuffer().toString(), "body"); + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ClientCodecTest, SingleFrameModeResponseTooLargeTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + initializeCodec(true, 4); + + encodingOneRequest(); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Content-Length: 5\r\n" + "custom: value\r\n" + "\r\n" + "body~"); + + EXPECT_CALL(codec_callbacks_, onDecodingFailure()); + codec_->decode(buffer, false); +} + +TEST_F(Http1ClientCodecTest, SingleFrameModeChunkedResponseDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + initializeCodec(true, 8 * 1024 * 1024); + + encodingOneRequest(); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n" // Chunk footer. + "0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).WillOnce(Invoke([](StreamFramePtr frame) { + auto* response = dynamic_cast(frame.get()); + + EXPECT_EQ(frame->frameFlags().endStream(), true); + + EXPECT_EQ(response->response_->getStatusValue(), "200"); + EXPECT_EQ(response->get("custom").value(), "value"); + + EXPECT_EQ(response->optionalBuffer().length(), 4); + EXPECT_EQ(response->optionalBuffer().toString(), "body"); + })); + + codec_->decode(buffer, false); +} + +TEST_F(Http1ClientCodecTest, SingleFrameModeMultipleBufferChunkedResponseDecodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + initializeCodec(true, 8 * 1024 * 1024); + + encodingOneRequest(); + + Buffer::OwnedImpl buffer; + + buffer.add("HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "custom: value\r\n" + "\r\n" + "4\r\n" // Chunk header. + "body" // Chunk body. + "\r\n"); // Chunk footer. + + codec_->decode(buffer, false); + + EXPECT_EQ(buffer.length(), 0); + + buffer.add("0\r\n" // Last chunk header. + "\r\n"); // Last chunk footer. + + EXPECT_CALL(codec_callbacks_, onDecodingSuccess(_)).WillOnce(Invoke([](StreamFramePtr frame) { + auto* response = dynamic_cast(frame.get()); + + EXPECT_EQ(frame->frameFlags().endStream(), true); + + EXPECT_EQ(response->response_->getStatusValue(), "200"); + EXPECT_EQ(response->get("custom").value(), "value"); + + EXPECT_EQ(response->optionalBuffer().length(), 4); + EXPECT_EQ(response->optionalBuffer().toString(), "body"); + })); + + codec_->decode(buffer, false); +} + +} // namespace +} // namespace Http1 +} // namespace Codec +} // namespace GenericProxy +} // namespace NetworkFilters +} // namespace Extensions +} // namespace Envoy diff --git a/contrib/golang/filters/http/test/test_data/access_log/go.mod b/contrib/golang/filters/http/test/test_data/access_log/go.mod index 01693aa92cd2..ed46c345ae77 100644 --- a/contrib/golang/filters/http/test/test_data/access_log/go.mod +++ b/contrib/golang/filters/http/test/test_data/access_log/go.mod @@ -4,6 +4,6 @@ go 1.20 require github.com/envoyproxy/envoy v1.24.0 -require google.golang.org/protobuf v1.30.0 // indirect +require google.golang.org/protobuf v1.33.0 replace github.com/envoyproxy/envoy => ../../../../../../../ diff --git a/contrib/golang/filters/http/test/test_data/action/go.mod b/contrib/golang/filters/http/test/test_data/action/go.mod index bda83466971f..56a3bc4ad157 100644 --- a/contrib/golang/filters/http/test/test_data/action/go.mod +++ b/contrib/golang/filters/http/test/test_data/action/go.mod @@ -4,8 +4,6 @@ go 1.20 require github.com/envoyproxy/envoy v1.24.0 -require github.com/google/go-cmp v0.5.9 // indirect - -require google.golang.org/protobuf v1.32.0 // indirect +require google.golang.org/protobuf v1.33.0 // indirect replace github.com/envoyproxy/envoy => ../../../../../../../ diff --git a/contrib/golang/filters/http/test/test_data/basic/go.mod b/contrib/golang/filters/http/test/test_data/basic/go.mod index b70054d4863a..77ecc15879bb 100644 --- a/contrib/golang/filters/http/test/test_data/basic/go.mod +++ b/contrib/golang/filters/http/test/test_data/basic/go.mod @@ -4,6 +4,6 @@ go 1.20 require github.com/envoyproxy/envoy v1.24.0 -require google.golang.org/protobuf v1.30.0 // indirect +require google.golang.org/protobuf v1.33.0 // indirect replace github.com/envoyproxy/envoy => ../../../../../../../ diff --git a/contrib/golang/filters/http/test/test_data/dummy/go.mod b/contrib/golang/filters/http/test/test_data/dummy/go.mod index beaf9d59c743..7f3cef141617 100644 --- a/contrib/golang/filters/http/test/test_data/dummy/go.mod +++ b/contrib/golang/filters/http/test/test_data/dummy/go.mod @@ -4,6 +4,6 @@ go 1.20 require github.com/envoyproxy/envoy v1.24.0 -require google.golang.org/protobuf v1.30.0 // indirect +require google.golang.org/protobuf v1.33.0 // indirect replace github.com/envoyproxy/envoy => ../../../../../../../ diff --git a/contrib/golang/filters/http/test/test_data/metric/go.mod b/contrib/golang/filters/http/test/test_data/metric/go.mod index b70054d4863a..2d4e1c1f68ee 100644 --- a/contrib/golang/filters/http/test/test_data/metric/go.mod +++ b/contrib/golang/filters/http/test/test_data/metric/go.mod @@ -4,6 +4,6 @@ go 1.20 require github.com/envoyproxy/envoy v1.24.0 -require google.golang.org/protobuf v1.30.0 // indirect +require google.golang.org/protobuf v1.33.0 replace github.com/envoyproxy/envoy => ../../../../../../../ diff --git a/contrib/golang/filters/http/test/test_data/passthrough/go.mod b/contrib/golang/filters/http/test/test_data/passthrough/go.mod index d6c620393300..7830ee8c6273 100644 --- a/contrib/golang/filters/http/test/test_data/passthrough/go.mod +++ b/contrib/golang/filters/http/test/test_data/passthrough/go.mod @@ -4,6 +4,6 @@ go 1.20 require github.com/envoyproxy/envoy v1.24.0 -require google.golang.org/protobuf v1.30.0 // indirect +require google.golang.org/protobuf v1.33.0 // indirect replace github.com/envoyproxy/envoy => ../../../../../../../ diff --git a/contrib/golang/filters/network/test/test_data/go.mod b/contrib/golang/filters/network/test/test_data/go.mod index 8c20e9bd14f6..e1a332e4375c 100644 --- a/contrib/golang/filters/network/test/test_data/go.mod +++ b/contrib/golang/filters/network/test/test_data/go.mod @@ -4,6 +4,6 @@ go 1.18 require github.com/envoyproxy/envoy v1.24.0 -require google.golang.org/protobuf v1.30.0 // indirect +require google.golang.org/protobuf v1.33.0 // indirect replace github.com/envoyproxy/envoy => ../../../../../../ diff --git a/contrib/postgres_proxy/filters/network/test/postgres_integration_test.cc b/contrib/postgres_proxy/filters/network/test/postgres_integration_test.cc index fb47ccde35d0..f027cfec1074 100644 --- a/contrib/postgres_proxy/filters/network/test/postgres_integration_test.cc +++ b/contrib/postgres_proxy/filters/network/test/postgres_integration_test.cc @@ -295,7 +295,8 @@ class UpstreamSSLBaseIntegrationTest : public PostgresBaseIntegrationTest { // The tls transport socket will be inserted into fake_upstream when // Envoy's upstream starttls transport socket is converted to secure mode. std::unique_ptr tls_context_manager = - std::make_unique(timeSystem()); + std::make_unique( + server_factory_context_); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext downstream_tls_context; @@ -527,7 +528,8 @@ class UpstreamAndDownstreamSSLIntegrationTest : public UpstreamSSLBaseIntegratio // The tls transport socket will be inserted into fake_upstream when // Envoy's upstream starttls transport socket is converted to secure mode. std::unique_ptr tls_context_manager = - std::make_unique(timeSystem()); + std::make_unique( + server_factory_context_); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext upstream_tls_context; diff --git a/contrib/qat/BUILD b/contrib/qat/BUILD index d435976e4953..08ddf04136c3 100644 --- a/contrib/qat/BUILD +++ b/contrib/qat/BUILD @@ -20,12 +20,16 @@ configure_make( configure_options = [ "--disable-fast-crc-in-assembler", "--disable-systemd", - "--disable-shared", "--with-pic", + "--enable-shared", "--enable-static", "--enable-samples=no", ], lib_source = "@com_github_intel_qatlib//:all", + out_shared_libs = [ + "libqat.so", + "libusdm.so", + ], out_static_libs = [ "libqat.a", "libusdm.a", diff --git a/contrib/qat/compression/qatzstd/compressor/source/BUILD b/contrib/qat/compression/qatzstd/compressor/source/BUILD new file mode 100644 index 000000000000..960fa2b9b55e --- /dev/null +++ b/contrib/qat/compression/qatzstd/compressor/source/BUILD @@ -0,0 +1,69 @@ +load("@rules_foreign_cc//foreign_cc:defs.bzl", "make") +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_contrib_extension", + "envoy_cc_library", + "envoy_contrib_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_contrib_package() + +make( + name = "qat-zstd", + build_data = ["@com_github_qat_zstd//:all"], + env = select({ + "//bazel:clang_build": { + "CFLAGS": "-Wno-error=unused-parameter -Wno-error=unused-command-line-argument -I$$EXT_BUILD_DEPS/qatlib/include -I$$EXT_BUILD_DEPS/zstd/include", + }, + "//conditions:default": { + "CFLAGS": "-I$$EXT_BUILD_DEPS/qatlib/include -I$$EXT_BUILD_DEPS/zstd/include", + }, + }), + includes = [], + lib_source = "@com_github_qat_zstd//:all", + out_static_libs = ["libqatseqprod.a"], + tags = ["skip_on_windows"], + target_compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], + targets = [ + "ENABLE_USDM_DRV=1", + "install", + ], + deps = [ + "//contrib/qat:qatlib", + "//external:zstd", + ], +) + +envoy_cc_library( + name = "compressor_lib", + srcs = ["qatzstd_compressor_impl.cc"], + hdrs = ["qatzstd_compressor_impl.h"], + deps = [ + ":qat-zstd", + "//envoy/compression/compressor:compressor_interface", + "//envoy/server:factory_context_interface", + "//source/common/buffer:buffer_lib", + "//source/common/compression/zstd/common:zstd_base_lib", + "//source/common/compression/zstd/compressor:compressor_base", + ], +) + +envoy_cc_contrib_extension( + name = "config", + srcs = ["config.cc"], + hdrs = ["config.h"], + deps = [ + ":compressor_lib", + ":qat-zstd", + "//envoy/event:dispatcher_interface", + "//envoy/thread_local:thread_local_interface", + "//source/common/http:headers_lib", + "//source/extensions/compression/common/compressor:compressor_factory_base_lib", + "@envoy_api//contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha:pkg_cc_proto", + ], +) diff --git a/contrib/qat/compression/qatzstd/compressor/source/config.cc b/contrib/qat/compression/qatzstd/compressor/source/config.cc new file mode 100644 index 000000000000..67a24d530d1b --- /dev/null +++ b/contrib/qat/compression/qatzstd/compressor/source/config.cc @@ -0,0 +1,81 @@ +#include "contrib/qat/compression/qatzstd/compressor/source/config.h" + +namespace Envoy { +namespace Extensions { +namespace Compression { +namespace Qatzstd { +namespace Compressor { + +QatzstdCompressorFactory::QatzstdCompressorFactory( + const envoy::extensions::compression::qatzstd::compressor::v3alpha::Qatzstd& qatzstd, + ThreadLocal::SlotAllocator& tls) + : compression_level_( + PROTOBUF_GET_WRAPPED_OR_DEFAULT(qatzstd, compression_level, ZSTD_CLEVEL_DEFAULT)), + enable_checksum_(qatzstd.enable_checksum()), strategy_(qatzstd.strategy()), + chunk_size_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(qatzstd, chunk_size, ZSTD_CStreamOutSize())), + enable_qat_zstd_(qatzstd.enable_qat_zstd()), + qat_zstd_fallback_threshold_(PROTOBUF_GET_WRAPPED_OR_DEFAULT( + qatzstd, qat_zstd_fallback_threshold, DefaultQatZstdFallbackThreshold)), + tls_slot_(nullptr) { + if (enable_qat_zstd_) { + tls_slot_ = ThreadLocal::TypedSlot::makeUnique(tls); + tls_slot_->set([](Event::Dispatcher&) { return std::make_shared(); }); + } +} + +QatzstdCompressorFactory::QatzstdThreadLocal::QatzstdThreadLocal() + : initialized_(false), sequenceProducerState_(nullptr) {} + +QatzstdCompressorFactory::QatzstdThreadLocal::~QatzstdThreadLocal() { + if (initialized_) { + /* Free sequence producer state */ + QZSTD_freeSeqProdState(sequenceProducerState_); + /* Stop QAT device, please call this function when + you won't use QAT anymore or before the process exits */ + QZSTD_stopQatDevice(); + } +} + +void* QatzstdCompressorFactory::QatzstdThreadLocal::GetQATSession() { + // The session must be initialized only once in every worker thread. + if (!initialized_) { + + int status = QZSTD_startQatDevice(); + // RELEASE_ASSERT(status == QZSTD_OK, "failed to initialize hardware"); + if (status != QZSTD_OK) { + ENVOY_LOG(warn, "Failed to initialize qat hardware"); + } else { + ENVOY_LOG(debug, "Initialize qat hardware successful"); + } + sequenceProducerState_ = QZSTD_createSeqProdState(); + initialized_ = true; + } + + return sequenceProducerState_; +} + +Envoy::Compression::Compressor::CompressorPtr QatzstdCompressorFactory::createCompressor() { + return std::make_unique( + compression_level_, enable_checksum_, strategy_, chunk_size_, enable_qat_zstd_, + qat_zstd_fallback_threshold_, enable_qat_zstd_ ? tls_slot_->get()->GetQATSession() : nullptr); +} + +Envoy::Compression::Compressor::CompressorFactoryPtr +QatzstdCompressorLibraryFactory::createCompressorFactoryFromProtoTyped( + const envoy::extensions::compression::qatzstd::compressor::v3alpha::Qatzstd& proto_config, + Server::Configuration::FactoryContext& context) { + return std::make_unique(proto_config, + context.serverFactoryContext().threadLocal()); +} + +/** + * Static registration for the zstd compressor library. @see NamedCompressorLibraryConfigFactory. + */ +REGISTER_FACTORY(QatzstdCompressorLibraryFactory, + Envoy::Compression::Compressor::NamedCompressorLibraryConfigFactory); + +} // namespace Compressor +} // namespace Qatzstd +} // namespace Compression +} // namespace Extensions +} // namespace Envoy diff --git a/contrib/qat/compression/qatzstd/compressor/source/config.h b/contrib/qat/compression/qatzstd/compressor/source/config.h new file mode 100644 index 000000000000..8e78cd8123be --- /dev/null +++ b/contrib/qat/compression/qatzstd/compressor/source/config.h @@ -0,0 +1,83 @@ +#pragma once + +#include "envoy/compression/compressor/factory.h" +#include "envoy/event/dispatcher.h" +#include "envoy/thread_local/thread_local.h" + +#include "source/common/common/logger.h" +#include "source/common/http/headers.h" +#include "source/extensions/compression/common/compressor/factory_base.h" + +#include "contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha/qatzstd.pb.h" +#include "contrib/envoy/extensions/compression/qatzstd/compressor/v3alpha/qatzstd.pb.validate.h" +#include "contrib/qat/compression/qatzstd/compressor/source/qatzstd_compressor_impl.h" +#include "qatseqprod.h" + +namespace Envoy { +namespace Extensions { +namespace Compression { +namespace Qatzstd { +namespace Compressor { + +// Default threshold for qat_zstd fallback to software. +const uint32_t DefaultQatZstdFallbackThreshold = 4000; + +namespace { + +const std::string& qatzstdStatsPrefix() { CONSTRUCT_ON_FIRST_USE(std::string, "qatzstd."); } +const std::string& qatzstdExtensionName() { + CONSTRUCT_ON_FIRST_USE(std::string, "envoy.compression.qatzstd.compressor"); +} + +} // namespace + +class QatzstdCompressorFactory : public Envoy::Compression::Compressor::CompressorFactory { +public: + QatzstdCompressorFactory( + const envoy::extensions::compression::qatzstd::compressor::v3alpha::Qatzstd& qatzstd, + ThreadLocal::SlotAllocator& tls); + + // Envoy::Compression::Compressor::CompressorFactory + Envoy::Compression::Compressor::CompressorPtr createCompressor() override; + const std::string& statsPrefix() const override { return qatzstdStatsPrefix(); } + const std::string& contentEncoding() const override { + return Http::CustomHeaders::get().ContentEncodingValues.Zstd; + } + +private: + struct QatzstdThreadLocal : public ThreadLocal::ThreadLocalObject, + public Logger::Loggable { + QatzstdThreadLocal(); + ~QatzstdThreadLocal() override; + void* GetQATSession(); + bool initialized_; + void* sequenceProducerState_; + }; + const uint32_t compression_level_; + const bool enable_checksum_; + const uint32_t strategy_; + const uint32_t chunk_size_; + const bool enable_qat_zstd_; + const uint32_t qat_zstd_fallback_threshold_; + ThreadLocal::TypedSlotPtr tls_slot_; +}; + +class QatzstdCompressorLibraryFactory + : public Compression::Common::Compressor::CompressorLibraryFactoryBase< + envoy::extensions::compression::qatzstd::compressor::v3alpha::Qatzstd> { +public: + QatzstdCompressorLibraryFactory() : CompressorLibraryFactoryBase(qatzstdExtensionName()) {} + +private: + Envoy::Compression::Compressor::CompressorFactoryPtr createCompressorFactoryFromProtoTyped( + const envoy::extensions::compression::qatzstd::compressor::v3alpha::Qatzstd& config, + Server::Configuration::FactoryContext& context) override; +}; + +DECLARE_FACTORY(QatzstdCompressorLibraryFactory); + +} // namespace Compressor +} // namespace Qatzstd +} // namespace Compression +} // namespace Extensions +} // namespace Envoy diff --git a/contrib/qat/compression/qatzstd/compressor/source/qatzstd_compressor_impl.cc b/contrib/qat/compression/qatzstd/compressor/source/qatzstd_compressor_impl.cc new file mode 100644 index 000000000000..7b1cb85a8aae --- /dev/null +++ b/contrib/qat/compression/qatzstd/compressor/source/qatzstd_compressor_impl.cc @@ -0,0 +1,85 @@ +#include "contrib/qat/compression/qatzstd/compressor/source/qatzstd_compressor_impl.h" + +namespace Envoy { +namespace Extensions { +namespace Compression { +namespace Qatzstd { +namespace Compressor { + +QatzstdCompressorImpl::QatzstdCompressorImpl(uint32_t compression_level, bool enable_checksum, + uint32_t strategy, uint32_t chunk_size, + bool enable_qat_zstd, + uint32_t qat_zstd_fallback_threshold, + void* sequenceProducerState) + : ZstdCompressorImplBase(compression_level, enable_checksum, strategy, chunk_size), + enable_qat_zstd_(enable_qat_zstd), qat_zstd_fallback_threshold_(qat_zstd_fallback_threshold), + sequenceProducerState_(sequenceProducerState), input_ptr_{std::make_unique( + chunk_size)}, + input_len_(0), chunk_size_(chunk_size) { + size_t result; + result = ZSTD_CCtx_setParameter(cctx_.get(), ZSTD_c_compressionLevel, compression_level_); + RELEASE_ASSERT(!ZSTD_isError(result), ""); + + ENVOY_LOG(debug, + "zstd new ZstdCompressorImpl, compression_level: {}, strategy: {}, chunk_size: " + "{}, enable_qat_zstd: {}, qat_zstd_fallback_threshold: {}", + compression_level, strategy, chunk_size, enable_qat_zstd, qat_zstd_fallback_threshold); + if (enable_qat_zstd_) { + /* register qatSequenceProducer */ + ZSTD_registerSequenceProducer(cctx_.get(), sequenceProducerState_, qatSequenceProducer); + result = ZSTD_CCtx_setParameter(cctx_.get(), ZSTD_c_enableSeqProducerFallback, 1); + RELEASE_ASSERT(!ZSTD_isError(result), ""); + } +} + +void QatzstdCompressorImpl::compressPreprocess(Buffer::Instance& buffer, + Envoy::Compression::Compressor::State state) { + ENVOY_LOG(debug, "zstd compress input size {}", buffer.length()); + if (enable_qat_zstd_ && state == Envoy::Compression::Compressor::State::Flush) { + // Fall back to software if input size less than threshold to achieve better performance. + if (buffer.length() < qat_zstd_fallback_threshold_) { + ENVOY_LOG(debug, "zstd compress fall back to software"); + ZSTD_registerSequenceProducer(cctx_.get(), nullptr, nullptr); + } + } +} + +void QatzstdCompressorImpl::setInput(const uint8_t* input, size_t size) { + input_.src = input; + input_.pos = 0; + input_.size = size; + input_len_ = 0; +} + +void QatzstdCompressorImpl::compressProcess(const Buffer::Instance& buffer, + const Buffer::RawSlice& slice, + Buffer::Instance& accumulation_buffer) { + if (slice.len_ == buffer.length() || slice.len_ > chunk_size_) { + if (input_len_ > 0) { + setInput(input_ptr_.get(), input_len_); + process(accumulation_buffer, ZSTD_e_continue); + } + setInput(static_cast(slice.mem_), slice.len_); + process(accumulation_buffer, ZSTD_e_continue); + } else { + if (input_len_ + slice.len_ > chunk_size_) { + setInput(input_ptr_.get(), input_len_); + process(accumulation_buffer, ZSTD_e_continue); + } + memcpy(input_ptr_.get() + input_len_, slice.mem_, slice.len_); // NOLINT(safe-memcpy) + input_len_ += slice.len_; + } +} + +void QatzstdCompressorImpl::compressPostprocess(Buffer::Instance& accumulation_buffer) { + if (input_len_ > 0) { + setInput(input_ptr_.get(), input_len_); + process(accumulation_buffer, ZSTD_e_continue); + } +} + +} // namespace Compressor +} // namespace Qatzstd +} // namespace Compression +} // namespace Extensions +} // namespace Envoy diff --git a/contrib/qat/compression/qatzstd/compressor/source/qatzstd_compressor_impl.h b/contrib/qat/compression/qatzstd/compressor/source/qatzstd_compressor_impl.h new file mode 100644 index 000000000000..1960b13b7671 --- /dev/null +++ b/contrib/qat/compression/qatzstd/compressor/source/qatzstd_compressor_impl.h @@ -0,0 +1,51 @@ +#pragma once + +#include "envoy/compression/compressor/compressor.h" +#include "envoy/server/factory_context.h" + +#include "source/common/common/logger.h" +#include "source/common/compression/zstd/common/base.h" +#include "source/common/compression/zstd/compressor/zstd_compressor_impl_base.h" + +#include "qatseqprod.h" + +namespace Envoy { +namespace Extensions { +namespace Compression { +namespace Qatzstd { +namespace Compressor { + +/** + * Implementation of compressor's interface. + */ +class QatzstdCompressorImpl : public Envoy::Compression::Zstd::Compressor::ZstdCompressorImplBase, + public Logger::Loggable { +public: + QatzstdCompressorImpl(uint32_t compression_level, bool enable_checksum, uint32_t strategy, + uint32_t chunk_size, bool enable_qat_zstd, + uint32_t qat_zstd_fallback_threshold, void* sequenceProducerState); + +private: + void compressPreprocess(Buffer::Instance& buffer, + Envoy::Compression::Compressor::State state) override; + + void compressProcess(const Buffer::Instance& buffer, const Buffer::RawSlice& slice, + Buffer::Instance& accumulation_buffer) override; + + void compressPostprocess(Buffer::Instance& accumulation_buffer) override; + + void setInput(const uint8_t* input, size_t size); + + bool enable_qat_zstd_; + const uint32_t qat_zstd_fallback_threshold_; + void* sequenceProducerState_; + std::unique_ptr input_ptr_; + uint64_t input_len_; + uint64_t chunk_size_; +}; + +} // namespace Compressor +} // namespace Qatzstd +} // namespace Compression +} // namespace Extensions +} // namespace Envoy diff --git a/contrib/qat/compression/qatzstd/compressor/test/BUILD b/contrib/qat/compression/qatzstd/compressor/test/BUILD new file mode 100644 index 000000000000..85297caa141a --- /dev/null +++ b/contrib/qat/compression/qatzstd/compressor/test/BUILD @@ -0,0 +1,20 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_test", + "envoy_contrib_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_contrib_package() + +envoy_cc_test( + name = "compressor_test", + srcs = ["qatzstd_compressor_impl_test.cc"], + deps = [ + "//contrib/qat/compression/qatzstd/compressor/source:config", + "//source/extensions/compression/zstd/decompressor:decompressor_lib", + "//test/mocks/server:factory_context_mocks", + "//test/test_common:utility_lib", + ], +) diff --git a/contrib/qat/compression/qatzstd/compressor/test/qatzstd_compressor_impl_test.cc b/contrib/qat/compression/qatzstd/compressor/test/qatzstd_compressor_impl_test.cc new file mode 100644 index 000000000000..257a23061ed2 --- /dev/null +++ b/contrib/qat/compression/qatzstd/compressor/test/qatzstd_compressor_impl_test.cc @@ -0,0 +1,110 @@ +#include "source/common/buffer/buffer_impl.h" +#include "source/common/stats/isolated_store_impl.h" +#include "source/extensions/compression/zstd/decompressor/zstd_decompressor_impl.h" + +#include "test/mocks/server/factory_context.h" +#include "test/test_common/utility.h" + +#include "contrib/qat/compression/qatzstd/compressor/source/config.h" +#include "gtest/gtest.h" +#include "qatseqprod.h" + +namespace Envoy { +namespace Extensions { +namespace Compression { +namespace Qatzstd { +namespace Compressor { +namespace { + +class QatzstdCompressorImplTest : public testing::Test { +protected: + void drainBuffer(Buffer::OwnedImpl& buffer) { + buffer.drain(buffer.length()); + ASSERT_EQ(0, buffer.length()); + } + + void verifyWithDecompressor(Envoy::Compression::Compressor::CompressorPtr compressor) { + Buffer::OwnedImpl buffer; + Buffer::OwnedImpl accumulation_buffer; + std::string original_text{}; + for (uint64_t i = 0; i < 10; i++) { + TestUtility::feedBufferWithRandomCharacters(buffer, default_input_size_ * i, i, i); + original_text.append(buffer.toString()); + ASSERT_EQ(default_input_size_ * i * i, buffer.length()); + compressor->compress(buffer, Envoy::Compression::Compressor::State::Flush); + accumulation_buffer.add(buffer); + drainBuffer(buffer); + } + + compressor->compress(buffer, Envoy::Compression::Compressor::State::Finish); + accumulation_buffer.add(buffer); + drainBuffer(buffer); + + Stats::IsolatedStoreImpl stats_store{}; + Zstd::Decompressor::ZstdDecompressorImpl decompressor{*stats_store.rootScope(), "test.", + default_ddict_manager_, 4096}; + + decompressor.decompress(accumulation_buffer, buffer); + std::string decompressed_text{buffer.toString()}; + + ASSERT_EQ(original_text.length(), decompressed_text.length()); + EXPECT_EQ(original_text, decompressed_text); + } + + Envoy::Compression::Compressor::CompressorFactoryPtr + createQatzstdCompressorFactoryFromConfig(const std::string& json) { + envoy::extensions::compression::qatzstd::compressor::v3alpha::Qatzstd qatzstd_config; + TestUtility::loadFromJson(json, qatzstd_config); + + return qatzstd_compressor_library_factory_.createCompressorFactoryFromProto(qatzstd_config, + context_); + } + + uint32_t default_input_size_{796}; + Zstd::Decompressor::ZstdDDictManagerPtr default_ddict_manager_{nullptr}; + QatzstdCompressorLibraryFactory qatzstd_compressor_library_factory_; + NiceMock context_; +}; + +class QatzstdConfigTest : public QatzstdCompressorImplTest, + public ::testing::WithParamInterface> {}; + +// These tests should pass even if required hardware or setup steps required for qatzstd are +// missing. Qatzstd uses a sofware fallback in this case. +INSTANTIATE_TEST_SUITE_P( + QatzstdConfigTestInstantiation, QatzstdConfigTest, + // First tuple has all default values. + ::testing::Values(std::make_tuple(1, 4096, true, 4096), std::make_tuple(2, 4096, true, 4096), + std::make_tuple(3, 65536, true, 4096), std::make_tuple(4, 4096, true, 4096), + std::make_tuple(5, 8192, true, 1024), std::make_tuple(6, 4096, false, 1024), + std::make_tuple(7, 4096, true, 1024), std::make_tuple(8, 8192, true, 4096), + std::make_tuple(9, 8192, true, 1024), std::make_tuple(10, 16384, true, 1024), + std::make_tuple(11, 8192, true, 8192), + std::make_tuple(12, 4096, true, 1024))); + +TEST_P(QatzstdConfigTest, LoadConfigAndVerifyWithDecompressor) { + std::tuple config_value_tuple = GetParam(); + std::string json{fmt::format(R"EOF({{ + "compression_level": {}, + "chunk_size": {}, + "enable_qat_zstd": {}, + "qat_zstd_fallback_threshold": {}, +}})EOF", + std::get<0>(config_value_tuple), std::get<1>(config_value_tuple), + std::get<2>(config_value_tuple), std::get<3>(config_value_tuple))}; + + Envoy::Compression::Compressor::CompressorFactoryPtr qatzstd_compressor_factory = + createQatzstdCompressorFactoryFromConfig(json); + + EXPECT_EQ("zstd", qatzstd_compressor_factory->contentEncoding()); + EXPECT_EQ("qatzstd.", qatzstd_compressor_factory->statsPrefix()); + + verifyWithDecompressor(qatzstd_compressor_factory->createCompressor()); +} + +} // namespace +} // namespace Compressor +} // namespace Qatzstd +} // namespace Compression +} // namespace Extensions +} // namespace Envoy diff --git a/docs/BUILD b/docs/BUILD index 16df02c3e5a6..3497d8d9f186 100644 --- a/docs/BUILD +++ b/docs/BUILD @@ -35,6 +35,7 @@ filegroup( "root/configuration/other_features/_include/hyperscan_matcher_multiple.yaml", "root/configuration/other_features/_include/hyperscan_regex_engine.yaml", "root/configuration/other_features/_include/qatzip.yaml", + "root/configuration/other_features/_include/qatzstd.yaml", "root/intro/arch_overview/security/_include/ssl.yaml", "root/configuration/listeners/network_filters/_include/generic_proxy_filter.yaml", "root/configuration/overview/_include/xds_api/oauth-sds-example.yaml", diff --git a/docs/inventories/v1.29/objects.inv b/docs/inventories/v1.29/objects.inv index 70feb4b0ae53..47a8dd464a41 100644 Binary files a/docs/inventories/v1.29/objects.inv and b/docs/inventories/v1.29/objects.inv differ diff --git a/docs/root/api-v3/config/contrib/qat/qat.rst b/docs/root/api-v3/config/contrib/qat/qat.rst index 3e524d700b13..c03e01dd4397 100644 --- a/docs/root/api-v3/config/contrib/qat/qat.rst +++ b/docs/root/api-v3/config/contrib/qat/qat.rst @@ -4,3 +4,4 @@ ../../../extensions/private_key_providers/qat/v3alpha/* ../../../extensions/compression/qatzip/compressor/v3alpha/* + ../../../extensions/compression/qatzstd/compressor/v3alpha/* diff --git a/docs/root/configuration/http/http_filters/rbac_filter.rst b/docs/root/configuration/http/http_filters/rbac_filter.rst index db30b5123a01..4e2e5990392e 100644 --- a/docs/root/configuration/http/http_filters/rbac_filter.rst +++ b/docs/root/configuration/http/http_filters/rbac_filter.rst @@ -33,7 +33,12 @@ The RBAC filter outputs statistics in the ``http..rbac.`` namespace ` comes from the owning HTTP connection manager. -For the shadow rule statistics ``shadow_allowed`` and ``shadow_denied``, the :ref:`shadow_rules_stat_prefix ` +For the rule statistics ``allowed`` and ``denied``, +the :ref:`rules_stat_prefix ` +can be used to add an extra prefix to output the statistics in the ``http..rbac..`` namespace. + +For the shadow rule statistics ``shadow_allowed`` and ``shadow_denied``, +the :ref:`shadow_rules_stat_prefix ` can be used to add an extra prefix to output the statistics in the ``http..rbac..`` namespace. .. csv-table:: diff --git a/docs/root/configuration/observability/access_log/stats.rst b/docs/root/configuration/observability/access_log/stats.rst index 0a61df4e4518..5bc23c23ef67 100644 --- a/docs/root/configuration/observability/access_log/stats.rst +++ b/docs/root/configuration/observability/access_log/stats.rst @@ -46,4 +46,5 @@ The Fluentd access log has statistics rooted at the *access_logs.fluentd.` + + +Qatzstd compressor provides Envoy with faster hardware-accelerated zstd compression by integrating with `Intel® QuickAssist Technology (Intel® QAT) `_ through the qatlib and QAT-ZSTD-Plugin libraries. + +Example configuration +--------------------- + +An example for Qatzstd compressor configuration is: + +.. literalinclude:: _include/qatzstd.yaml + :language: yaml + + +How it works +------------ + +If enabled, the Qatzstd compressor will: + +- attach Qat hardware +- create Threadlocal Qatzstd context for each worker thread + +When new http package come, one worker thread will process it using its Qatzstd context and send the data needed to be compressed to +Qat hardware using standard zstd api. + +Installing and using QAT-ZSTD-Plugin +------------------------------------ + +For information on how to build/install and use QAT-ZSTD-Plugin see `introduction `_. diff --git a/docs/root/configuration/other_features/wasm.rst b/docs/root/configuration/other_features/wasm.rst index f34146e68138..25c23c7ccf6e 100644 --- a/docs/root/configuration/other_features/wasm.rst +++ b/docs/root/configuration/other_features/wasm.rst @@ -12,10 +12,9 @@ The following runtimes are supported by Envoy: envoy.wasm.runtime.v8, "`V8 `_-based runtime" envoy.wasm.runtime.wamr, "`WAMR `_ runtime" envoy.wasm.runtime.wasmtime, "`Wasmtime `_ runtime" - envoy.wasm.runtime.wavm, "`WAVM `_ runtime" envoy.wasm.runtime.null, "Compiled modules linked into Envoy" -WAMR(WASM-Micro-Runtime), Wasmtime and WAVM runtimes are not included in Envoy release image by default. +WAMR(WASM-Micro-Runtime), Wasmtime runtime is not included in Envoy release image by default. Wasm runtime emits the following statistics: diff --git a/docs/versions.yaml b/docs/versions.yaml index 43dfa10ac3cb..580e355d1ab6 100644 --- a/docs/versions.yaml +++ b/docs/versions.yaml @@ -22,4 +22,4 @@ "1.26": 1.26.7 "1.27": 1.27.3 "1.28": 1.28.1 -"1.29": 1.29.1 +"1.29": 1.29.2 diff --git a/envoy/common/exception.h b/envoy/common/exception.h index 4d5d9fd45bc0..745936ee26bc 100644 --- a/envoy/common/exception.h +++ b/envoy/common/exception.h @@ -17,7 +17,7 @@ namespace Envoy { #define throwEnvoyExceptionOrPanic(x) PANIC(x) #define throwExceptionOrPanic(x, y) PANIC(y) #else -#define throwEnvoyExceptionOrPanic(x) throw EnvoyException(x) +#define throwEnvoyExceptionOrPanic(x) throw ::Envoy::EnvoyException(x) #define throwExceptionOrPanic(y, x) throw y(x) #endif @@ -29,6 +29,12 @@ class EnvoyException : public std::runtime_error { EnvoyException(const std::string& message) : std::runtime_error(message) {} }; +#define SET_AND_RETURN_IF_NOT_OK(check_status, set_status) \ + if (!check_status.ok()) { \ + set_status = check_status; \ + return; \ + } + #define THROW_IF_NOT_OK_REF(status) \ do { \ if (!(status).ok()) { \ diff --git a/envoy/filesystem/watcher.h b/envoy/filesystem/watcher.h index dd5c97b286e2..3cfd754a6cdb 100644 --- a/envoy/filesystem/watcher.h +++ b/envoy/filesystem/watcher.h @@ -8,6 +8,7 @@ #include "envoy/common/platform.h" #include "envoy/common/pure.h" +#include "absl/status/statusor.h" #include "absl/strings/string_view.h" namespace Envoy { @@ -35,8 +36,9 @@ class Watcher { * for the given directory. * @param events supplies the events to watch. * @param cb supplies the callback to invoke when a change occurs. + * @return a failure status if the file does not exist */ - virtual void addWatch(absl::string_view path, uint32_t events, OnChangedCb cb) PURE; + virtual absl::Status addWatch(absl::string_view path, uint32_t events, OnChangedCb cb) PURE; }; using WatcherPtr = std::unique_ptr; diff --git a/envoy/grpc/google_grpc_creds.h b/envoy/grpc/google_grpc_creds.h index 32dd71efdd75..1cc5188fb09f 100644 --- a/envoy/grpc/google_grpc_creds.h +++ b/envoy/grpc/google_grpc_creds.h @@ -2,10 +2,10 @@ #include -#include "envoy/api/api.h" #include "envoy/common/pure.h" #include "envoy/config/core/v3/grpc_service.pb.h" #include "envoy/config/typed_config.h" +#include "envoy/server/factory_context.h" #include "grpcpp/grpcpp.h" @@ -33,7 +33,7 @@ class GoogleGrpcCredentialsFactory : public Config::UntypedFactory { */ virtual std::shared_ptr getChannelCredentials(const envoy::config::core::v3::GrpcService& grpc_service_config, - Api::Api& api) PURE; + Server::Configuration::CommonFactoryContext& context) PURE; std::string category() const override { return "envoy.grpc_credentials"; } }; diff --git a/envoy/runtime/runtime.h b/envoy/runtime/runtime.h index 28952a52e97f..a3c1f8bd1353 100644 --- a/envoy/runtime/runtime.h +++ b/envoy/runtime/runtime.h @@ -247,8 +247,10 @@ class Loader { * Merge the given map of key-value pairs into the runtime's state. To remove a previous merge for * a key, use an empty string as the value. * @param values the values to merge + * @return a status indicating success or failure. */ - virtual void mergeValues(const absl::node_hash_map& values) PURE; + virtual absl::Status + mergeValues(const absl::node_hash_map& values) PURE; /** * Initiate all RTDS subscriptions. The `on_done` callback is invoked when all RTDS requests diff --git a/envoy/server/admin.h b/envoy/server/admin.h index 251b0cf47b44..48c1629fef44 100644 --- a/envoy/server/admin.h +++ b/envoy/server/admin.h @@ -281,6 +281,11 @@ class Admin { * Closes the listening socket for the admin. */ virtual void closeSocket() PURE; + + /** + * Creates a streaming request context from the url path in the admin stream. + */ + virtual RequestPtr makeRequest(AdminStream& admin_stream) const PURE; }; } // namespace Server diff --git a/envoy/server/factory_context.h b/envoy/server/factory_context.h index 8a139fbcb0c4..f6a34d53cf87 100644 --- a/envoy/server/factory_context.h +++ b/envoy/server/factory_context.h @@ -36,6 +36,11 @@ #include "source/common/protobuf/protobuf.h" namespace Envoy { + +namespace Regex { +class Engine; +} + namespace Server { namespace Configuration { @@ -129,6 +134,11 @@ class CommonFactoryContext { * @return ServerLifecycleNotifier& the lifecycle notifier for the server. */ virtual ServerLifecycleNotifier& lifecycleNotifier() PURE; + + /** + * @return the server regex engine. + */ + virtual Regex::Engine& regexEngine() PURE; }; /** diff --git a/envoy/server/instance.h b/envoy/server/instance.h index 683fef31b60d..a6fe23cd4c75 100644 --- a/envoy/server/instance.h +++ b/envoy/server/instance.h @@ -253,6 +253,11 @@ class Instance { */ virtual Configuration::StatsConfig& statsConfig() PURE; + /** + * @return the server regex engine. + */ + virtual Regex::Engine& regexEngine() PURE; + /** * @return envoy::config::bootstrap::v3::Bootstrap& the servers bootstrap configuration. */ diff --git a/envoy/ssl/context_manager.h b/envoy/ssl/context_manager.h index 8c7fae3707b2..3c0b243a47c6 100644 --- a/envoy/ssl/context_manager.h +++ b/envoy/ssl/context_manager.h @@ -10,6 +10,13 @@ #include "envoy/stats/scope.h" namespace Envoy { + +namespace Server { +namespace Configuration { +class CommonFactoryContext; +} // namespace Configuration +} // namespace Server + namespace Ssl { // Opaque type defined and used by the ``ServerContext``. @@ -70,15 +77,5 @@ class ContextManager { using ContextManagerPtr = std::unique_ptr; -class ContextManagerFactory : public Config::UntypedFactory { -public: - ~ContextManagerFactory() override = default; - virtual ContextManagerPtr createContextManager(TimeSource& time_source) PURE; - - // There could be only one factory thus the name is static. - std::string name() const override { return "ssl_context_manager"; } - std::string category() const override { return "envoy.ssl_context_manager"; } -}; - } // namespace Ssl } // namespace Envoy diff --git a/envoy/upstream/health_checker.h b/envoy/upstream/health_checker.h index 901401a6cf25..f5e3045da79b 100644 --- a/envoy/upstream/health_checker.h +++ b/envoy/upstream/health_checker.h @@ -94,12 +94,20 @@ class HealthCheckEventLogger { * Log a healthy host addition event. * @param health_checker_type supplies the type of health checker that generated the event. * @param host supplies the host that generated the event. - * @param healthy_threshold supplied the configured healthy threshold for this health check. * @param first_check whether this is a fast path success on the first health check for this host. */ virtual void logAddHealthy(envoy::data::core::v3::HealthCheckerType health_checker_type, const HostDescriptionConstSharedPtr& host, bool first_check) PURE; + /** + * A health check was successful. + * @param health_checker_type supplies the type of health checker that generated the event. + * @param host supplies the host that generated the event. + */ + virtual void + logSuccessfulHealthCheck(envoy::data::core::v3::HealthCheckerType health_checker_type, + const HostDescriptionConstSharedPtr& host) PURE; + /** * Log a degraded healthy host event. * @param health_checker_type supplies the type of health checker that generated the event. diff --git a/examples/local_ratelimit/Dockerfile-nginx b/examples/local_ratelimit/Dockerfile-nginx index eff6acc5fa7d..2794de868137 100644 --- a/examples/local_ratelimit/Dockerfile-nginx +++ b/examples/local_ratelimit/Dockerfile-nginx @@ -1 +1 @@ -FROM nginx@sha256:c26ae7472d624ba1fafd296e73cecc4f93f853088e6a9c13c0d52f6ca5865107 +FROM nginx@sha256:6db391d1c0cfb30588ba0bf72ea999404f2764febf0f1f196acd5867ac7efa7e diff --git a/examples/redis/Dockerfile-redis b/examples/redis/Dockerfile-redis index c0098b611cf2..825ed0e86758 100644 --- a/examples/redis/Dockerfile-redis +++ b/examples/redis/Dockerfile-redis @@ -1 +1 @@ -FROM redis@sha256:e647cfe134bf5e8e74e620f66346f93418acfc240b71dd85640325cb7cd01402 +FROM redis@sha256:3134997edb04277814aa51a4175a588d45eb4299272f8eff2307bbf8b39e4d43 diff --git a/examples/shared/build/Dockerfile b/examples/shared/build/Dockerfile index 6cfa7fdd567a..92ea0ad5f146 100644 --- a/examples/shared/build/Dockerfile +++ b/examples/shared/build/Dockerfile @@ -1,4 +1,4 @@ -FROM envoyproxy/envoy-build-ubuntu:0ca52447572ee105a4730da5e76fe47c9c5a7c64@sha256:d736c58f06f36848e7966752cc7e01519cc1b5101a178d5c6634807e8ac3deab +FROM envoyproxy/envoy-build-ubuntu:f94a38f62220a2b017878b790b6ea98a0f6c5f9c@sha256:2dd96b6f43c08ccabd5f4747fce5854f5f96af509b32e5cf6493f136e9833649 ENV DEBIAN_FRONTEND=noninteractive RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \ diff --git a/examples/shared/golang/Dockerfile b/examples/shared/golang/Dockerfile index 5a940c392906..5a7a92edfe95 100644 --- a/examples/shared/golang/Dockerfile +++ b/examples/shared/golang/Dockerfile @@ -1,9 +1,9 @@ -FROM debian:bookworm-slim@sha256:d02c76d82364cedca16ba3ed6f9102406fa9fa8833076a609cabf14270f43dfc as os-base +FROM debian:bookworm-slim@sha256:ccb33c3ac5b02588fc1d9e4fc09b952e433d0c54d8618d0ee1afadf1f3cf2455 as os-base RUN rm -f /etc/apt/apt.conf.d/docker-clean \ && echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' | tee /etc/apt/apt.conf.d/keep-cache -FROM golang:1.22.0-bookworm@sha256:925fe3fa28ba428cf67a7947ae838f8a1523117b40e3e6b5106c378e3f97fa29 as golang-base +FROM golang:1.22.1-bookworm@sha256:d996c645c9934e770e64f05fc2bc103755197b43fd999b3aa5419142e1ee6d78 as golang-base FROM golang-base as golang-control-plane-builder diff --git a/examples/shared/jaeger/Dockerfile b/examples/shared/jaeger/Dockerfile index 13fb4ff4b21d..996f0b6c83dc 100644 --- a/examples/shared/jaeger/Dockerfile +++ b/examples/shared/jaeger/Dockerfile @@ -1,4 +1,4 @@ -FROM jaegertracing/all-in-one@sha256:2c8e4a0bec794046d92d0487f9abc423c60ebf0e970b832eec1bbb623f11134a +FROM jaegertracing/all-in-one@sha256:15b4256cc70141664c34c7aaa7e261588bbbfba7951393a82a49ff3db9f0f402 HEALTHCHECK \ --interval=1s \ --timeout=1s \ diff --git a/examples/shared/node/Dockerfile b/examples/shared/node/Dockerfile index 3d497d08223a..22d2a68c7995 100644 --- a/examples/shared/node/Dockerfile +++ b/examples/shared/node/Dockerfile @@ -1,4 +1,4 @@ -FROM node:21.7-bookworm-slim@sha256:fbdb66de323c6aa010c6afc04dda9a4031d113ebc623588f42633b124b3a5ac6 as node-base +FROM node:21.7-bookworm-slim@sha256:ab9d8781cb3fe8b6429d68c6ce19512a4ff526a6688240249ca15069b8124626 as node-base FROM node-base as node-http-auth diff --git a/examples/shared/postgres/Dockerfile b/examples/shared/postgres/Dockerfile index eb29c21895f4..b00d7aa9bb9d 100644 --- a/examples/shared/postgres/Dockerfile +++ b/examples/shared/postgres/Dockerfile @@ -1,3 +1,3 @@ -FROM postgres:latest@sha256:f58300ac8d393b2e3b09d36ea12d7d24ee9440440e421472a300e929ddb63460 +FROM postgres:latest@sha256:6b841c8f6a819884207402f1209a8116844365df15fca8cf556fc54a24c70800 COPY docker-healthcheck.sh /usr/local/bin/ HEALTHCHECK CMD ["docker-healthcheck.sh"] diff --git a/examples/shared/websocket/Dockerfile b/examples/shared/websocket/Dockerfile index 79ab5716f451..eec89e154fc0 100644 --- a/examples/shared/websocket/Dockerfile +++ b/examples/shared/websocket/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:bookworm-slim@sha256:d02c76d82364cedca16ba3ed6f9102406fa9fa8833076a609cabf14270f43dfc as websocket-base +FROM debian:bookworm-slim@sha256:ccb33c3ac5b02588fc1d9e4fc09b952e433d0c54d8618d0ee1afadf1f3cf2455 as websocket-base ENV DEBIAN_FRONTEND=noninteractive RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \ diff --git a/examples/zipkin/Dockerfile-zipkin b/examples/zipkin/Dockerfile-zipkin index 5e3813094aee..a3d2df5247fb 100644 --- a/examples/zipkin/Dockerfile-zipkin +++ b/examples/zipkin/Dockerfile-zipkin @@ -1 +1 @@ -FROM openzipkin/zipkin:latest@sha256:be6eedb41ecae348c1d61abe9b9f22e5af582604fec4ad12e4fa782e99746a6f +FROM openzipkin/zipkin:latest@sha256:8448c6d50247a413ea5d38393307b4a751896bb0fcfb31a21e95ecd194c0b4c7 diff --git a/examples/zipkin/verify.sh b/examples/zipkin/verify.sh index 412e04082df0..0744d0a41be8 100755 --- a/examples/zipkin/verify.sh +++ b/examples/zipkin/verify.sh @@ -19,5 +19,5 @@ responds_with \ run_log "View the traces in Zipkin UI" responds_with \ - "" \ + "" \ "http://localhost:${PORT_UI}/zipkin/" diff --git a/mobile/.bazelrc b/mobile/.bazelrc index 0fc992ca2e46..f60cad86caa8 100644 --- a/mobile/.bazelrc +++ b/mobile/.bazelrc @@ -295,9 +295,7 @@ build:mobile-remote-ci-macos-kotlin --@com_envoyproxy_protoc_gen_validate//bazel build:mobile-remote-ci-macos-swift --config=mobile-remote-ci-macos build:mobile-remote-ci-macos-swift --config=mobile-test-ios -build:mobile-remote-ci-macos-swift --config=mobile-remote-ci-macos build:mobile-remote-ci-macos-swift --define=envoy_mobile_request_compression=disabled -build:mobile-remote-ci-macos-swift --define=envoy_mobile_swift_cxx_interop=disabled build:mobile-remote-ci-macos-swift --define=google_grpc=disabled build:mobile-remote-ci-macos-swift --define=envoy_mobile_xds=disabled build:mobile-remote-ci-macos-swift --@envoy//bazel:http3=False diff --git a/mobile/.github/CODEOWNERS b/mobile/.github/CODEOWNERS deleted file mode 100644 index f24bf5f43472..000000000000 --- a/mobile/.github/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -* @lyft/envoy-mobile-admin diff --git a/mobile/bazel/EnvoyMobileTestSuite.kt b/mobile/bazel/EnvoyMobileTestSuite.kt index 04a4130a5ddc..8941e4bdcdf9 100644 --- a/mobile/bazel/EnvoyMobileTestSuite.kt +++ b/mobile/bazel/EnvoyMobileTestSuite.kt @@ -18,7 +18,7 @@ object EnvoyMobileTestSuite { .enableAnnotationInfo() .enableMethodInfo() .ignoreClassVisibility() - .acceptPackages("io.envoyproxy", "test.kotlin.integration", "org.chromium.net") + .acceptPackages("io.envoyproxy", "test.java.integration", "test.kotlin.integration", "org.chromium.net") .scan() scan.getClassesWithMethodAnnotation(junitTestAnnotation) .asSequence() diff --git a/mobile/library/cc/BUILD b/mobile/library/cc/BUILD index 516f8c372a5e..be0f83254934 100644 --- a/mobile/library/cc/BUILD +++ b/mobile/library/cc/BUILD @@ -41,6 +41,7 @@ envoy_cc_library( "@envoy_api//envoy/extensions/transport_sockets/http_11_proxy/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/transport_sockets/quic/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/transport_sockets/raw_buffer/v3:pkg_cc_proto", + "@envoy_mobile//library/common:internal_engine_types_lib", "@envoy_mobile//library/common/config:certificates_lib", "@envoy_mobile//library/common/extensions/cert_validator/platform_bridge:platform_bridge_cc_proto", "@envoy_mobile//library/common/extensions/filters/http/local_error:filter_cc_proto", @@ -60,7 +61,6 @@ envoy_cc_library( srcs = [ "bridge_utility.cc", "engine.cc", - "engine_callbacks.cc", "headers.cc", "headers_builder.cc", "key_value_store.cc", @@ -85,7 +85,6 @@ envoy_cc_library( "bridge_utility.h", "direct_response_testing.h", "engine.h", - "engine_callbacks.h", "envoy_error.h", "headers.h", "headers_builder.h", diff --git a/mobile/library/cc/engine_builder.cc b/mobile/library/cc/engine_builder.cc index 8152ccef64a4..d925cf6c66b0 100644 --- a/mobile/library/cc/engine_builder.cc +++ b/mobile/library/cc/engine_builder.cc @@ -138,7 +138,7 @@ void XdsBuilder::build(envoy::config::bootstrap::v3::Bootstrap& bootstrap) const } #endif -EngineBuilder::EngineBuilder() : callbacks_(std::make_shared()) { +EngineBuilder::EngineBuilder() : callbacks_(std::make_unique()) { #ifndef ENVOY_ENABLE_QUIC enable_http3_ = false; #endif @@ -899,9 +899,9 @@ EngineSharedPtr EngineBuilder::build() { envoy_event_tracker null_tracker{}; - Envoy::InternalEngine* envoy_engine = new Envoy::InternalEngine( - callbacks_->asEnvoyEngineCallbacks(), - (envoy_logger_.has_value()) ? *envoy_logger_ : null_logger, null_tracker); + InternalEngine* envoy_engine = + new InternalEngine(std::move(callbacks_), + (envoy_logger_.has_value()) ? *envoy_logger_ : null_logger, null_tracker); for (const auto& [name, store] : key_value_stores_) { // TODO(goaway): This leaks, but it's tied to the life of the engine. diff --git a/mobile/library/cc/engine_builder.h b/mobile/library/cc/engine_builder.h index 746d32f6f901..1a3e0ab5c859 100644 --- a/mobile/library/cc/engine_builder.h +++ b/mobile/library/cc/engine_builder.h @@ -14,10 +14,10 @@ #include "absl/types/optional.h" #include "direct_response_testing.h" #include "library/cc/engine.h" -#include "library/cc/engine_callbacks.h" #include "library/cc/key_value_store.h" #include "library/cc/log_level.h" #include "library/cc/string_accessor.h" +#include "library/common/internal_engine_types.h" #include "library/common/types/matcher_data.h" namespace Envoy { @@ -123,7 +123,8 @@ class XdsBuilder final { class EngineBuilder { public: EngineBuilder(); - virtual ~EngineBuilder() {} + EngineBuilder(EngineBuilder&&) = default; + virtual ~EngineBuilder() = default; EngineBuilder& addLogLevel(LogLevel log_level); EngineBuilder& setLogger(envoy_logger envoy_logger); @@ -215,7 +216,7 @@ class EngineBuilder { LogLevel log_level_ = LogLevel::info; absl::optional envoy_logger_; - EngineCallbacksSharedPtr callbacks_; + std::unique_ptr callbacks_; int connect_timeout_seconds_ = 30; int dns_refresh_seconds_ = 60; diff --git a/mobile/library/cc/engine_callbacks.cc b/mobile/library/cc/engine_callbacks.cc deleted file mode 100644 index ba570a8d1fe6..000000000000 --- a/mobile/library/cc/engine_callbacks.cc +++ /dev/null @@ -1,31 +0,0 @@ -#include "library/cc/engine_callbacks.h" - -namespace Envoy { -namespace Platform { - -namespace { - -void c_on_engine_running(void* context) { - auto engine_callbacks = *static_cast(context); - std::function on_engine_running_cb = std::move(engine_callbacks->on_engine_running); - engine_callbacks->on_engine_running = {}; - on_engine_running_cb(); -} - -void c_on_exit(void* context) { - auto engine_callbacks_ptr = static_cast(context); - delete engine_callbacks_ptr; -} - -} // namespace - -envoy_engine_callbacks EngineCallbacks::asEnvoyEngineCallbacks() { - return envoy_engine_callbacks{ - &c_on_engine_running, - &c_on_exit, - new EngineCallbacksSharedPtr(shared_from_this()), - }; -} - -} // namespace Platform -} // namespace Envoy diff --git a/mobile/library/cc/engine_callbacks.h b/mobile/library/cc/engine_callbacks.h deleted file mode 100644 index ee82028c84f4..000000000000 --- a/mobile/library/cc/engine_callbacks.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include - -#include "library/cc/engine.h" -#include "library/common/types/c_types.h" - -namespace Envoy { -namespace Platform { - -struct EngineCallbacks : public std::enable_shared_from_this { - std::function on_engine_running; - // unused: - // std::function on_exit; - - envoy_engine_callbacks asEnvoyEngineCallbacks(); -}; - -using EngineCallbacksSharedPtr = std::shared_ptr; - -} // namespace Platform -} // namespace Envoy diff --git a/mobile/library/common/BUILD b/mobile/library/common/BUILD index f2c47473ceaa..69f9a7b288c2 100644 --- a/mobile/library/common/BUILD +++ b/mobile/library/common/BUILD @@ -24,6 +24,7 @@ envoy_cc_library( repository = "@envoy", deps = [ ":engine_common_lib", + ":internal_engine_types_lib", "//library/common/bridge:utility_lib", "//library/common/data:utility_lib", "//library/common/event:provisional_dispatcher_lib", @@ -73,3 +74,11 @@ envoy_cc_library( "@envoy//source/exe:envoy_stripped_main_base_lib", ], ) + +envoy_cc_library( + name = "internal_engine_types_lib", + hdrs = [ + "internal_engine_types.h", + ], + repository = "@envoy", +) diff --git a/mobile/library/common/extensions/cert_validator/platform_bridge/config.cc b/mobile/library/common/extensions/cert_validator/platform_bridge/config.cc index 223e6ff8e925..807ec2dcfef7 100644 --- a/mobile/library/common/extensions/cert_validator/platform_bridge/config.cc +++ b/mobile/library/common/extensions/cert_validator/platform_bridge/config.cc @@ -9,7 +9,7 @@ namespace Tls { CertValidatorPtr PlatformBridgeCertValidatorFactory::createCertValidator( const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - TimeSource& /*time_source*/) { + Server::Configuration::CommonFactoryContext& /*context*/) { return std::make_unique(config, stats); } diff --git a/mobile/library/common/extensions/cert_validator/platform_bridge/config.h b/mobile/library/common/extensions/cert_validator/platform_bridge/config.h index bc884fddc3ac..d070c3fc99af 100644 --- a/mobile/library/common/extensions/cert_validator/platform_bridge/config.h +++ b/mobile/library/common/extensions/cert_validator/platform_bridge/config.h @@ -15,8 +15,9 @@ namespace Tls { class PlatformBridgeCertValidatorFactory : public CertValidatorFactory, public Config::TypedFactory { public: - CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, - SslStats& stats, TimeSource& time_source) override; + CertValidatorPtr + createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, + Server::Configuration::CommonFactoryContext& context) override; std::string name() const override { return "envoy_mobile.cert_validator.platform_bridge_cert_validator"; diff --git a/mobile/library/common/internal_engine.cc b/mobile/library/common/internal_engine.cc index 9269ba676678..c185777fefdd 100644 --- a/mobile/library/common/internal_engine.cc +++ b/mobile/library/common/internal_engine.cc @@ -15,10 +15,10 @@ static std::atomic current_stream_handle_{0}; envoy_stream_t InternalEngine::initStream() { return current_stream_handle_++; } -InternalEngine::InternalEngine(envoy_engine_callbacks callbacks, envoy_logger logger, - envoy_event_tracker event_tracker, +InternalEngine::InternalEngine(std::unique_ptr callbacks, + envoy_logger logger, envoy_event_tracker event_tracker, Thread::PosixThreadFactoryPtr thread_factory) - : thread_factory_(std::move(thread_factory)), callbacks_(callbacks), logger_(logger), + : thread_factory_(std::move(thread_factory)), callbacks_(std::move(callbacks)), logger_(logger), event_tracker_(event_tracker), dispatcher_(std::make_unique()) { ExtensionRegistry::registerFactories(); @@ -33,9 +33,10 @@ InternalEngine::InternalEngine(envoy_engine_callbacks callbacks, envoy_logger lo Runtime::maybeSetRuntimeGuard("envoy.reloadable_features.dfp_mixed_scheme", true); } -InternalEngine::InternalEngine(envoy_engine_callbacks callbacks, envoy_logger logger, - envoy_event_tracker event_tracker) - : InternalEngine(callbacks, logger, event_tracker, Thread::PosixThreadFactory::create()) {} +InternalEngine::InternalEngine(std::unique_ptr callbacks, + envoy_logger logger, envoy_event_tracker event_tracker) + : InternalEngine(std::move(callbacks), logger, event_tracker, + Thread::PosixThreadFactory::create()) {} envoy_status_t InternalEngine::run(const std::string& config, const std::string& log_level) { // Start the Envoy on the dedicated thread. @@ -128,9 +129,7 @@ envoy_status_t InternalEngine::main(std::shared_ptr opti server_->serverFactoryContext().scope(), server_->api().randomGenerator()); dispatcher_->drain(server_->dispatcher()); - if (callbacks_.on_engine_running != nullptr) { - callbacks_.on_engine_running(callbacks_.context); - } + callbacks_->on_engine_running(); }); } // mutex_ @@ -147,7 +146,7 @@ envoy_status_t InternalEngine::main(std::shared_ptr opti bug_handler_registration_.reset(nullptr); assert_handler_registration_.reset(nullptr); - callbacks_.on_exit(callbacks_.context); + callbacks_->on_exit(); return run_success ? ENVOY_SUCCESS : ENVOY_FAILURE; } diff --git a/mobile/library/common/internal_engine.h b/mobile/library/common/internal_engine.h index 3ac09ced5d07..9adeb99280ee 100644 --- a/mobile/library/common/internal_engine.h +++ b/mobile/library/common/internal_engine.h @@ -1,17 +1,16 @@ #pragma once #include "envoy/server/lifecycle_notifier.h" -#include "envoy/stats/store.h" #include "source/common/common/logger.h" #include "source/common/common/macros.h" #include "source/common/common/posix/thread_impl.h" #include "source/common/common/thread.h" -#include "absl/base/call_once.h" #include "extension_registry.h" #include "library/common/engine_common.h" #include "library/common/http/client.h" +#include "library/common/internal_engine_types.h" #include "library/common/logger/logger_delegate.h" #include "library/common/network/connectivity_manager.h" #include "library/common/types/c_types.h" @@ -26,7 +25,7 @@ class InternalEngine : public Logger::Loggable { * @param logger, the callbacks to use for engine logging. * @param event_tracker, the event tracker to use for the emission of events. */ - InternalEngine(envoy_engine_callbacks callbacks, envoy_logger logger, + InternalEngine(std::unique_ptr callbacks, envoy_logger logger, envoy_event_tracker event_tracker); /** @@ -123,7 +122,7 @@ class InternalEngine : public Logger::Loggable { private: GTEST_FRIEND_CLASS(InternalEngineTest, ThreadCreationFailed); - InternalEngine(envoy_engine_callbacks callbacks, envoy_logger logger, + InternalEngine(std::unique_ptr callbacks, envoy_logger logger, envoy_event_tracker event_tracker, Thread::PosixThreadFactoryPtr thread_factory); envoy_status_t main(std::shared_ptr options); @@ -134,7 +133,7 @@ class InternalEngine : public Logger::Loggable { Event::Dispatcher* event_dispatcher_{}; Stats::ScopeSharedPtr client_scope_; Stats::StatNameSetPtr stat_name_set_; - envoy_engine_callbacks callbacks_; + std::unique_ptr callbacks_; envoy_logger logger_; envoy_event_tracker event_tracker_; Assert::ActionRegistrationPtr assert_handler_registration_; diff --git a/mobile/library/common/internal_engine_types.h b/mobile/library/common/internal_engine_types.h new file mode 100644 index 000000000000..7058127e1b65 --- /dev/null +++ b/mobile/library/common/internal_engine_types.h @@ -0,0 +1,23 @@ +#pragma once + +//================================================================================================== +// READ THIS BEFORE UPDATING THIS FILE +//================================================================================================== +// Keep the code here (including the includes) as simple as possible given that this file will be +// directly included by Swift and the Swift/C++ interop is far from complete. Including headers or +// having code that is not supported by Swift may lead into weird compilation errors that can be +// difficult to debug. +// For more information, see +// https://github.com/apple/swift/blob/swift-5.7.3-RELEASE/docs/CppInteroperability/CppInteroperabilityStatus.md + +#include + +namespace Envoy { + +/** The callbacks for the `InternalEngine`. */ +struct InternalEngineCallbacks { + std::function on_engine_running = [] {}; + std::function on_exit = [] {}; +}; + +} // namespace Envoy diff --git a/mobile/library/common/types/c_types.h b/mobile/library/common/types/c_types.h index c942e11d298f..a75e5f036db4 100644 --- a/mobile/library/common/types/c_types.h +++ b/mobile/library/common/types/c_types.h @@ -407,19 +407,6 @@ typedef void (*envoy_on_complete_f)(envoy_stream_intel stream_intel, typedef void (*envoy_on_cancel_f)(envoy_stream_intel stream_intel, envoy_final_stream_intel final_stream_intel, void* context); -/** - * Called when the envoy engine is exiting. - */ -typedef void (*envoy_on_exit_f)(void* context); - -/** - * Called when the envoy has finished its async setup and returned post-init callbacks. - * - * @param context, contains the necessary state to carry out platform-specific dispatch and - * execution. - */ -typedef void (*envoy_on_engine_running_f)(void* context); - /** * Called when envoy's logger logs data. * @@ -482,16 +469,6 @@ typedef struct { void* context; } envoy_http_callbacks; -/** - * Interface that can handle engine callbacks. - */ -typedef struct { - envoy_on_engine_running_f on_engine_running; - envoy_on_exit_f on_exit; - // Context passed through to callbacks to provide dispatch and execution state. - void* context; -} envoy_engine_callbacks; - /** * Interface for logging. */ diff --git a/mobile/library/java/io/envoyproxy/envoymobile/engine/AndroidJniLibrary.java b/mobile/library/java/io/envoyproxy/envoymobile/engine/AndroidJniLibrary.java index dae533e009a0..7bcd24764c52 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/engine/AndroidJniLibrary.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/engine/AndroidJniLibrary.java @@ -1,7 +1,6 @@ package io.envoyproxy.envoymobile.engine; import android.content.Context; -import android.net.ConnectivityManager; public class AndroidJniLibrary { // Internal reference to helper object used to load and initialize the native diff --git a/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyConfiguration.java b/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyConfiguration.java index 207257faa559..84c0d1107909 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyConfiguration.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyConfiguration.java @@ -42,6 +42,7 @@ public enum TrustChainVerification { public final List quicCanonicalSuffixes; public final Boolean enableGzipDecompression; public final Boolean enableBrotliDecompression; + public final Boolean enablePortMigration; public final Boolean enableSocketTagging; public final Boolean enableInterfaceBinding; public final Integer h2ConnectionKeepaliveIdleIntervalMilliseconds; @@ -110,6 +111,7 @@ public enum TrustChainVerification { * decompression. * @param enableBrotliDecompression whether to enable response brotli * decompression. + * @param enablePortMigration whether to enable quic port migration. * @param enableSocketTagging whether to enable socket tagging. * @param enableInterfaceBinding whether to allow interface binding. * @param h2ConnectionKeepaliveIdleIntervalMilliseconds rate in milliseconds seconds to send h2 @@ -157,7 +159,7 @@ public EnvoyConfiguration( boolean enableDrainPostDnsRefresh, boolean enableHttp3, String http3ConnectionOptions, String http3ClientConnectionOptions, Map quicHints, List quicCanonicalSuffixes, boolean enableGzipDecompression, - boolean enableBrotliDecompression, boolean enableSocketTagging, + boolean enableBrotliDecompression, boolean enablePortMigration, boolean enableSocketTagging, boolean enableInterfaceBinding, int h2ConnectionKeepaliveIdleIntervalMilliseconds, int h2ConnectionKeepaliveTimeoutSeconds, int maxConnectionsPerHost, int streamIdleTimeoutSeconds, int perTryIdleTimeoutSeconds, String appVersion, String appId, @@ -192,6 +194,7 @@ public EnvoyConfiguration( this.quicCanonicalSuffixes = quicCanonicalSuffixes; this.enableGzipDecompression = enableGzipDecompression; this.enableBrotliDecompression = enableBrotliDecompression; + this.enablePortMigration = enablePortMigration; this.enableSocketTagging = enableSocketTagging; this.enableInterfaceBinding = enableInterfaceBinding; this.h2ConnectionKeepaliveIdleIntervalMilliseconds = @@ -258,8 +261,8 @@ public long createBootstrap() { dnsFailureRefreshSecondsMax, dnsQueryTimeoutSeconds, dnsMinRefreshSeconds, dnsPreresolve, enableDNSCache, dnsCacheSaveIntervalSeconds, enableDrainPostDnsRefresh, enableHttp3, http3ConnectionOptions, http3ClientConnectionOptions, quicHints, quicSuffixes, - enableGzipDecompression, enableBrotliDecompression, enableSocketTagging, - enableInterfaceBinding, h2ConnectionKeepaliveIdleIntervalMilliseconds, + enableGzipDecompression, enableBrotliDecompression, enablePortMigration, + enableSocketTagging, enableInterfaceBinding, h2ConnectionKeepaliveIdleIntervalMilliseconds, h2ConnectionKeepaliveTimeoutSeconds, maxConnectionsPerHost, streamIdleTimeoutSeconds, perTryIdleTimeoutSeconds, appVersion, appId, enforceTrustChainVerification, filterChain, enablePlatformCertificatesValidation, runtimeGuards, rtdsResourceName, rtdsTimeoutSeconds, diff --git a/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyHTTPStream.java b/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyHTTPStream.java index dfa3e2c371e4..6284fff1d674 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyHTTPStream.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyHTTPStream.java @@ -2,9 +2,7 @@ import io.envoyproxy.envoymobile.engine.types.EnvoyHTTPCallbacks; -import java.nio.charset.StandardCharsets; import java.nio.ByteBuffer; -import java.util.ArrayList; import java.util.List; import java.util.Map; diff --git a/mobile/library/java/io/envoyproxy/envoymobile/engine/HeaderMatchConfig.java b/mobile/library/java/io/envoyproxy/envoymobile/engine/HeaderMatchConfig.java index 708cbf9e4713..c8764eba4682 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/engine/HeaderMatchConfig.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/engine/HeaderMatchConfig.java @@ -1,7 +1,5 @@ package io.envoyproxy.envoymobile.engine; -import java.util.List; - /* Datatype used by the EnvoyConfiguration to header matches for virtual clusters * * All the fields here map to the fields by the respective names in the HeaderMatcher message diff --git a/mobile/library/java/io/envoyproxy/envoymobile/engine/JniBridgeUtility.java b/mobile/library/java/io/envoyproxy/envoymobile/engine/JniBridgeUtility.java index 6adb49b36b0a..bcd5cd7c4839 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/engine/JniBridgeUtility.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/engine/JniBridgeUtility.java @@ -1,7 +1,6 @@ package io.envoyproxy.envoymobile.engine; import java.nio.charset.StandardCharsets; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.Map; diff --git a/mobile/library/java/io/envoyproxy/envoymobile/engine/JniLibrary.java b/mobile/library/java/io/envoyproxy/envoymobile/engine/JniLibrary.java index aa2b82c8e4ed..ea8a928591fe 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/engine/JniLibrary.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/engine/JniLibrary.java @@ -301,7 +301,7 @@ public static native long createBootstrap( boolean enableDrainPostDnsRefresh, boolean enableHttp3, String http3ConnectionOptions, String http3ClientConnectionOptions, byte[][] quicHints, byte[][] quicCanonicalSuffixes, boolean enableGzipDecompression, boolean enableBrotliDecompression, - boolean enableSocketTagging, boolean enableInterfaceBinding, + boolean enablePortMigration, boolean enableSocketTagging, boolean enableInterfaceBinding, long h2ConnectionKeepaliveIdleIntervalMilliseconds, long h2ConnectionKeepaliveTimeoutSeconds, long maxConnectionsPerHost, long streamIdleTimeoutSeconds, long perTryIdleTimeoutSeconds, String appVersion, String appId, boolean trustChainVerification, byte[][] filterChain, diff --git a/mobile/library/java/io/envoyproxy/envoymobile/utilities/AndroidNetworkLibrary.java b/mobile/library/java/io/envoyproxy/envoymobile/utilities/AndroidNetworkLibrary.java index d7b07e05265e..99ac5265f428 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/utilities/AndroidNetworkLibrary.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/utilities/AndroidNetworkLibrary.java @@ -13,12 +13,8 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.Socket; import java.net.SocketAddress; -import java.net.SocketException; import java.net.SocketImpl; -import java.net.URLConnection; import java.net.Socket; import java.security.KeyStoreException; diff --git a/mobile/library/java/io/envoyproxy/envoymobile/utilities/ContextUtils.java b/mobile/library/java/io/envoyproxy/envoymobile/utilities/ContextUtils.java index ce05e83efdd0..c738ee51a092 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/utilities/ContextUtils.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/utilities/ContextUtils.java @@ -1,6 +1,5 @@ package io.envoyproxy.envoymobile.utilities; -import android.app.Activity; import android.app.Application; import android.content.BroadcastReceiver; import android.content.Context; @@ -8,13 +7,10 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; -import android.content.res.AssetManager; import android.os.Build; import android.os.Handler; -import android.os.Process; import android.preference.PreferenceManager; -import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; /** diff --git a/mobile/library/java/io/envoyproxy/envoymobile/utilities/FakeX509Util.java b/mobile/library/java/io/envoyproxy/envoymobile/utilities/FakeX509Util.java index 42db7a5e4f66..afa33586eb1c 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/utilities/FakeX509Util.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/utilities/FakeX509Util.java @@ -1,6 +1,5 @@ package io.envoyproxy.envoymobile.utilities; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Set; diff --git a/mobile/library/java/org/chromium/net/BidirectionalStream.java b/mobile/library/java/org/chromium/net/BidirectionalStream.java index e99ba187d189..03424dddebdc 100644 --- a/mobile/library/java/org/chromium/net/BidirectionalStream.java +++ b/mobile/library/java/org/chromium/net/BidirectionalStream.java @@ -1,6 +1,5 @@ package org.chromium.net; -import android.annotation.SuppressLint; import java.nio.ByteBuffer; import java.util.concurrent.Executor; diff --git a/mobile/library/java/org/chromium/net/impl/CronvoyBidirectionalStream.java b/mobile/library/java/org/chromium/net/impl/CronvoyBidirectionalStream.java index 19870e3b31e8..9cd0a34d6846 100644 --- a/mobile/library/java/org/chromium/net/impl/CronvoyBidirectionalStream.java +++ b/mobile/library/java/org/chromium/net/impl/CronvoyBidirectionalStream.java @@ -13,7 +13,6 @@ import org.chromium.net.CallbackException; import org.chromium.net.CronetException; import org.chromium.net.ExperimentalBidirectionalStream; -import org.chromium.net.NetworkException; import org.chromium.net.RequestFinishedInfo; import org.chromium.net.UrlResponseInfo; import org.chromium.net.impl.Annotations.RequestPriority; @@ -27,7 +26,6 @@ import java.nio.ByteBuffer; import java.util.AbstractMap; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; diff --git a/mobile/library/java/org/chromium/net/impl/CronvoyEngineBuilderImpl.java b/mobile/library/java/org/chromium/net/impl/CronvoyEngineBuilderImpl.java index 300050049da7..bbd867c55b3e 100644 --- a/mobile/library/java/org/chromium/net/impl/CronvoyEngineBuilderImpl.java +++ b/mobile/library/java/org/chromium/net/impl/CronvoyEngineBuilderImpl.java @@ -62,6 +62,7 @@ final static class Pkp { private String mQuicClientConnectionOptions = ""; private boolean mHttp2Enabled; private boolean mBrotiEnabled; + private boolean mPortMigrationEnabled; private boolean mDisableCache; private int mHttpCacheMode; private long mHttpCacheMaxSize; @@ -239,6 +240,13 @@ public CronvoyEngineBuilderImpl addQuicCanonicalSuffix(String suffix) { List quicCanonicalSuffixes() { return mQuicCanonicalSuffixes; } + public CronvoyEngineBuilderImpl enablePortMigration(boolean enablePortMigration) { + mPortMigrationEnabled = enablePortMigration; + return this; + } + + boolean portMigrationEnabled() { return mPortMigrationEnabled; } + @Override public CronvoyEngineBuilderImpl addPublicKeyPins(String hostName, Set pinsSha256, boolean includeSubdomains, Date expirationDate) { diff --git a/mobile/library/java/org/chromium/net/impl/NativeCronvoyEngineBuilderImpl.java b/mobile/library/java/org/chromium/net/impl/NativeCronvoyEngineBuilderImpl.java index 3ee404f7a5ff..6ba9dcf6e90a 100644 --- a/mobile/library/java/org/chromium/net/impl/NativeCronvoyEngineBuilderImpl.java +++ b/mobile/library/java/org/chromium/net/impl/NativeCronvoyEngineBuilderImpl.java @@ -45,7 +45,7 @@ public class NativeCronvoyEngineBuilderImpl extends CronvoyEngineBuilderImpl { private final List mDnsFallbackNameservers = Collections.emptyList(); private final boolean mEnableDnsFilterUnroutableFamilies = true; private final boolean mDnsUseSystemResolver = true; - private final boolean mEnableDrainPostDnsRefresh = false; + private boolean mEnableDrainPostDnsRefresh = false; private final boolean mEnableGzipDecompression = true; private final boolean mEnableSocketTag = true; private final boolean mEnableInterfaceBinding = false; @@ -71,6 +71,17 @@ public class NativeCronvoyEngineBuilderImpl extends CronvoyEngineBuilderImpl { */ public NativeCronvoyEngineBuilderImpl(Context context) { super(context); } + /** + * Enable draining of the connections after a DNS refresh changes the host address mapping. + * The default behavior is to not enable draining post DNS refresh. + * + * @param enable If true, enable drain post DNS refresh; otherwise, don't. + */ + public NativeCronvoyEngineBuilderImpl setEnableDrainPostDnsRefresh(boolean enable) { + mEnableDrainPostDnsRefresh = enable; + return this; + } + /** * Indicates to skip the TLS certificate verification. * @@ -125,11 +136,12 @@ private EnvoyConfiguration createEnvoyConfiguration() { mDnsPreresolveHostnames, mEnableDNSCache, mDnsCacheSaveIntervalSeconds, mEnableDrainPostDnsRefresh, quicEnabled(), quicConnectionOptions(), quicClientConnectionOptions(), quicHints(), quicCanonicalSuffixes(), - mEnableGzipDecompression, brotliEnabled(), mEnableSocketTag, mEnableInterfaceBinding, - mH2ConnectionKeepaliveIdleIntervalMilliseconds, mH2ConnectionKeepaliveTimeoutSeconds, - mMaxConnectionsPerHost, mStreamIdleTimeoutSeconds, mPerTryIdleTimeoutSeconds, mAppVersion, - mAppId, mTrustChainVerification, nativeFilterChain, platformFilterChain, stringAccessors, - keyValueStores, runtimeGuards, mEnablePlatformCertificatesValidation, + mEnableGzipDecompression, brotliEnabled(), portMigrationEnabled(), mEnableSocketTag, + mEnableInterfaceBinding, mH2ConnectionKeepaliveIdleIntervalMilliseconds, + mH2ConnectionKeepaliveTimeoutSeconds, mMaxConnectionsPerHost, mStreamIdleTimeoutSeconds, + mPerTryIdleTimeoutSeconds, mAppVersion, mAppId, mTrustChainVerification, nativeFilterChain, + platformFilterChain, stringAccessors, keyValueStores, runtimeGuards, + mEnablePlatformCertificatesValidation, /*rtdsResourceName=*/"", /*rtdsTimeoutSeconds=*/0, /*xdsAddress=*/"", /*xdsPort=*/0, /*xdsGrpcInitialMetadata=*/Collections.emptyMap(), /*xdsSslRootCerts=*/"", mNodeId, mNodeRegion, mNodeZone, mNodeSubZone, diff --git a/mobile/library/jni/android_network_utility.cc b/mobile/library/jni/android_network_utility.cc index e8e2f71604cd..0a0fb87b2702 100644 --- a/mobile/library/jni/android_network_utility.cc +++ b/mobile/library/jni/android_network_utility.cc @@ -88,7 +88,6 @@ LocalRefUniquePtr callJvmVerifyX509CertChain(Envoy::JNI::JniHelper& jni const std::vector& cert_chain, std::string auth_type, absl::string_view hostname) { - jni_log("[Envoy]", "jvmVerifyX509CertChain"); LocalRefUniquePtr jcls_AndroidNetworkLibrary = findClass("io.envoyproxy.envoymobile.utilities.AndroidNetworkLibrary"); jmethodID jmid_verifyServerCertificates = jni_helper.getStaticMethodId( @@ -107,8 +106,6 @@ LocalRefUniquePtr callJvmVerifyX509CertChain(Envoy::JNI::JniHelper& jni envoy_cert_validation_result verifyX509CertChain(const std::vector& certs, absl::string_view hostname) { - jni_log("[Envoy]", "verifyX509CertChain"); - envoy_cert_verify_status_t result; bool is_issued_by_known_root; std::vector verified_chain; diff --git a/mobile/library/jni/java_jni_support.cc b/mobile/library/jni/java_jni_support.cc index 003cc31754b6..346e2cc1afbd 100644 --- a/mobile/library/jni/java_jni_support.cc +++ b/mobile/library/jni/java_jni_support.cc @@ -2,10 +2,6 @@ // NOLINT(namespace-envoy) -int jni_log_fmt(const char* /*tag*/, const char* /*fmt*/, void* /*value*/) { return 0; } - -int jni_log(const char* /*tag*/, const char* /*str*/) { return 0; } - jint attach_jvm(JavaVM* vm, JNIEnv** p_env, void* thr_args) { return vm->AttachCurrentThread(reinterpret_cast(p_env), thr_args); } diff --git a/mobile/library/jni/jni_impl.cc b/mobile/library/jni/jni_impl.cc index 3146f9271002..fd86a5c7cae4 100644 --- a/mobile/library/jni/jni_impl.cc +++ b/mobile/library/jni/jni_impl.cc @@ -33,26 +33,6 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* /*reserved*/) { // JniLibrary -static void jvm_on_engine_running(void* context) { - if (context == nullptr) { - return; - } - - jni_log("[Envoy]", "jvm_on_engine_running"); - Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); - jobject j_context = static_cast(context); - Envoy::JNI::LocalRefUniquePtr jcls_JvmonEngineRunningContext = - jni_helper.getObjectClass(j_context); - jmethodID jmid_onEngineRunning = jni_helper.getMethodId( - jcls_JvmonEngineRunningContext.get(), "invokeOnEngineRunning", "()Ljava/lang/Object;"); - Envoy::JNI::LocalRefUniquePtr unused = - jni_helper.callObjectMethod(j_context, jmid_onEngineRunning); - - // TODO(goaway): This isn't re-used by other engine callbacks, so it's safe to delete here. - // This will need to be updated for https://github.com/envoyproxy/envoy-mobile/issues/332 - jni_helper.getEnv()->DeleteGlobalRef(j_context); -} - static void jvm_on_log(envoy_log_level log_level, envoy_data data, const void* context) { if (context == nullptr) { return; @@ -71,17 +51,7 @@ static void jvm_on_log(envoy_log_level log_level, envoy_data data, const void* c release_envoy_data(data); } -static void jvm_on_exit(void*) { - jni_log("[Envoy]", "library is exiting"); - // Note that this is not dispatched because the thread that - // needs to be detached is the engine thread. - // This function is called from the context of the engine's - // thread due to it being posted to the engine's event dispatcher. - Envoy::JNI::JavaVirtualMachine::detachCurrentThread(); -} - static void jvm_on_track(envoy_map events, const void* context) { - jni_log("[Envoy]", "jvm_on_track"); if (context == nullptr) { return; } @@ -109,10 +79,32 @@ Java_io_envoyproxy_envoymobile_engine_JniLibrary_setLogLevel(JNIEnv* /*env*/, jc extern "C" JNIEXPORT jlong JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_initEngine( JNIEnv* env, jclass, jobject on_start_context, jobject envoy_logger_context, jobject j_event_tracker) { + std::unique_ptr callbacks = + std::make_unique(); + jobject retained_on_start_context = env->NewGlobalRef(on_start_context); // Required to keep context in memory - envoy_engine_callbacks native_callbacks = {jvm_on_engine_running, jvm_on_exit, - retained_on_start_context}; + callbacks->on_engine_running = [retained_on_start_context] { + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); + Envoy::JNI::LocalRefUniquePtr jcls_JvmonEngineRunningContext = + jni_helper.getObjectClass(retained_on_start_context); + jmethodID jmid_onEngineRunning = jni_helper.getMethodId( + jcls_JvmonEngineRunningContext.get(), "invokeOnEngineRunning", "()Ljava/lang/Object;"); + Envoy::JNI::LocalRefUniquePtr unused = + jni_helper.callObjectMethod(retained_on_start_context, jmid_onEngineRunning); + + // TODO(goaway): This isn't re-used by other engine callbacks, so it's safe to delete here. + // This will need to be updated for https://github.com/envoyproxy/envoy-mobile/issues/332 + jni_helper.getEnv()->DeleteGlobalRef(retained_on_start_context); + }; + + callbacks->on_exit = [] { + // Note that this is not dispatched because the thread that + // needs to be detached is the engine thread. + // This function is called from the context of the engine's + // thread due to it being posted to the engine's event dispatcher. + Envoy::JNI::JavaVirtualMachine::detachCurrentThread(); + }; const jobject retained_logger_context = env->NewGlobalRef(envoy_logger_context); envoy_logger logger = {nullptr, nullptr, nullptr}; @@ -125,13 +117,12 @@ extern "C" JNIEXPORT jlong JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibr // TODO(goaway): The retained_context leaks, but it's tied to the life of the engine. // This will need to be updated for https://github.com/envoyproxy/envoy-mobile/issues/332. jobject retained_context = env->NewGlobalRef(j_event_tracker); - jni_log_fmt("[Envoy]", "retained_context: %p", retained_context); event_tracker.track = jvm_on_track; event_tracker.context = retained_context; } return reinterpret_cast( - new Envoy::InternalEngine(native_callbacks, logger, event_tracker)); + new Envoy::InternalEngine(std::move(callbacks), logger, event_tracker)); } extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_runEngine( @@ -180,7 +171,6 @@ extern "C" JNIEXPORT jstring JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_dumpStats(JNIEnv* env, jclass, // class jlong engine_handle) { - jni_log("[Envoy]", "dumpStats"); auto engine = reinterpret_cast(engine_handle); std::string stats = engine->dumpStats(); Envoy::JNI::JniHelper jni_helper(env); @@ -228,7 +218,6 @@ static void passHeaders(const char* method, const Envoy::Types::ManagedEnvoyHead static Envoy::JNI::LocalRefUniquePtr jvm_on_headers(const char* method, const Envoy::Types::ManagedEnvoyHeaders& headers, bool end_stream, envoy_stream_intel stream_intel, void* context) { - jni_log("[Envoy]", "jvm_on_headers"); Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(context); passHeaders("passHeader", headers, j_context); @@ -337,7 +326,6 @@ static Envoy::JNI::LocalRefUniquePtr jvm_on_data(const char* metho bool end_stream, envoy_stream_intel stream_intel, void* context) { - jni_log("[Envoy]", "jvm_on_data"); Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(context); @@ -432,18 +420,13 @@ static envoy_filter_data_status jvm_http_filter_on_response_data(envoy_data data /*pending_headers*/ pending_headers}; } -static void jvm_on_metadata(envoy_headers metadata, envoy_stream_intel /*stream_intel*/, - void* /*context*/) { - jni_log("[Envoy]", "jvm_on_metadata"); - jni_log("[Envoy]", std::to_string(metadata.length).c_str()); -} +static void jvm_on_metadata(envoy_headers /* metadata */, envoy_stream_intel /*stream_intel*/, + void* /*context*/) {} static Envoy::JNI::LocalRefUniquePtr jvm_on_trailers(const char* method, envoy_headers trailers, envoy_stream_intel stream_intel, void* context) { - jni_log("[Envoy]", "jvm_on_trailers"); - Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(context); passHeaders("passHeader", trailers, j_context); @@ -555,8 +538,6 @@ jvm_http_filter_on_response_trailers(envoy_headers trailers, envoy_stream_intel static void jvm_http_filter_set_request_callbacks(envoy_http_filter_callbacks callbacks, const void* context) { - jni_log("[Envoy]", "jvm_http_filter_set_request_callbacks"); - Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(const_cast(context)); Envoy::JNI::LocalRefUniquePtr jcls_JvmCallbackContext = @@ -575,8 +556,6 @@ static void jvm_http_filter_set_request_callbacks(envoy_http_filter_callbacks ca static void jvm_http_filter_set_response_callbacks(envoy_http_filter_callbacks callbacks, const void* context) { - jni_log("[Envoy]", "jvm_http_filter_set_response_callbacks"); - Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(const_cast(context)); Envoy::JNI::LocalRefUniquePtr jcls_JvmCallbackContext = @@ -596,8 +575,6 @@ static envoy_filter_resume_status jvm_http_filter_on_resume(const char* method, envoy_headers* headers, envoy_data* data, envoy_headers* trailers, bool end_stream, envoy_stream_intel stream_intel, const void* context) { - jni_log("[Envoy]", "jvm_on_resume"); - Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(const_cast(context)); jlong headers_length = -1; @@ -666,8 +643,6 @@ jvm_http_filter_on_resume_response(envoy_headers* headers, envoy_data* data, static void call_jvm_on_complete(envoy_stream_intel stream_intel, envoy_final_stream_intel final_stream_intel, void* context) { - jni_log("[Envoy]", "jvm_on_complete"); - Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(context); @@ -686,7 +661,6 @@ static void call_jvm_on_complete(envoy_stream_intel stream_intel, static void call_jvm_on_error(envoy_error error, envoy_stream_intel stream_intel, envoy_final_stream_intel final_stream_intel, void* context) { - jni_log("[Envoy]", "jvm_on_error"); Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(context); @@ -717,8 +691,6 @@ static void jvm_on_error(envoy_error error, envoy_stream_intel stream_intel, static void call_jvm_on_cancel(envoy_stream_intel stream_intel, envoy_final_stream_intel final_stream_intel, void* context) { - jni_log("[Envoy]", "jvm_on_cancel"); - Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(context); @@ -761,8 +733,6 @@ static void jvm_http_filter_on_cancel(envoy_stream_intel stream_intel, } static void jvm_on_send_window_available(envoy_stream_intel stream_intel, void* context) { - jni_log("[Envoy]", "jvm_on_send_window_available"); - Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(context); @@ -780,7 +750,6 @@ static void jvm_on_send_window_available(envoy_stream_intel stream_intel, void* // JvmKeyValueStoreContext static envoy_data jvm_kv_store_read(envoy_data key, const void* context) { - jni_log("[Envoy]", "jvm_kv_store_read"); Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(const_cast(context)); @@ -799,7 +768,6 @@ static envoy_data jvm_kv_store_read(envoy_data key, const void* context) { } static void jvm_kv_store_remove(envoy_data key, const void* context) { - jni_log("[Envoy]", "jvm_kv_store_remove"); Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(const_cast(context)); @@ -814,7 +782,6 @@ static void jvm_kv_store_remove(envoy_data key, const void* context) { } static void jvm_kv_store_save(envoy_data key, envoy_data value, const void* context) { - jni_log("[Envoy]", "jvm_kv_store_save"); Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); jobject j_context = static_cast(const_cast(context)); @@ -833,15 +800,11 @@ static void jvm_kv_store_save(envoy_data key, envoy_data value, const void* cont // JvmFilterFactoryContext static const void* jvm_http_filter_init(const void* context) { - jni_log("[Envoy]", "jvm_filter_init"); - Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); envoy_http_filter* c_filter = static_cast(const_cast(context)); jobject j_context = static_cast(const_cast(c_filter->static_context)); - jni_log_fmt("[Envoy]", "j_context: %p", j_context); - Envoy::JNI::LocalRefUniquePtr jcls_JvmFilterFactoryContext = jni_helper.getObjectClass(j_context); jmethodID jmid_create = @@ -850,7 +813,6 @@ static const void* jvm_http_filter_init(const void* context) { Envoy::JNI::LocalRefUniquePtr j_filter = jni_helper.callObjectMethod(j_context, jmid_create); - jni_log_fmt("[Envoy]", "j_filter: %p", j_filter.get()); Envoy::JNI::GlobalRefUniquePtr retained_filter = jni_helper.newGlobalRef(j_filter.get()); return retained_filter.release(); @@ -913,10 +875,7 @@ Java_io_envoyproxy_envoymobile_engine_JniLibrary_registerKeyValueStore(JNIEnv* e // TODO(goaway): The java context here leaks, but it's tied to the life of the engine. // This will need to be updated for https://github.com/envoyproxy/envoy-mobile/issues/332 - jni_log("[Envoy]", "registerKeyValueStore"); - jni_log_fmt("[Envoy]", "j_context: %p", j_context); jobject retained_context = env->NewGlobalRef(j_context); - jni_log_fmt("[Envoy]", "retained_context: %p", retained_context); envoy_kv_store* api = static_cast(safe_malloc(sizeof(envoy_kv_store))); api->save = jvm_kv_store_save; api->read = jvm_kv_store_read; @@ -938,10 +897,7 @@ Java_io_envoyproxy_envoymobile_engine_JniLibrary_registerFilterFactory(JNIEnv* e // TODO(goaway): Everything here leaks, but it's all be tied to the life of the engine. // This will need to be updated for https://github.com/envoyproxy/envoy-mobile/issues/332 - jni_log("[Envoy]", "registerFilterFactory"); - jni_log_fmt("[Envoy]", "j_context: %p", j_context); jobject retained_context = env->NewGlobalRef(j_context); - jni_log_fmt("[Envoy]", "retained_context: %p", retained_context); envoy_http_filter* api = static_cast(safe_malloc(sizeof(envoy_http_filter))); api->init_filter = jvm_http_filter_init; api->on_request_headers = jvm_http_filter_on_request_headers; @@ -970,7 +926,6 @@ Java_io_envoyproxy_envoymobile_engine_JniLibrary_registerFilterFactory(JNIEnv* e extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_EnvoyHTTPFilterCallbacksImpl_callResumeIteration( JNIEnv* env, jclass, jlong callback_handle, jobject j_context) { - jni_log("[Envoy]", "callResumeIteration"); // Context is only passed here to ensure it's not inadvertently gc'd during execution of this // function. To be extra safe, do an explicit retain with a GlobalRef. jobject retained_context = env->NewGlobalRef(j_context); @@ -983,7 +938,6 @@ Java_io_envoyproxy_envoymobile_engine_EnvoyHTTPFilterCallbacksImpl_callResumeIte extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_EnvoyHTTPFilterCallbacksImpl_callResetIdleTimer( JNIEnv* env, jclass, jlong callback_handle, jobject j_context) { - jni_log("[Envoy]", "callResetIdleTimer"); // Context is only passed here to ensure it's not inadvertently gc'd during execution of this // function. To be extra safe, do an explicit retain with a GlobalRef. jobject retained_context = env->NewGlobalRef(j_context); @@ -996,7 +950,6 @@ Java_io_envoyproxy_envoymobile_engine_EnvoyHTTPFilterCallbacksImpl_callResetIdle extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_EnvoyHTTPFilterCallbacksImpl_callReleaseCallbacks( JNIEnv* /*env*/, jclass, jlong callback_handle) { - jni_log("[Envoy]", "callReleaseCallbacks"); envoy_http_filter_callbacks* callbacks = reinterpret_cast(callback_handle); callbacks->release_callbacks(callbacks->callback_context); @@ -1017,9 +970,6 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibra JNIEnv* env, jclass, jlong engine_handle, jlong stream_handle, jobject data, jint length, jboolean end_stream) { Envoy::JNI::JniHelper jni_helper(env); - if (end_stream) { - jni_log("[Envoy]", "jvm_send_data_end_stream"); - } return reinterpret_cast(engine_handle) ->sendData(static_cast(stream_handle), Envoy::JNI::javaByteBufferToEnvoyData(jni_helper, data, length), end_stream); @@ -1036,9 +986,6 @@ Java_io_envoyproxy_envoymobile_engine_JniLibrary_sendDataByteArray(JNIEnv* env, jbyteArray data, jint length, jboolean end_stream) { Envoy::JNI::JniHelper jni_helper(env); - if (end_stream) { - jni_log("[Envoy]", "jvm_send_data_end_stream"); - } return reinterpret_cast(engine_handle) ->sendData(static_cast(stream_handle), Envoy::JNI::javaByteArrayToEnvoyData(jni_helper, data, length), end_stream); @@ -1057,7 +1004,6 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibra extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_sendTrailers( JNIEnv* env, jclass, jlong engine_handle, jlong stream_handle, jobjectArray trailers) { Envoy::JNI::JniHelper jni_helper(env); - jni_log("[Envoy]", "jvm_send_trailers"); return reinterpret_cast(engine_handle) ->sendTrailers(static_cast(stream_handle), Envoy::JNI::javaArrayOfObjectArrayToEnvoyHeaders(jni_helper, trailers)); @@ -1173,7 +1119,8 @@ void configureBuilder(Envoy::JNI::JniHelper& jni_helper, jlong connect_timeout_s jstring http3_connection_options, jstring http3_client_connection_options, jobjectArray quic_hints, jobjectArray quic_canonical_suffixes, jboolean enable_gzip_decompression, jboolean enable_brotli_decompression, - jboolean enable_socket_tagging, jboolean enable_interface_binding, + jboolean enable_port_migration, jboolean enable_socket_tagging, + jboolean enable_interface_binding, jlong h2_connection_keepalive_idle_interval_milliseconds, jlong h2_connection_keepalive_timeout_seconds, jlong max_connections_per_host, jlong stream_idle_timeout_seconds, jlong per_try_idle_timeout_seconds, @@ -1218,6 +1165,7 @@ void configureBuilder(Envoy::JNI::JniHelper& jni_helper, jlong connect_timeout_s for (const std::string& suffix : suffixes) { builder.addQuicCanonicalSuffix(suffix); } + builder.enablePortMigration(enable_port_migration); #endif builder.enableInterfaceBinding(enable_interface_binding == JNI_TRUE); @@ -1268,8 +1216,9 @@ extern "C" JNIEXPORT jlong JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibr jboolean enable_http3, jstring http3_connection_options, jstring http3_client_connection_options, jobjectArray quic_hints, jobjectArray quic_canonical_suffixes, jboolean enable_gzip_decompression, - jboolean enable_brotli_decompression, jboolean enable_socket_tagging, - jboolean enable_interface_binding, jlong h2_connection_keepalive_idle_interval_milliseconds, + jboolean enable_brotli_decompression, jboolean enable_port_migration, + jboolean enable_socket_tagging, jboolean enable_interface_binding, + jlong h2_connection_keepalive_idle_interval_milliseconds, jlong h2_connection_keepalive_timeout_seconds, jlong max_connections_per_host, jlong stream_idle_timeout_seconds, jlong per_try_idle_timeout_seconds, jstring app_version, jstring app_id, jboolean trust_chain_verification, jobjectArray filter_chain, @@ -1288,8 +1237,8 @@ extern "C" JNIEXPORT jlong JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibr enable_dns_cache, dns_cache_save_interval_seconds, enable_drain_post_dns_refresh, enable_http3, http3_connection_options, http3_client_connection_options, quic_hints, quic_canonical_suffixes, enable_gzip_decompression, - enable_brotli_decompression, enable_socket_tagging, enable_interface_binding, - h2_connection_keepalive_idle_interval_milliseconds, + enable_brotli_decompression, enable_port_migration, enable_socket_tagging, + enable_interface_binding, h2_connection_keepalive_idle_interval_milliseconds, h2_connection_keepalive_timeout_seconds, max_connections_per_host, stream_idle_timeout_seconds, per_try_idle_timeout_seconds, app_version, app_id, trust_chain_verification, filter_chain, enable_platform_certificates_validation, @@ -1338,7 +1287,6 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_resetConnectivityState(JNIEnv* /*env*/, jclass, // class jlong engine) { - jni_log("[Envoy]", "resetConnectivityState"); return reinterpret_cast(engine)->resetConnectivityState(); } @@ -1346,7 +1294,6 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_setPreferredNetwork(JNIEnv* /*env*/, jclass, // class jlong engine, jint network) { - jni_log("[Envoy]", "setting preferred network"); return reinterpret_cast(engine)->setPreferredNetwork( static_cast(network)); } @@ -1355,8 +1302,6 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibra JNIEnv* env, jclass, // class jlong engine, jstring host, jint port) { - jni_log("[Envoy]", "setProxySettings"); - Envoy::JNI::JniHelper jni_helper(env); Envoy::JNI::StringUtfUniquePtr java_host = jni_helper.getStringUtfChars(host, nullptr); const uint16_t native_port = static_cast(port); @@ -1368,7 +1313,6 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibra } static void jvm_add_test_root_certificate(const uint8_t* cert, size_t len) { - jni_log("[Envoy]", "jvm_add_test_root_certificate"); Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); Envoy::JNI::LocalRefUniquePtr jcls_AndroidNetworkLibrary = Envoy::JNI::findClass("io.envoyproxy.envoymobile.utilities.AndroidNetworkLibrary"); @@ -1382,7 +1326,6 @@ static void jvm_add_test_root_certificate(const uint8_t* cert, size_t len) { } static void jvm_clear_test_root_certificate() { - jni_log("[Envoy]", "jvm_clear_test_root_certificate"); Envoy::JNI::JniHelper jni_helper(Envoy::JNI::getEnv()); Envoy::JNI::LocalRefUniquePtr jcls_AndroidNetworkLibrary = Envoy::JNI::findClass("io.envoyproxy.envoymobile.utilities.AndroidNetworkLibrary"); diff --git a/mobile/library/jni/jni_support.h b/mobile/library/jni/jni_support.h index 45fef67f3fa3..a66c87648710 100644 --- a/mobile/library/jni/jni_support.h +++ b/mobile/library/jni/jni_support.h @@ -4,8 +4,4 @@ // NOLINT(namespace-envoy) -extern "C" int jni_log_fmt(const char* tag, const char* fmt, void* value); - -extern "C" int jni_log(const char* tag, const char* str); - extern "C" jint attach_jvm(JavaVM* vm, JNIEnv** p_env, void* thr_args); diff --git a/mobile/library/jni/ndk_jni_support.cc b/mobile/library/jni/ndk_jni_support.cc index ecd4f2c98a82..98403edbbfef 100644 --- a/mobile/library/jni/ndk_jni_support.cc +++ b/mobile/library/jni/ndk_jni_support.cc @@ -1,19 +1,7 @@ -#include - #include "library/jni/jni_support.h" // NOLINT(namespace-envoy) -int jni_log_fmt(const char* /*tag*/, const char* /*fmt*/, void* /*value*/) { - // For debug logging, use __android_log_print(ANDROID_LOG_VERBOSE, tag, fmt, value); - return 0; -} - -int jni_log(const char* /*tag*/, const char* /*str*/) { - // For debug logging, use __android_log_write(ANDROID_LOG_VERBOSE, tag, str); - return 0; -} - jint attach_jvm(JavaVM* vm, JNIEnv** p_env, void* thr_args) { return vm->AttachCurrentThread(p_env, thr_args); } diff --git a/mobile/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt b/mobile/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt index 06324b588a47..2176ac3b79ca 100644 --- a/mobile/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt +++ b/mobile/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt @@ -159,6 +159,7 @@ open class EngineBuilder(private val configuration: BaseConfiguration = Standard private var quicCanonicalSuffixes = mutableListOf() private var enableGzipDecompression = true private var enableBrotliDecompression = false + private var enablePortMigration = false private var enableSocketTagging = false private var enableInterfaceBinding = false private var h2ConnectionKeepaliveIdleIntervalMilliseconds = 1 @@ -323,6 +324,17 @@ open class EngineBuilder(private val configuration: BaseConfiguration = Standard return this } + /** + * Specify whether to do quic port migration or not. Defaults to false. + * + * @param enablePortMigration whether or not to allow quic port migration. + * @return This builder. + */ + fun enablePortMigration(enablePortMigration: Boolean): EngineBuilder { + this.enablePortMigration = enablePortMigration + return this + } + /** * Specify whether to support socket tagging or not. Defaults to false. * @@ -655,6 +667,7 @@ open class EngineBuilder(private val configuration: BaseConfiguration = Standard quicCanonicalSuffixes, enableGzipDecompression, enableBrotliDecompression, + enablePortMigration, enableSocketTagging, enableInterfaceBinding, h2ConnectionKeepaliveIdleIntervalMilliseconds, diff --git a/mobile/library/objective-c/EnvoyEngineImpl.mm b/mobile/library/objective-c/EnvoyEngineImpl.mm index 57f9f3fb2b9f..4680e71d1884 100644 --- a/mobile/library/objective-c/EnvoyEngineImpl.mm +++ b/mobile/library/objective-c/EnvoyEngineImpl.mm @@ -20,25 +20,6 @@ @interface EnvoyConfiguration (CXX) - (std::unique_ptr)generateBootstrap; @end -static void ios_on_engine_running(void *context) { - // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block - // is necessary to act as a breaker for any Objective-C allocation that happens. - @autoreleasepool { - EnvoyEngineImpl *engineImpl = (__bridge EnvoyEngineImpl *)context; - if (engineImpl.onEngineRunning) { - engineImpl.onEngineRunning(); - } - } -} - -static void ios_on_exit(void *context) { - // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block - // is necessary to act as a breaker for any Objective-C allocation that happens. - @autoreleasepool { - NSLog(@"[Envoy] library is exiting"); - } -} - static void ios_on_log(envoy_log_level log_level, envoy_data data, const void *context) { // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool block // is necessary to act as a breaker for any Objective-C allocation that happens. @@ -414,8 +395,24 @@ - (instancetype)initWithRunningCallback:(nullable void (^)())onEngineRunning } self.onEngineRunning = onEngineRunning; - envoy_engine_callbacks native_callbacks = {ios_on_engine_running, ios_on_exit, - (__bridge void *)(self)}; + std::unique_ptr native_callbacks = + std::make_unique(); + native_callbacks->on_engine_running = [self] { + // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool + // block is necessary to act as a breaker for any Objective-C allocation that happens. + @autoreleasepool { + if (self.onEngineRunning) { + self.onEngineRunning(); + } + } + }; + native_callbacks->on_exit = [] { + // This code block runs inside the Envoy event loop. Therefore, an explicit autoreleasepool + // block is necessary to act as a breaker for any Objective-C allocation that happens. + @autoreleasepool { + NSLog(@"[Envoy] library is exiting"); + } + }; envoy_logger native_logger = {NULL, NULL, NULL}; if (logger) { @@ -435,7 +432,8 @@ - (instancetype)initWithRunningCallback:(nullable void (^)())onEngineRunning native_event_tracker.context = CFBridgingRetain(objcEventTracker); } - _engine = new Envoy::InternalEngine(native_callbacks, native_logger, native_event_tracker); + _engine = + new Envoy::InternalEngine(std::move(native_callbacks), native_logger, native_event_tracker); _engineHandle = reinterpret_cast(_engine); if (networkMonitoringMode == 1) { diff --git a/mobile/library/swift/EnvoyCxxSwiftInterop/cxx_swift_interop.h b/mobile/library/swift/EnvoyCxxSwiftInterop/cxx_swift_interop.h index 04a63aaa4655..d4ff534773f9 100644 --- a/mobile/library/swift/EnvoyCxxSwiftInterop/cxx_swift_interop.h +++ b/mobile/library/swift/EnvoyCxxSwiftInterop/cxx_swift_interop.h @@ -2,10 +2,12 @@ #include "library/cc/bridge_utility.h" #include "library/cc/direct_response_testing.h" +#include "library/cc/engine.h" #include "library/cc/engine_builder.h" #include "library/common/data/utility.h" #include "library/common/extensions/filters/http/platform_bridge/c_types.h" #include "library/common/stats/utility.h" +#include "library/common/types/c_types.h" // This file exists in order to expose headers for Envoy's C++ libraries // to Envoy Mobile's Swift implementation. diff --git a/mobile/test/cc/unit/envoy_config_test.cc b/mobile/test/cc/unit/envoy_config_test.cc index 38eaffc6e108..989bd9643322 100644 --- a/mobile/test/cc/unit/envoy_config_test.cc +++ b/mobile/test/cc/unit/envoy_config_test.cc @@ -379,7 +379,7 @@ TEST(TestConfig, XdsConfig) { "com.google.envoymobile.io.myapp"); } -TEST(TestConfig, CopyConstructor) { +TEST(TestConfig, MoveConstructor) { EngineBuilder engine_builder; engine_builder.setRuntimeGuard("test_feature_false", true).enableGzipDecompression(false); @@ -388,18 +388,18 @@ TEST(TestConfig, CopyConstructor) { EXPECT_THAT(bootstrap_str, HasSubstr("\"test_feature_false\" value { bool_value: true }")); EXPECT_THAT(bootstrap_str, Not(HasSubstr("envoy.filters.http.decompressor"))); - EngineBuilder engine_builder_copy(engine_builder); - engine_builder_copy.enableGzipDecompression(true); + EngineBuilder engine_builder_move1(std::move(engine_builder)); + engine_builder_move1.enableGzipDecompression(true); XdsBuilder xdsBuilder("FAKE_XDS_SERVER", 0); xdsBuilder.addClusterDiscoveryService(); - engine_builder_copy.setXds(xdsBuilder); - bootstrap_str = engine_builder_copy.generateBootstrap()->ShortDebugString(); + engine_builder_move1.setXds(xdsBuilder); + bootstrap_str = engine_builder_move1.generateBootstrap()->ShortDebugString(); EXPECT_THAT(bootstrap_str, HasSubstr("\"test_feature_false\" value { bool_value: true }")); EXPECT_THAT(bootstrap_str, HasSubstr("envoy.filters.http.decompressor")); EXPECT_THAT(bootstrap_str, HasSubstr("FAKE_XDS_SERVER")); - EngineBuilder engine_builder_copy2(engine_builder_copy); - bootstrap_str = engine_builder_copy2.generateBootstrap()->ShortDebugString(); + EngineBuilder engine_builder_move2(std::move(engine_builder_move1)); + bootstrap_str = engine_builder_move2.generateBootstrap()->ShortDebugString(); EXPECT_THAT(bootstrap_str, HasSubstr("\"test_feature_false\" value { bool_value: true }")); EXPECT_THAT(bootstrap_str, HasSubstr("envoy.filters.http.decompressor")); EXPECT_THAT(bootstrap_str, HasSubstr("FAKE_XDS_SERVER")); diff --git a/mobile/test/common/integration/BUILD b/mobile/test/common/integration/BUILD index a60cc0281793..e2fd61c2abda 100644 --- a/mobile/test/common/integration/BUILD +++ b/mobile/test/common/integration/BUILD @@ -184,6 +184,7 @@ envoy_cc_test_library( "@envoy//source/exe:process_wide_lib", "@envoy//test/integration:autonomous_upstream_lib", "@envoy//test/integration:utility_lib", + "@envoy//test/mocks/server:server_factory_context_mocks", "@envoy//test/mocks/server:transport_socket_factory_context_mocks", "@envoy//test/test_common:environment_lib", "@envoy_build_config//:extension_registry", @@ -213,6 +214,7 @@ envoy_cc_test_library( "@envoy//source/exe:process_wide_lib", "@envoy//test/integration:autonomous_upstream_lib", "@envoy//test/integration:utility_lib", + "@envoy//test/mocks/server:server_factory_context_mocks", "@envoy//test/mocks/server:transport_socket_factory_context_mocks", "@envoy//test/test_common:environment_lib", "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", diff --git a/mobile/test/common/integration/test_server.cc b/mobile/test/common/integration/test_server.cc index 5c478560fa55..6e840f9c8545 100644 --- a/mobile/test/common/integration/test_server.cc +++ b/mobile/test/common/integration/test_server.cc @@ -25,7 +25,7 @@ namespace Envoy { Network::DownstreamTransportSocketFactoryPtr TestServer::createQuicUpstreamTlsContext( testing::NiceMock& factory_context) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager{time_system_}; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager{server_factory_context_}; tls_context.mutable_common_tls_context()->add_alpn_protocols("h3"); envoy::extensions::transport_sockets::tls::v3::TlsCertificate* certs = tls_context.mutable_common_tls_context()->add_tls_certificates(); diff --git a/mobile/test/common/integration/test_server.h b/mobile/test/common/integration/test_server.h index dc3e7786e87e..6c7c0506b6f0 100644 --- a/mobile/test/common/integration/test_server.h +++ b/mobile/test/common/integration/test_server.h @@ -8,6 +8,7 @@ #include "envoy/extensions/transport_sockets/quic/v3/quic_transport.pb.h" #include "test/integration/autonomous_upstream.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/server/transport_socket_factory_context.h" #include "test/integration/server.h" @@ -26,6 +27,7 @@ enum class TestServerType { class TestServer : public ListenerHooks { private: testing::NiceMock factory_context_; + testing::NiceMock server_factory_context_; Stats::IsolatedStoreImpl stats_store_; Event::GlobalTimeSystem time_system_; Api::ApiPtr api_; @@ -35,7 +37,7 @@ class TestServer : public ListenerHooks { Thread::SkipAsserts skip_asserts_; ProcessWide process_wide; Thread::MutexBasicLockable lock; - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{time_system_}; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{server_factory_context_}; std::unique_ptr runfiles_; // Either test_server_ will be set for test_server_type is a proxy, otherwise upstream_ will be diff --git a/mobile/test/common/integration/xds_test_server.h b/mobile/test/common/integration/xds_test_server.h index de2fa1252721..f00bc8cb4030 100644 --- a/mobile/test/common/integration/xds_test_server.h +++ b/mobile/test/common/integration/xds_test_server.h @@ -7,6 +7,7 @@ #include "test/integration/fake_upstream.h" #include "test/integration/server.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/server/transport_socket_factory_context.h" #include "test/test_common/test_time.h" @@ -36,6 +37,7 @@ class XdsTestServer { private: testing::NiceMock factory_context_; + testing::NiceMock server_factory_context_; Stats::IsolatedStoreImpl stats_store_; Event::GlobalTimeSystem time_system_; Api::ApiPtr api_; @@ -44,7 +46,7 @@ class XdsTestServer { Event::DispatcherPtr dispatcher_; FakeUpstreamConfig upstream_config_; Thread::MutexBasicLockable lock_; - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{time_system_}; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{server_factory_context_}; std::unique_ptr runfiles_; std::unique_ptr xds_upstream_; FakeHttpConnectionPtr xds_connection_; diff --git a/mobile/test/common/integration/xds_test_server_interface.cc b/mobile/test/common/integration/xds_test_server_interface.cc index 12152d5d9071..d4476e768327 100644 --- a/mobile/test/common/integration/xds_test_server_interface.cc +++ b/mobile/test/common/integration/xds_test_server_interface.cc @@ -12,6 +12,10 @@ static std::weak_ptr weak_test_server_; static std::shared_ptr testServer() { return weak_test_server_.lock(); } void initXdsServer() { + // This is called via JNI from kotlin tests, and Envoy doesn't consider it a test thread + // which triggers some failures of `ASSERT_IS_MAIN_OR_TEST_THREAD()`. + Envoy::Thread::SkipAsserts skip; + Envoy::ExtensionRegistry::registerFactories(); strong_test_server_ = std::make_shared(); weak_test_server_ = strong_test_server_; diff --git a/mobile/test/common/internal_engine_test.cc b/mobile/test/common/internal_engine_test.cc index 93fc7b440a98..80c62b55df7d 100644 --- a/mobile/test/common/internal_engine_test.cc +++ b/mobile/test/common/internal_engine_test.cc @@ -116,8 +116,8 @@ struct EngineTestContext { // between the main thread and the engine thread both writing to the // Envoy::Logger::current_log_context global. struct TestEngine { - TestEngine(envoy_engine_callbacks callbacks, const std::string& level) { - engine_.reset(new Envoy::InternalEngine(callbacks, {}, {})); + TestEngine(std::unique_ptr callbacks, const std::string& level) { + engine_.reset(new Envoy::InternalEngine(std::move(callbacks), {}, {})); Platform::EngineBuilder builder; auto bootstrap = builder.generateBootstrap(); std::string yaml = Envoy::MessageUtil::getYamlStringFromMessage(*bootstrap); @@ -133,6 +133,15 @@ struct TestEngine { std::unique_ptr engine_; }; +std::unique_ptr +createDefaultEngineCallbacks(EngineTestContext& test_context) { + std::unique_ptr engine_callbacks = + std::make_unique(); + engine_callbacks->on_engine_running = [&] { test_context.on_engine_running.Notify(); }; + engine_callbacks->on_exit = [&] { test_context.on_exit.Notify(); }; + return engine_callbacks; +} + // Transform C map to C++ map. [[maybe_unused]] static inline std::map toMap(envoy_map map) { std::map new_map; @@ -175,21 +184,8 @@ class InternalEngineTest : public testing::Test { }; TEST_F(InternalEngineTest, EarlyExit) { - const std::string level = "debug"; - EngineTestContext test_context{}; - envoy_engine_callbacks callbacks{[](void* context) -> void { - auto* engine_running = - static_cast(context); - engine_running->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* exit = static_cast(context); - exit->on_exit.Notify(); - } /*on_exit*/, - &test_context /*context*/}; - - engine_ = std::make_unique(callbacks, level); + engine_ = std::make_unique(createDefaultEngineCallbacks(test_context), LEVEL_DEBUG); ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(10))); ASSERT_EQ(engine_->terminate(), ENVOY_SUCCESS); @@ -202,17 +198,8 @@ TEST_F(InternalEngineTest, EarlyExit) { } TEST_F(InternalEngineTest, AccessEngineAfterInitialization) { - const std::string level = "debug"; - EngineTestContext test_context{}; - envoy_engine_callbacks callbacks{[](void* context) -> void { - auto* engine_running = - static_cast(context); - engine_running->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void*) -> void {} /*on_exit*/, &test_context /*context*/}; - - engine_ = std::make_unique(callbacks, level); + engine_ = std::make_unique(createDefaultEngineCallbacks(test_context), LEVEL_DEBUG); engine_->handle(); ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(10))); @@ -231,17 +218,8 @@ TEST_F(InternalEngineTest, AccessEngineAfterInitialization) { TEST_F(InternalEngineTest, RecordCounter) { EngineTestContext test_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* engine_running = - static_cast(context); - engine_running->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* exit = static_cast(context); - exit->on_exit.Notify(); - } /*on_exit*/, - &test_context /*context*/}; - std::unique_ptr engine(new Envoy::InternalEngine(engine_cbs, {}, {})); + std::unique_ptr engine( + new InternalEngine(createDefaultEngineCallbacks(test_context), {}, {})); engine->run(MINIMAL_TEST_CONFIG, LEVEL_DEBUG); ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(3))); @@ -253,15 +231,6 @@ TEST_F(InternalEngineTest, RecordCounter) { TEST_F(InternalEngineTest, Logger) { EngineTestContext test_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* test_context = static_cast(context); - test_context->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* test_context = static_cast(context); - test_context->on_exit.Notify(); - } /*on_exit*/, - &test_context /*context*/}; envoy_logger logger{[](envoy_log_level, envoy_data data, const void* context) -> void { auto* test_context = @@ -277,7 +246,8 @@ TEST_F(InternalEngineTest, Logger) { test_context->on_logger_release.Notify(); } /* release */, &test_context}; - std::unique_ptr engine(new Envoy::InternalEngine(engine_cbs, logger, {})); + std::unique_ptr engine( + new InternalEngine(createDefaultEngineCallbacks(test_context), logger, {})); engine->run(MINIMAL_TEST_CONFIG, LEVEL_DEBUG); ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(3))); @@ -291,17 +261,9 @@ TEST_F(InternalEngineTest, Logger) { TEST_F(InternalEngineTest, EventTrackerRegistersDefaultAPI) { EngineTestContext test_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* test_context = static_cast(context); - test_context->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* test_context = static_cast(context); - test_context->on_exit.Notify(); - } /*on_exit*/, - &test_context /*context*/}; - std::unique_ptr engine(new Envoy::InternalEngine(engine_cbs, {}, {})); + std::unique_ptr engine( + new InternalEngine(createDefaultEngineCallbacks(test_context), {}, {})); engine->run(MINIMAL_TEST_CONFIG, LEVEL_DEBUG); // A default event tracker is registered in external API registry. @@ -324,15 +286,7 @@ TEST_F(InternalEngineTest, EventTrackerRegistersDefaultAPI) { TEST_F(InternalEngineTest, EventTrackerRegistersAPI) { EngineTestContext test_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* test_context = static_cast(context); - test_context->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* test_context = static_cast(context); - test_context->on_exit.Notify(); - } /*on_exit*/, - &test_context /*context*/}; + envoy_event_tracker event_tracker{[](envoy_map map, const void* context) -> void { const auto new_map = toMap(map); if (new_map.count("foo") && new_map.at("foo") == "bar") { @@ -343,8 +297,8 @@ TEST_F(InternalEngineTest, EventTrackerRegistersAPI) { } /*track*/, &test_context /*context*/}; - std::unique_ptr engine( - new Envoy::InternalEngine(engine_cbs, {}, event_tracker)); + std::unique_ptr engine( + new InternalEngine(createDefaultEngineCallbacks(test_context), {}, event_tracker)); engine->run(MINIMAL_TEST_CONFIG, LEVEL_DEBUG); ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(3))); @@ -364,15 +318,6 @@ TEST_F(InternalEngineTest, EventTrackerRegistersAPI) { TEST_F(InternalEngineTest, EventTrackerRegistersAssertionFailureRecordAction) { EngineTestContext test_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* test_context = static_cast(context); - test_context->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* test_context = static_cast(context); - test_context->on_exit.Notify(); - } /*on_exit*/, - &test_context /*context*/}; envoy_event_tracker event_tracker{ [](envoy_map map, const void* context) -> void { @@ -385,8 +330,8 @@ TEST_F(InternalEngineTest, EventTrackerRegistersAssertionFailureRecordAction) { } /*track*/, &test_context /*context*/}; - std::unique_ptr engine( - new Envoy::InternalEngine(engine_cbs, {}, event_tracker)); + std::unique_ptr engine( + new InternalEngine(createDefaultEngineCallbacks(test_context), {}, event_tracker)); engine->run(MINIMAL_TEST_CONFIG, LEVEL_DEBUG); ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(3))); @@ -403,15 +348,6 @@ TEST_F(InternalEngineTest, EventTrackerRegistersAssertionFailureRecordAction) { TEST_F(InternalEngineTest, EventTrackerRegistersEnvoyBugRecordAction) { EngineTestContext test_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* test_context = static_cast(context); - test_context->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* test_context = static_cast(context); - test_context->on_exit.Notify(); - } /*on_exit*/, - &test_context /*context*/}; envoy_event_tracker event_tracker{[](envoy_map map, const void* context) -> void { const auto new_map = toMap(map); @@ -424,8 +360,8 @@ TEST_F(InternalEngineTest, EventTrackerRegistersEnvoyBugRecordAction) { } /*track*/, &test_context /*context*/}; - std::unique_ptr engine( - new Envoy::InternalEngine(engine_cbs, {}, event_tracker)); + std::unique_ptr engine( + new InternalEngine(createDefaultEngineCallbacks(test_context), {}, event_tracker)); engine->run(MINIMAL_TEST_CONFIG, LEVEL_DEBUG); ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(3))); @@ -441,23 +377,12 @@ TEST_F(InternalEngineTest, EventTrackerRegistersEnvoyBugRecordAction) { } TEST_F(InternalEngineTest, BasicStream) { - const std::string level = "debug"; - EngineTestContext engine_cbs_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* engine_running = - static_cast(context); - engine_running->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* exit = static_cast(context); - exit->on_exit.Notify(); - } /*on_exit*/, - &engine_cbs_context /*context*/}; - std::unique_ptr engine(new Envoy::InternalEngine(engine_cbs, {}, {})); - engine->run(BUFFERED_TEST_CONFIG, level); - - ASSERT_TRUE( - engine_cbs_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(10))); + EngineTestContext test_context{}; + std::unique_ptr engine( + new InternalEngine(createDefaultEngineCallbacks(test_context), {}, {})); + engine->run(BUFFERED_TEST_CONFIG, LEVEL_DEBUG); + + ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(10))); absl::Notification on_complete_notification; envoy_http_callbacks stream_cbs{ @@ -499,29 +424,18 @@ TEST_F(InternalEngineTest, BasicStream) { engine->terminate(); - ASSERT_TRUE(engine_cbs_context.on_exit.WaitForNotificationWithTimeout(absl::Seconds(10))); + ASSERT_TRUE(test_context.on_exit.WaitForNotificationWithTimeout(absl::Seconds(10))); } TEST_F(InternalEngineTest, ResetStream) { - EngineTestContext engine_cbs_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* engine_running = - static_cast(context); - engine_running->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* exit = static_cast(context); - exit->on_exit.Notify(); - } /*on_exit*/, - &engine_cbs_context /*context*/}; - + EngineTestContext test_context{}; // There is nothing functional about the config used to run the engine, as the created stream is // immediately reset. - std::unique_ptr engine(new Envoy::InternalEngine(engine_cbs, {}, {})); + std::unique_ptr engine( + new InternalEngine(createDefaultEngineCallbacks(test_context), {}, {})); engine->run(MINIMAL_TEST_CONFIG, LEVEL_DEBUG); - ASSERT_TRUE( - engine_cbs_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(10))); + ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(10))); absl::Notification on_cancel_notification; envoy_http_callbacks stream_cbs{ @@ -548,50 +462,30 @@ TEST_F(InternalEngineTest, ResetStream) { engine->terminate(); - ASSERT_TRUE(engine_cbs_context.on_exit.WaitForNotificationWithTimeout(absl::Seconds(10))); + ASSERT_TRUE(test_context.on_exit.WaitForNotificationWithTimeout(absl::Seconds(10))); } TEST_F(InternalEngineTest, RegisterPlatformApi) { - EngineTestContext engine_cbs_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* engine_running = - static_cast(context); - engine_running->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* exit = static_cast(context); - exit->on_exit.Notify(); - } /*on_exit*/, - &engine_cbs_context /*context*/}; - + EngineTestContext test_context{}; // Using the minimal envoy mobile config that allows for running the engine. - std::unique_ptr engine(new Envoy::InternalEngine(engine_cbs, {}, {})); + std::unique_ptr engine( + new InternalEngine(createDefaultEngineCallbacks(test_context), {}, {})); engine->run(MINIMAL_TEST_CONFIG, LEVEL_DEBUG); - ASSERT_TRUE( - engine_cbs_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(10))); + ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(10))); uint64_t fake_api; Envoy::Api::External::registerApi("api", &fake_api); engine->terminate(); - ASSERT_TRUE(engine_cbs_context.on_exit.WaitForNotificationWithTimeout(absl::Seconds(10))); + ASSERT_TRUE(test_context.on_exit.WaitForNotificationWithTimeout(absl::Seconds(10))); } TEST_F(InternalEngineTest, ResetConnectivityState) { EngineTestContext test_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* engine_running = - static_cast(context); - engine_running->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* exit = static_cast(context); - exit->on_exit.Notify(); - } /*on_exit*/, - &test_context /*context*/}; - std::unique_ptr engine(new Envoy::InternalEngine(engine_cbs, {}, {})); + std::unique_ptr engine( + new InternalEngine(createDefaultEngineCallbacks(test_context), {}, {})); engine->run(MINIMAL_TEST_CONFIG, LEVEL_DEBUG); ASSERT_TRUE(test_context.on_engine_running.WaitForNotificationWithTimeout(absl::Seconds(3))); @@ -664,23 +558,12 @@ TEST_F(InternalEngineTest, SetLogger) { } TEST_F(InternalEngineTest, ThreadCreationFailed) { - const std::string level = "debug"; - EngineTestContext engine_cbs_context{}; - envoy_engine_callbacks engine_cbs{[](void* context) -> void { - auto* engine_running = - static_cast(context); - engine_running->on_engine_running.Notify(); - } /*on_engine_running*/, - [](void* context) -> void { - auto* exit = static_cast(context); - exit->on_exit.Notify(); - } /*on_exit*/, - &engine_cbs_context /*context*/}; + EngineTestContext test_context{}; auto thread_factory = std::make_unique(); EXPECT_CALL(*thread_factory, createThread(_, _, false)).WillOnce(Return(ByMove(nullptr))); - std::unique_ptr engine( - new Envoy::InternalEngine(engine_cbs, {}, {}, std::move(thread_factory))); - envoy_status_t status = engine->run(BUFFERED_TEST_CONFIG, level); + std::unique_ptr engine(new InternalEngine( + createDefaultEngineCallbacks(test_context), {}, {}, std::move(thread_factory))); + envoy_status_t status = engine->run(BUFFERED_TEST_CONFIG, LEVEL_DEBUG); EXPECT_EQ(status, ENVOY_FAILURE); // Calling `terminate()` should not crash. EXPECT_EQ(engine->terminate(), ENVOY_FAILURE); diff --git a/mobile/test/java/integration/AndroidEngineSocketTagTest.java b/mobile/test/java/integration/AndroidEngineSocketTagTest.java index e5f7a2f8d3c9..0bbd3014166f 100644 --- a/mobile/test/java/integration/AndroidEngineSocketTagTest.java +++ b/mobile/test/java/integration/AndroidEngineSocketTagTest.java @@ -1,4 +1,4 @@ -package test.kotlin.integration; +package test.java.integration; import static com.google.common.truth.Truth.assertThat; @@ -6,34 +6,15 @@ import androidx.test.core.app.ApplicationProvider; import io.envoyproxy.envoymobile.AndroidEngineBuilder; import io.envoyproxy.envoymobile.Engine; -import io.envoyproxy.envoymobile.EnvoyError; -import io.envoyproxy.envoymobile.FinalStreamIntel; import io.envoyproxy.envoymobile.LogLevel; -import io.envoyproxy.envoymobile.RequestHeaders; -import io.envoyproxy.envoymobile.RequestHeadersBuilder; import io.envoyproxy.envoymobile.RequestMethod; -import io.envoyproxy.envoymobile.ResponseHeaders; -import io.envoyproxy.envoymobile.ResponseTrailers; import io.envoyproxy.envoymobile.Stream; -import io.envoyproxy.envoymobile.StreamIntel; import io.envoyproxy.envoymobile.engine.AndroidJniLibrary; import io.envoyproxy.envoymobile.engine.testing.RequestScenario; import io.envoyproxy.envoymobile.engine.testing.Response; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.ByteBuffer; -import java.util.AbstractMap.SimpleImmutableEntry; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; + import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import okhttp3.mockwebserver.Dispatcher; import okhttp3.mockwebserver.MockResponse; diff --git a/mobile/test/java/integration/AndroidEnvoyEngineStartUpTest.java b/mobile/test/java/integration/AndroidEnvoyEngineStartUpTest.java index fbfad680af6b..f90ea60e1d3b 100644 --- a/mobile/test/java/integration/AndroidEnvoyEngineStartUpTest.java +++ b/mobile/test/java/integration/AndroidEnvoyEngineStartUpTest.java @@ -1,4 +1,4 @@ -package test.kotlin.integration; +package test.java.integration; import android.content.Context; import androidx.test.core.app.ApplicationProvider; diff --git a/mobile/test/java/integration/AndroidEnvoyExplicitFlowTest.java b/mobile/test/java/integration/AndroidEnvoyExplicitFlowTest.java index fa0e0e8a29bb..ede4a7c2ccbe 100644 --- a/mobile/test/java/integration/AndroidEnvoyExplicitFlowTest.java +++ b/mobile/test/java/integration/AndroidEnvoyExplicitFlowTest.java @@ -1,4 +1,4 @@ -package test.kotlin.integration; +package test.java.integration; import static com.google.common.truth.Truth.assertThat; @@ -6,34 +6,22 @@ import androidx.test.core.app.ApplicationProvider; import io.envoyproxy.envoymobile.AndroidEngineBuilder; import io.envoyproxy.envoymobile.Engine; -import io.envoyproxy.envoymobile.EnvoyError; -import io.envoyproxy.envoymobile.FinalStreamIntel; import io.envoyproxy.envoymobile.LogLevel; -import io.envoyproxy.envoymobile.RequestHeaders; -import io.envoyproxy.envoymobile.RequestHeadersBuilder; import io.envoyproxy.envoymobile.RequestMethod; -import io.envoyproxy.envoymobile.ResponseHeaders; -import io.envoyproxy.envoymobile.ResponseTrailers; import io.envoyproxy.envoymobile.Stream; -import io.envoyproxy.envoymobile.StreamIntel; import io.envoyproxy.envoymobile.engine.AndroidJniLibrary; import io.envoyproxy.envoymobile.engine.testing.RequestScenario; import io.envoyproxy.envoymobile.engine.testing.Response; -import java.net.MalformedURLException; -import java.net.URL; + import java.nio.ByteBuffer; -import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import okhttp3.mockwebserver.Dispatcher; import okhttp3.mockwebserver.MockResponse; diff --git a/mobile/test/java/integration/AndroidEnvoyFlowTest.java b/mobile/test/java/integration/AndroidEnvoyFlowTest.java index fbdbcd446cbc..0451ba5d5dc8 100644 --- a/mobile/test/java/integration/AndroidEnvoyFlowTest.java +++ b/mobile/test/java/integration/AndroidEnvoyFlowTest.java @@ -1,4 +1,4 @@ -package test.kotlin.integration; +package test.java.integration; import static com.google.common.truth.Truth.assertThat; @@ -6,37 +6,22 @@ import androidx.test.core.app.ApplicationProvider; import io.envoyproxy.envoymobile.AndroidEngineBuilder; import io.envoyproxy.envoymobile.Engine; -import io.envoyproxy.envoymobile.EnvoyError; -import io.envoyproxy.envoymobile.RequestHeaders; -import io.envoyproxy.envoymobile.RequestHeadersBuilder; import io.envoyproxy.envoymobile.RequestMethod; -import io.envoyproxy.envoymobile.ResponseHeaders; -import io.envoyproxy.envoymobile.ResponseTrailers; import io.envoyproxy.envoymobile.Stream; import io.envoyproxy.envoymobile.engine.AndroidJniLibrary; import io.envoyproxy.envoymobile.engine.testing.RequestScenario; import io.envoyproxy.envoymobile.engine.testing.Response; import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; import java.nio.ByteBuffer; -import java.util.AbstractMap.SimpleImmutableEntry; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import okhttp3.mockwebserver.Dispatcher; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; import org.junit.After; -import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; diff --git a/mobile/test/java/io/envoyproxy/envoymobile/engine/EnvoyConfigurationTest.kt b/mobile/test/java/io/envoyproxy/envoymobile/engine/EnvoyConfigurationTest.kt index f2f2a6c1813f..fe9af5a86d3b 100644 --- a/mobile/test/java/io/envoyproxy/envoymobile/engine/EnvoyConfigurationTest.kt +++ b/mobile/test/java/io/envoyproxy/envoymobile/engine/EnvoyConfigurationTest.kt @@ -83,6 +83,7 @@ class EnvoyConfigurationTest { quicCanonicalSuffixes: MutableList = mutableListOf(".opq.com", ".xyz.com"), enableGzipDecompression: Boolean = true, enableBrotliDecompression: Boolean = false, + enablePortMigration: Boolean = true, enableSocketTagging: Boolean = false, enableInterfaceBinding: Boolean = false, h2ConnectionKeepaliveIdleIntervalMilliseconds: Int = 222, @@ -131,6 +132,7 @@ class EnvoyConfigurationTest { quicCanonicalSuffixes, enableGzipDecompression, enableBrotliDecompression, + enablePortMigration, enableSocketTagging, enableInterfaceBinding, h2ConnectionKeepaliveIdleIntervalMilliseconds, @@ -205,6 +207,7 @@ class EnvoyConfigurationTest { assertThat(resolvedTemplate).contains(".xyz.com") assertThat(resolvedTemplate).contains("connection_options: 5RTO") assertThat(resolvedTemplate).contains("client_connection_options: MPQC") + assertThat(resolvedTemplate).contains("num_timeouts_to_trigger_port_migration: 4") // Per Host Limits assertThat(resolvedTemplate).contains("max_connections: 543") diff --git a/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/QuicTestServerTest.java b/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/QuicTestServerTest.java index 43c2e025e4c9..5c1054697574 100644 --- a/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/QuicTestServerTest.java +++ b/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/QuicTestServerTest.java @@ -7,28 +7,16 @@ import io.envoyproxy.envoymobile.AndroidEngineBuilder; import io.envoyproxy.envoymobile.Custom; import io.envoyproxy.envoymobile.Engine; -import io.envoyproxy.envoymobile.EnvoyError; import io.envoyproxy.envoymobile.LogLevel; -import io.envoyproxy.envoymobile.RequestHeaders; -import io.envoyproxy.envoymobile.RequestHeadersBuilder; import io.envoyproxy.envoymobile.RequestMethod; -import io.envoyproxy.envoymobile.ResponseHeaders; -import io.envoyproxy.envoymobile.ResponseTrailers; import io.envoyproxy.envoymobile.Stream; import io.envoyproxy.envoymobile.engine.AndroidJniLibrary; import io.envoyproxy.envoymobile.engine.JniLibrary; import io.envoyproxy.envoymobile.engine.testing.RequestScenario; import io.envoyproxy.envoymobile.engine.testing.Response; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; + import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.junit.After; import org.junit.Before; diff --git a/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/RequestScenario.java b/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/RequestScenario.java index 9f7d6b2758e1..e8922c7099af 100644 --- a/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/RequestScenario.java +++ b/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/RequestScenario.java @@ -1,35 +1,18 @@ package io.envoyproxy.envoymobile.engine.testing; -import io.envoyproxy.envoymobile.AndroidEngineBuilder; -import io.envoyproxy.envoymobile.Engine; -import io.envoyproxy.envoymobile.EnvoyError; -import io.envoyproxy.envoymobile.FinalStreamIntel; -import io.envoyproxy.envoymobile.LogLevel; import io.envoyproxy.envoymobile.RequestHeaders; import io.envoyproxy.envoymobile.RequestHeadersBuilder; import io.envoyproxy.envoymobile.RequestMethod; -import io.envoyproxy.envoymobile.ResponseHeaders; -import io.envoyproxy.envoymobile.ResponseTrailers; -import io.envoyproxy.envoymobile.Stream; -import io.envoyproxy.envoymobile.StreamIntel; -import io.envoyproxy.envoymobile.engine.AndroidJniLibrary; + import java.net.MalformedURLException; import java.net.URL; import java.nio.ByteBuffer; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; public final class RequestScenario { public int responseBufferSize = 1000; diff --git a/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/Response.java b/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/Response.java index d60bb5778940..eb53b93e2187 100644 --- a/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/Response.java +++ b/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/Response.java @@ -1,16 +1,9 @@ package io.envoyproxy.envoymobile.engine.testing; -import io.envoyproxy.envoymobile.AndroidEngineBuilder; -import io.envoyproxy.envoymobile.Engine; import io.envoyproxy.envoymobile.EnvoyError; import io.envoyproxy.envoymobile.FinalStreamIntel; -import io.envoyproxy.envoymobile.LogLevel; -import io.envoyproxy.envoymobile.RequestHeaders; -import io.envoyproxy.envoymobile.RequestHeadersBuilder; -import io.envoyproxy.envoymobile.RequestMethod; import io.envoyproxy.envoymobile.ResponseHeaders; import io.envoyproxy.envoymobile.ResponseTrailers; -import io.envoyproxy.envoymobile.Stream; import io.envoyproxy.envoymobile.StreamIntel; import java.nio.ByteBuffer; import java.util.ArrayList; diff --git a/mobile/test/java/org/chromium/net/CronetUrlRequestTest.java b/mobile/test/java/org/chromium/net/CronetUrlRequestTest.java index a15bbc8f8d2d..1c984c747df1 100644 --- a/mobile/test/java/org/chromium/net/CronetUrlRequestTest.java +++ b/mobile/test/java/org/chromium/net/CronetUrlRequestTest.java @@ -10,7 +10,6 @@ import static org.junit.Assert.fail; import static org.robolectric.Shadows.shadowOf; -import android.content.Context; import android.content.Intent; import android.Manifest; import android.net.ConnectivityManager; @@ -22,7 +21,6 @@ import androidx.test.filters.SmallTest; import androidx.test.rule.GrantPermissionRule; import java.io.IOException; -import java.net.ConnectException; import java.nio.ByteBuffer; import java.util.AbstractMap; import java.util.ArrayList; diff --git a/mobile/test/java/org/chromium/net/DiskStorageTest.java b/mobile/test/java/org/chromium/net/DiskStorageTest.java index 72b925afb603..297839316d03 100644 --- a/mobile/test/java/org/chromium/net/DiskStorageTest.java +++ b/mobile/test/java/org/chromium/net/DiskStorageTest.java @@ -14,7 +14,7 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; -import java.util.Arrays; + import org.chromium.net.testing.CronetTestRule; import org.chromium.net.testing.Feature; import org.chromium.net.testing.FileUtils; diff --git a/mobile/test/java/org/chromium/net/impl/CronvoyEngineTest.java b/mobile/test/java/org/chromium/net/impl/CronvoyEngineTest.java index 862b73e281b1..b9df8f5eb9f7 100644 --- a/mobile/test/java/org/chromium/net/impl/CronvoyEngineTest.java +++ b/mobile/test/java/org/chromium/net/impl/CronvoyEngineTest.java @@ -8,7 +8,6 @@ import static org.junit.Assert.assertThrows; -import android.content.Context; import androidx.test.core.app.ApplicationProvider; import io.envoyproxy.envoymobile.RequestMethod; import io.envoyproxy.envoymobile.engine.AndroidJniLibrary; diff --git a/mobile/test/java/org/chromium/net/testing/CronetTestRule.java b/mobile/test/java/org/chromium/net/testing/CronetTestRule.java index 1dfeb1921303..692d9c628db7 100644 --- a/mobile/test/java/org/chromium/net/testing/CronetTestRule.java +++ b/mobile/test/java/org/chromium/net/testing/CronetTestRule.java @@ -9,7 +9,7 @@ import android.os.StrictMode; import android.util.Log; import androidx.test.platform.app.InstrumentationRegistry; -import io.envoyproxy.envoymobile.LogLevel; + import io.envoyproxy.envoymobile.engine.AndroidJniLibrary; import java.io.File; import java.lang.annotation.Annotation; @@ -24,9 +24,7 @@ import org.chromium.net.CronetEngine; import org.chromium.net.ExperimentalCronetEngine; import org.chromium.net.UrlResponseInfo; -import org.chromium.net.impl.CronvoyEngineBuilderImpl; import org.chromium.net.impl.NativeCronvoyProvider; -import org.chromium.net.impl.CronvoyUserAgent; import org.junit.Assert; import org.junit.rules.TestRule; import org.junit.runner.Description; diff --git a/mobile/test/java/org/chromium/net/testing/CronetTestRuleTest.java b/mobile/test/java/org/chromium/net/testing/CronetTestRuleTest.java index 8519dd113b48..895c581316f9 100644 --- a/mobile/test/java/org/chromium/net/testing/CronetTestRuleTest.java +++ b/mobile/test/java/org/chromium/net/testing/CronetTestRuleTest.java @@ -2,7 +2,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import androidx.test.ext.junit.runners.AndroidJUnit4; diff --git a/mobile/test/java/org/chromium/net/testing/MockUrlRequestJobFactory.java b/mobile/test/java/org/chromium/net/testing/MockUrlRequestJobFactory.java index a65cf41cb984..4c358bbf5e0c 100644 --- a/mobile/test/java/org/chromium/net/testing/MockUrlRequestJobFactory.java +++ b/mobile/test/java/org/chromium/net/testing/MockUrlRequestJobFactory.java @@ -1,7 +1,5 @@ package org.chromium.net.testing; -import static junit.framework.Assert.assertTrue; - import org.chromium.net.CronetEngine; import org.chromium.net.ExperimentalCronetEngine; diff --git a/mobile/test/jni/test_jni_impl.cc b/mobile/test/jni/test_jni_impl.cc index 455665ac3b45..5e7477d47554 100644 --- a/mobile/test/jni/test_jni_impl.cc +++ b/mobile/test/jni/test_jni_impl.cc @@ -13,70 +13,60 @@ extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeStartHttpProxyTestServer(JNIEnv* env, jclass clazz) { - jni_log("[QTS]", "starting server"); start_server(Envoy::TestServerType::HTTP_PROXY); } extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeStartHttpsProxyTestServer( JNIEnv* env, jclass clazz) { - jni_log("[QTS]", "starting server"); start_server(Envoy::TestServerType::HTTPS_PROXY); } extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeStartHttp3TestServer(JNIEnv* env, jclass clazz) { - jni_log("[QTS]", "starting server"); start_server(Envoy::TestServerType::HTTP3); } extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeGetServerPort(JNIEnv* env, jclass clazz) { - jni_log("[QTS]", "getting server port"); return get_server_port(); } extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeStartHttp2TestServer(JNIEnv* env, jclass clazz) { - jni_log("[QTS]", "starting server"); start_server(Envoy::TestServerType::HTTP2_WITH_TLS); } extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeShutdownTestServer(JNIEnv* env, jclass clazz) { - jni_log("[QTS]", "shutting down server"); shutdown_server(); } extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeInitXdsTestServer(JNIEnv* env, jclass clazz) { - jni_log("[XTS]", "initializing xDS server"); initXdsServer(); } extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeStartXdsTestServer(JNIEnv* env, jclass clazz) { - jni_log("[XTS]", "starting xDS server"); startXdsServer(); } extern "C" JNIEXPORT jstring JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeGetXdsTestServerHost(JNIEnv* env, jclass clazz) { - jni_log("[XTS]", "getting xDS server host"); return env->NewStringUTF(getXdsServerHost()); } extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeGetXdsTestServerPort(JNIEnv* env, jclass clazz) { - jni_log("[XTS]", "getting xDS server port"); return getXdsServerPort(); } @@ -85,7 +75,6 @@ extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeSendDiscoveryResponse(JNIEnv* env, jclass clazz, jstring yaml) { - jni_log("[XTS]", "sending DiscoveryResponse from the xDS server"); const char* yaml_chars = env->GetStringUTFChars(yaml, /* isCopy= */ nullptr); // The yaml utilities have non-relevant thread asserts. Envoy::Thread::SkipAsserts skip; @@ -99,7 +88,6 @@ Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeSendDiscoveryRespons extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_testing_TestJni_nativeShutdownXdsTestServer(JNIEnv* env, jclass clazz) { - jni_log("[XTS]", "shutting down xDS server"); shutdownXdsServer(); } diff --git a/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPRequestUsingProxyTest.kt b/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPRequestUsingProxyTest.kt index 05dcb60e665f..b771f075245e 100644 --- a/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPRequestUsingProxyTest.kt +++ b/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPRequestUsingProxyTest.kt @@ -33,7 +33,7 @@ import org.robolectric.RobolectricTestRunner // │ │ // └──────────────────┘ @RunWith(RobolectricTestRunner::class) -class PerformHTTPRequestUsingProxy { +class ProxyInfoIntentPerformHTTPRequestUsingProxyTest { init { JniLibrary.loadTestLibrary() JniLibrary.load() diff --git a/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestBadHostnameTest.kt b/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestBadHostnameTest.kt index 7182356cebe6..e0baa24faa05 100644 --- a/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestBadHostnameTest.kt +++ b/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestBadHostnameTest.kt @@ -33,7 +33,7 @@ import org.robolectric.RobolectricTestRunner // │ │ // └──────────────────┘ @RunWith(RobolectricTestRunner::class) -class PerformHTTPSRequestBadHostname { +class ProxyInfoIntentPerformHTTPSRequestBadHostnameTest { init { JniLibrary.loadTestLibrary() JniLibrary.load() diff --git a/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingAsyncProxyTest.kt b/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingAsyncProxyTest.kt index 672879b70d54..2af1a663feda 100644 --- a/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingAsyncProxyTest.kt +++ b/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingAsyncProxyTest.kt @@ -16,6 +16,7 @@ import io.envoyproxy.envoymobile.engine.testing.TestJni import java.util.concurrent.CountDownLatch import java.util.concurrent.Executors import java.util.concurrent.TimeUnit +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito @@ -33,12 +34,13 @@ import org.robolectric.RobolectricTestRunner // │ │ // └──────────────────┘ @RunWith(RobolectricTestRunner::class) -class PerformHTTPSRequestUsingAsyncProxyTest { +class ProxyInfoIntentPerformHTTPSRequestUsingAsyncProxyTest { init { JniLibrary.loadTestLibrary() JniLibrary.load() } + @Ignore("https://github.com/envoyproxy/envoy/issues/33014") @Test fun `performs an HTTPs request through a proxy using async DNS resolution`() { TestJni.startHttpsProxyTestServer() diff --git a/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingProxyTest.kt b/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingProxyTest.kt index 626a4ee49864..5521050f4037 100644 --- a/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingProxyTest.kt +++ b/mobile/test/kotlin/integration/proxying/ProxyInfoIntentPerformHTTPSRequestUsingProxyTest.kt @@ -16,6 +16,7 @@ import io.envoyproxy.envoymobile.engine.testing.TestJni import java.util.concurrent.CountDownLatch import java.util.concurrent.Executors import java.util.concurrent.TimeUnit +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito @@ -33,12 +34,13 @@ import org.robolectric.RobolectricTestRunner // │ │ // └──────────────────┘ @RunWith(RobolectricTestRunner::class) -class PerformHTTPSRequestUsingProxy { +class ProxyInfoIntentPerformHTTPSRequestUsingProxyTest { init { JniLibrary.loadTestLibrary() JniLibrary.load() } + @Ignore("https://github.com/envoyproxy/envoy/issues/33014") @Test fun `performs an HTTPs request through a proxy`() { TestJni.startHttpsProxyTestServer() diff --git a/mobile/test/kotlin/integration/proxying/ProxyPollPerformHTTPRequestUsingProxyTest.kt b/mobile/test/kotlin/integration/proxying/ProxyPollPerformHTTPRequestUsingProxyTest.kt index 08504ef1d13f..cb23afc6c3dc 100644 --- a/mobile/test/kotlin/integration/proxying/ProxyPollPerformHTTPRequestUsingProxyTest.kt +++ b/mobile/test/kotlin/integration/proxying/ProxyPollPerformHTTPRequestUsingProxyTest.kt @@ -32,7 +32,7 @@ import org.robolectric.RobolectricTestRunner // │ │ // └──────────────────┘ @RunWith(RobolectricTestRunner::class) -class PerformHTTPRequestUsingProxy { +class ProxyPollPerformHTTPRequestUsingProxyTest { init { AndroidJniLibrary.loadTestLibrary() JniLibrary.loadTestLibrary() diff --git a/mobile/test/kotlin/integration/proxying/ProxyPollPerformHTTPRequestWithoutUsingPACProxyTest.kt b/mobile/test/kotlin/integration/proxying/ProxyPollPerformHTTPRequestWithoutUsingPACProxyTest.kt index efeb9e30f11f..21a08ad18722 100644 --- a/mobile/test/kotlin/integration/proxying/ProxyPollPerformHTTPRequestWithoutUsingPACProxyTest.kt +++ b/mobile/test/kotlin/integration/proxying/ProxyPollPerformHTTPRequestWithoutUsingPACProxyTest.kt @@ -34,7 +34,7 @@ import org.robolectric.RobolectricTestRunner // │ │ // └─────────────────────────────────────┘ @RunWith(RobolectricTestRunner::class) -class PerformHTTPRequestUsingProxy { +class ProxyPollPerformHTTPRequestWithoutUsingPACProxyTest { init { JniLibrary.loadTestLibrary() JniLibrary.load() diff --git a/mobile/test/swift/integration/proxying/HTTPRequestUsingProxyTest.swift b/mobile/test/swift/integration/proxying/HTTPRequestUsingProxyTest.swift index 424159b9e6e4..50d7705d3e01 100644 --- a/mobile/test/swift/integration/proxying/HTTPRequestUsingProxyTest.swift +++ b/mobile/test/swift/integration/proxying/HTTPRequestUsingProxyTest.swift @@ -12,7 +12,8 @@ final class HTTPRequestUsingProxyTest: XCTestCase { register_test_extensions() } - func testHTTPRequestUsingProxy() throws { + // https://github.com/envoyproxy/envoy/issues/33014 + func skipped_testHTTPRequestUsingProxy() throws { EnvoyTestServer.startHttpProxyServer() let port = EnvoyTestServer.getEnvoyPort() @@ -65,7 +66,8 @@ final class HTTPRequestUsingProxyTest: XCTestCase { EnvoyTestServer.shutdownTestServer() } - func testHTTPSRequestUsingProxy() throws { + // https://github.com/envoyproxy/envoy/issues/33014 + func skipped_testHTTPSRequestUsingProxy() throws { EnvoyTestServer.startHttpsProxyServer() let port = EnvoyTestServer.getEnvoyPort() @@ -120,7 +122,8 @@ final class HTTPRequestUsingProxyTest: XCTestCase { EnvoyTestServer.shutdownTestServer() } - func testHTTPSRequestUsingPacFileUrlResolver() throws { + // https://github.com/envoyproxy/envoy/issues/33014 + func skipped_testHTTPSRequestUsingPacFileUrlResolver() throws { EnvoyTestServer.startHttpsProxyServer() let port = EnvoyTestServer.getEnvoyPort() @@ -175,7 +178,8 @@ final class HTTPRequestUsingProxyTest: XCTestCase { EnvoyTestServer.shutdownTestServer() } - func testHTTPRequestUsingProxyCancelStream() throws { + // https://github.com/envoyproxy/envoy/issues/33014 + func skipped_testHTTPRequestUsingProxyCancelStream() throws { EnvoyTestServer.startHttpProxyServer() let port = EnvoyTestServer.getEnvoyPort() diff --git a/mobile/third_party/rbe_configs/config/BUILD b/mobile/third_party/rbe_configs/config/BUILD index add523ffc4d8..84a24d8f450a 100644 --- a/mobile/third_party/rbe_configs/config/BUILD +++ b/mobile/third_party/rbe_configs/config/BUILD @@ -42,7 +42,7 @@ platform( "@bazel_tools//tools/cpp:clang", ], exec_properties = { - "container-image": "docker://envoyproxy/envoy-build-ubuntu:mobile-0ca52447572ee105a4730da5e76fe47c9c5a7c64@sha256:13674edd989ada8b94257f410e38e5e8e4c6a0a9c2fdf8bda9261047a7f843e4", + "container-image": "docker://envoyproxy/envoy-build-ubuntu:mobile-f94a38f62220a2b017878b790b6ea98a0f6c5f9c@sha256:c9b6001d0c24170ae77b0ad39e1b0fcda49580c72a8578755dc65aec9be85be3", "OSFamily": "Linux", "Pool": "linux", }, @@ -57,7 +57,7 @@ platform( "@bazel_tools//tools/cpp:clang", ], exec_properties = { - "container-image": "docker://envoyproxy/envoy-build-ubuntu:mobile-0ca52447572ee105a4730da5e76fe47c9c5a7c64@sha256:13674edd989ada8b94257f410e38e5e8e4c6a0a9c2fdf8bda9261047a7f843e4", + "container-image": "docker://envoyproxy/envoy-build-ubuntu:mobile-f94a38f62220a2b017878b790b6ea98a0f6c5f9c@sha256:c9b6001d0c24170ae77b0ad39e1b0fcda49580c72a8578755dc65aec9be85be3", "OSFamily": "Linux", "Pool": "linux", # Necessary to workaround https://github.com/google/sanitizers/issues/916, otherwise, dangling threads in the diff --git a/source/common/common/BUILD b/source/common/common/BUILD index 6d630ec2a028..67d035aed07b 100644 --- a/source/common/common/BUILD +++ b/source/common/common/BUILD @@ -52,6 +52,8 @@ envoy_cc_library( ":assert_lib", "//envoy/common:backoff_strategy_interface", "//envoy/common:random_generator_interface", + "//source/common/protobuf:utility_lib_header", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], ) diff --git a/source/common/common/backoff_strategy.h b/source/common/common/backoff_strategy.h index 9e854a32a061..59ed378d29cf 100644 --- a/source/common/common/backoff_strategy.h +++ b/source/common/common/backoff_strategy.h @@ -5,8 +5,11 @@ #include "envoy/common/backoff_strategy.h" #include "envoy/common/random_generator.h" +#include "envoy/config/core/v3/backoff.pb.h" +#include "envoy/config/core/v3/backoff.pb.validate.h" #include "source/common/common/assert.h" +#include "source/common/protobuf/utility.h" namespace Envoy { @@ -98,4 +101,22 @@ class FixedBackOffStrategy : public BackOffStrategy { uint64_t interval_ms_; }; +class BackOffStrategyUtils { +public: + static absl::Status + validateBackOffStrategyConfig(envoy::config::core::v3::BackoffStrategy backoff_strategy, + uint64_t default_base_interval_ms, uint64_t max_interval_factor) { + uint64_t base_interval_ms = + PROTOBUF_GET_MS_OR_DEFAULT(backoff_strategy, base_interval, default_base_interval_ms); + uint64_t max_interval_ms = PROTOBUF_GET_MS_OR_DEFAULT(backoff_strategy, max_interval, + base_interval_ms * max_interval_factor); + + if (max_interval_ms < base_interval_ms) { + return absl::InvalidArgumentError("max_interval must be greater or equal to base_interval"); + } + + return absl::OkStatus(); + } +}; + } // namespace Envoy diff --git a/source/common/common/logger.h b/source/common/common/logger.h index a90e43628e1e..7fb16e1ae1d8 100644 --- a/source/common/common/logger.h +++ b/source/common/common/logger.h @@ -45,6 +45,7 @@ const static bool should_log = true; FUNCTION(config) \ FUNCTION(connection) \ FUNCTION(conn_handler) \ + FUNCTION(compression) \ FUNCTION(decompression) \ FUNCTION(dns) \ FUNCTION(dubbo) \ @@ -559,9 +560,8 @@ class ExtractedMessage : public spdlog::custom_flag_formatter { #define ENVOY_TAGGED_LOG_TO_LOGGER(LOGGER, LEVEL, TAGS, FORMAT, ...) \ do { \ if (ENVOY_LOG_COMP_LEVEL(LOGGER, LEVEL)) { \ - ENVOY_LOG_TO_LOGGER(LOGGER, LEVEL, \ - fmt::runtime(::Envoy::Logger::Utility::serializeLogTags(TAGS) + FORMAT), \ - ##__VA_ARGS__); \ + ENVOY_LOG_TO_LOGGER(LOGGER, LEVEL, "{}" FORMAT, \ + ::Envoy::Logger::Utility::serializeLogTags(TAGS), ##__VA_ARGS__); \ } \ } while (0) @@ -570,10 +570,8 @@ class ExtractedMessage : public spdlog::custom_flag_formatter { if (ENVOY_LOG_COMP_LEVEL(LOGGER, LEVEL)) { \ std::map log_tags = TAGS; \ log_tags.emplace("ConnectionId", std::to_string((CONNECTION).id())); \ - ENVOY_LOG_TO_LOGGER( \ - LOGGER, LEVEL, \ - fmt::runtime(::Envoy::Logger::Utility::serializeLogTags(log_tags) + FORMAT), \ - ##__VA_ARGS__); \ + ENVOY_LOG_TO_LOGGER(LOGGER, LEVEL, "{}" FORMAT, \ + ::Envoy::Logger::Utility::serializeLogTags(log_tags), ##__VA_ARGS__); \ } \ } while (0) @@ -584,10 +582,8 @@ class ExtractedMessage : public spdlog::custom_flag_formatter { log_tags.emplace("ConnectionId", \ (STREAM).connection() ? std::to_string((STREAM).connection()->id()) : "0"); \ log_tags.emplace("StreamId", std::to_string((STREAM).streamId())); \ - ENVOY_LOG_TO_LOGGER( \ - LOGGER, LEVEL, \ - fmt::runtime(::Envoy::Logger::Utility::serializeLogTags(log_tags) + FORMAT), \ - ##__VA_ARGS__); \ + ENVOY_LOG_TO_LOGGER(LOGGER, LEVEL, "{}" FORMAT, \ + ::Envoy::Logger::Utility::serializeLogTags(log_tags), ##__VA_ARGS__); \ } \ } while (0) @@ -596,10 +592,8 @@ class ExtractedMessage : public spdlog::custom_flag_formatter { if (ENVOY_LOG_COMP_LEVEL(LOGGER, LEVEL)) { \ std::map log_tags; \ log_tags.emplace("ConnectionId", std::to_string((CONNECTION).id())); \ - ENVOY_LOG_TO_LOGGER( \ - LOGGER, LEVEL, \ - fmt::runtime(::Envoy::Logger::Utility::serializeLogTags(log_tags) + FORMAT), \ - ##__VA_ARGS__); \ + ENVOY_LOG_TO_LOGGER(LOGGER, LEVEL, "{}" FORMAT, \ + ::Envoy::Logger::Utility::serializeLogTags(log_tags), ##__VA_ARGS__); \ } \ } while (0) @@ -610,10 +604,8 @@ class ExtractedMessage : public spdlog::custom_flag_formatter { log_tags.emplace("ConnectionId", \ (STREAM).connection() ? std::to_string((STREAM).connection()->id()) : "0"); \ log_tags.emplace("StreamId", std::to_string((STREAM).streamId())); \ - ENVOY_LOG_TO_LOGGER( \ - LOGGER, LEVEL, \ - fmt::runtime(::Envoy::Logger::Utility::serializeLogTags(log_tags) + FORMAT), \ - ##__VA_ARGS__); \ + ENVOY_LOG_TO_LOGGER(LOGGER, LEVEL, "{}" FORMAT, \ + ::Envoy::Logger::Utility::serializeLogTags(log_tags), ##__VA_ARGS__); \ } \ } while (0) @@ -674,10 +666,11 @@ class ExtractedMessage : public spdlog::custom_flag_formatter { if (ENVOY_LOG_COMP_LEVEL(ENVOY_LOGGER(), LEVEL)) { \ std::map log_tags; \ log_tags.emplace("ConnectionId", std::to_string((CONNECTION).id())); \ - const auto combined_format = ::Envoy::Logger::Utility::serializeLogTags(log_tags) + FORMAT; \ - ENVOY_LOG_TO_LOGGER(ENVOY_LOGGER(), LEVEL, fmt::runtime(combined_format), ##__VA_ARGS__); \ + ENVOY_LOG_TO_LOGGER(ENVOY_LOGGER(), LEVEL, "{}" FORMAT, \ + ::Envoy::Logger::Utility::serializeLogTags(log_tags), ##__VA_ARGS__); \ ::Envoy::Logger::Registry::getSink()->logWithStableName( \ - EVENT_NAME, #LEVEL, (ENVOY_LOGGER()).name(), combined_format, ##__VA_ARGS__); \ + EVENT_NAME, #LEVEL, (ENVOY_LOGGER()).name(), "{}" FORMAT, \ + ::Envoy::Logger::Utility::serializeLogTags(log_tags), ##__VA_ARGS__); \ } \ } while (0) diff --git a/source/common/common/matchers.cc b/source/common/common/matchers.cc index d69fe3c72822..4dded56b26f0 100644 --- a/source/common/common/matchers.cc +++ b/source/common/common/matchers.cc @@ -201,9 +201,10 @@ bool PathMatcher::match(const absl::string_view path) const { return matcher_.match(Http::PathUtil::removeQueryAndFragment(path)); } -StringMatcherPtr getExtensionStringMatcher(const ::xds::core::v3::TypedExtensionConfig& config) { +StringMatcherPtr getExtensionStringMatcher(const ::xds::core::v3::TypedExtensionConfig& config, + ThreadLocal::SlotAllocator& tls, Api::Api& api) { auto factory = Config::Utility::getAndCheckFactory(config, false); - return factory->createStringMatcher(config.typed_config()); + return factory->createStringMatcher(config.typed_config(), tls, api); } } // namespace Matchers diff --git a/source/common/common/matchers.h b/source/common/common/matchers.h index ae66633b6660..1ee93a032295 100644 --- a/source/common/common/matchers.h +++ b/source/common/common/matchers.h @@ -86,29 +86,63 @@ class UniversalStringMatcher : public StringMatcher { bool match(absl::string_view) const override { return true; } }; -StringMatcherPtr getExtensionStringMatcher(const ::xds::core::v3::TypedExtensionConfig& config); +StringMatcherPtr getExtensionStringMatcher(const ::xds::core::v3::TypedExtensionConfig& config, + ThreadLocal::SlotAllocator& tls, Api::Api& api); template -class StringMatcherImpl : public ValueMatcher, public StringMatcher { +class PrivateStringMatcherImpl : public ValueMatcher, public StringMatcher { public: - explicit StringMatcherImpl(const StringMatcherType& matcher) : matcher_(matcher) { + // TODO(ggreenway): convert all but the first parameter into + // `Server::Configuration::CommonFactoryContext`. + explicit PrivateStringMatcherImpl(const StringMatcherType& matcher, Regex::Engine* regex_engine, + ThreadLocal::SlotAllocator* tls, Api::Api* api) + : matcher_(matcher) { if (matcher.match_pattern_case() == StringMatcherType::MatchPatternCase::kSafeRegex) { if (matcher.ignore_case()) { ExceptionUtil::throwEnvoyException("ignore_case has no effect for safe_regex."); } - regex_ = Regex::Utility::parseRegex(matcher_.safe_regex()); + if (regex_engine != nullptr) { + regex_ = Regex::Utility::parseRegex(matcher_.safe_regex(), *regex_engine); + } else { + // TODO(ggreenway): remove this branch when we always have an engine. This is only + // needed to make tests not complain about dereferencing a null pointer, even though + // the reference isn't actually used. + regex_ = Regex::Utility::parseRegex(matcher_.safe_regex()); + } } else if (matcher.match_pattern_case() == StringMatcherType::MatchPatternCase::kContains) { if (matcher_.ignore_case()) { // Cache the lowercase conversion of the Contains matcher for future use lowercase_contains_match_ = absl::AsciiStrToLower(matcher_.contains()); } - } else { - initialize(matcher); + } else if (matcher.has_custom()) { + custom_ = getExtensionStringMatcher(matcher.custom(), *tls, *api); } } // StringMatcher - bool match(const absl::string_view value) const override { return match(value, matcher_); } + bool match(const absl::string_view value) const override { + switch (matcher_.match_pattern_case()) { + case StringMatcherType::MatchPatternCase::kExact: + return matcher_.ignore_case() ? absl::EqualsIgnoreCase(value, matcher_.exact()) + : value == matcher_.exact(); + case StringMatcherType::MatchPatternCase::kPrefix: + return matcher_.ignore_case() ? absl::StartsWithIgnoreCase(value, matcher_.prefix()) + : absl::StartsWith(value, matcher_.prefix()); + case StringMatcherType::MatchPatternCase::kSuffix: + return matcher_.ignore_case() ? absl::EndsWithIgnoreCase(value, matcher_.suffix()) + : absl::EndsWith(value, matcher_.suffix()); + case StringMatcherType::MatchPatternCase::kContains: + return matcher_.ignore_case() + ? absl::StrContains(absl::AsciiStrToLower(value), lowercase_contains_match_) + : absl::StrContains(value, matcher_.contains()); + case StringMatcherType::MatchPatternCase::kSafeRegex: + return regex_->match(value); + case StringMatcherType::MatchPatternCase::kCustom: + return custom_->match(value); + default: + PANIC("unexpected"); + } + } bool match(const ProtobufWkt::Value& value) const override { @@ -139,63 +173,40 @@ class StringMatcherImpl : public ValueMatcher, public StringMatcher { } private: - // Type `xds::type::matcher::v3::StringMatcher` doesn't have an extension type, so use function - // overloading to only handle that case for type `envoy::type::matcher::v3::StringMatcher` to - // prevent compilation errors on use of `kCustom`. - - void initialize(const xds::type::matcher::v3::StringMatcher&) {} - - void initialize(const envoy::type::matcher::v3::StringMatcher& matcher) { - if (matcher.has_custom()) { - custom_ = getExtensionStringMatcher(matcher.custom()); - } - } - - bool match(const absl::string_view value, const xds::type::matcher::v3::StringMatcher&) const { - return matchCommon(value); - } - - bool match(const absl::string_view value, - const envoy::type::matcher::v3::StringMatcher& matcher) const { - if (matcher.match_pattern_case() == - envoy::type::matcher::v3::StringMatcher::MatchPatternCase::kCustom) { - return custom_->match(value); - } - return matchCommon(value); - } - - // StringMatcher - bool matchCommon(const absl::string_view value) const { - switch (matcher_.match_pattern_case()) { - case StringMatcherType::MatchPatternCase::kExact: - return matcher_.ignore_case() ? absl::EqualsIgnoreCase(value, matcher_.exact()) - : value == matcher_.exact(); - case StringMatcherType::MatchPatternCase::kPrefix: - return matcher_.ignore_case() ? absl::StartsWithIgnoreCase(value, matcher_.prefix()) - : absl::StartsWith(value, matcher_.prefix()); - case StringMatcherType::MatchPatternCase::kSuffix: - return matcher_.ignore_case() ? absl::EndsWithIgnoreCase(value, matcher_.suffix()) - : absl::EndsWith(value, matcher_.suffix()); - case StringMatcherType::MatchPatternCase::kContains: - return matcher_.ignore_case() - ? absl::StrContains(absl::AsciiStrToLower(value), lowercase_contains_match_) - : absl::StrContains(value, matcher_.contains()); - case StringMatcherType::MatchPatternCase::kSafeRegex: - return regex_->match(value); - default: - PANIC("unexpected"); - } - } - const StringMatcherType matcher_; Regex::CompiledMatcherPtr regex_; std::string lowercase_contains_match_; StringMatcherPtr custom_; }; +// Temporarily create two separate types with different constructors, inheriting from the same +// implementation, to make it easier to find and replace all usage of the old one. +// TODO(ggreenway): delete these two extra classes, make `PrivateStringMatcherImpl` back into +// `StringMatcherImpl`. +template +class StringMatcherImplWithContext : public PrivateStringMatcherImpl { +public: + explicit StringMatcherImplWithContext(const StringMatcherType& matcher, + Server::Configuration::CommonFactoryContext& context) + : PrivateStringMatcherImpl(matcher, &context.regexEngine(), + &context.threadLocal(), &context.api()) {} +}; + +template +class StringMatcherImpl : public PrivateStringMatcherImpl { +public: + explicit StringMatcherImpl(const StringMatcherType& matcher) + : PrivateStringMatcherImpl( + matcher, Regex::EngineSingleton::getExisting(), + InjectableSingleton::getExisting(), + InjectableSingleton::getExisting()) {} +}; + class StringMatcherExtensionFactory : public Config::TypedFactory { public: - virtual StringMatcherPtr createStringMatcher(const ProtobufWkt::Any& config) PURE; + // TODO(ggreenway): Convert all but first parameter to `CommonFactoryContext`. + virtual StringMatcherPtr createStringMatcher(const ProtobufWkt::Any& config, + ThreadLocal::SlotAllocator& tls, Api::Api& api) PURE; std::string category() const override { return "envoy.string_matcher"; } }; diff --git a/source/common/common/posix/thread_impl.cc b/source/common/common/posix/thread_impl.cc index e89fb16382c3..dc90d1fd2c56 100644 --- a/source/common/common/posix/thread_impl.cc +++ b/source/common/common/posix/thread_impl.cc @@ -99,6 +99,7 @@ void PosixThread::join() { bool PosixThread::joinable() const { return !joined_; } ThreadId PosixThread::pthreadId() const { + ASSERT(!joined_); #if defined(__linux__) return ThreadId(static_cast(thread_handle_->handle())); #elif defined(__APPLE__) diff --git a/source/common/common/regex.h b/source/common/common/regex.h index 4189df75fc24..e64fcf90f2ec 100644 --- a/source/common/common/regex.h +++ b/source/common/common/regex.h @@ -79,6 +79,16 @@ class Utility { return EngineSingleton::get().matcher(matcher.regex()); } + + template + static CompiledMatcherPtr parseRegex(const RegexMatcherType& matcher, Engine& engine) { + // Fallback deprecated engine type in regex matcher. + if (matcher.has_google_re2()) { + return std::make_unique(matcher); + } + + return engine.matcher(matcher.regex()); + } }; } // namespace Regex diff --git a/source/common/compression/zstd/common/BUILD b/source/common/compression/zstd/common/BUILD new file mode 100644 index 000000000000..e9ecaa5338b2 --- /dev/null +++ b/source/common/compression/zstd/common/BUILD @@ -0,0 +1,19 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_library( + name = "zstd_base_lib", + srcs = ["base.cc"], + hdrs = ["base.h"], + external_deps = ["zstd"], + deps = [ + "//source/common/buffer:buffer_lib", + ], +) diff --git a/source/extensions/compression/zstd/common/base.cc b/source/common/compression/zstd/common/base.cc similarity index 86% rename from source/extensions/compression/zstd/common/base.cc rename to source/common/compression/zstd/common/base.cc index 3682f3626934..89dc2941cb2d 100644 --- a/source/extensions/compression/zstd/common/base.cc +++ b/source/common/compression/zstd/common/base.cc @@ -1,7 +1,6 @@ -#include "source/extensions/compression/zstd/common/base.h" +#include "source/common/compression/zstd/common/base.h" namespace Envoy { -namespace Extensions { namespace Compression { namespace Zstd { namespace Common { @@ -27,5 +26,4 @@ void Base::getOutput(Buffer::Instance& output_buffer) { } // namespace Common } // namespace Zstd } // namespace Compression -} // namespace Extensions } // namespace Envoy diff --git a/source/extensions/compression/zstd/common/base.h b/source/common/compression/zstd/common/base.h similarity index 92% rename from source/extensions/compression/zstd/common/base.h rename to source/common/compression/zstd/common/base.h index 6a3c626a3701..8582fe3f86f3 100644 --- a/source/extensions/compression/zstd/common/base.h +++ b/source/common/compression/zstd/common/base.h @@ -7,7 +7,6 @@ #include "zstd.h" namespace Envoy { -namespace Extensions { namespace Compression { namespace Zstd { namespace Common { @@ -29,5 +28,4 @@ struct Base { } // namespace Common } // namespace Zstd } // namespace Compression -} // namespace Extensions } // namespace Envoy diff --git a/source/common/compression/zstd/compressor/BUILD b/source/common/compression/zstd/compressor/BUILD new file mode 100644 index 000000000000..16a3fbe97f1e --- /dev/null +++ b/source/common/compression/zstd/compressor/BUILD @@ -0,0 +1,20 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_library( + name = "compressor_base", + srcs = ["zstd_compressor_impl_base.cc"], + hdrs = ["zstd_compressor_impl_base.h"], + deps = [ + "//envoy/compression/compressor:compressor_interface", + "//source/common/buffer:buffer_lib", + "//source/common/compression/zstd/common:zstd_base_lib", + ], +) diff --git a/source/common/compression/zstd/compressor/zstd_compressor_impl_base.cc b/source/common/compression/zstd/compressor/zstd_compressor_impl_base.cc new file mode 100644 index 000000000000..2a04f72b83c0 --- /dev/null +++ b/source/common/compression/zstd/compressor/zstd_compressor_impl_base.cc @@ -0,0 +1,59 @@ +#include "source/common/compression/zstd/compressor/zstd_compressor_impl_base.h" + +#include "source/common/buffer/buffer_impl.h" + +namespace Envoy { +namespace Compression { +namespace Zstd { +namespace Compressor { + +ZstdCompressorImplBase::ZstdCompressorImplBase(uint32_t compression_level, bool enable_checksum, + uint32_t strategy, uint32_t chunk_size) + : Common::Base(chunk_size), cctx_(ZSTD_createCCtx(), &ZSTD_freeCCtx), + compression_level_(compression_level) { + size_t result; + result = ZSTD_CCtx_setParameter(cctx_.get(), ZSTD_c_checksumFlag, enable_checksum); + RELEASE_ASSERT(!ZSTD_isError(result), ""); + + result = ZSTD_CCtx_setParameter(cctx_.get(), ZSTD_c_strategy, strategy); + RELEASE_ASSERT(!ZSTD_isError(result), ""); +} + +void ZstdCompressorImplBase::compress(Buffer::Instance& buffer, + Envoy::Compression::Compressor::State state) { + compressPreprocess(buffer, state); + + Buffer::OwnedImpl accumulation_buffer; + for (const Buffer::RawSlice& input_slice : buffer.getRawSlices()) { + if (input_slice.len_ > 0) { + compressProcess(buffer, input_slice, accumulation_buffer); + buffer.drain(input_slice.len_); + } + } + + compressPostprocess(accumulation_buffer); + + ASSERT(buffer.length() == 0); + buffer.move(accumulation_buffer); + + if (state == Envoy::Compression::Compressor::State::Finish) { + process(buffer, ZSTD_e_end); + } +} + +void ZstdCompressorImplBase::process(Buffer::Instance& output_buffer, ZSTD_EndDirective mode) { + bool finished; + do { + const size_t remaining = ZSTD_compressStream2(cctx_.get(), &output_, &input_, mode); + getOutput(output_buffer); + // If we're on the last chunk we're finished when zstd returns 0, + // which means its consumed all the input AND finished the frame. + // Otherwise, we're finished when we've consumed all the input. + finished = (ZSTD_e_end == mode) ? (remaining == 0) : (input_.pos == input_.size); + } while (!finished); +} + +} // namespace Compressor +} // namespace Zstd +} // namespace Compression +} // namespace Envoy diff --git a/source/common/compression/zstd/compressor/zstd_compressor_impl_base.h b/source/common/compression/zstd/compressor/zstd_compressor_impl_base.h new file mode 100644 index 000000000000..954b3aa17f9a --- /dev/null +++ b/source/common/compression/zstd/compressor/zstd_compressor_impl_base.h @@ -0,0 +1,43 @@ +#pragma once + +#include "envoy/compression/compressor/compressor.h" + +#include "source/common/compression/zstd/common/base.h" + +namespace Envoy { +namespace Compression { +namespace Zstd { +namespace Compressor { + +/** + * Implementation of compressor's interface. + */ +class ZstdCompressorImplBase : public Common::Base, + public Envoy::Compression::Compressor::Compressor, + NonCopyable { +public: + ZstdCompressorImplBase(uint32_t compression_level, bool enable_checksum, uint32_t strategy, + uint32_t chunk_size); + + // Compression::Compressor::Compressor + void compress(Buffer::Instance& buffer, Envoy::Compression::Compressor::State state) override; + + void process(Buffer::Instance& output_buffer, ZSTD_EndDirective mode); + +protected: + virtual void compressPreprocess(Buffer::Instance& buffer, + Envoy::Compression::Compressor::State state) PURE; + + virtual void compressProcess(const Buffer::Instance& buffer, const Buffer::RawSlice& input_slice, + Buffer::Instance& accumulation_buffer) PURE; + + virtual void compressPostprocess(Buffer::Instance& accumulation_buffer) PURE; + + std::unique_ptr cctx_; + const uint32_t compression_level_; +}; + +} // namespace Compressor +} // namespace Zstd +} // namespace Compression +} // namespace Envoy diff --git a/source/common/config/watched_directory.cc b/source/common/config/watched_directory.cc index fc2a3a832a50..483cc08460d5 100644 --- a/source/common/config/watched_directory.cc +++ b/source/common/config/watched_directory.cc @@ -6,8 +6,9 @@ namespace Config { WatchedDirectory::WatchedDirectory(const envoy::config::core::v3::WatchedDirectory& config, Event::Dispatcher& dispatcher) { watcher_ = dispatcher.createFilesystemWatcher(); - watcher_->addWatch(absl::StrCat(config.path(), "/"), Filesystem::Watcher::Events::MovedTo, - [this](uint32_t) { cb_(); }); + THROW_IF_NOT_OK(watcher_->addWatch(absl::StrCat(config.path(), "/"), + Filesystem::Watcher::Events::MovedTo, + [this](uint32_t) { cb_(); })); } } // namespace Config diff --git a/source/common/config/well_known_names.cc b/source/common/config/well_known_names.cc index 4e390568c31d..104b182bfb1a 100644 --- a/source/common/config/well_known_names.cc +++ b/source/common/config/well_known_names.cc @@ -207,6 +207,9 @@ TagNameValues::TagNameValues() { // (.).rbac.** addTokenized(RBAC_PREFIX, "$.rbac.**"); + + // http..rbac.(.)* + addTokenized(RBAC_HTTP_PREFIX, "http.*.rbac.$.**"); } void TagNameValues::addRe2(const std::string& name, const std::string& regex, diff --git a/source/common/config/well_known_names.h b/source/common/config/well_known_names.h index a0a40cc79496..8cf5fbd14715 100644 --- a/source/common/config/well_known_names.h +++ b/source/common/config/well_known_names.h @@ -123,6 +123,8 @@ class TagNameValues { const std::string CONNECTION_LIMIT_PREFIX = "envoy.connection_limit_prefix"; // Stats prefix for the RBAC network filter const std::string RBAC_PREFIX = "envoy.rbac_prefix"; + // Stats prefix for the RBAC http filter + const std::string RBAC_HTTP_PREFIX = "envoy.rbac_http_prefix"; // Stats prefix for the TCP Proxy network filter const std::string TCP_PREFIX = "envoy.tcp_prefix"; // Stats prefix for the UDP Proxy network filter diff --git a/source/common/event/dispatcher_impl.cc b/source/common/event/dispatcher_impl.cc index 778c8c779287..d89072dc00f7 100644 --- a/source/common/event/dispatcher_impl.cc +++ b/source/common/event/dispatcher_impl.cc @@ -55,16 +55,16 @@ DispatcherImpl::DispatcherImpl(const std::string& name, Api::Api& api, Event::TimeSystem& time_system, const ScaledRangeTimerManagerFactory& scaled_timer_factory, const Buffer::WatermarkFactorySharedPtr& watermark_factory) - : DispatcherImpl(name, api.threadFactory(), api.timeSource(), api.randomGenerator(), - api.fileSystem(), time_system, scaled_timer_factory, + : DispatcherImpl(name, api.threadFactory(), api.timeSource(), api.fileSystem(), time_system, + scaled_timer_factory, watermark_factory != nullptr ? watermark_factory : std::make_shared( api.bootstrap().overload_manager().buffer_factory_config())) {} DispatcherImpl::DispatcherImpl(const std::string& name, Thread::ThreadFactory& thread_factory, - TimeSource& time_source, Random::RandomGenerator&, - Filesystem::Instance& file_system, Event::TimeSystem& time_system, + TimeSource& time_source, Filesystem::Instance& file_system, + Event::TimeSystem& time_system, const ScaledRangeTimerManagerFactory& scaled_timer_factory, const Buffer::WatermarkFactorySharedPtr& watermark_factory) : name_(name), thread_factory_(thread_factory), time_source_(time_source), diff --git a/source/common/event/dispatcher_impl.h b/source/common/event/dispatcher_impl.h index 6b133fe10984..5e48b2a83829 100644 --- a/source/common/event/dispatcher_impl.h +++ b/source/common/event/dispatcher_impl.h @@ -43,10 +43,9 @@ class DispatcherImpl : Logger::Loggable, DispatcherImpl(const std::string& name, Api::Api& api, Event::TimeSystem& time_system, const ScaledRangeTimerManagerFactory& scaled_timer_factory, const Buffer::WatermarkFactorySharedPtr& watermark_factory); - // TODO(alyssawilk) remove random_generator. DispatcherImpl(const std::string& name, Thread::ThreadFactory& thread_factory, - TimeSource& time_source, Random::RandomGenerator& random_generator, - Filesystem::Instance& file_system, Event::TimeSystem& time_system, + TimeSource& time_source, Filesystem::Instance& file_system, + Event::TimeSystem& time_system, const ScaledRangeTimerManagerFactory& scaled_timer_factory, const Buffer::WatermarkFactorySharedPtr& watermark_factory); ~DispatcherImpl() override; diff --git a/source/common/filesystem/inotify/watcher_impl.cc b/source/common/filesystem/inotify/watcher_impl.cc index 864da79f5b59..1f53022ddcb7 100644 --- a/source/common/filesystem/inotify/watcher_impl.cc +++ b/source/common/filesystem/inotify/watcher_impl.cc @@ -32,23 +32,24 @@ WatcherImpl::WatcherImpl(Event::Dispatcher& dispatcher, Filesystem::Instance& fi WatcherImpl::~WatcherImpl() { close(inotify_fd_); } -void WatcherImpl::addWatch(absl::string_view path, uint32_t events, OnChangedCb callback) { +absl::Status WatcherImpl::addWatch(absl::string_view path, uint32_t events, OnChangedCb callback) { // Because of general inotify pain, we always watch the directory that the file lives in, // and then synthetically raise per file events. auto result_or_error = file_system_.splitPathFromFilename(path); - THROW_IF_STATUS_NOT_OK(result_or_error, throw); + RETURN_IF_STATUS_NOT_OK(result_or_error); const PathSplitResult result = result_or_error.value(); const uint32_t watch_mask = IN_MODIFY | IN_MOVED_TO; int watch_fd = inotify_add_watch(inotify_fd_, std::string(result.directory_).c_str(), watch_mask); if (watch_fd == -1) { - throwEnvoyExceptionOrPanic( + return absl::InvalidArgumentError( fmt::format("unable to add filesystem watch for file {}: {}", path, errorDetails(errno))); } ENVOY_LOG(debug, "added watch for directory: '{}' file: '{}' fd: {}", result.directory_, result.file_, watch_fd); callback_map_[watch_fd].watches_.push_back({std::string(result.file_), events, callback}); + return absl::OkStatus(); } void WatcherImpl::onInotifyEvent() { diff --git a/source/common/filesystem/inotify/watcher_impl.h b/source/common/filesystem/inotify/watcher_impl.h index f2474b3c1d73..bf76f5f17de1 100644 --- a/source/common/filesystem/inotify/watcher_impl.h +++ b/source/common/filesystem/inotify/watcher_impl.h @@ -26,7 +26,7 @@ class WatcherImpl : public Watcher, Logger::Loggable { ~WatcherImpl() override; // Filesystem::Watcher - void addWatch(absl::string_view path, uint32_t events, OnChangedCb cb) override; + absl::Status addWatch(absl::string_view path, uint32_t events, OnChangedCb cb) override; private: struct FileWatch { diff --git a/source/common/filesystem/kqueue/watcher_impl.cc b/source/common/filesystem/kqueue/watcher_impl.cc index 200c08dc8104..ca20aab97a90 100644 --- a/source/common/filesystem/kqueue/watcher_impl.cc +++ b/source/common/filesystem/kqueue/watcher_impl.cc @@ -32,11 +32,13 @@ WatcherImpl::~WatcherImpl() { watches_.clear(); } -void WatcherImpl::addWatch(absl::string_view path, uint32_t events, Watcher::OnChangedCb cb) { +absl::Status WatcherImpl::addWatch(absl::string_view path, uint32_t events, + Watcher::OnChangedCb cb) { FileWatchPtr watch = addWatch(path, events, cb, false); if (watch == nullptr) { - throwEnvoyExceptionOrPanic(absl::StrCat("invalid watch path ", path)); + return absl::InvalidArgumentError(absl::StrCat("invalid watch path ", path)); } + return absl::OkStatus(); } WatcherImpl::FileWatchPtr WatcherImpl::addWatch(absl::string_view path, uint32_t events, diff --git a/source/common/filesystem/kqueue/watcher_impl.h b/source/common/filesystem/kqueue/watcher_impl.h index ba5d908a05a0..22e7f9f06dbc 100644 --- a/source/common/filesystem/kqueue/watcher_impl.h +++ b/source/common/filesystem/kqueue/watcher_impl.h @@ -27,7 +27,7 @@ class WatcherImpl : public Watcher, Logger::Loggable { ~WatcherImpl(); // Filesystem::Watcher - void addWatch(absl::string_view path, uint32_t events, OnChangedCb cb) override; + absl::Status addWatch(absl::string_view path, uint32_t events, OnChangedCb cb) override; private: struct FileWatch : LinkedObject { diff --git a/source/common/filesystem/win32/watcher_impl.cc b/source/common/filesystem/win32/watcher_impl.cc index 601c787461bf..6cb9d00a1bc7 100644 --- a/source/common/filesystem/win32/watcher_impl.cc +++ b/source/common/filesystem/win32/watcher_impl.cc @@ -50,13 +50,13 @@ WatcherImpl::~WatcherImpl() { ::CloseHandle(thread_exit_event_); } -void WatcherImpl::addWatch(absl::string_view path, uint32_t events, OnChangedCb cb) { +absl::Status WatcherImpl::addWatch(absl::string_view path, uint32_t events, OnChangedCb cb) { if (path == Platform::null_device_path) { - return; + return absl::OkStatus(); } const absl::StatusOr result_or_error = file_system_.splitPathFromFilename(path); - THROW_IF_STATUS_NOT_OK(result_or_error, throw); + RETURN_IF_STATUS_NOT_OK(result_or_error); const PathSplitResult& result = result_or_error.value(); // ReadDirectoryChangesW only has a Unicode version, so we need // to use wide strings here @@ -67,7 +67,7 @@ void WatcherImpl::addWatch(absl::string_view path, uint32_t events, OnChangedCb directory.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); if (dir_handle == INVALID_HANDLE_VALUE) { - throw EnvoyException( + return absl::InvalidArgumentError( fmt::format("unable to open directory {}: {}", result.directory_, GetLastError())); } std::string fii_key(sizeof(FILE_ID_INFO), '\0'); @@ -108,6 +108,7 @@ void WatcherImpl::addWatch(absl::string_view path, uint32_t events, OnChangedCb callback_map_[fii_key]->watches_.push_back({file, events, cb}); ENVOY_LOG(debug, "added watch for file '{}' in directory '{}'", result.file_, result.directory_); + return absl::OkStatus(); } void WatcherImpl::onDirectoryEvent() { diff --git a/source/common/filesystem/win32/watcher_impl.h b/source/common/filesystem/win32/watcher_impl.h index ce6acd518b49..5431d5f3f0a2 100644 --- a/source/common/filesystem/win32/watcher_impl.h +++ b/source/common/filesystem/win32/watcher_impl.h @@ -31,7 +31,7 @@ class WatcherImpl : public Watcher, Logger::Loggable { ~WatcherImpl(); // Filesystem::Watcher - void addWatch(absl::string_view path, uint32_t events, OnChangedCb cb) override; + absl::Status addWatch(absl::string_view path, uint32_t events, OnChangedCb cb) override; private: static void issueFirstRead(ULONG_PTR param); diff --git a/source/common/formatter/substitution_format_string.h b/source/common/formatter/substitution_format_string.h index 3cdd032a9eee..fc1d6f5e6e83 100644 --- a/source/common/formatter/substitution_format_string.h +++ b/source/common/formatter/substitution_format_string.h @@ -22,16 +22,18 @@ namespace Formatter { */ class SubstitutionFormatStringUtils { public: + using FormattersConfig = + ProtobufWkt::RepeatedPtrField; + /** - * Generate a formatter object from config SubstitutionFormatString. + * Parse list of formatter configurations to commands. */ template - static FormatterBasePtr - fromProtoConfig(const envoy::config::core::v3::SubstitutionFormatString& config, + static std::vector> + parseFormatters(const FormattersConfig& formatters, Server::Configuration::GenericFactoryContext& context) { - // Instantiate formatter extensions. std::vector> commands; - for (const auto& formatter : config.formatters()) { + for (const auto& formatter : formatters) { auto* factory = Envoy::Config::Utility::getFactory>(formatter); if (!factory) { @@ -47,12 +49,24 @@ class SubstitutionFormatStringUtils { commands.push_back(std::move(parser)); } + return commands; + } + + /** + * Generate a formatter object from config SubstitutionFormatString. + */ + template + static FormatterBasePtr + fromProtoConfig(const envoy::config::core::v3::SubstitutionFormatString& config, + Server::Configuration::GenericFactoryContext& context) { + // Instantiate formatter extensions. + auto commands = parseFormatters(config.formatters(), context); switch (config.format_case()) { case envoy::config::core::v3::SubstitutionFormatString::FormatCase::kTextFormat: return std::make_unique>( config.text_format(), config.omit_empty_values(), commands); case envoy::config::core::v3::SubstitutionFormatString::FormatCase::kJsonFormat: - return std::make_unique>( + return createJsonFormatter( config.json_format(), true, config.omit_empty_values(), config.has_json_format_options() ? config.json_format_options().sort_properties() : false, commands); @@ -75,9 +89,10 @@ class SubstitutionFormatStringUtils { template static FormatterBasePtr createJsonFormatter(const ProtobufWkt::Struct& struct_format, bool preserve_types, - bool omit_empty_values, bool sort_properties) { + bool omit_empty_values, bool sort_properties, + const std::vector>& commands = {}) { return std::make_unique>( - struct_format, preserve_types, omit_empty_values, sort_properties); + struct_format, preserve_types, omit_empty_values, sort_properties, commands); } }; diff --git a/source/common/grpc/async_client_manager_impl.cc b/source/common/grpc/async_client_manager_impl.cc index f2875e30466e..9d3bf65d132c 100644 --- a/source/common/grpc/async_client_manager_impl.cc +++ b/source/common/grpc/async_client_manager_impl.cc @@ -56,11 +56,11 @@ AsyncClientFactoryImpl::AsyncClientFactoryImpl(Upstream::ClusterManager& cm, } AsyncClientManagerImpl::AsyncClientManagerImpl( - Upstream::ClusterManager& cm, ThreadLocal::Instance& tls, TimeSource& time_source, - Api::Api& api, const StatNames& stat_names, + Upstream::ClusterManager& cm, ThreadLocal::Instance& tls, + Server::Configuration::CommonFactoryContext& context, const StatNames& stat_names, const envoy::config::bootstrap::v3::Bootstrap::GrpcAsyncClientManagerConfig& config) - : cm_(cm), tls_(tls), time_source_(time_source), api_(api), stat_names_(stat_names), - raw_async_client_cache_(tls_) { + : tls_(tls), cm_(cm), context_(context), stat_names_(stat_names), + raw_async_client_cache_(context.threadLocal()) { const auto max_cached_entry_idle_duration = std::chrono::milliseconds( PROTOBUF_GET_MS_OR_DEFAULT(config, max_cached_entry_idle_duration, DefaultEntryIdleDuration)); @@ -69,11 +69,10 @@ AsyncClientManagerImpl::AsyncClientManagerImpl( return std::make_shared(dispatcher, max_cached_entry_idle_duration); }); #ifdef ENVOY_GOOGLE_GRPC - google_tls_slot_ = tls.allocateSlot(); + google_tls_slot_ = context_.threadLocal().allocateSlot(); + Api::Api& api = context_.api(); google_tls_slot_->set( [&api](Event::Dispatcher&) { return std::make_shared(api); }); -#else - UNREFERENCED_PARAMETER(api_); #endif } @@ -83,17 +82,18 @@ RawAsyncClientPtr AsyncClientFactoryImpl::createUncachedRawAsyncClient() { GoogleAsyncClientFactoryImpl::GoogleAsyncClientFactoryImpl( ThreadLocal::Instance& tls, ThreadLocal::Slot* google_tls_slot, Stats::Scope& scope, - const envoy::config::core::v3::GrpcService& config, Api::Api& api, const StatNames& stat_names, + const envoy::config::core::v3::GrpcService& config, + Server::Configuration::CommonFactoryContext& context, const StatNames& stat_names, absl::Status& creation_status) : tls_(tls), google_tls_slot_(google_tls_slot), scope_(scope.createScope(fmt::format("grpc.{}.", config.google_grpc().stat_prefix()))), - config_(config), api_(api), stat_names_(stat_names) { + config_(config), factory_context_(context), stat_names_(stat_names) { #ifndef ENVOY_GOOGLE_GRPC UNREFERENCED_PARAMETER(tls_); UNREFERENCED_PARAMETER(google_tls_slot_); UNREFERENCED_PARAMETER(scope_); UNREFERENCED_PARAMETER(config_); - UNREFERENCED_PARAMETER(api_); + UNREFERENCED_PARAMETER(factory_context_); UNREFERENCED_PARAMETER(stat_names_); creation_status = absl::InvalidArgumentError("Google C++ gRPC client is not linked"); return; @@ -128,7 +128,7 @@ RawAsyncClientPtr GoogleAsyncClientFactoryImpl::createUncachedRawAsyncClient() { GoogleGenericStubFactory stub_factory; return std::make_unique( tls_.dispatcher(), google_tls_slot_->getTyped(), stub_factory, - scope_, config_, api_, stat_names_); + scope_, config_, factory_context_, stat_names_); #else return nullptr; #endif @@ -142,11 +142,11 @@ AsyncClientManagerImpl::factoryForGrpcService(const envoy::config::core::v3::Grp switch (config.target_specifier_case()) { case envoy::config::core::v3::GrpcService::TargetSpecifierCase::kEnvoyGrpc: factory = std::make_unique(cm_, config, skip_cluster_check, - time_source_, creation_status); + context_.timeSource(), creation_status); break; case envoy::config::core::v3::GrpcService::TargetSpecifierCase::kGoogleGrpc: factory = std::make_unique( - tls_, google_tls_slot_.get(), scope, config, api_, stat_names_, creation_status); + tls_, google_tls_slot_.get(), scope, config, context_, stat_names_, creation_status); break; case envoy::config::core::v3::GrpcService::TargetSpecifierCase::TARGET_SPECIFIER_NOT_SET: PANIC_DUE_TO_PROTO_UNSET; diff --git a/source/common/grpc/async_client_manager_impl.h b/source/common/grpc/async_client_manager_impl.h index 54c3cf6b2b00..c1591dd19395 100644 --- a/source/common/grpc/async_client_manager_impl.h +++ b/source/common/grpc/async_client_manager_impl.h @@ -33,7 +33,8 @@ class GoogleAsyncClientFactoryImpl : public AsyncClientFactory { public: GoogleAsyncClientFactoryImpl(ThreadLocal::Instance& tls, ThreadLocal::Slot* google_tls_slot, Stats::Scope& scope, - const envoy::config::core::v3::GrpcService& config, Api::Api& api, + const envoy::config::core::v3::GrpcService& config, + Server::Configuration::CommonFactoryContext& context, const StatNames& stat_names, absl::Status& creation_status); RawAsyncClientPtr createUncachedRawAsyncClient() override; @@ -42,15 +43,15 @@ class GoogleAsyncClientFactoryImpl : public AsyncClientFactory { ThreadLocal::Slot* google_tls_slot_; Stats::ScopeSharedPtr scope_; const envoy::config::core::v3::GrpcService config_; - Api::Api& api_; + Server::Configuration::CommonFactoryContext& factory_context_; const StatNames& stat_names_; }; class AsyncClientManagerImpl : public AsyncClientManager { public: AsyncClientManagerImpl( - Upstream::ClusterManager& cm, ThreadLocal::Instance& tls, TimeSource& time_source, - Api::Api& api, const StatNames& stat_names, + Upstream::ClusterManager& cm, ThreadLocal::Instance& tls, + Server::Configuration::CommonFactoryContext& context, const StatNames& stat_names, const envoy::config::bootstrap::v3::Bootstrap::GrpcAsyncClientManagerConfig& config); absl::StatusOr getOrCreateRawAsyncClient(const envoy::config::core::v3::GrpcService& config, Stats::Scope& scope, @@ -92,11 +93,10 @@ class AsyncClientManagerImpl : public AsyncClientManager { }; private: - Upstream::ClusterManager& cm_; ThreadLocal::Instance& tls_; + Upstream::ClusterManager& cm_; // Need to track outside of `context_` due to startup ordering. + Server::Configuration::CommonFactoryContext& context_; ThreadLocal::SlotPtr google_tls_slot_; - TimeSource& time_source_; - Api::Api& api_; const StatNames& stat_names_; ThreadLocal::TypedSlot raw_async_client_cache_; }; diff --git a/source/common/grpc/google_async_client_impl.cc b/source/common/grpc/google_async_client_impl.cc index eea658811b45..8c0e9a6d7e36 100644 --- a/source/common/grpc/google_async_client_impl.cc +++ b/source/common/grpc/google_async_client_impl.cc @@ -82,7 +82,8 @@ GoogleAsyncClientImpl::GoogleAsyncClientImpl(Event::Dispatcher& dispatcher, GoogleStubFactory& stub_factory, Stats::ScopeSharedPtr scope, const envoy::config::core::v3::GrpcService& config, - Api::Api& api, const StatNames& stat_names) + Server::Configuration::CommonFactoryContext& context, + const StatNames& stat_names) : dispatcher_(dispatcher), tls_(tls), stat_prefix_(config.google_grpc().stat_prefix()), target_uri_(config.google_grpc().target_uri()), scope_(scope), per_stream_buffer_limit_bytes_(PROTOBUF_GET_WRAPPED_OR_DEFAULT( @@ -94,7 +95,7 @@ GoogleAsyncClientImpl::GoogleAsyncClientImpl(Event::Dispatcher& dispatcher, // smart enough to do connection pooling and reuse with identical channel args, so this should // have comparable overhead to what we are doing in Grpc::AsyncClientImpl, i.e. no expensive // new connection implied. - std::shared_ptr channel = GoogleGrpcUtils::createChannel(config, api); + std::shared_ptr channel = GoogleGrpcUtils::createChannel(config, context); // Get state with try_to_connect = true to try connection at channel creation. // This is for initializing gRPC channel at channel creation. This GetState(true) is used to poke // the gRPC lb at channel creation, it doesn't have any effect no matter it succeeds or fails. But diff --git a/source/common/grpc/google_async_client_impl.h b/source/common/grpc/google_async_client_impl.h index 9fb4ad022fd8..45eceae18434 100644 --- a/source/common/grpc/google_async_client_impl.h +++ b/source/common/grpc/google_async_client_impl.h @@ -175,7 +175,8 @@ class GoogleAsyncClientImpl final : public RawAsyncClient, Logger::Loggable getChannelCredentials(const envoy::config::core::v3::GrpcService& grpc_service_config, - Api::Api& api) override { - return CredsUtility::defaultChannelCredentials(grpc_service_config, api); + Server::Configuration::CommonFactoryContext& context) override { + return CredsUtility::defaultChannelCredentials(grpc_service_config, context.api()); } std::string name() const override { return "envoy.grpc_credentials.default"; } diff --git a/source/common/grpc/google_grpc_utils.cc b/source/common/grpc/google_grpc_utils.cc index fbe891306692..9ad8a0f5999c 100644 --- a/source/common/grpc/google_grpc_utils.cc +++ b/source/common/grpc/google_grpc_utils.cc @@ -26,7 +26,7 @@ namespace { std::shared_ptr getGoogleGrpcChannelCredentials(const envoy::config::core::v3::GrpcService& grpc_service, - Api::Api& api) { + Server::Configuration::CommonFactoryContext& context) { GoogleGrpcCredentialsFactory* credentials_factory = nullptr; const std::string& google_grpc_credentials_factory_name = grpc_service.google_grpc().credentials_factory_name(); @@ -41,7 +41,7 @@ getGoogleGrpcChannelCredentials(const envoy::config::core::v3::GrpcService& grpc throw EnvoyException(absl::StrCat("Unknown google grpc credentials factory: ", google_grpc_credentials_factory_name)); } - return credentials_factory->getChannelCredentials(grpc_service, api); + return credentials_factory->getChannelCredentials(grpc_service, context); } } // namespace @@ -133,8 +133,10 @@ GoogleGrpcUtils::channelArgsFromConfig(const envoy::config::core::v3::GrpcServic } std::shared_ptr -GoogleGrpcUtils::createChannel(const envoy::config::core::v3::GrpcService& config, Api::Api& api) { - std::shared_ptr creds = getGoogleGrpcChannelCredentials(config, api); +GoogleGrpcUtils::createChannel(const envoy::config::core::v3::GrpcService& config, + Server::Configuration::CommonFactoryContext& context) { + std::shared_ptr creds = + getGoogleGrpcChannelCredentials(config, context); const grpc::ChannelArguments args = channelArgsFromConfig(config); return CreateCustomChannel(config.google_grpc().target_uri(), creds, args); } diff --git a/source/common/grpc/google_grpc_utils.h b/source/common/grpc/google_grpc_utils.h index 859a61ccfff9..102bc0c3f25d 100644 --- a/source/common/grpc/google_grpc_utils.h +++ b/source/common/grpc/google_grpc_utils.h @@ -3,10 +3,10 @@ #include #include -#include "envoy/api/api.h" #include "envoy/buffer/buffer.h" #include "envoy/common/platform.h" #include "envoy/config/core/v3/grpc_service.pb.h" +#include "envoy/server/factory_context.h" #include "grpcpp/grpcpp.h" @@ -46,7 +46,8 @@ class GoogleGrpcUtils { * @return static std::shared_ptr a gRPC channel. */ static std::shared_ptr - createChannel(const envoy::config::core::v3::GrpcService& config, Api::Api& api); + createChannel(const envoy::config::core::v3::GrpcService& config, + Server::Configuration::CommonFactoryContext& context); }; } // namespace Grpc diff --git a/source/common/http/http3_status_tracker_impl.cc b/source/common/http/http3_status_tracker_impl.cc index c065fe6e3202..4f0dc6db28b9 100644 --- a/source/common/http/http3_status_tracker_impl.cc +++ b/source/common/http/http3_status_tracker_impl.cc @@ -5,10 +5,10 @@ namespace Http { namespace { -// Initially, HTTP/3 is be marked broken for 5 minutes. -const std::chrono::minutes DefaultExpirationTime{5}; -// Cap the broken period at just under 1 day. -const int MaxConsecutiveBrokenCount = 8; +// Initially, HTTP/3 is marked broken for 1 second. +const std::chrono::seconds DefaultExpirationTime{1}; +// Cap the broken period around a day and a half. +const int MaxConsecutiveBrokenCount = 17; } // namespace Http3StatusTrackerImpl::Http3StatusTrackerImpl(Event::Dispatcher& dispatcher) @@ -25,10 +25,10 @@ bool Http3StatusTrackerImpl::hasHttp3FailedRecently() const { void Http3StatusTrackerImpl::markHttp3Broken() { state_ = State::Broken; if (!expiration_timer_->enabled()) { - std::chrono::minutes expiration_in_min = + std::chrono::seconds expiration_in_sec = DefaultExpirationTime * (1 << consecutive_broken_count_); expiration_timer_->enableTimer( - std::chrono::duration_cast(expiration_in_min)); + std::chrono::duration_cast(expiration_in_sec)); if (consecutive_broken_count_ < MaxConsecutiveBrokenCount) { ++consecutive_broken_count_; } diff --git a/source/common/network/udp_listener_impl.cc b/source/common/network/udp_listener_impl.cc index a184eaae7036..2411b860609a 100644 --- a/source/common/network/udp_listener_impl.cc +++ b/source/common/network/udp_listener_impl.cc @@ -101,7 +101,7 @@ void UdpListenerImpl::handleReadCallback() { cb_.onReadReady(); const Api::IoErrorPtr result = Utility::readPacketsFromSocket( socket_->ioHandle(), *socket_->connectionInfoProvider().localAddress(), *this, time_source_, - config_.prefer_gro_, packets_dropped_); + config_.prefer_gro_, /*allow_mmsg=*/true, packets_dropped_); if (result == nullptr) { // No error. The number of reads was limited by read rate. There are more packets to read. // Register to read more in the next event loop. diff --git a/source/common/network/utility.cc b/source/common/network/utility.cc index eb7bb2a72951..bec004ed38de 100644 --- a/source/common/network/utility.cc +++ b/source/common/network/utility.cc @@ -33,6 +33,7 @@ namespace Envoy { namespace Network { +namespace { Address::InstanceConstSharedPtr instanceOrNull(StatusOr address) { if (address.ok()) { @@ -41,6 +42,8 @@ Address::InstanceConstSharedPtr instanceOrNull(StatusOr(); + IoHandle::RecvMsgOutput output(1, packets_dropped); - if (use_gro) { - Buffer::InstancePtr buffer = std::make_unique(); - IoHandle::RecvMsgOutput output(1, packets_dropped); + // TODO(yugant): Avoid allocating 24k for each read by getting memory from UdpPacketProcessor + const uint64_t max_rx_datagram_size_with_gro = + NUM_DATAGRAMS_PER_RECEIVE * udp_packet_processor.maxDatagramSize(); + ENVOY_LOG_MISC(trace, "starting gro recvmsg with max={}", max_rx_datagram_size_with_gro); - // TODO(yugant): Avoid allocating 24k for each read by getting memory from UdpPacketProcessor - const uint64_t max_rx_datagram_size_with_gro = - NUM_DATAGRAMS_PER_RECEIVE * udp_packet_processor.maxDatagramSize(); - ENVOY_LOG_MISC(trace, "starting gro recvmsg with max={}", max_rx_datagram_size_with_gro); + Api::IoCallUint64Result result = + receiveMessage(max_rx_datagram_size_with_gro, buffer, output, handle, local_address); - Api::IoCallUint64Result result = - receiveMessage(max_rx_datagram_size_with_gro, buffer, output, handle, local_address); + if (!result.ok() || output.msg_[0].truncated_and_dropped_) { + return result; + } - if (!result.ok() || output.msg_[0].truncated_and_dropped_) { - return result; - } + const uint64_t gso_size = output.msg_[0].gso_size_; + ENVOY_LOG_MISC(trace, "gro recvmsg bytes {} with gso_size as {}", result.return_value_, gso_size); - const uint64_t gso_size = output.msg_[0].gso_size_; - ENVOY_LOG_MISC(trace, "gro recvmsg bytes {} with gso_size as {}", result.return_value_, - gso_size); + // Skip gso segmentation and proceed as a single payload. + if (gso_size == 0u) { + passPayloadToProcessor( + result.return_value_, std::move(buffer), std::move(output.msg_[0].peer_address_), + std::move(output.msg_[0].local_address_), udp_packet_processor, receive_time); + return result; + } - // Skip gso segmentation and proceed as a single payload. - if (gso_size == 0u) { - passPayloadToProcessor( - result.return_value_, std::move(buffer), std::move(output.msg_[0].peer_address_), - std::move(output.msg_[0].local_address_), udp_packet_processor, receive_time); - return result; - } + // Segment the buffer read by the recvmsg syscall into gso_sized sub buffers. + // TODO(mattklein123): The following code should be optimized to avoid buffer copies, either by + // switching to slices or by using a CoW buffer type. + while (buffer->length() > 0) { + const uint64_t bytes_to_copy = std::min(buffer->length(), gso_size); + Buffer::InstancePtr sub_buffer = std::make_unique(); + sub_buffer->move(*buffer, bytes_to_copy); + passPayloadToProcessor(bytes_to_copy, std::move(sub_buffer), output.msg_[0].peer_address_, + output.msg_[0].local_address_, udp_packet_processor, receive_time); + } - // Segment the buffer read by the recvmsg syscall into gso_sized sub buffers. - // TODO(mattklein123): The following code should be optimized to avoid buffer copies, either by - // switching to slices or by using a CoW buffer type. - while (buffer->length() > 0) { - const uint64_t bytes_to_copy = std::min(buffer->length(), gso_size); - Buffer::InstancePtr sub_buffer = std::make_unique(); - sub_buffer->move(*buffer, bytes_to_copy); - passPayloadToProcessor(bytes_to_copy, std::move(sub_buffer), output.msg_[0].peer_address_, - output.msg_[0].local_address_, udp_packet_processor, receive_time); - } + return result; +} +Api::IoCallUint64Result readFromSocketRecvMmsg(IoHandle& handle, + const Address::Instance& local_address, + UdpPacketProcessor& udp_packet_processor, + MonotonicTime receive_time, + uint32_t* packets_dropped) { + ASSERT(Api::OsSysCallsSingleton::get().supportsMmsg(), + "cannot use recvmmsg when the platform doesn't support it."); + const auto max_rx_datagram_size = udp_packet_processor.maxDatagramSize(); + + // Buffer::ReservationSingleSlice is always passed by value, and can only be constructed + // by Buffer::Instance::reserve(), so this is needed to keep a fixed array + // in which all elements are legally constructed. + struct BufferAndReservation { + BufferAndReservation(uint64_t max_rx_datagram_size) + : buffer_(std::make_unique()), + reservation_(buffer_->reserveSingleSlice(max_rx_datagram_size, true)) {} + + Buffer::InstancePtr buffer_; + Buffer::ReservationSingleSlice reservation_; + }; + constexpr uint32_t num_slices_per_packet = 1u; + absl::InlinedVector buffers; + RawSliceArrays slices(NUM_DATAGRAMS_PER_RECEIVE, + absl::FixedArray(num_slices_per_packet)); + for (uint32_t i = 0; i < NUM_DATAGRAMS_PER_RECEIVE; i++) { + buffers.push_back(max_rx_datagram_size); + slices[i][0] = buffers[i].reservation_.slice(); + } + + IoHandle::RecvMsgOutput output(NUM_DATAGRAMS_PER_RECEIVE, packets_dropped); + ENVOY_LOG_MISC(trace, "starting recvmmsg with packets={} max={}", NUM_DATAGRAMS_PER_RECEIVE, + max_rx_datagram_size); + Api::IoCallUint64Result result = handle.recvmmsg(slices, local_address.ip()->port(), output); + if (!result.ok()) { return result; } - if (handle.supportsMmsg()) { - const auto max_rx_datagram_size = udp_packet_processor.maxDatagramSize(); - - // Buffer::ReservationSingleSlice is always passed by value, and can only be constructed - // by Buffer::Instance::reserve(), so this is needed to keep a fixed array - // in which all elements are legally constructed. - struct BufferAndReservation { - BufferAndReservation(uint64_t max_rx_datagram_size) - : buffer_(std::make_unique()), - reservation_(buffer_->reserveSingleSlice(max_rx_datagram_size, true)) {} - - Buffer::InstancePtr buffer_; - Buffer::ReservationSingleSlice reservation_; - }; - constexpr uint32_t num_slices_per_packet = 1u; - absl::InlinedVector buffers; - RawSliceArrays slices(NUM_DATAGRAMS_PER_RECEIVE, - absl::FixedArray(num_slices_per_packet)); - for (uint32_t i = 0; i < NUM_DATAGRAMS_PER_RECEIVE; i++) { - buffers.push_back(max_rx_datagram_size); - slices[i][0] = buffers[i].reservation_.slice(); + uint64_t packets_read = result.return_value_; + ENVOY_LOG_MISC(trace, "recvmmsg read {} packets", packets_read); + for (uint64_t i = 0; i < packets_read; ++i) { + if (output.msg_[i].truncated_and_dropped_) { + continue; } - IoHandle::RecvMsgOutput output(NUM_DATAGRAMS_PER_RECEIVE, packets_dropped); - ENVOY_LOG_MISC(trace, "starting recvmmsg with packets={} max={}", NUM_DATAGRAMS_PER_RECEIVE, - max_rx_datagram_size); - Api::IoCallUint64Result result = handle.recvmmsg(slices, local_address.ip()->port(), output); - if (!result.ok()) { - return result; - } - - uint64_t packets_read = result.return_value_; - ENVOY_LOG_MISC(trace, "recvmmsg read {} packets", packets_read); - for (uint64_t i = 0; i < packets_read; ++i) { - if (output.msg_[i].truncated_and_dropped_) { - continue; - } - - Buffer::RawSlice* slice = slices[i].data(); - const uint64_t msg_len = output.msg_[i].msg_len_; - ASSERT(msg_len <= slice->len_); - ENVOY_LOG_MISC(debug, "Receive a packet with {} bytes from {}", msg_len, - output.msg_[i].peer_address_->asString()); + Buffer::RawSlice* slice = slices[i].data(); + const uint64_t msg_len = output.msg_[i].msg_len_; + ASSERT(msg_len <= slice->len_); + ENVOY_LOG_MISC(debug, "Receive a packet with {} bytes from {}", msg_len, + output.msg_[i].peer_address_->asString()); - buffers[i].reservation_.commit(std::min(max_rx_datagram_size, msg_len)); + buffers[i].reservation_.commit(std::min(max_rx_datagram_size, msg_len)); - passPayloadToProcessor(msg_len, std::move(buffers[i].buffer_), output.msg_[i].peer_address_, - output.msg_[i].local_address_, udp_packet_processor, receive_time); - } - return result; + passPayloadToProcessor(msg_len, std::move(buffers[i].buffer_), output.msg_[i].peer_address_, + output.msg_[i].local_address_, udp_packet_processor, receive_time); } + return result; +} +Api::IoCallUint64Result readFromSocketRecvMsg(IoHandle& handle, + const Address::Instance& local_address, + UdpPacketProcessor& udp_packet_processor, + MonotonicTime receive_time, + uint32_t* packets_dropped) { Buffer::InstancePtr buffer = std::make_unique(); IoHandle::RecvMsgOutput output(1, packets_dropped); @@ -717,27 +732,60 @@ Api::IoCallUint64Result Utility::readFromSocket(IoHandle& handle, return result; } +} // namespace + +Api::IoCallUint64Result +Utility::readFromSocket(IoHandle& handle, const Address::Instance& local_address, + UdpPacketProcessor& udp_packet_processor, MonotonicTime receive_time, + UdpRecvMsgMethod recv_msg_method, uint32_t* packets_dropped) { + if (recv_msg_method == UdpRecvMsgMethod::RecvMsgWithGro) { + return readFromSocketRecvGro(handle, local_address, udp_packet_processor, receive_time, + packets_dropped); + } else if (recv_msg_method == UdpRecvMsgMethod::RecvMmsg) { + return readFromSocketRecvMmsg(handle, local_address, udp_packet_processor, receive_time, + packets_dropped); + } + return readFromSocketRecvMsg(handle, local_address, udp_packet_processor, receive_time, + packets_dropped); +} + Api::IoErrorPtr Utility::readPacketsFromSocket(IoHandle& handle, const Address::Instance& local_address, UdpPacketProcessor& udp_packet_processor, - TimeSource& time_source, bool prefer_gro, - uint32_t& packets_dropped) { + TimeSource& time_source, bool allow_gro, + bool allow_mmsg, uint32_t& packets_dropped) { + UdpRecvMsgMethod recv_msg_method = UdpRecvMsgMethod::RecvMsg; + if (allow_gro && handle.supportsUdpGro()) { + recv_msg_method = UdpRecvMsgMethod::RecvMsgWithGro; + } else if (allow_mmsg && handle.supportsMmsg()) { + recv_msg_method = UdpRecvMsgMethod::RecvMmsg; + } + // Read at least one time, and attempt to read numPacketsExpectedPerEventLoop() packets unless // this goes over MAX_NUM_PACKETS_PER_EVENT_LOOP. size_t num_packets_to_read = std::min( MAX_NUM_PACKETS_PER_EVENT_LOOP, udp_packet_processor.numPacketsExpectedPerEventLoop()); - const bool use_gro = prefer_gro && handle.supportsUdpGro(); - size_t num_reads = - use_gro ? (num_packets_to_read / NUM_DATAGRAMS_PER_RECEIVE) - : (handle.supportsMmsg() ? (num_packets_to_read / NUM_DATAGRAMS_PER_RECEIVE) - : num_packets_to_read); + size_t num_reads; + switch (recv_msg_method) { + case UdpRecvMsgMethod::RecvMsgWithGro: + num_reads = (num_packets_to_read / NUM_DATAGRAMS_PER_RECEIVE); + break; + case UdpRecvMsgMethod::RecvMmsg: + num_reads = (num_packets_to_read / NUM_DATAGRAMS_PER_RECEIVE); + break; + case UdpRecvMsgMethod::RecvMsg: + num_reads = num_packets_to_read; + break; + } // Make sure to read at least once. num_reads = std::max(1, num_reads); + do { const uint32_t old_packets_dropped = packets_dropped; const MonotonicTime receive_time = time_source.monotonicTime(); - Api::IoCallUint64Result result = Utility::readFromSocket( - handle, local_address, udp_packet_processor, receive_time, use_gro, &packets_dropped); + Api::IoCallUint64Result result = + Utility::readFromSocket(handle, local_address, udp_packet_processor, receive_time, + recv_msg_method, &packets_dropped); if (!result.ok()) { // No more to read or encountered a system error. diff --git a/source/common/network/utility.h b/source/common/network/utility.h index 1d2352549191..14aa8fe9b28b 100644 --- a/source/common/network/utility.h +++ b/source/common/network/utility.h @@ -85,6 +85,17 @@ struct ResolvedUdpSocketConfig { bool prefer_gro_; }; +// The different options for receiving UDP packet(s) from system calls. +enum class UdpRecvMsgMethod { + // The `recvmsg` system call. + RecvMsg, + // The `recvmsg` system call using GRO (generic receive offload). This is the preferred method, + // if the platform supports it. + RecvMsgWithGro, + // The `recvmmsg` system call. + RecvMmsg, +}; + /** * Common network utility routines. */ @@ -352,15 +363,15 @@ class Utility { * @param udp_packet_processor is the callback to receive the packet. * @param receive_time is the timestamp passed to udp_packet_processor for the * receive time of the packet. - * @param prefer_gro supplies whether to use GRO if the OS supports it. + * @param recv_msg_method the type of system call and socket options combination to use when + * receiving packets from the kernel. * @param packets_dropped is the output parameter for number of packets dropped in kernel. If the * caller is not interested in it, nullptr can be passed in. */ - static Api::IoCallUint64Result readFromSocket(IoHandle& handle, - const Address::Instance& local_address, - UdpPacketProcessor& udp_packet_processor, - MonotonicTime receive_time, bool use_gro, - uint32_t* packets_dropped); + static Api::IoCallUint64Result + readFromSocket(IoHandle& handle, const Address::Instance& local_address, + UdpPacketProcessor& udp_packet_processor, MonotonicTime receive_time, + UdpRecvMsgMethod recv_msg_method, uint32_t* packets_dropped); /** * Read some packets from a given UDP socket and pass the packet to a given @@ -369,7 +380,11 @@ class Utility { * @param local_address is the socket's local address used to populate port. * @param udp_packet_processor is the callback to receive the packets. * @param time_source is the time source used to generate the time stamp of the received packets. - * @param prefer_gro supplies whether to use GRO if the OS supports it. + * @param allow_gro whether to use GRO, iff the platform supports it. This function will check + * the IoHandle to ensure the platform supports GRO before using it. + * @param allow_mmsg whether to use recvmmsg, iff the platform supports it. This function will + * check the IoHandle to ensure the platform supports recvmmsg before using it. If `allow_gro` is + * true and the platform supports GRO, then it will take precedence over using recvmmsg. * @param packets_dropped is the output parameter for number of packets dropped in kernel. * Return the io error encountered or nullptr if no io error but read stopped * because of MAX_NUM_PACKETS_PER_EVENT_LOOP. @@ -384,8 +399,8 @@ class Utility { static Api::IoErrorPtr readPacketsFromSocket(IoHandle& handle, const Address::Instance& local_address, UdpPacketProcessor& udp_packet_processor, - TimeSource& time_source, bool prefer_gro, - uint32_t& packets_dropped); + TimeSource& time_source, bool allow_gro, + bool allow_mmsg, uint32_t& packets_dropped); private: static void throwWithMalformedIp(absl::string_view ip_address); diff --git a/source/common/quic/BUILD b/source/common/quic/BUILD index 2b9890ed086f..7aafa7be99f8 100644 --- a/source/common/quic/BUILD +++ b/source/common/quic/BUILD @@ -180,6 +180,7 @@ envoy_cc_library( "//envoy/http:codec_interface", "//envoy/http:persistent_quic_info_interface", "//envoy/registry", + "//source/common/runtime:runtime_lib", "//source/common/tls:ssl_socket_lib", "//source/extensions/quic/crypto_stream:envoy_quic_crypto_client_stream_lib", "@com_github_google_quiche//:quic_core_http_spdy_session_lib", @@ -362,6 +363,7 @@ envoy_cc_library( "//envoy/event:dispatcher_interface", "//source/common/network:socket_option_factory_lib", "//source/common/network:udp_packet_writer_handler_lib", + "//source/common/runtime:runtime_lib", "@com_github_google_quiche//:quic_core_connection_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], diff --git a/source/common/quic/client_connection_factory_impl.cc b/source/common/quic/client_connection_factory_impl.cc index 3317e11463bc..08d7ce0fa05c 100644 --- a/source/common/quic/client_connection_factory_impl.cc +++ b/source/common/quic/client_connection_factory_impl.cc @@ -1,5 +1,7 @@ #include "source/common/quic/client_connection_factory_impl.h" +#include "source/common/runtime/runtime_features.h" + namespace Envoy { namespace Quic { @@ -45,7 +47,8 @@ std::unique_ptr createQuicNetworkConnection( ASSERT(!quic_versions.empty()); auto connection = std::make_unique( quic::QuicUtils::CreateRandomConnectionId(), server_addr, info_impl->conn_helper_, - info_impl->alarm_factory_, quic_versions, local_addr, dispatcher, options, generator); + info_impl->alarm_factory_, quic_versions, local_addr, dispatcher, options, generator, + Runtime::runtimeFeatureEnabled("envoy.reloadable_features.prefer_quic_client_udp_gro")); // TODO (danzh) move this temporary config and initial RTT configuration to h3 pool. quic::QuicConfig config = info_impl->quic_config_; diff --git a/source/common/quic/envoy_quic_client_connection.cc b/source/common/quic/envoy_quic_client_connection.cc index aa17972506f5..7b379f05ca84 100644 --- a/source/common/quic/envoy_quic_client_connection.cc +++ b/source/common/quic/envoy_quic_client_connection.cc @@ -7,6 +7,7 @@ #include "source/common/network/socket_option_factory.h" #include "source/common/network/udp_packet_writer_handler_impl.h" #include "source/common/quic/envoy_quic_utils.h" +#include "source/common/runtime/runtime_features.h" namespace Envoy { namespace Quic { @@ -30,35 +31,38 @@ EnvoyQuicClientConnection::EnvoyQuicClientConnection( const quic::ParsedQuicVersionVector& supported_versions, Network::Address::InstanceConstSharedPtr local_addr, Event::Dispatcher& dispatcher, const Network::ConnectionSocket::OptionsSharedPtr& options, - quic::ConnectionIdGeneratorInterface& generator) + quic::ConnectionIdGeneratorInterface& generator, const bool prefer_gro) : EnvoyQuicClientConnection( server_connection_id, helper, alarm_factory, supported_versions, dispatcher, - createConnectionSocket(initial_peer_address, local_addr, options), generator) {} + createConnectionSocket(initial_peer_address, local_addr, options, prefer_gro), generator, + prefer_gro) {} EnvoyQuicClientConnection::EnvoyQuicClientConnection( const quic::QuicConnectionId& server_connection_id, quic::QuicConnectionHelperInterface& helper, quic::QuicAlarmFactory& alarm_factory, const quic::ParsedQuicVersionVector& supported_versions, Event::Dispatcher& dispatcher, Network::ConnectionSocketPtr&& connection_socket, - quic::ConnectionIdGeneratorInterface& generator) + quic::ConnectionIdGeneratorInterface& generator, const bool prefer_gro) : EnvoyQuicClientConnection( server_connection_id, helper, alarm_factory, new EnvoyQuicPacketWriter( std::make_unique(connection_socket->ioHandle())), /*owns_writer=*/true, supported_versions, dispatcher, std::move(connection_socket), - generator) {} + generator, prefer_gro) {} EnvoyQuicClientConnection::EnvoyQuicClientConnection( const quic::QuicConnectionId& server_connection_id, quic::QuicConnectionHelperInterface& helper, quic::QuicAlarmFactory& alarm_factory, quic::QuicPacketWriter* writer, bool owns_writer, const quic::ParsedQuicVersionVector& supported_versions, Event::Dispatcher& dispatcher, Network::ConnectionSocketPtr&& connection_socket, - quic::ConnectionIdGeneratorInterface& generator) + quic::ConnectionIdGeneratorInterface& generator, const bool prefer_gro) : quic::QuicConnection(server_connection_id, quic::QuicSocketAddress(), envoyIpAddressToQuicSocketAddress( connection_socket->connectionInfoProvider().remoteAddress()->ip()), &helper, &alarm_factory, writer, owns_writer, quic::Perspective::IS_CLIENT, supported_versions, generator), - QuicNetworkConnection(std::move(connection_socket)), dispatcher_(dispatcher) {} + QuicNetworkConnection(std::move(connection_socket)), dispatcher_(dispatcher), + prefer_gro_(prefer_gro), disallow_mmsg_(Runtime::runtimeFeatureEnabled( + "envoy.reloadable_features.disallow_quic_client_udp_mmsg")) {} void EnvoyQuicClientConnection::processPacket( Network::Address::InstanceConstSharedPtr local_address, @@ -81,7 +85,7 @@ void EnvoyQuicClientConnection::processPacket( (peer_address->ip()->version() == Network::Address::IpVersion::v4 ? "v4" : "v6"), (HasPendingPathValidation() ? "" : " no"), (self_address().IsInitialized() ? "" : " not")); - ENVOY_CONN_LOG(error, error_message, *this); + ENVOY_CONN_LOG(error, "{}", *this, error_message); if (num_packets_with_unknown_dst_address_ > 10) { // If too many packets are without destination addresses, close the connection. CloseConnection(quic::QUIC_PACKET_READ_ERROR, error_message, @@ -175,7 +179,7 @@ void EnvoyQuicClientConnection::probeWithNewPort(const quic::QuicSocketAddress& // The probing socket will have the same host but a different port. auto probing_socket = createConnectionSocket(connectionSocket()->connectionInfoProvider().remoteAddress(), - new_local_address, connectionSocket()->options()); + new_local_address, connectionSocket()->options(), prefer_gro_); setUpConnectionSocket(*probing_socket, delegate_); auto writer = std::make_unique( std::make_unique(probing_socket->ioHandle())); @@ -249,7 +253,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(), /*prefer_gro=*/false, packets_dropped_); + *this, dispatcher_.timeSource(), prefer_gro_, !disallow_mmsg_, 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. diff --git a/source/common/quic/envoy_quic_client_connection.h b/source/common/quic/envoy_quic_client_connection.h index 43cbc0d12901..b2faee057fcb 100644 --- a/source/common/quic/envoy_quic_client_connection.h +++ b/source/common/quic/envoy_quic_client_connection.h @@ -60,7 +60,7 @@ class EnvoyQuicClientConnection : public quic::QuicConnection, Network::Address::InstanceConstSharedPtr local_addr, Event::Dispatcher& dispatcher, const Network::ConnectionSocket::OptionsSharedPtr& options, - quic::ConnectionIdGeneratorInterface& generator); + quic::ConnectionIdGeneratorInterface& generator, bool prefer_gro); EnvoyQuicClientConnection(const quic::QuicConnectionId& server_connection_id, quic::QuicConnectionHelperInterface& helper, @@ -69,7 +69,7 @@ class EnvoyQuicClientConnection : public quic::QuicConnection, const quic::ParsedQuicVersionVector& supported_versions, Event::Dispatcher& dispatcher, Network::ConnectionSocketPtr&& connection_socket, - quic::ConnectionIdGeneratorInterface& generator); + quic::ConnectionIdGeneratorInterface& generator, bool prefer_gro); // Network::UdpPacketProcessor void processPacket(Network::Address::InstanceConstSharedPtr local_address, @@ -135,7 +135,7 @@ class EnvoyQuicClientConnection : public quic::QuicConnection, const quic::ParsedQuicVersionVector& supported_versions, Event::Dispatcher& dispatcher, Network::ConnectionSocketPtr&& connection_socket, - quic::ConnectionIdGeneratorInterface& generator); + quic::ConnectionIdGeneratorInterface& generator, bool prefer_gro); void onFileEvent(uint32_t events, Network::ConnectionSocket& connection_socket); @@ -150,6 +150,8 @@ class EnvoyQuicClientConnection : public quic::QuicConnection, bool migrate_port_on_path_degrading_{false}; uint8_t num_socket_switches_{0}; size_t num_packets_with_unknown_dst_address_{0}; + const bool prefer_gro_; + const bool disallow_mmsg_; }; } // namespace Quic diff --git a/source/common/quic/envoy_quic_client_stream.cc b/source/common/quic/envoy_quic_client_stream.cc index 18e02bb0bdc3..1e659ac61e4c 100644 --- a/source/common/quic/envoy_quic_client_stream.cc +++ b/source/common/quic/envoy_quic_client_stream.cc @@ -23,7 +23,7 @@ EnvoyQuicClientStream::EnvoyQuicClientStream( const envoy::config::core::v3::Http3ProtocolOptions& http3_options) : quic::QuicSpdyClientStream(id, client_session, type), EnvoyQuicStream( - *this, + *this, *client_session, // Flow control receive window should be larger than 8k so that the send buffer can fully // utilize congestion control window before it reaches the high watermark. static_cast(GetReceiveWindow().value()), *filterManagerConnection(), @@ -31,6 +31,7 @@ EnvoyQuicClientStream::EnvoyQuicClientStream( stats, http3_options) { ASSERT(static_cast(GetReceiveWindow().value()) > 8 * 1024, "Send buffer limit should be larger than 8KB."); + RegisterMetadataVisitor(this); } Http::Status EnvoyQuicClientStream::encodeHeaders(const Http::RequestHeaderMap& headers, @@ -411,6 +412,17 @@ QuicFilterManagerConnectionImpl* EnvoyQuicClientStream::filterManagerConnection( return dynamic_cast(session()); } +void EnvoyQuicClientStream::OnMetadataComplete(size_t /*frame_len*/, + const quic::QuicHeaderList& header_list) { + if (mustRejectMetadata(header_list.uncompressed_header_bytes())) { + onStreamError(true, quic::QUIC_HEADERS_TOO_LARGE); + return; + } + if (!header_list.empty()) { + response_decoder_->decodeMetadata(metadataMapFromHeaderList(header_list)); + } +} + void EnvoyQuicClientStream::onStreamError(absl::optional should_close_connection, quic::QuicRstStreamErrorCode rst_code) { if (details_.empty()) { diff --git a/source/common/quic/envoy_quic_client_stream.h b/source/common/quic/envoy_quic_client_stream.h index 98607867ea5b..2cb1c5105909 100644 --- a/source/common/quic/envoy_quic_client_stream.h +++ b/source/common/quic/envoy_quic_client_stream.h @@ -9,6 +9,8 @@ #endif #include "quiche/common/simple_buffer_allocator.h" #include "quiche/quic/core/http/quic_spdy_client_stream.h" +#include "quiche/quic/core/qpack/qpack_encoder.h" +#include "quiche/quic/core/qpack/qpack_instruction_encoder.h" namespace Envoy { namespace Quic { @@ -16,7 +18,8 @@ namespace Quic { // This class is a quic stream and also a request encoder. class EnvoyQuicClientStream : public quic::QuicSpdyClientStream, public EnvoyQuicStream, - public Http::RequestEncoder { + public Http::RequestEncoder, + public quic::QuicSpdyStream::MetadataVisitor { public: EnvoyQuicClientStream(quic::QuicStreamId id, quic::QuicSpdyClientSession* client_session, quic::StreamType type, Http::Http3::CodecStats& stats, @@ -52,6 +55,9 @@ class EnvoyQuicClientStream : public quic::QuicSpdyClientStream, void clearWatermarkBuffer(); + // quic::QuicSpdyStream::MetadataVisitor + void OnMetadataComplete(size_t frame_len, const quic::QuicHeaderList& header_list) override; + protected: // EnvoyQuicStream void switchStreamBlockState() override; diff --git a/source/common/quic/envoy_quic_server_stream.cc b/source/common/quic/envoy_quic_server_stream.cc index 1932010257f7..94a82cce10d7 100644 --- a/source/common/quic/envoy_quic_server_stream.cc +++ b/source/common/quic/envoy_quic_server_stream.cc @@ -30,7 +30,7 @@ EnvoyQuicServerStream::EnvoyQuicServerStream( headers_with_underscores_action) : quic::QuicSpdyServerStreamBase(id, session, type), EnvoyQuicStream( - *this, + *this, *session, // Flow control receive window should be larger than 8k to fully utilize congestion // control window before it reaches the high watermark. static_cast(GetReceiveWindow().value()), *filterManagerConnection(), @@ -42,6 +42,7 @@ EnvoyQuicServerStream::EnvoyQuicServerStream( stats_gatherer_ = new QuicStatsGatherer(&filterManagerConnection()->dispatcher().timeSource()); set_ack_listener(stats_gatherer_); + RegisterMetadataVisitor(this); } void EnvoyQuicServerStream::encode1xxHeaders(const Http::ResponseHeaderMap& headers) { @@ -449,6 +450,17 @@ EnvoyQuicServerStream::validateHeader(absl::string_view header_name, return result; } +void EnvoyQuicServerStream::OnMetadataComplete(size_t /*frame_len*/, + const quic::QuicHeaderList& header_list) { + if (mustRejectMetadata(header_list.uncompressed_header_bytes())) { + onStreamError(true, quic::QUIC_HEADERS_TOO_LARGE); + return; + } + if (!header_list.empty()) { + request_decoder_->decodeMetadata(metadataMapFromHeaderList(header_list)); + } +} + void EnvoyQuicServerStream::onStreamError(absl::optional should_close_connection, quic::QuicRstStreamErrorCode rst) { if (details_.empty()) { diff --git a/source/common/quic/envoy_quic_server_stream.h b/source/common/quic/envoy_quic_server_stream.h index bb9c32003b07..9153217efcb1 100644 --- a/source/common/quic/envoy_quic_server_stream.h +++ b/source/common/quic/envoy_quic_server_stream.h @@ -8,6 +8,8 @@ #include "quiche/common/platform/api/quiche_reference_counted.h" #include "quiche/quic/core/http/quic_spdy_server_stream_base.h" +#include "quiche/quic/core/qpack/qpack_encoder.h" +#include "quiche/quic/core/qpack/qpack_instruction_encoder.h" namespace Envoy { namespace Quic { @@ -15,7 +17,8 @@ namespace Quic { // This class is a quic stream and also a response encoder. class EnvoyQuicServerStream : public quic::QuicSpdyServerStreamBase, public EnvoyQuicStream, - public Http::ResponseEncoder { + public Http::ResponseEncoder, + public quic::QuicSpdyStream::MetadataVisitor { public: EnvoyQuicServerStream(quic::QuicStreamId id, quic::QuicSpdySession* session, quic::StreamType type, Http::Http3::CodecStats& stats, @@ -78,6 +81,9 @@ class EnvoyQuicServerStream : public quic::QuicSpdyServerStreamBase, Http::HeaderUtility::HeaderValidationResult validateHeader(absl::string_view header_name, absl::string_view header_value) override; + // quic::QuicSpdyStream::MetadataVisitor + void OnMetadataComplete(size_t frame_len, const quic::QuicHeaderList& header_list) override; + protected: // EnvoyQuicStream void switchStreamBlockState() override; diff --git a/source/common/quic/envoy_quic_stream.cc b/source/common/quic/envoy_quic_stream.cc index 8ca1e7950466..280c7ab91513 100644 --- a/source/common/quic/envoy_quic_stream.cc +++ b/source/common/quic/envoy_quic_stream.cc @@ -102,10 +102,87 @@ void EnvoyQuicStream::encodeTrailersImpl(spdy::Http2HeaderBlock&& trailers) { onLocalEndStream(); } -void EnvoyQuicStream::encodeMetadata(const Http::MetadataMapVector& /*metadata_map_vector*/) { - // Metadata Frame is not supported in QUICHE. - ENVOY_STREAM_LOG(debug, "METADATA is not supported in Http3.", *this); - stats_.metadata_not_supported_error_.inc(); +std::unique_ptr +EnvoyQuicStream::metadataMapFromHeaderList(const quic::QuicHeaderList& header_list) { + auto metadata_map = std::make_unique(); + for (const auto& [key, value] : header_list) { + (*metadata_map)[key] = value; + } + return metadata_map; +} + +namespace { + +// Returns a new `unique_ptr` containing the characters copied from `str`. +std::unique_ptr dataFromString(const std::string& str) { + auto data = std::make_unique(str.length()); + memcpy(&data[0], str.data(), str.length()); // NOLINT(safe-memcpy) + return data; +} + +void serializeMetadata(const Http::MetadataMapPtr& metadata, quic::QuicStreamId id, + absl::InlinedVector& slices) { + quic::NoopDecoderStreamErrorDelegate decoder_stream_error_delegate; + quic::QpackEncoder qpack_encoder(&decoder_stream_error_delegate, + quic::HuffmanEncoding::kDisabled); + + spdy::Http2HeaderBlock header_block; + for (const auto& [key, value] : *metadata) { + header_block.AppendValueOrAddHeader(key, value); + } + + // The METADATA frame consist of a frame header, which includes payload + // length, and a payload, which is the QPACK-encoded metadata block. In order + // to generate the frame header, the payload needs to be generated first. + std::string metadata_frame_payload = + qpack_encoder.EncodeHeaderList(id, header_block, + /* encoder_stream_sent_byte_count = */ nullptr); + std::string metadata_frame_header = + quic::HttpEncoder::SerializeMetadataFrameHeader(metadata_frame_payload.size()); + + slices.emplace_back(dataFromString(metadata_frame_header), metadata_frame_header.length()); + slices.emplace_back(dataFromString(metadata_frame_payload), metadata_frame_payload.length()); +} + +} // namespace + +void EnvoyQuicStream::encodeMetadata(const Http::MetadataMapVector& metadata_map_vector) { + if (!http3_options_.allow_metadata()) { + ENVOY_STREAM_LOG(debug, "METADATA not supported by config.", *this); + stats_.metadata_not_supported_error_.inc(); + return; + } + if (quic_stream_.write_side_closed()) { + return; + } + ASSERT(!local_end_stream_); + + for (const Http::MetadataMapPtr& metadata : metadata_map_vector) { + absl::InlinedVector quic_slices; + quic_slices.reserve(2); + serializeMetadata(metadata, quic_stream_.id(), quic_slices); + absl::Span metadata_frame(quic_slices); + + SendBufferMonitor::ScopedWatermarkBufferUpdater updater(&quic_stream_, this); + quic::QuicConsumedData result{0, false}; + { + IncrementalBytesSentTracker tracker(quic_stream_, *mutableBytesMeter(), false); + result = quic_stream_.WriteMemSlices(metadata_frame, /*end_stream=*/false); + } + // QUIC stream must take all. + if (result.bytes_consumed == 0) { + IS_ENVOY_BUG(fmt::format("Send buffer didn't take all the data. Stream is write {} with {} " + "bytes in send buffer. Current write was rejected.", + quic_stream_.write_side_closed() ? "closed" : "open", + quic_stream_.BufferedDataBytes())); + quic_stream_.Reset(quic::QUIC_ERROR_PROCESSING_STREAM); + return; + } + if (!quic_session_.connection()->connected()) { + // Return early if sending METADATA caused the connection to close. + return; + } + } } } // namespace Quic diff --git a/source/common/quic/envoy_quic_stream.h b/source/common/quic/envoy_quic_stream.h index 360788d2fdcd..bbe4759339ae 100644 --- a/source/common/quic/envoy_quic_stream.h +++ b/source/common/quic/envoy_quic_stream.h @@ -33,13 +33,13 @@ class EnvoyQuicStream : public virtual Http::StreamEncoder, public: // |buffer_limit| is the high watermark of the stream send buffer, and the low // watermark will be half of it. - EnvoyQuicStream(quic::QuicSpdyStream& quic_stream, uint32_t buffer_limit, - QuicFilterManagerConnectionImpl& filter_manager_connection, + EnvoyQuicStream(quic::QuicSpdyStream& quic_stream, quic::QuicSession& quic_session, + uint32_t buffer_limit, QuicFilterManagerConnectionImpl& filter_manager_connection, std::function below_low_watermark, std::function above_high_watermark, Http::Http3::CodecStats& stats, const envoy::config::core::v3::Http3ProtocolOptions& http3_options) : Http::MultiplexedStreamImplBase(filter_manager_connection.dispatcher()), stats_(stats), - http3_options_(http3_options), quic_stream_(quic_stream), + http3_options_(http3_options), quic_stream_(quic_stream), quic_session_(quic_session), send_buffer_simulation_(buffer_limit / 2, buffer_limit, std::move(below_low_watermark), std::move(above_high_watermark), ENVOY_LOGGER()), filter_manager_connection_(filter_manager_connection), @@ -180,6 +180,17 @@ class EnvoyQuicStream : public virtual Http::StreamEncoder, void encodeTrailersImpl(spdy::Http2HeaderBlock&& trailers); + // Converts `header_list` into a new `Http::MetadataMap`. + std::unique_ptr + metadataMapFromHeaderList(const quic::QuicHeaderList& header_list); + + // Returns true if the cumulative limit on METADATA headers has been reached + // after adding `bytes`. + bool mustRejectMetadata(size_t bytes) { + received_metadata_bytes_ += bytes; + return received_metadata_bytes_ > 1 << 20; + } + #ifdef ENVOY_ENABLE_HTTP_DATAGRAMS // Setting |http_datagram_handler_| enables HTTP Datagram support. std::unique_ptr http_datagram_handler_; @@ -210,8 +221,9 @@ class EnvoyQuicStream : public virtual Http::StreamEncoder, bool saw_regular_headers_{false}; private: - // QUIC stream that this EnvoyQuicStream wraps. + // QUIC stream and session that this EnvoyQuicStream wraps. quic::QuicSpdyStream& quic_stream_; + quic::QuicSession& quic_session_; // Keeps track of bytes buffered in the stream send buffer in QUICHE and reacts // upon crossing high and low watermarks. @@ -232,6 +244,7 @@ class EnvoyQuicStream : public virtual Http::StreamEncoder, absl::optional content_length_; size_t received_content_bytes_{0}; http2::adapter::HeaderValidator header_validator_; + size_t received_metadata_bytes_{0}; }; // Object used for updating a BytesMeter to track bytes sent on a QuicStream since this object was diff --git a/source/common/quic/envoy_quic_utils.cc b/source/common/quic/envoy_quic_utils.cc index 1d4b0bc68aa6..2d111361c45c 100644 --- a/source/common/quic/envoy_quic_utils.cc +++ b/source/common/quic/envoy_quic_utils.cc @@ -5,6 +5,7 @@ #include "envoy/common/platform.h" #include "envoy/config/core/v3/base.pb.h" +#include "source/common/api/os_sys_calls_impl.h" #include "source/common/http/utility.h" #include "source/common/network/socket_option_factory.h" #include "source/common/network/utility.h" @@ -137,7 +138,8 @@ Http::StreamResetReason quicErrorCodeToEnvoyRemoteResetReason(quic::QuicErrorCod Network::ConnectionSocketPtr createConnectionSocket(const Network::Address::InstanceConstSharedPtr& peer_addr, Network::Address::InstanceConstSharedPtr& local_addr, - const Network::ConnectionSocket::OptionsSharedPtr& options) { + const Network::ConnectionSocket::OptionsSharedPtr& options, + const bool prefer_gro) { if (local_addr == nullptr) { local_addr = Network::Utility::getLocalAddress(peer_addr->ip()->version()); } @@ -149,6 +151,9 @@ createConnectionSocket(const Network::Address::InstanceConstSharedPtr& peer_addr } connection_socket->addOptions(Network::SocketOptionFactory::buildIpPacketInfoOptions()); connection_socket->addOptions(Network::SocketOptionFactory::buildRxQueueOverFlowOptions()); + if (prefer_gro && Api::OsSysCallsSingleton::get().supportsUdpGro()) { + connection_socket->addOptions(Network::SocketOptionFactory::buildUdpGroOptions()); + } if (options != nullptr) { connection_socket->addOptions(options); } diff --git a/source/common/quic/envoy_quic_utils.h b/source/common/quic/envoy_quic_utils.h index ae63d9a78297..1dc6991ca3d3 100644 --- a/source/common/quic/envoy_quic_utils.h +++ b/source/common/quic/envoy_quic_utils.h @@ -161,7 +161,8 @@ Http::StreamResetReason quicErrorCodeToEnvoyRemoteResetReason(quic::QuicErrorCod Network::ConnectionSocketPtr createConnectionSocket(const Network::Address::InstanceConstSharedPtr& peer_addr, Network::Address::InstanceConstSharedPtr& local_addr, - const Network::ConnectionSocket::OptionsSharedPtr& options); + const Network::ConnectionSocket::OptionsSharedPtr& options, + bool prefer_gro = false); // Convert a cert in string form to X509 object. // Return nullptr if the bytes passed cannot be passed. diff --git a/source/common/quic/platform/quiche_flags_constants.h b/source/common/quic/platform/quiche_flags_constants.h index cff579bad68c..a38bd99a0fc6 100644 --- a/source/common/quic/platform/quiche_flags_constants.h +++ b/source/common/quic/platform/quiche_flags_constants.h @@ -17,6 +17,8 @@ /* Envoy only supports RFC-v1 in the long term, so disable IETF draft 29 implementation by \ * default. */ \ KEY_VALUE_PAIR(quic_disable_version_draft_29, true) \ + /* Enable support for HTTP/3 metadata decoding in QUICHE. */ \ + KEY_VALUE_PAIR(quic_enable_http3_metadata_decoding, true) \ /* This flag enables BBR, otherwise QUIC will use Cubic which is less performant */ \ KEY_VALUE_PAIR(quic_default_to_bbr, true) diff --git a/source/common/quic/platform/quiche_flags_impl.cc b/source/common/quic/platform/quiche_flags_impl.cc index f676005f91b7..0f8d64fcfce9 100644 --- a/source/common/quic/platform/quiche_flags_impl.cc +++ b/source/common/quic/platform/quiche_flags_impl.cc @@ -117,7 +117,7 @@ FlagRegistry::FlagRegistry() : reloadable_flags_(makeReloadableFlagMap()) {} FlagRegistry& FlagRegistry::getInstance() { static auto* instance = new FlagRegistry(); ASSERT(sizeof(quiche_reloadable_flag_overrides) / sizeof(std::pair) == - 2); + 3); ASSERT(sizeof(quiche_protocol_flag_overrides) / sizeof(std::pair>) == 3); diff --git a/source/common/quic/platform/quiche_flags_impl.h b/source/common/quic/platform/quiche_flags_impl.h index 77c31aea9432..74d9570f4f4a 100644 --- a/source/common/quic/platform/quiche_flags_impl.h +++ b/source/common/quic/platform/quiche_flags_impl.h @@ -57,15 +57,14 @@ namespace quiche { #define SetQuicheFlagImpl(flag, value) absl::SetFlag(&FLAGS_envoy_##flag, value) -#define GetQuicheReloadableFlagImpl(module, flag) \ - absl::GetFlag(FLAGS_envoy_quic_reloadable_flag_##flag) +#define GetQuicheReloadableFlagImpl(flag) absl::GetFlag(FLAGS_envoy_quic_reloadable_flag_##flag) -#define SetQuicheReloadableFlagImpl(module, flag, value) \ +#define SetQuicheReloadableFlagImpl(flag, value) \ absl::SetFlag(&FLAGS_envoy_quic_reloadable_flag_##flag, value) -#define GetQuicheRestartFlagImpl(module, flag) absl::GetFlag(FLAGS_envoy_quic_restart_flag_##flag) +#define GetQuicheRestartFlagImpl(flag) absl::GetFlag(FLAGS_envoy_quic_restart_flag_##flag) -#define SetQuicheRestartFlagImpl(module, flag, value) \ +#define SetQuicheRestartFlagImpl(flag, value) \ absl::SetFlag(&FLAGS_envoy_quic_restart_flag_##flag, value) } // namespace quiche diff --git a/source/common/quic/platform/quiche_stack_trace_impl.h b/source/common/quic/platform/quiche_stack_trace_impl.h index f7b802127185..e50f52f4471f 100644 --- a/source/common/quic/platform/quiche_stack_trace_impl.h +++ b/source/common/quic/platform/quiche_stack_trace_impl.h @@ -8,11 +8,44 @@ #include #include +#include #include "source/server/backtrace.h" +#include "absl/debugging/stacktrace.h" +#include "absl/debugging/symbolize.h" + namespace quiche { +// NOLINTNEXTLINE(readability-identifier-naming) +inline std::vector CurrentStackTraceImpl() { + constexpr int kMaxStackSize = 64; + std::vector stacktrace(kMaxStackSize, nullptr); + const int depth = absl::GetStackTrace(stacktrace.data(), stacktrace.size(), + /*skip_count=*/0); + if (depth <= 0) { + return {}; + } + stacktrace.resize(depth); + return stacktrace; +} + +// NOLINTNEXTLINE(readability-identifier-naming) +inline std::string SymbolizeStackTraceImpl(absl::Span stacktrace) { + std::ostringstream os; + for (size_t i = 0; i < stacktrace.size(); ++i) { + void* const addr = stacktrace[i]; + char out[1024]; + const bool success = absl::Symbolize(addr, out, sizeof(out)); + if (success) { + os << "#" << i << " " << out << " [" << addr << "]\n"; + } else { + os << "#" << i << " [" << addr << "]\n"; + } + } + return os.str(); +} + // NOLINTNEXTLINE(readability-identifier-naming) inline std::string QuicheStackTraceImpl() { Envoy::BackwardsTrace t; diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index 43dfaccb33e5..52b262a05e13 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -659,7 +659,14 @@ RouteEntryImplBase::RouteEntryImplBase(const CommonVirtualHostSharedPtr& vhost, (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.enable_connect_udp_support") && absl::EqualsIgnoreCase(upgrade_config.upgrade_type(), Http::Headers::get().UpgradeValues.ConnectUdp))) { - connect_config_ = std::make_unique(upgrade_config.connect_config()); + if (Runtime::runtimeFeatureEnabled( + "envoy.reloadable_features.http_route_connect_proxy_by_default")) { + if (upgrade_config.has_connect_config()) { + connect_config_ = std::make_unique(upgrade_config.connect_config()); + } + } else { + connect_config_ = std::make_unique(upgrade_config.connect_config()); + } } else if (upgrade_config.has_connect_config()) { throwEnvoyExceptionOrPanic(absl::StrCat("Non-CONNECT upgrade type ", upgrade_config.upgrade_type(), " has ConnectConfig")); diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 65cd8b6d80d1..a644d0929bfe 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -38,6 +38,7 @@ RUNTIME_GUARD(envoy_reloadable_features_copy_response_code_to_downstream_stream_ RUNTIME_GUARD(envoy_reloadable_features_defer_processing_backedup_streams); RUNTIME_GUARD(envoy_reloadable_features_detect_and_raise_rst_tcp_connection); RUNTIME_GUARD(envoy_reloadable_features_dfp_mixed_scheme); +RUNTIME_GUARD(envoy_reloadable_features_disallow_quic_client_udp_mmsg); RUNTIME_GUARD(envoy_reloadable_features_dns_cache_set_first_resolve_complete); RUNTIME_GUARD(envoy_reloadable_features_edf_lb_host_scheduler_init_fix); RUNTIME_GUARD(envoy_reloadable_features_edf_lb_locality_scheduler_init_fix); @@ -63,6 +64,7 @@ RUNTIME_GUARD(envoy_reloadable_features_http_allow_partial_urls_in_referer); RUNTIME_GUARD(envoy_reloadable_features_http_filter_avoid_reentrant_local_reply); // Delay deprecation and decommission until UHV is enabled. RUNTIME_GUARD(envoy_reloadable_features_http_reject_path_with_fragment); +RUNTIME_GUARD(envoy_reloadable_features_http_route_connect_proxy_by_default); RUNTIME_GUARD(envoy_reloadable_features_immediate_response_use_filter_mutation_rule); RUNTIME_GUARD(envoy_reloadable_features_locality_routing_use_new_routing_logic); RUNTIME_GUARD(envoy_reloadable_features_lowercase_scheme); @@ -74,6 +76,7 @@ RUNTIME_GUARD(envoy_reloadable_features_oauth_make_token_cookie_httponly); RUNTIME_GUARD(envoy_reloadable_features_oauth_use_standard_max_age_value); RUNTIME_GUARD(envoy_reloadable_features_oauth_use_url_encoding); RUNTIME_GUARD(envoy_reloadable_features_original_dst_rely_on_idle_timeout); +RUNTIME_GUARD(envoy_reloadable_features_prefer_quic_client_udp_gro); RUNTIME_GUARD(envoy_reloadable_features_proxy_status_mapping_more_core_response_flags); RUNTIME_GUARD(envoy_reloadable_features_proxy_status_upstream_request_timeout); RUNTIME_GUARD(envoy_reloadable_features_quic_fix_filter_manager_uaf); diff --git a/source/common/runtime/runtime_impl.cc b/source/common/runtime/runtime_impl.cc index 17511407813a..2dada0d1be29 100644 --- a/source/common/runtime/runtime_impl.cc +++ b/source/common/runtime/runtime_impl.cc @@ -399,7 +399,7 @@ SnapshotImpl::Entry SnapshotImpl::createEntry(const ProtobufWkt::Value& value, return entry; } -void AdminLayer::mergeValues(const absl::node_hash_map& values) { +absl::Status AdminLayer::mergeValues(const absl::node_hash_map& values) { #ifdef ENVOY_ENABLE_YAML for (const auto& kv : values) { values_.erase(kv.first); @@ -408,37 +408,38 @@ void AdminLayer::mergeValues(const absl::node_hash_map } } stats_.admin_overrides_active_.set(values_.empty() ? 0 : 1); + return absl::OkStatus(); #else - IS_ENVOY_BUG("Runtime admin reload requires YAML support"); UNREFERENCED_PARAMETER(values); - return; + return absl::InvalidArgumentError("Runtime admin reload requires YAML support"); #endif } -DiskLayer::DiskLayer(absl::string_view name, const std::string& path, Api::Api& api) +DiskLayer::DiskLayer(absl::string_view name, const std::string& path, Api::Api& api, + absl::Status& creation_status) : OverrideLayerImpl{name} { - walkDirectory(path, "", 1, api); + creation_status = walkDirectory(path, "", 1, api); } -void DiskLayer::walkDirectory(const std::string& path, const std::string& prefix, uint32_t depth, - Api::Api& api) { +absl::Status DiskLayer::walkDirectory(const std::string& path, const std::string& prefix, + uint32_t depth, Api::Api& api) { // Maximum recursion depth for walkDirectory(). static constexpr uint32_t MaxWalkDepth = 16; ENVOY_LOG(debug, "walking directory: {}", path); if (depth > MaxWalkDepth) { - throwEnvoyExceptionOrPanic(absl::StrCat("Walk recursion depth exceeded ", MaxWalkDepth)); + return absl::InvalidArgumentError(absl::StrCat("Walk recursion depth exceeded ", MaxWalkDepth)); } // Check if this is an obviously bad path. if (api.fileSystem().illegalPath(path)) { - throwEnvoyExceptionOrPanic(absl::StrCat("Invalid path: ", path)); + return absl::InvalidArgumentError(absl::StrCat("Invalid path: ", path)); } Filesystem::Directory directory(path); Filesystem::DirectoryIteratorImpl it = directory.begin(); - THROW_IF_NOT_OK_REF(it.status()); + RETURN_IF_STATUS_NOT_OK(it); for (; it != directory.end(); ++it) { - THROW_IF_NOT_OK_REF(it.status()); + RETURN_IF_STATUS_NOT_OK(it); Filesystem::DirectoryEntry entry = *it; std::string full_path = path + "/" + entry.name_; std::string full_prefix; @@ -450,7 +451,8 @@ void DiskLayer::walkDirectory(const std::string& path, const std::string& prefix if (entry.type_ == Filesystem::FileType::Directory && entry.name_ != "." && entry.name_ != "..") { - walkDirectory(full_path, full_prefix, depth + 1, api); + absl::Status status = walkDirectory(full_path, full_prefix, depth + 1, api); + RETURN_IF_NOT_OK(status); } else if (entry.type_ == Filesystem::FileType::Regular) { // Suck the file into a string. This is not very efficient but it should be good enough // for small files. Also, as noted elsewhere, none of this is non-blocking which could @@ -461,7 +463,7 @@ void DiskLayer::walkDirectory(const std::string& path, const std::string& prefix // Read the file and remove any comments. A comment is a line starting with a '#' character. // Comments are useful for placeholder files with no value. auto file_or_error = api.fileSystem().fileReadToEnd(full_path); - THROW_IF_STATUS_NOT_OK(file_or_error, throw); + RETURN_IF_STATUS_NOT_OK(file_or_error); const std::string text_file{file_or_error.value()}; const auto lines = StringUtil::splitToken(text_file, "\n"); @@ -484,26 +486,32 @@ void DiskLayer::walkDirectory(const std::string& path, const std::string& prefix #else IS_ENVOY_BUG("Runtime admin reload requires YAML support"); UNREFERENCED_PARAMETER(value); - return; + return absl::OkStatus(); #endif } } - THROW_IF_NOT_OK_REF(it.status()); + RETURN_IF_STATUS_NOT_OK(it); + return absl::OkStatus(); } -ProtoLayer::ProtoLayer(absl::string_view name, const ProtobufWkt::Struct& proto) +ProtoLayer::ProtoLayer(absl::string_view name, const ProtobufWkt::Struct& proto, + absl::Status& creation_status) : OverrideLayerImpl{name} { + creation_status = absl::OkStatus(); for (const auto& f : proto.fields()) { - walkProtoValue(f.second, f.first); + creation_status = walkProtoValue(f.second, f.first); + if (!creation_status.ok()) { + return; + } } } -void ProtoLayer::walkProtoValue(const ProtobufWkt::Value& v, const std::string& prefix) { +absl::Status ProtoLayer::walkProtoValue(const ProtobufWkt::Value& v, const std::string& prefix) { switch (v.kind_case()) { case ProtobufWkt::Value::KIND_NOT_SET: case ProtobufWkt::Value::kListValue: case ProtobufWkt::Value::kNullValue: - throwEnvoyExceptionOrPanic(absl::StrCat("Invalid runtime entry value for ", prefix)); + return absl::InvalidArgumentError(absl::StrCat("Invalid runtime entry value for ", prefix)); break; case ProtobufWkt::Value::kStringValue: SnapshotImpl::addEntry(values_, prefix, v, ""); @@ -525,26 +533,32 @@ void ProtoLayer::walkProtoValue(const ProtobufWkt::Value& v, const std::string& break; } for (const auto& f : s.fields()) { - walkProtoValue(f.second, prefix + "." + f.first); + absl::Status status = walkProtoValue(f.second, prefix + "." + f.first); + RETURN_IF_NOT_OK(status); } break; } } + return absl::OkStatus(); } LoaderImpl::LoaderImpl(Event::Dispatcher& dispatcher, ThreadLocal::SlotAllocator& tls, const envoy::config::bootstrap::v3::LayeredRuntime& config, const LocalInfo::LocalInfo& local_info, Stats::Store& store, Random::RandomGenerator& generator, - ProtobufMessage::ValidationVisitor& validation_visitor, Api::Api& api) + ProtobufMessage::ValidationVisitor& validation_visitor, Api::Api& api, + absl::Status& creation_status) : generator_(generator), stats_(generateStats(store)), tls_(tls.allocateSlot()), config_(config), service_cluster_(local_info.clusterName()), api_(api), init_watcher_("RTDS", [this]() { onRtdsReady(); }), store_(store) { + creation_status = absl::OkStatus(); absl::node_hash_set layer_names; for (const auto& layer : config_.layers()) { auto ret = layer_names.insert(layer.name()); if (!ret.second) { - throwEnvoyExceptionOrPanic(absl::StrCat("Duplicate layer name: ", layer.name())); + creation_status = + absl::InvalidArgumentError(absl::StrCat("Duplicate layer name: ", layer.name())); + return; } switch (layer.layer_specifier_case()) { case envoy::config::bootstrap::v3::RuntimeLayer::LayerSpecifierCase::kStaticLayer: @@ -552,8 +566,9 @@ LoaderImpl::LoaderImpl(Event::Dispatcher& dispatcher, ThreadLocal::SlotAllocator break; case envoy::config::bootstrap::v3::RuntimeLayer::LayerSpecifierCase::kAdminLayer: if (admin_layer_ != nullptr) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( "Too many admin layers specified in LayeredRuntime, at most one may be specified"); + return; } admin_layer_ = std::make_unique(layer.name(), stats_); break; @@ -561,8 +576,12 @@ LoaderImpl::LoaderImpl(Event::Dispatcher& dispatcher, ThreadLocal::SlotAllocator if (watcher_ == nullptr) { watcher_ = dispatcher.createFilesystemWatcher(); } - watcher_->addWatch(layer.disk_layer().symlink_root(), Filesystem::Watcher::Events::MovedTo, - [this](uint32_t) -> void { loadNewSnapshot(); }); + creation_status = watcher_->addWatch( + layer.disk_layer().symlink_root(), Filesystem::Watcher::Events::MovedTo, + [this](uint32_t) -> void { THROW_IF_NOT_OK(loadNewSnapshot()); }); + if (!creation_status.ok()) { + return; + } break; case envoy::config::bootstrap::v3::RuntimeLayer::LayerSpecifierCase::kRtdsLayer: subscriptions_.emplace_back( @@ -570,11 +589,12 @@ LoaderImpl::LoaderImpl(Event::Dispatcher& dispatcher, ThreadLocal::SlotAllocator init_manager_.add(subscriptions_.back()->init_target_); break; case envoy::config::bootstrap::v3::RuntimeLayer::LayerSpecifierCase::LAYER_SPECIFIER_NOT_SET: - throwEnvoyExceptionOrPanic("layer specifier not set"); + creation_status = absl::InvalidArgumentError("layer specifier not set"); + return; } } - loadNewSnapshot(); + creation_status = loadNewSnapshot(); } void LoaderImpl::initialize(Upstream::ClusterManager& cm) { @@ -626,7 +646,7 @@ RtdsSubscription::onConfigUpdate(const std::vector& } ENVOY_LOG(debug, "Reloading RTDS snapshot for onConfigUpdate"); proto_.CopyFrom(runtime.layer()); - parent_.loadNewSnapshot(); + RETURN_IF_NOT_OK(parent_.loadNewSnapshot()); init_target_.ready(); return absl::OkStatus(); } @@ -680,13 +700,15 @@ absl::Status RtdsSubscription::onConfigRemoved( } ENVOY_LOG(debug, "Clear RTDS snapshot for onConfigUpdate"); proto_.Clear(); - parent_.loadNewSnapshot(); + RETURN_IF_NOT_OK(parent_.loadNewSnapshot()); init_target_.ready(); return absl::OkStatus(); } -void LoaderImpl::loadNewSnapshot() { - std::shared_ptr ptr = createNewSnapshot(); +absl::Status LoaderImpl::loadNewSnapshot() { + auto snapshot_or_error = createNewSnapshot(); + RETURN_IF_STATUS_NOT_OK(snapshot_or_error); + std::shared_ptr ptr = std::move(snapshot_or_error.value()); tls_->set([ptr](Event::Dispatcher&) -> ThreadLocal::ThreadLocalObjectSharedPtr { return std::static_pointer_cast(ptr); }); @@ -697,6 +719,7 @@ void LoaderImpl::loadNewSnapshot() { absl::MutexLock lock(&snapshot_mutex_); thread_safe_snapshot_ = ptr; } + return absl::OkStatus(); } const Snapshot& LoaderImpl::snapshot() { @@ -716,12 +739,12 @@ SnapshotConstSharedPtr LoaderImpl::threadsafeSnapshot() { } } -void LoaderImpl::mergeValues(const absl::node_hash_map& values) { +absl::Status LoaderImpl::mergeValues(const absl::node_hash_map& values) { if (admin_layer_ == nullptr) { - throwEnvoyExceptionOrPanic("No admin layer specified"); + return absl::InvalidArgumentError("No admin layer specified"); } - admin_layer_->mergeValues(values); - loadNewSnapshot(); + RETURN_IF_NOT_OK(admin_layer_->mergeValues(values)); + return loadNewSnapshot(); } Stats::Scope& LoaderImpl::getRootScope() { return *store_.rootScope(); } @@ -735,15 +758,18 @@ RuntimeStats LoaderImpl::generateStats(Stats::Store& store) { return stats; } -SnapshotImplPtr LoaderImpl::createNewSnapshot() { +absl::StatusOr LoaderImpl::createNewSnapshot() { std::vector layers; uint32_t disk_layers = 0; uint32_t error_layers = 0; uint32_t rtds_layer = 0; + absl::Status creation_status; for (const auto& layer : config_.layers()) { switch (layer.layer_specifier_case()) { case envoy::config::bootstrap::v3::RuntimeLayer::LayerSpecifierCase::kStaticLayer: - layers.emplace_back(std::make_unique(layer.name(), layer.static_layer())); + layers.emplace_back( + std::make_unique(layer.name(), layer.static_layer(), creation_status)); + RETURN_IF_NOT_OK(creation_status); break; case envoy::config::bootstrap::v3::RuntimeLayer::LayerSpecifierCase::kDiskLayer: { std::string path = @@ -752,18 +778,27 @@ SnapshotImplPtr LoaderImpl::createNewSnapshot() { absl::StrAppend(&path, "/", service_cluster_); } if (api_.fileSystem().directoryExists(path)) { + std::unique_ptr disk_layer; + std::string error; TRY_ASSERT_MAIN_THREAD { - layers.emplace_back(std::make_unique(layer.name(), path, api_)); - ++disk_layers; + absl::Status creation_status; + disk_layer = std::make_unique(layer.name(), path, api_, creation_status); + if (!creation_status.ok()) { + error = creation_status.message(); + } + END_TRY } - END_TRY - CATCH(EnvoyException & e, { + CATCH(EnvoyException & e, { error = e.what(); }); + if (error.empty()) { + layers.emplace_back(std::move(disk_layer)); + ++disk_layers; + } else { // TODO(htuch): Consider latching here, rather than ignoring the // layer. This would be consistent with filesystem RTDS. ++error_layers; ENVOY_LOG(debug, "error loading runtime values for layer {} from disk: {}", - layer.DebugString(), e.what()); - }); + layer.DebugString(), error); + } } break; } @@ -772,7 +807,9 @@ SnapshotImplPtr LoaderImpl::createNewSnapshot() { break; case envoy::config::bootstrap::v3::RuntimeLayer::LayerSpecifierCase::kRtdsLayer: { auto* subscription = subscriptions_[rtds_layer++].get(); - layers.emplace_back(std::make_unique(layer.name(), subscription->proto_)); + layers.emplace_back( + std::make_unique(layer.name(), subscription->proto_, creation_status)); + RETURN_IF_NOT_OK(creation_status); break; } case envoy::config::bootstrap::v3::RuntimeLayer::LayerSpecifierCase::LAYER_SPECIFIER_NOT_SET: diff --git a/source/common/runtime/runtime_impl.h b/source/common/runtime/runtime_impl.h index e2ebde9e0c8b..68f27f09bc08 100644 --- a/source/common/runtime/runtime_impl.h +++ b/source/common/runtime/runtime_impl.h @@ -117,8 +117,8 @@ class OverrideLayerImpl : public Snapshot::OverrideLayer { /** * Extension of OverrideLayerImpl that maintains an in-memory set of values. These values can be - * modified programmatically via mergeValues(). AdminLayer is so named because it can be accessed - * and manipulated by Envoy's admin interface. + * modified programmatically via mergeValues(). AdminLayer is so named because it + * can be accessed and manipulated by Envoy's admin interface. */ class AdminLayer : public OverrideLayerImpl { public: @@ -135,7 +135,7 @@ class AdminLayer : public OverrideLayerImpl { * Merge the provided values into our entry map. An empty value indicates that a key should be * removed from our map. */ - void mergeValues(const absl::node_hash_map& values); + absl::Status mergeValues(const absl::node_hash_map& values); private: RuntimeStats& stats_; @@ -148,11 +148,12 @@ using AdminLayerPtr = std::unique_ptr; */ class DiskLayer : public OverrideLayerImpl, Logger::Loggable { public: - DiskLayer(absl::string_view name, const std::string& path, Api::Api& api); + DiskLayer(absl::string_view name, const std::string& path, Api::Api& api, + absl::Status& creation_status); private: - void walkDirectory(const std::string& path, const std::string& prefix, uint32_t depth, - Api::Api& api); + absl::Status walkDirectory(const std::string& path, const std::string& prefix, uint32_t depth, + Api::Api& api); const std::string path_; const Filesystem::WatcherPtr watcher_; @@ -163,10 +164,11 @@ class DiskLayer : public OverrideLayerImpl, Logger::Loggable { public: - ProtoLayer(absl::string_view name, const ProtobufWkt::Struct& proto); + ProtoLayer(absl::string_view name, const ProtobufWkt::Struct& proto, + absl::Status& creation_status); private: - void walkProtoValue(const ProtobufWkt::Value& v, const std::string& prefix); + absl::Status walkProtoValue(const ProtobufWkt::Value& v, const std::string& prefix); }; class LoaderImpl; @@ -216,13 +218,14 @@ class LoaderImpl : public Loader, Logger::Loggable { const envoy::config::bootstrap::v3::LayeredRuntime& config, const LocalInfo::LocalInfo& local_info, Stats::Store& store, Random::RandomGenerator& generator, - ProtobufMessage::ValidationVisitor& validation_visitor, Api::Api& api); + ProtobufMessage::ValidationVisitor& validation_visitor, Api::Api& api, + absl::Status& creation_status); // Runtime::Loader void initialize(Upstream::ClusterManager& cm) override; const Snapshot& snapshot() override; SnapshotConstSharedPtr threadsafeSnapshot() override; - void mergeValues(const absl::node_hash_map& values) override; + absl::Status mergeValues(const absl::node_hash_map& values) override; void startRtdsSubscriptions(ReadyCallback on_done) override; Stats::Scope& getRootScope() override; void countDeprecatedFeatureUse() const override; @@ -231,9 +234,9 @@ class LoaderImpl : public Loader, Logger::Loggable { friend RtdsSubscription; // Create a new Snapshot - SnapshotImplPtr createNewSnapshot(); + absl::StatusOr createNewSnapshot(); // Load a new Snapshot into TLS - void loadNewSnapshot(); + absl::Status loadNewSnapshot(); RuntimeStats generateStats(Stats::Store& store); void onRtdsReady(); diff --git a/source/common/secret/sds_api.cc b/source/common/secret/sds_api.cc index 344da3ff5833..e378652c313c 100644 --- a/source/common/secret/sds_api.cc +++ b/source/common/secret/sds_api.cc @@ -122,9 +122,9 @@ absl::Status SdsApi::onConfigUpdate(const std::vectoraddWatch(absl::StrCat(result_or_error.value().directory_, "/"), - Filesystem::Watcher::Events::MovedTo, - [this](uint32_t) { onWatchUpdate(); }); + RETURN_IF_NOT_OK(watcher_->addWatch(absl::StrCat(result_or_error.value().directory_, "/"), + Filesystem::Watcher::Events::MovedTo, + [this](uint32_t) { onWatchUpdate(); })); } } else { watcher_.reset(); // Destroy the old watch if any diff --git a/source/common/tls/cert_validator/default_validator.cc b/source/common/tls/cert_validator/default_validator.cc index 8d049f7ba0c9..39319a7312b3 100644 --- a/source/common/tls/cert_validator/default_validator.cc +++ b/source/common/tls/cert_validator/default_validator.cc @@ -44,8 +44,8 @@ namespace Tls { DefaultCertValidator::DefaultCertValidator( const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - TimeSource& time_source) - : config_(config), stats_(stats), time_source_(time_source) { + Server::Configuration::CommonFactoryContext& context) + : config_(config), stats_(stats), context_(context) { if (config_ != nullptr) { allow_untrusted_certificate_ = config_->trustChainVerification() == envoy::extensions::transport_sockets::tls::v3:: @@ -155,7 +155,7 @@ int DefaultCertValidator::initializeSslContexts(std::vector contexts, if (!cert_validation_config->subjectAltNameMatchers().empty()) { for (const envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher& matcher : cert_validation_config->subjectAltNameMatchers()) { - auto san_matcher = createStringSanMatcher(matcher); + auto san_matcher = createStringSanMatcher(matcher, context_); if (san_matcher == nullptr) { throwEnvoyExceptionOrPanic( absl::StrCat("Failed to create string SAN matcher of type ", matcher.san_type())); @@ -548,18 +548,19 @@ Envoy::Ssl::CertificateDetailsPtr DefaultCertValidator::getCaCertInformation() c if (ca_cert_ == nullptr) { return nullptr; } - return Utility::certificateDetails(ca_cert_.get(), getCaFileName(), time_source_); + return Utility::certificateDetails(ca_cert_.get(), getCaFileName(), context_.timeSource()); } absl::optional DefaultCertValidator::daysUntilFirstCertExpires() const { - return Utility::getDaysUntilExpiration(ca_cert_.get(), time_source_); + return Utility::getDaysUntilExpiration(ca_cert_.get(), context_.timeSource()); } class DefaultCertValidatorFactory : public CertValidatorFactory { public: - CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, - SslStats& stats, TimeSource& time_source) override { - return std::make_unique(config, stats, time_source); + CertValidatorPtr + createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, + Server::Configuration::CommonFactoryContext& context) override { + return std::make_unique(config, stats, context); } std::string name() const override { return "envoy.tls.cert_validator.default"; } diff --git a/source/common/tls/cert_validator/default_validator.h b/source/common/tls/cert_validator/default_validator.h index c3e88bd09ca1..f17a01dd3d5d 100644 --- a/source/common/tls/cert_validator/default_validator.h +++ b/source/common/tls/cert_validator/default_validator.h @@ -35,7 +35,7 @@ namespace Tls { class DefaultCertValidator : public CertValidator, Logger::Loggable { public: DefaultCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, - SslStats& stats, TimeSource& time_source); + SslStats& stats, Server::Configuration::CommonFactoryContext& context); ~DefaultCertValidator() override = default; @@ -110,7 +110,7 @@ class DefaultCertValidator : public CertValidator, Logger::Loggable ca_cert_; diff --git a/source/common/tls/cert_validator/factory.h b/source/common/tls/cert_validator/factory.h index 40f3fc3de92b..8f6aebbd6b4c 100644 --- a/source/common/tls/cert_validator/factory.h +++ b/source/common/tls/cert_validator/factory.h @@ -21,7 +21,7 @@ class CertValidatorFactory : public Config::UntypedFactory { public: virtual CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - TimeSource& time_source) PURE; + Server::Configuration::CommonFactoryContext& context) PURE; std::string category() const override { return "envoy.tls.cert_validator"; } }; diff --git a/source/common/tls/cert_validator/san_matcher.cc b/source/common/tls/cert_validator/san_matcher.cc index 13429c3fcdcc..0229ca1c1273 100644 --- a/source/common/tls/cert_validator/san_matcher.cc +++ b/source/common/tls/cert_validator/san_matcher.cc @@ -28,7 +28,8 @@ bool StringSanMatcher::match(const GENERAL_NAME* general_name) const { } SanMatcherPtr createStringSanMatcher( - envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher const& matcher) { + envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher const& matcher, + Server::Configuration::CommonFactoryContext& context) { // Verify that a new san type has not been added. static_assert(envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SanType_MAX == 4); @@ -36,13 +37,13 @@ SanMatcherPtr createStringSanMatcher( switch (matcher.san_type()) { PANIC_ON_PROTO_ENUM_SENTINEL_VALUES; case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::DNS: - return SanMatcherPtr{std::make_unique(GEN_DNS, matcher.matcher())}; + return SanMatcherPtr{std::make_unique(GEN_DNS, matcher.matcher(), context)}; case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::EMAIL: - return SanMatcherPtr{std::make_unique(GEN_EMAIL, matcher.matcher())}; + return SanMatcherPtr{std::make_unique(GEN_EMAIL, matcher.matcher(), context)}; case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::URI: - return SanMatcherPtr{std::make_unique(GEN_URI, matcher.matcher())}; + return SanMatcherPtr{std::make_unique(GEN_URI, matcher.matcher(), context)}; case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::IP_ADDRESS: - return SanMatcherPtr{std::make_unique(GEN_IPADD, matcher.matcher())}; + return SanMatcherPtr{std::make_unique(GEN_IPADD, matcher.matcher(), context)}; case envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SAN_TYPE_UNSPECIFIED: PANIC("unhandled value"); } diff --git a/source/common/tls/cert_validator/san_matcher.h b/source/common/tls/cert_validator/san_matcher.h index 260e9cc3075e..b9409555209e 100644 --- a/source/common/tls/cert_validator/san_matcher.h +++ b/source/common/tls/cert_validator/san_matcher.h @@ -34,16 +34,18 @@ 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) {} + StringSanMatcher(int general_name_type, envoy::type::matcher::v3::StringMatcher matcher, + Server::Configuration::CommonFactoryContext& context) + : general_name_type_(general_name_type), matcher_(matcher, context) {} private: const int general_name_type_; - const Matchers::StringMatcherImpl matcher_; + const Matchers::StringMatcherImplWithContext matcher_; }; SanMatcherPtr createStringSanMatcher( - const envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher& matcher); + const envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher& matcher, + Server::Configuration::CommonFactoryContext& context); } // namespace Tls } // namespace TransportSockets diff --git a/source/common/tls/context_impl.cc b/source/common/tls/context_impl.cc index 7d86c94bd5f6..2669fd8559f5 100644 --- a/source/common/tls/context_impl.cc +++ b/source/common/tls/context_impl.cc @@ -80,8 +80,9 @@ int ContextImpl::sslExtendedSocketInfoIndex() { } ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, - TimeSource& time_source, Ssl::ContextAdditionalInitFunc additional_init) - : scope_(scope), stats_(generateSslStats(scope)), time_source_(time_source), + Server::Configuration::CommonFactoryContext& factory_context, + Ssl::ContextAdditionalInitFunc additional_init) + : scope_(scope), stats_(generateSslStats(scope)), factory_context_(factory_context), tls_max_version_(config.maxProtocolVersion()), stat_name_set_(scope.symbolTable().makeSet("TransportSockets::Tls")), unknown_ssl_cipher_(stat_name_set_->add("unknown_ssl_cipher")), @@ -104,7 +105,7 @@ ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& c } cert_validator_ = cert_validator_factory->createCertValidator( - config.certificateValidationContext(), stats_, time_source_); + config.certificateValidationContext(), stats_, factory_context_); const auto tls_certificates = config.tlsCertificates(); tls_contexts_.resize(std::max(static_cast(1), tls_certificates.size())); @@ -609,7 +610,7 @@ absl::optional ContextImpl::daysUntilFirstCertExpires() const { } for (auto& ctx : tls_contexts_) { const absl::optional tmp = - Utility::getDaysUntilExpiration(ctx.cert_chain_.get(), time_source_); + Utility::getDaysUntilExpiration(ctx.cert_chain_.get(), factory_context_.timeSource()); if (!tmp.has_value()) { return absl::nullopt; } @@ -643,7 +644,7 @@ std::vector ContextImpl::getCertChainInformat } auto detail = Utility::certificateDetails(ctx.cert_chain_.get(), ctx.getCertChainFileName(), - time_source_); + factory_context_.timeSource()); auto ocsp_resp = ctx.ocsp_response_.get(); if (ocsp_resp) { auto* ocsp_details = detail->mutable_ocsp_details(); @@ -659,8 +660,8 @@ std::vector ContextImpl::getCertChainInformat ClientContextImpl::ClientContextImpl(Stats::Scope& scope, const Envoy::Ssl::ClientContextConfig& config, - TimeSource& time_source) - : ContextImpl(scope, config, time_source, nullptr /* additional_init */), + Server::Configuration::CommonFactoryContext& factory_context) + : ContextImpl(scope, config, factory_context, nullptr /* additional_init */), server_name_indication_(config.serverNameIndication()), allow_renegotiation_(config.allowRenegotiation()), enforce_rsa_key_usage_(config.enforceRsaKeyUsage()), @@ -789,9 +790,9 @@ int ClientContextImpl::newSessionKey(SSL_SESSION* session) { ServerContextImpl::ServerContextImpl(Stats::Scope& scope, const Envoy::Ssl::ServerContextConfig& config, const std::vector& server_names, - TimeSource& time_source, + Server::Configuration::CommonFactoryContext& factory_context, Ssl::ContextAdditionalInitFunc additional_init) - : ContextImpl(scope, config, time_source, additional_init), + : ContextImpl(scope, config, factory_context, additional_init), session_ticket_keys_(config.sessionTicketKeys()), ocsp_staple_policy_(config.ocspStaplePolicy()), full_scan_certs_on_sni_mismatch_(config.fullScanCertsOnSNIMismatch()) { @@ -888,7 +889,8 @@ ServerContextImpl::ServerContextImpl(Stats::Scope& scope, throwEnvoyExceptionOrPanic("Required OCSP response is missing from TLS context"); } } else { - auto response = std::make_unique(ocsp_resp_bytes, time_source_); + auto response = std::make_unique(ocsp_resp_bytes, + factory_context_.timeSource()); if (!response->matchesCertificate(*ctx.cert_chain_)) { throwEnvoyExceptionOrPanic("OCSP response does not match its TLS certificate"); } diff --git a/source/common/tls/context_impl.h b/source/common/tls/context_impl.h index 9bffa93a74a4..71f04229af51 100644 --- a/source/common/tls/context_impl.h +++ b/source/common/tls/context_impl.h @@ -115,7 +115,8 @@ class ContextImpl : public virtual Envoy::Ssl::Context, protected: friend class ContextImplPeer; - ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, TimeSource& time_source, + ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, + Server::Configuration::CommonFactoryContext& factory_context, Ssl::ContextAdditionalInitFunc additional_init); /** @@ -151,7 +152,7 @@ class ContextImpl : public virtual Envoy::Ssl::Context, std::vector parsed_alpn_protocols_; bssl::UniquePtr cert_chain_; std::string cert_chain_file_path_; - TimeSource& time_source_; + Server::Configuration::CommonFactoryContext& factory_context_; const unsigned tls_max_version_; mutable Stats::StatNameSetPtr stat_name_set_; const Stats::StatName unknown_ssl_cipher_; @@ -173,7 +174,7 @@ using ContextImplSharedPtr = std::shared_ptr; class ClientContextImpl : public ContextImpl, public Envoy::Ssl::ClientContext { public: ClientContextImpl(Stats::Scope& scope, const Envoy::Ssl::ClientContextConfig& config, - TimeSource& time_source); + Server::Configuration::CommonFactoryContext& factory_context); bssl::UniquePtr newSsl(const Network::TransportSocketOptionsConstSharedPtr& options) override; @@ -195,7 +196,8 @@ enum class OcspStapleAction { Staple, NoStaple, Fail, ClientNotCapable }; class ServerContextImpl : public ContextImpl, public Envoy::Ssl::ServerContext { public: ServerContextImpl(Stats::Scope& scope, const Envoy::Ssl::ServerContextConfig& config, - const std::vector& server_names, TimeSource& time_source, + const std::vector& server_names, + Server::Configuration::CommonFactoryContext& factory_context, Ssl::ContextAdditionalInitFunc additional_init); // Select the TLS certificate context in SSL_CTX_set_select_certificate_cb() callback with diff --git a/source/common/tls/context_manager_impl.cc b/source/common/tls/context_manager_impl.cc index b76a81264e21..1c0e8d027aeb 100644 --- a/source/common/tls/context_manager_impl.cc +++ b/source/common/tls/context_manager_impl.cc @@ -15,17 +15,19 @@ namespace Extensions { namespace TransportSockets { namespace Tls { -ContextManagerImpl::ContextManagerImpl(TimeSource& time_source) : time_source_(time_source) {} +ContextManagerImpl::ContextManagerImpl(Server::Configuration::CommonFactoryContext& factory_context) + : factory_context_(factory_context) {} Envoy::Ssl::ClientContextSharedPtr ContextManagerImpl::createSslClientContext(Stats::Scope& scope, const Envoy::Ssl::ClientContextConfig& config) { + ASSERT_IS_MAIN_OR_TEST_THREAD(); if (!config.isReady()) { return nullptr; } Envoy::Ssl::ClientContextSharedPtr context = - std::make_shared(scope, config, time_source_); + std::make_shared(scope, config, factory_context_); contexts_.insert(context); return context; } @@ -33,12 +35,13 @@ ContextManagerImpl::createSslClientContext(Stats::Scope& scope, Envoy::Ssl::ServerContextSharedPtr ContextManagerImpl::createSslServerContext( Stats::Scope& scope, const Envoy::Ssl::ServerContextConfig& config, const std::vector& server_names, Ssl::ContextAdditionalInitFunc additional_init) { + ASSERT_IS_MAIN_OR_TEST_THREAD(); if (!config.isReady()) { return nullptr; } Envoy::Ssl::ServerContextSharedPtr context = std::make_shared( - scope, config, server_names, time_source_, std::move(additional_init)); + scope, config, server_names, factory_context_, std::move(additional_init)); contexts_.insert(context); return context; } diff --git a/source/common/tls/context_manager_impl.h b/source/common/tls/context_manager_impl.h index db72e1d308e0..31df3addd743 100644 --- a/source/common/tls/context_manager_impl.h +++ b/source/common/tls/context_manager_impl.h @@ -5,6 +5,7 @@ #include #include "envoy/common/time.h" +#include "envoy/server/factory_context.h" #include "envoy/ssl/context_manager.h" #include "envoy/ssl/private_key/private_key.h" #include "envoy/stats/scope.h" @@ -18,14 +19,13 @@ namespace Tls { /** * The SSL context manager has the following threading model: - * Contexts can be allocated via any thread (through in practice they are only allocated on the main - * thread). They can be released from any thread (and in practice are since cluster information can - * be released from any thread). Context allocation/free is a very uncommon thing so we just do a - * global lock to protect it all. + * Contexts can be allocated the main thread. They can be released from any thread (and in practice + * are since cluster information can be released from any thread). Context allocation/free is a very + * uncommon thing so we just do a global lock to protect it all. */ class ContextManagerImpl final : public Envoy::Ssl::ContextManager { public: - explicit ContextManagerImpl(TimeSource& time_source); + explicit ContextManagerImpl(Server::Configuration::CommonFactoryContext& factory_context); ~ContextManagerImpl() override = default; // Ssl::ContextManager @@ -45,7 +45,7 @@ class ContextManagerImpl final : public Envoy::Ssl::ContextManager { void removeContext(const Envoy::Ssl::ContextSharedPtr& old_context) override; private: - TimeSource& time_source_; + Server::Configuration::CommonFactoryContext& factory_context_; absl::flat_hash_set contexts_; PrivateKeyMethodManagerImpl private_key_method_manager_{}; }; diff --git a/source/common/upstream/cluster_manager_impl.cc b/source/common/upstream/cluster_manager_impl.cc index 4a2a4f076afa..41e490ae0271 100644 --- a/source/common/upstream/cluster_manager_impl.cc +++ b/source/common/upstream/cluster_manager_impl.cc @@ -295,12 +295,12 @@ void ClusterManagerInitHelper::setPrimaryClustersInitializedCb( ClusterManagerImpl::ClusterManagerImpl( const envoy::config::bootstrap::v3::Bootstrap& bootstrap, ClusterManagerFactory& factory, - Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, - const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager, - Event::Dispatcher& main_thread_dispatcher, OptRef admin, - ProtobufMessage::ValidationContext& validation_context, Api::Api& api, - Http::Context& http_context, Grpc::Context& grpc_context, Router::Context& router_context, - const Server::Instance& server) + Server::Configuration::CommonFactoryContext& context, Stats::Store& stats, + ThreadLocal::Instance& tls, Runtime::Loader& runtime, const LocalInfo::LocalInfo& local_info, + AccessLog::AccessLogManager& log_manager, Event::Dispatcher& main_thread_dispatcher, + OptRef admin, ProtobufMessage::ValidationContext& validation_context, + Api::Api& api, Http::Context& http_context, Grpc::Context& grpc_context, + Router::Context& router_context, const Server::Instance& server) : server_(server), factory_(factory), runtime_(runtime), stats_(stats), tls_(tls), random_(api.randomGenerator()), deferred_cluster_creation_(bootstrap.cluster_manager().enable_deferred_cluster_creation()), @@ -331,8 +331,7 @@ ClusterManagerImpl::ClusterManagerImpl( }); } async_client_manager_ = std::make_unique( - *this, tls, time_source_, api, grpc_context.statNames(), - bootstrap.grpc_async_client_manager_config()); + *this, tls, context, grpc_context.statNames(), bootstrap.grpc_async_client_manager_config()); const auto& cm_config = bootstrap.cluster_manager(); if (cm_config.has_outlier_detection()) { const std::string event_log_file_path = cm_config.outlier_detection().event_log_path(); @@ -2222,7 +2221,7 @@ void ClusterManagerImpl::ThreadLocalClusterManagerImpl::tcpConnPoolIsIdle( ClusterManagerPtr ProdClusterManagerFactory::clusterManagerFromProto( const envoy::config::bootstrap::v3::Bootstrap& bootstrap) { auto cluster_manager_impl = std::unique_ptr{new ClusterManagerImpl( - bootstrap, *this, stats_, tls_, context_.runtime(), context_.localInfo(), + bootstrap, *this, context_, stats_, tls_, context_.runtime(), context_.localInfo(), context_.accessLogManager(), context_.mainThreadDispatcher(), context_.admin(), context_.messageValidationContext(), context_.api(), http_context_, context_.grpcContext(), context_.routerContext(), server_)}; diff --git a/source/common/upstream/cluster_manager_impl.h b/source/common/upstream/cluster_manager_impl.h index 0c0a0c857c87..9a093d7561dc 100644 --- a/source/common/upstream/cluster_manager_impl.h +++ b/source/common/upstream/cluster_manager_impl.h @@ -386,7 +386,8 @@ class ClusterManagerImpl : public ClusterManager, // ClusterManagerImpl's constructor should not be invoked directly; create instances from the // clusterManagerFromProto() static method. The init() method must be called after construction. ClusterManagerImpl(const envoy::config::bootstrap::v3::Bootstrap& bootstrap, - ClusterManagerFactory& factory, Stats::Store& stats, + ClusterManagerFactory& factory, + Server::Configuration::CommonFactoryContext& context, Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager, diff --git a/source/common/upstream/health_checker_event_logger.cc b/source/common/upstream/health_checker_event_logger.cc index c75d3a55e6db..b6218cb0c07c 100644 --- a/source/common/upstream/health_checker_event_logger.cc +++ b/source/common/upstream/health_checker_event_logger.cc @@ -37,6 +37,13 @@ void HealthCheckEventLoggerImpl::logAddHealthy( }); } +void HealthCheckEventLoggerImpl::logSuccessfulHealthCheck( + envoy::data::core::v3::HealthCheckerType health_checker_type, + const HostDescriptionConstSharedPtr& host) { + createHealthCheckEvent(health_checker_type, *host, + [](auto& event) { event.mutable_successful_health_check_event(); }); +} + void HealthCheckEventLoggerImpl::logDegraded( envoy::data::core::v3::HealthCheckerType health_checker_type, const HostDescriptionConstSharedPtr& host) { diff --git a/source/common/upstream/health_checker_event_logger.h b/source/common/upstream/health_checker_event_logger.h index 808e2ea0dec2..38bd0ed9d0a6 100644 --- a/source/common/upstream/health_checker_event_logger.h +++ b/source/common/upstream/health_checker_event_logger.h @@ -48,6 +48,8 @@ class HealthCheckEventLoggerImpl : public HealthCheckEventLogger { envoy::data::core::v3::HealthCheckFailureType failure_type) override; void logAddHealthy(envoy::data::core::v3::HealthCheckerType health_checker_type, const HostDescriptionConstSharedPtr& host, bool first_check) override; + void logSuccessfulHealthCheck(envoy::data::core::v3::HealthCheckerType health_checker_type, + const HostDescriptionConstSharedPtr& host) override; void logUnhealthy(envoy::data::core::v3::HealthCheckerType health_checker_type, const HostDescriptionConstSharedPtr& host, envoy::data::core::v3::HealthCheckFailureType failure_type, diff --git a/source/exe/BUILD b/source/exe/BUILD index a4651aae9b4f..0f7947c1d07a 100644 --- a/source/exe/BUILD +++ b/source/exe/BUILD @@ -7,6 +7,7 @@ load( "envoy_cc_posix_without_linux_library", "envoy_cc_win32_library", "envoy_package", + "envoy_select_admin_functionality", "envoy_select_enable_http3", "envoy_select_signal_trace", ) @@ -102,7 +103,7 @@ envoy_cc_library( hdrs = [ "main_common.h", ], - deps = [ + deps = envoy_select_admin_functionality([":admin_response_lib"]) + [ ":platform_impl_lib", ":process_wide_lib", ":stripped_main_base_lib", @@ -118,6 +119,19 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "admin_response_lib", + srcs = ["admin_response.cc"], + hdrs = ["admin_response.h"], + deps = [ + "//source/common/buffer:buffer_lib", + "//source/common/http:header_map_lib", + "//source/server:server_lib", + "//source/server/admin:admin_lib", + "//source/server/admin:utils_lib", + ], +) + envoy_cc_library( name = "main_common_with_all_extensions_lib", deps = [ diff --git a/source/exe/admin_response.cc b/source/exe/admin_response.cc new file mode 100644 index 000000000000..0c1ab0958bec --- /dev/null +++ b/source/exe/admin_response.cc @@ -0,0 +1,191 @@ +#include "source/exe/admin_response.h" + +#include "envoy/server/admin.h" + +#include "source/server/admin/admin_filter.h" +#include "source/server/admin/utils.h" + +namespace Envoy { + +AdminResponse::AdminResponse(Server::Instance& server, absl::string_view path, + absl::string_view method, SharedPtrSet response_set) + : server_(server), opt_admin_(server.admin()), shared_response_set_(response_set) { + request_headers_->setMethod(method); + request_headers_->setPath(path); +} + +AdminResponse::~AdminResponse() { + cancel(); + shared_response_set_->detachResponse(this); +} + +void AdminResponse::getHeaders(HeadersFn fn) { + auto request_headers = [response = shared_from_this()]() { response->requestHeaders(); }; + + // First check for cancelling or termination. + { + absl::MutexLock lock(&mutex_); + ASSERT(headers_fn_ == nullptr); + if (cancelled_) { + return; + } + headers_fn_ = fn; + if (terminated_ || !opt_admin_) { + sendErrorLockHeld(); + return; + } + } + server_.dispatcher().post(request_headers); +} + +void AdminResponse::nextChunk(BodyFn fn) { + auto request_next_chunk = [response = shared_from_this()]() { response->requestNextChunk(); }; + + // Note the caller may race a call to nextChunk with the server being + // terminated. + { + absl::MutexLock lock(&mutex_); + ASSERT(body_fn_ == nullptr); + if (cancelled_) { + return; + } + body_fn_ = fn; + if (terminated_ || !opt_admin_) { + sendAbortChunkLockHeld(); + return; + } + } + + // Note that nextChunk may be called from any thread -- it's the callers choice, + // including the Envoy main thread, which would occur if the caller initiates + // the request of a chunk upon receipt of the previous chunk. + // + // In that case it may race against the AdminResponse object being deleted, + // in which case the callbacks, held in a shared_ptr, will be cancelled + // from the destructor. If that happens *before* we post to the main thread, + // we will just skip and never call fn. + server_.dispatcher().post(request_next_chunk); +} + +// Called by the user if it is not longer interested in the result of the +// admin request. After calling cancel() the caller must not call nextChunk or +// getHeaders. +void AdminResponse::cancel() { + absl::MutexLock lock(&mutex_); + cancelled_ = true; + headers_fn_ = nullptr; + body_fn_ = nullptr; +} + +bool AdminResponse::cancelled() const { + absl::MutexLock lock(&mutex_); + return cancelled_; +} + +// Called from terminateAdminRequests when the Envoy server +// terminates. After this is called, the caller may need to complete the +// admin response, and so calls to getHeader and nextChunk remain valid, +// resulting in 503 and an empty body. +void AdminResponse::terminate() { + ASSERT_IS_MAIN_OR_TEST_THREAD(); + absl::MutexLock lock(&mutex_); + if (!terminated_) { + terminated_ = true; + sendErrorLockHeld(); + sendAbortChunkLockHeld(); + } +} + +void AdminResponse::requestHeaders() { + ASSERT_IS_MAIN_OR_TEST_THREAD(); + { + absl::MutexLock lock(&mutex_); + if (cancelled_ || terminated_) { + return; + } + } + Server::AdminFilter filter(*opt_admin_); + filter.decodeHeaders(*request_headers_, false); + request_ = opt_admin_->makeRequest(filter); + code_ = request_->start(*response_headers_); + { + absl::MutexLock lock(&mutex_); + if (headers_fn_ == nullptr || cancelled_) { + return; + } + Server::Utility::populateFallbackResponseHeaders(code_, *response_headers_); + headers_fn_(code_, *response_headers_); + headers_fn_ = nullptr; + } +} + +void AdminResponse::requestNextChunk() { + ASSERT_IS_MAIN_OR_TEST_THREAD(); + { + absl::MutexLock lock(&mutex_); + if (cancelled_ || terminated_ || !more_data_) { + return; + } + } + ASSERT(response_.length() == 0); + more_data_ = request_->nextChunk(response_); + { + absl::MutexLock lock(&mutex_); + if (sent_end_stream_ || cancelled_) { + return; + } + sent_end_stream_ = !more_data_; + body_fn_(response_, more_data_); + ASSERT(response_.length() == 0); + body_fn_ = nullptr; + } +} + +void AdminResponse::sendAbortChunkLockHeld() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_) { + if (!sent_end_stream_ && body_fn_ != nullptr) { + response_.drain(response_.length()); + body_fn_(response_, false); + sent_end_stream_ = true; + } + body_fn_ = nullptr; +} + +void AdminResponse::sendErrorLockHeld() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_) { + if (headers_fn_ != nullptr) { + code_ = Http::Code::InternalServerError; + Server::Utility::populateFallbackResponseHeaders(code_, *response_headers_); + headers_fn_(code_, *response_headers_); + headers_fn_ = nullptr; + } +} + +void AdminResponse::PtrSet::terminateAdminRequests() { + ASSERT_IS_MAIN_OR_TEST_THREAD(); + + absl::MutexLock lock(&mutex_); + accepting_admin_requests_ = false; + for (AdminResponse* response : response_set_) { + // Consider the possibility of response being deleted due to its creator + // dropping its last reference right here. From its destructor it will call + // detachResponse(), which is mutex-ed against this loop, so before the + // memory becomes invalid, the call to terminate will complete. + response->terminate(); + } + response_set_.clear(); +} + +void AdminResponse::PtrSet::attachResponse(AdminResponse* response) { + absl::MutexLock lock(&mutex_); + if (accepting_admin_requests_) { + response_set_.insert(response); + } else { + response->terminate(); + } +} + +void AdminResponse::PtrSet::detachResponse(AdminResponse* response) { + absl::MutexLock lock(&mutex_); + response_set_.erase(response); +} + +} // namespace Envoy diff --git a/source/exe/admin_response.h b/source/exe/admin_response.h new file mode 100644 index 000000000000..b3683b5707c7 --- /dev/null +++ b/source/exe/admin_response.h @@ -0,0 +1,188 @@ +#pragma once + +#include + +#include "envoy/server/instance.h" + +#include "source/common/buffer/buffer_impl.h" +#include "source/common/http/header_map_impl.h" + +#include "absl/container/flat_hash_set.h" +#include "absl/synchronization/mutex.h" + +namespace Envoy { + +class AdminResponse; + +// Holds context for a streaming response from the admin system, enabling +// flow-control into another system. This is particularly important when the +// generated response is very large, such that holding it in memory may cause +// fragmentation or out-of-memory failures. It is possible to interleave xDS +// response handling, overload management, and other admin requests during the +// streaming of a long admin response. +// +// There can be be multiple AdminResponses at a time; each are separately +// managed. However they will obtain their data from Envoy functions that +// run on the main thread. +// +// Responses may still be active after the server has shut down, and is no +// longer running its main thread dispatcher. In this state, the callbacks +// will be called with appropriate error codes. +// +// Requests can also be cancelled explicitly by calling cancel(). After +// cancel() is called, no further callbacks will be called by the response. +// +// The lifecycle of an AdminResponse is rendered as a finite state machine +// bubble diagram: +// https://docs.google.com/drawings/d/1njUl1twApEMoxmjaG4b7optTh5fcb_YNcfSnkHbdfq0/view +class AdminResponse : public std::enable_shared_from_this { +public: + // AdminResponse can outlive MainCommonBase. But AdminResponse needs a + // reliable way of knowing whether MainCommonBase is alive, so we do this with + // PtrSet, which is held by MainCommonBase and all the active AdminResponses. + // via shared_ptr. This gives MainCommonBase a reliable way of notifying all + // active responses that it is being shut down, and thus all responses need to + // be terminated. And it gives a reliable way for AdminResponse to detach + // itself, whether or not MainCommonBase is already deleted. + // + // In summary: + // * MainCommonBase can outlive AdminResponse so we need detachResponse. + // * AdminResponse can outlive MainCommonBase, so we need shared_ptr. + class PtrSet { + public: + /** + * Called when an AdminResponse is created. When terminateAdminRequests is + * called, all outstanding response objects have their terminate() methods + * called. + * + * @param response the response pointer to be added to the set. + */ + void attachResponse(AdminResponse* response); + + /** + * Called when an AdminResponse is terminated, either by completing normally + * or having the caller call cancel on it. Either way it needs to be removed + * from the set that will be used by terminateAdminRequests below. + * + * @param response the response pointer to be removed from the set. + */ + void detachResponse(AdminResponse* response); + + /** + * Called after the server run-loop finishes; any outstanding streaming + * admin requests will otherwise hang as the main-thread dispatcher loop + * will no longer run. + */ + void terminateAdminRequests(); + + mutable absl::Mutex mutex_; + absl::flat_hash_set response_set_ ABSL_GUARDED_BY(mutex_); + bool accepting_admin_requests_ ABSL_GUARDED_BY(mutex_) = true; + }; + using SharedPtrSet = std::shared_ptr; + + AdminResponse(Server::Instance& server, absl::string_view path, absl::string_view method, + SharedPtrSet response_set); + ~AdminResponse(); + + /** + * Requests the headers for the response. This can be called from any + * thread, and HeaderFn may also be called from any thread. + * + * HeadersFn will not be called after cancel(). It is invalid to + * to call nextChunk from within HeadersFn -- the caller must trigger + * such a call on another thread, after HeadersFn returns. Calling + * nextChunk from HeadersFn may deadlock. + * + * If the server is shut down during the operation, headersFn may + * be called with a 503, if it has not already been called. + * + * @param fn The function to be called with the headers and status code. + */ + using HeadersFn = std::function; + void getHeaders(HeadersFn fn); + + /** + * Requests a new chunk. This can be called from any thread, and the BodyFn + * callback may also be called from any thread. BodyFn will be called in a + * loop until the Buffer passed to it is fully drained. When 'false' is + * passed as the second arg to BodyFn, that signifies the end of the + * response, and nextChunk must not be called again. + * + * BodyFn will not be called after cancel(). It is invalid to + * to call nextChunk from within BodyFn -- the caller must trigger + * such a call on another thread, after BodyFn returns. Calling + * nextChunk from BodyFn may deadlock. + * + * If the server is shut down during the operation, bodyFn will + * be called with an empty body and 'false' for more_data, if + * this has not already occurred. + * + * @param fn A function to be called on each chunk. + */ + using BodyFn = std::function; + void nextChunk(BodyFn fn); + + /** + * Requests that any outstanding callbacks be dropped. This can be called + * when the context in which the request is made is destroyed. This enables + * an application to implement a. The Response itself is held as a + * shared_ptr as that makes it much easier to manage cancellation across + * multiple threads. + */ + void cancel(); + + /** + * @return whether the request was cancelled. + */ + bool cancelled() const; + +private: + /** + * Called when the server is terminated. This calls any outstanding + * callbacks to be called. If nextChunk is called after termination, + * its callback is called false for the second arg, indicating + * end of stream. + */ + void terminate(); + + void requestHeaders(); + void requestNextChunk(); + void sendAbortChunkLockHeld() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + void sendErrorLockHeld() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + Server::Instance& server_; + OptRef opt_admin_; + Buffer::OwnedImpl response_; + Http::Code code_; + Server::Admin::RequestPtr request_; + Http::RequestHeaderMapPtr request_headers_{Http::RequestHeaderMapImpl::create()}; + Http::ResponseHeaderMapPtr response_headers_{Http::ResponseHeaderMapImpl::create()}; + bool more_data_ = true; + + // True if cancel() was explicitly called by the user; headers and body + // callbacks are never called after cancel(). + bool cancelled_ ABSL_GUARDED_BY(mutex_) = false; + + // True if the Envoy server has stopped running its main loop. Headers and + // body requests can be initiated and called back are called after terminate, + // so callers do not have to special case this -- the request will simply fail + // with an empty response. + bool terminated_ ABSL_GUARDED_BY(mutex_) = false; + + // Used to indicate whether the body function has been called with false + // as its second argument. That must always happen at most once, even + // if terminate races with the normal end-of-stream marker. more=false + // may never be sent if the request is cancelled, nor deleted prior to + // it being requested. + bool sent_end_stream_ ABSL_GUARDED_BY(mutex_) = false; + + HeadersFn headers_fn_ ABSL_GUARDED_BY(mutex_); + BodyFn body_fn_ ABSL_GUARDED_BY(mutex_); + mutable absl::Mutex mutex_; + + SharedPtrSet shared_response_set_; +}; +using AdminResponseSharedPtr = std::shared_ptr; + +} // namespace Envoy diff --git a/source/exe/main_common.cc b/source/exe/main_common.cc index d18a575f2dd0..87b3342f3335 100644 --- a/source/exe/main_common.cc +++ b/source/exe/main_common.cc @@ -10,6 +10,7 @@ #include "source/common/common/compiler_requirements.h" #include "source/common/common/logger.h" #include "source/common/common/perf_annotation.h" +#include "source/common/common/thread.h" #include "source/common/network/utility.h" #include "source/common/stats/thread_local_store.h" #include "source/exe/platform_impl.h" @@ -56,26 +57,46 @@ MainCommonBase::MainCommonBase(const Server::Options& options, Event::TimeSystem std::unique_ptr process_context) : StrippedMainBase(options, time_system, listener_hooks, component_factory, std::move(platform_impl), std::move(random_generator), - std::move(process_context), createFunction()) {} + std::move(process_context), createFunction()) +#ifdef ENVOY_ADMIN_FUNCTIONALITY + , + shared_response_set_(std::make_shared()) +#endif +{ +} bool MainCommonBase::run() { + // Avoid returning from inside switch cases to minimize uncovered lines + // while avoiding gcc warnings by hitting the final return. + bool ret = false; + switch (options_.mode()) { case Server::Mode::Serve: runServer(); - return true; +#ifdef ENVOY_ADMIN_FUNCTIONALITY + shared_response_set_->terminateAdminRequests(); +#endif + ret = true; + break; case Server::Mode::Validate: - return Server::validateConfig( + ret = Server::validateConfig( options_, Network::Utility::getLocalAddress(options_.localAddressIpVersion()), component_factory_, platform_impl_->threadFactory(), platform_impl_->fileSystem(), process_context_ ? ProcessContextOptRef(std::ref(*process_context_)) : absl::nullopt); + break; case Server::Mode::InitOnly: PERF_DUMP(); - return true; + ret = true; + break; } - return false; // for gcc. + return ret; } #ifdef ENVOY_ADMIN_FUNCTIONALITY + +// This request variant buffers the entire response in one string. New uses +// should opt for the streaming version below, where an AdminResponse object +// is created and used to stream data with flow-control. void MainCommonBase::adminRequest(absl::string_view path_and_query, absl::string_view method, const AdminRequestFn& handler) { std::string path_and_query_buf = std::string(path_and_query); @@ -89,6 +110,14 @@ void MainCommonBase::adminRequest(absl::string_view path_and_query, absl::string handler(*response_headers, body); }); } + +AdminResponseSharedPtr MainCommonBase::adminRequest(absl::string_view path_and_query, + absl::string_view method) { + auto response = + std::make_shared(*server(), path_and_query, method, shared_response_set_); + shared_response_set_->attachResponse(response.get()); + return response; +} #endif MainCommon::MainCommon(const std::vector& args) diff --git a/source/exe/main_common.h b/source/exe/main_common.h index 349decdec0cc..500f293ce7f1 100644 --- a/source/exe/main_common.h +++ b/source/exe/main_common.h @@ -10,6 +10,10 @@ #include "source/common/stats/symbol_table.h" #include "source/common/stats/thread_local_store.h" #include "source/common/thread_local/thread_local_impl.h" + +#ifdef ENVOY_ADMIN_FUNCTIONALITY +#include "source/exe/admin_response.h" +#endif #include "source/exe/process_wide.h" #include "source/exe/stripped_main_base.h" #include "source/server/listener_hooks.h" @@ -37,20 +41,45 @@ class MainCommonBase : public StrippedMainBase { using AdminRequestFn = std::function; - // Makes an admin-console request by path, calling handler() when complete. - // The caller can initiate this from any thread, but it posts the request - // onto the main thread, so the handler is called asynchronously. - // - // This is designed to be called from downstream consoles, so they can access - // the admin console information stream without opening up a network port. - // - // This should only be called while run() is active; ensuring this is the - // responsibility of the caller. - // - // TODO(jmarantz): consider std::future for encapsulating this delayed request - // semantics, rather than a handler callback. + /** + * Makes an admin-console request by path, calling handler() when complete. + * The caller can initiate this from any thread, but it posts the request + * onto the main thread, so the handler is called asynchronously. + * + * This is designed to be called from downstream consoles, so they can access + * the admin console information stream without opening up a network port. + * + * This should only be called while run() is active; ensuring this is the + * responsibility of the caller. + * + * TODO(jmarantz): consider std::future for encapsulating this delayed request + * semantics, rather than a handler callback. + * + * Consider using the 2-arg version of adminRequest, below, which enables + * streaming of large responses one chunk at a time, without holding + * potentially huge response text in memory. + * + * @param path_and_query the URL to send to admin, including any query params. + * @param method the HTTP method: "GET" or "POST" + * @param handler an async callback that will be sent the serialized headers + * and response. + */ void adminRequest(absl::string_view path_and_query, absl::string_view method, const AdminRequestFn& handler); + + /** + * Initiates a streaming response to an admin request. The caller interacts + * with the returned AdminResponse object, and can thus control the pace of + * handling chunks of response text. + * + * @param path_and_query the URL to send to admin, including any query params. + * @param method the HTTP method: "GET" or "POST" + * @return AdminResponseSharedPtr the response object + */ + AdminResponseSharedPtr adminRequest(absl::string_view path_and_query, absl::string_view method); + +private: + AdminResponse::SharedPtrSet shared_response_set_; #endif }; @@ -82,6 +111,9 @@ class MainCommon { const MainCommonBase::AdminRequestFn& handler) { base_.adminRequest(path_and_query, method, handler); } + AdminResponseSharedPtr adminRequest(absl::string_view path_and_query, absl::string_view method) { + return base_.adminRequest(path_and_query, method); + } #endif static std::string hotRestartVersion(bool hot_restart_enabled); diff --git a/source/extensions/access_loggers/fluentd/config.cc b/source/extensions/access_loggers/fluentd/config.cc index e9929ab0c318..a61cfb54227c 100644 --- a/source/extensions/access_loggers/fluentd/config.cc +++ b/source/extensions/access_loggers/fluentd/config.cc @@ -45,6 +45,16 @@ FluentdAccessLogFactory::createAccessLogInstance(const Protobuf::Message& config throw EnvoyException(fmt::format("cluster '{}' was not found", proto_config.cluster())); } + if (proto_config.has_retry_options() && proto_config.retry_options().has_backoff_options()) { + status = BackOffStrategyUtils::validateBackOffStrategyConfig( + proto_config.retry_options().backoff_options(), DefaultBaseBackoffIntervalMs, + DefaultMaxBackoffIntervalFactor); + if (!status.ok()) { + throw EnvoyException( + "max_backoff_interval must be greater or equal to base_backoff_interval"); + } + } + // Supporting nested object serialization is more complex with MessagePack. // Using an already existing JSON formatter, and later converting the JSON string to a msgpack // payload. @@ -60,6 +70,7 @@ FluentdAccessLogFactory::createAccessLogInstance(const Protobuf::Message& config std::move(filter), std::move(fluentd_formatter), std::make_shared(proto_config), context.serverFactoryContext().threadLocal(), + context.serverFactoryContext().api().randomGenerator(), getAccessLoggerCacheSingleton(context.serverFactoryContext())); } diff --git a/source/extensions/access_loggers/fluentd/fluentd_access_log_impl.cc b/source/extensions/access_loggers/fluentd/fluentd_access_log_impl.cc index ce1a96694d90..a6f312d66d96 100644 --- a/source/extensions/access_loggers/fluentd/fluentd_access_log_impl.cc +++ b/source/extensions/access_loggers/fluentd/fluentd_access_log_impl.cc @@ -12,17 +12,26 @@ namespace Fluentd { using MessagePackBuffer = msgpack::sbuffer; using MessagePackPacker = msgpack::packer; -FluentdAccessLoggerImpl::FluentdAccessLoggerImpl(Tcp::AsyncTcpClientPtr client, +FluentdAccessLoggerImpl::FluentdAccessLoggerImpl(Upstream::ThreadLocalCluster& cluster, + Tcp::AsyncTcpClientPtr client, Event::Dispatcher& dispatcher, const FluentdAccessLogConfig& config, + BackOffStrategyPtr backoff_strategy, Stats::Scope& parent_scope) : tag_(config.tag()), id_(dispatcher.name()), + max_connect_attempts_( + config.has_retry_options() && config.retry_options().has_max_connect_attempts() + ? absl::optional(config.retry_options().max_connect_attempts().value()) + : absl::nullopt), stats_scope_(parent_scope.createScope(config.stat_prefix())), fluentd_stats_( {ACCESS_LOG_FLUENTD_STATS(POOL_COUNTER(*stats_scope_), POOL_GAUGE(*stats_scope_))}), - client_(std::move(client)), - buffer_flush_interval_msec_(PROTOBUF_GET_MS_OR_DEFAULT(config, buffer_flush_interval, 1000)), - max_buffer_size_bytes_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, buffer_size_bytes, 16384)), + cluster_(cluster), backoff_strategy_(std::move(backoff_strategy)), client_(std::move(client)), + buffer_flush_interval_msec_( + PROTOBUF_GET_MS_OR_DEFAULT(config, buffer_flush_interval, DefaultBufferFlushIntervalMs)), + max_buffer_size_bytes_( + PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, buffer_size_bytes, DefaultMaxBufferSize)), + retry_timer_(dispatcher.createTimer([this]() -> void { onBackoffCallback(); })), flush_timer_(dispatcher.createTimer([this]() { flush(); flush_timer_->enableTimer(buffer_flush_interval_msec_); @@ -35,27 +44,21 @@ void FluentdAccessLoggerImpl::onEvent(Network::ConnectionEvent event) { connecting_ = false; if (event == Network::ConnectionEvent::Connected) { + backoff_strategy_->reset(); + retry_timer_->disableTimer(); flush(); } else if (event == Network::ConnectionEvent::LocalClose || event == Network::ConnectionEvent::RemoteClose) { ENVOY_LOG(debug, "upstream connection was closed"); - // TODO(ohadvano): add an option to reconnect to the upstream, if configured - fluentd_stats_.connections_closed_.inc(); - disconnected_ = true; - clearBuffer(); - - ASSERT(flush_timer_ != nullptr); - flush_timer_->disableTimer(); + maybeReconnect(); } } void FluentdAccessLoggerImpl::log(EntryPtr&& entry) { - if (disconnected_) { + if (disconnected_ || approximate_message_size_bytes_ >= max_buffer_size_bytes_) { fluentd_stats_.entries_lost_.inc(); // We will lose the data deliberately so the buffer doesn't grow infinitely. - // Since the client is disconnected, there's nothing much we can do with the data anyway. - // TODO(ohadvano): add an option to reconnect to the upstream, if configured return; } @@ -63,6 +66,8 @@ void FluentdAccessLoggerImpl::log(EntryPtr&& entry) { entries_.push_back(std::move(entry)); fluentd_stats_.entries_buffered_.inc(); if (approximate_message_size_bytes_ >= max_buffer_size_bytes_) { + // If we exceeded the buffer limit, immediately flush the logs instead of waiting for + // the next flush interval, to allow new logs to be buffered. flush(); } } @@ -76,8 +81,7 @@ void FluentdAccessLoggerImpl::flush() { } if (!client_->connected()) { - connecting_ = true; - client_->connect(); + connect(); return; } @@ -102,6 +106,42 @@ void FluentdAccessLoggerImpl::flush() { clearBuffer(); } +void FluentdAccessLoggerImpl::connect() { + connect_attempts_++; + if (!client_->connect()) { + ENVOY_LOG(debug, "no healthy upstream"); + maybeReconnect(); + return; + } + + connecting_ = true; +} + +void FluentdAccessLoggerImpl::maybeReconnect() { + if (max_connect_attempts_.has_value() && connect_attempts_ >= max_connect_attempts_) { + ENVOY_LOG(debug, "max connection attempts reached"); + cluster_.info()->trafficStats()->upstream_cx_connect_attempts_exceeded_.inc(); + setDisconnected(); + return; + } + + uint64_t next_backoff_ms = backoff_strategy_->nextBackOffMs(); + retry_timer_->enableTimer(std::chrono::milliseconds(next_backoff_ms)); + ENVOY_LOG(debug, "reconnect attempt scheduled for {} ms", next_backoff_ms); +} + +void FluentdAccessLoggerImpl::onBackoffCallback() { + fluentd_stats_.reconnect_attempts_.inc(); + this->connect(); +} + +void FluentdAccessLoggerImpl::setDisconnected() { + disconnected_ = true; + clearBuffer(); + ASSERT(flush_timer_ != nullptr); + flush_timer_->disableTimer(); +} + void FluentdAccessLoggerImpl::clearBuffer() { entries_.clear(); approximate_message_size_bytes_ = 0; @@ -117,7 +157,8 @@ FluentdAccessLoggerCacheImpl::FluentdAccessLoggerCacheImpl( } FluentdAccessLoggerSharedPtr -FluentdAccessLoggerCacheImpl::getOrCreateLogger(const FluentdAccessLogConfigSharedPtr config) { +FluentdAccessLoggerCacheImpl::getOrCreateLogger(const FluentdAccessLogConfigSharedPtr config, + Random::RandomGenerator& random) { auto& cache = tls_slot_->getTyped(); const auto cache_key = MessageUtil::hash(*config); const auto it = cache.access_loggers_.find(cache_key); @@ -125,25 +166,41 @@ FluentdAccessLoggerCacheImpl::getOrCreateLogger(const FluentdAccessLogConfigShar return it->second.lock(); } + auto* cluster = cluster_manager_.getThreadLocalCluster(config->cluster()); auto client = - cluster_manager_.getThreadLocalCluster(config->cluster()) - ->tcpAsyncClient(nullptr, std::make_shared(false)); + cluster->tcpAsyncClient(nullptr, std::make_shared(false)); + + uint64_t base_interval_ms = DefaultBaseBackoffIntervalMs; + uint64_t max_interval_ms = base_interval_ms * DefaultMaxBackoffIntervalFactor; + + if (config->has_retry_options() && config->retry_options().has_backoff_options()) { + base_interval_ms = PROTOBUF_GET_MS_OR_DEFAULT(config->retry_options().backoff_options(), + base_interval, DefaultBaseBackoffIntervalMs); + max_interval_ms = + PROTOBUF_GET_MS_OR_DEFAULT(config->retry_options().backoff_options(), max_interval, + base_interval_ms * DefaultMaxBackoffIntervalFactor); + } + + BackOffStrategyPtr backoff_strategy = std::make_unique( + base_interval_ms, max_interval_ms, random); const auto logger = std::make_shared( - std::move(client), cache.dispatcher_, *config, *stats_scope_); + *cluster, std::move(client), cache.dispatcher_, *config, std::move(backoff_strategy), + *stats_scope_); cache.access_loggers_.emplace(cache_key, logger); return logger; } FluentdAccessLog::FluentdAccessLog(AccessLog::FilterPtr&& filter, FluentdFormatterPtr&& formatter, const FluentdAccessLogConfigSharedPtr config, - ThreadLocal::SlotAllocator& tls, + ThreadLocal::SlotAllocator& tls, Random::RandomGenerator& random, FluentdAccessLoggerCacheSharedPtr access_logger_cache) : ImplBase(std::move(filter)), formatter_(std::move(formatter)), tls_slot_(tls.allocateSlot()), config_(config), access_logger_cache_(access_logger_cache) { tls_slot_->set( - [config = config_, access_logger_cache = access_logger_cache_](Event::Dispatcher&) { - return std::make_shared(access_logger_cache->getOrCreateLogger(config)); + [config = config_, &random, access_logger_cache = access_logger_cache_](Event::Dispatcher&) { + return std::make_shared( + access_logger_cache->getOrCreateLogger(config, random)); }); } diff --git a/source/extensions/access_loggers/fluentd/fluentd_access_log_impl.h b/source/extensions/access_loggers/fluentd/fluentd_access_log_impl.h index 11a03530d32d..95e84632177f 100644 --- a/source/extensions/access_loggers/fluentd/fluentd_access_log_impl.h +++ b/source/extensions/access_loggers/fluentd/fluentd_access_log_impl.h @@ -18,6 +18,11 @@ using FluentdAccessLogConfig = envoy::extensions::access_loggers::fluentd::v3::FluentdAccessLogConfig; using FluentdAccessLogConfigSharedPtr = std::shared_ptr; +static constexpr uint64_t DefaultBaseBackoffIntervalMs = 500; +static constexpr uint64_t DefaultMaxBackoffIntervalFactor = 10; +static constexpr uint64_t DefaultBufferFlushIntervalMs = 1000; +static constexpr uint64_t DefaultMaxBufferSize = 16384; + // Entry represents a single Fluentd message, msgpack format based, as specified in: // https://github.com/fluent/fluentd/wiki/Forward-Protocol-Specification-v1#entry class Entry { @@ -49,6 +54,7 @@ using FluentdAccessLoggerSharedPtr = std::shared_ptr; COUNTER(entries_lost) \ COUNTER(entries_buffered) \ COUNTER(events_sent) \ + COUNTER(reconnect_attempts) \ COUNTER(connections_closed) struct AccessLogFluentdStats { @@ -59,8 +65,9 @@ class FluentdAccessLoggerImpl : public Tcp::AsyncTcpClientCallbacks, public FluentdAccessLogger, public Logger::Loggable { public: - FluentdAccessLoggerImpl(Tcp::AsyncTcpClientPtr client, Event::Dispatcher& dispatcher, - const FluentdAccessLogConfig& config, Stats::Scope& parent_scope); + FluentdAccessLoggerImpl(Upstream::ThreadLocalCluster& cluster, Tcp::AsyncTcpClientPtr client, + Event::Dispatcher& dispatcher, const FluentdAccessLogConfig& config, + BackOffStrategyPtr backoff_strategy, Stats::Scope& parent_scope); // Tcp::AsyncTcpClientCallbacks void onEvent(Network::ConnectionEvent event) override; @@ -73,19 +80,28 @@ class FluentdAccessLoggerImpl : public Tcp::AsyncTcpClientCallbacks, private: void flush(); + void connect(); + void maybeReconnect(); + void onBackoffCallback(); + void setDisconnected(); void clearBuffer(); bool disconnected_ = false; bool connecting_ = false; std::string tag_; std::string id_; + uint32_t connect_attempts_{0}; + absl::optional max_connect_attempts_{}; const Stats::ScopeSharedPtr stats_scope_; AccessLogFluentdStats fluentd_stats_; std::vector entries_; uint64_t approximate_message_size_bytes_ = 0; + Upstream::ThreadLocalCluster& cluster_; + const BackOffStrategyPtr backoff_strategy_; const Tcp::AsyncTcpClientPtr client_; const std::chrono::milliseconds buffer_flush_interval_msec_; const uint64_t max_buffer_size_bytes_; + const Event::TimerPtr retry_timer_; const Event::TimerPtr flush_timer_; }; @@ -98,7 +114,8 @@ class FluentdAccessLoggerCache { * @return FluentdAccessLoggerSharedPtr ready for logging requests. */ virtual FluentdAccessLoggerSharedPtr - getOrCreateLogger(const FluentdAccessLogConfigSharedPtr config) PURE; + getOrCreateLogger(const FluentdAccessLogConfigSharedPtr config, + Random::RandomGenerator& random) PURE; }; using FluentdAccessLoggerCacheSharedPtr = std::shared_ptr; @@ -108,8 +125,8 @@ class FluentdAccessLoggerCacheImpl : public Singleton::Instance, public FluentdA FluentdAccessLoggerCacheImpl(Upstream::ClusterManager& cluster_manager, Stats::Scope& parent_scope, ThreadLocal::SlotAllocator& tls); - FluentdAccessLoggerSharedPtr - getOrCreateLogger(const FluentdAccessLogConfigSharedPtr config) override; + FluentdAccessLoggerSharedPtr getOrCreateLogger(const FluentdAccessLogConfigSharedPtr config, + Random::RandomGenerator& random) override; private: /** @@ -135,6 +152,7 @@ class FluentdAccessLog : public Common::ImplBase { public: FluentdAccessLog(AccessLog::FilterPtr&& filter, FluentdFormatterPtr&& formatter, const FluentdAccessLogConfigSharedPtr config, ThreadLocal::SlotAllocator& tls, + Random::RandomGenerator& random, FluentdAccessLoggerCacheSharedPtr access_logger_cache); private: diff --git a/source/extensions/all_extensions.bzl b/source/extensions/all_extensions.bzl index 8cbdeb844d81..f239a04b9537 100644 --- a/source/extensions/all_extensions.bzl +++ b/source/extensions/all_extensions.bzl @@ -36,6 +36,7 @@ _core_extensions = [ "envoy.network.dns_resolver.cares", "envoy.network.dns_resolver.apple", "envoy.load_balancing_policies.round_robin", + "envoy.transport_sockets.tls", ] # Return all core extensions to be compiled into Envoy. diff --git a/source/extensions/common/aws/signer_base_impl.h b/source/extensions/common/aws/signer_base_impl.h index 7dff4a6aed2b..a8bb754facf2 100644 --- a/source/extensions/common/aws/signer_base_impl.h +++ b/source/extensions/common/aws/signer_base_impl.h @@ -63,18 +63,22 @@ using AwsSigningHeaderExclusionVector = std::vector { public: SignerBaseImpl(absl::string_view service_name, absl::string_view region, - const CredentialsProviderSharedPtr& credentials_provider, TimeSource& time_source, + const CredentialsProviderSharedPtr& credentials_provider, + Server::Configuration::CommonFactoryContext& context, const AwsSigningHeaderExclusionVector& matcher_config, const bool query_string = false, const uint16_t expiration_time = SignatureQueryParameterValues::DefaultExpiration) - : service_name_(service_name), region_(region), credentials_provider_(credentials_provider), - query_string_(query_string), expiration_time_(expiration_time), time_source_(time_source), + : service_name_(service_name), region_(region), + excluded_header_matchers_(defaultMatchers(context)), + credentials_provider_(credentials_provider), query_string_(query_string), + expiration_time_(expiration_time), time_source_(context.timeSource()), long_date_formatter_(std::string(SignatureConstants::LongDateFormat)), short_date_formatter_(std::string(SignatureConstants::ShortDateFormat)) { for (const auto& matcher : matcher_config) { excluded_header_matchers_.emplace_back( - std::make_unique>( - matcher)); + std::make_unique< + Matchers::StringMatcherImplWithContext>( + matcher, context)); } } @@ -128,14 +132,16 @@ class SignerBaseImpl : public Signer, public Logger::Loggable { const std::map& signed_headers, const uint16_t expiration_time) const; - std::vector defaultMatchers() const { + std::vector + defaultMatchers(Server::Configuration::CommonFactoryContext& context) 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)); + std::make_unique< + Matchers::StringMatcherImplWithContext>( + m, context)); } return matcher_ptrs; } @@ -145,7 +151,7 @@ class SignerBaseImpl : public Signer, public Logger::Loggable { 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(); + std::vector excluded_header_matchers_; CredentialsProviderSharedPtr credentials_provider_; const bool query_string_; const uint16_t expiration_time_; diff --git a/source/extensions/common/aws/sigv4_signer_impl.h b/source/extensions/common/aws/sigv4_signer_impl.h index f9e047ec7754..68b78dd8037f 100644 --- a/source/extensions/common/aws/sigv4_signer_impl.h +++ b/source/extensions/common/aws/sigv4_signer_impl.h @@ -41,11 +41,12 @@ using AwsSigningHeaderExclusionVector = std::vector(MagicNumber); // Serialize type and flag. - uint8_t flag = static_cast(context.serializeType()); + uint8_t flag = static_cast(SerializeType::Hessian2); switch (context.messageType()) { case MessageType::Response: @@ -167,7 +166,6 @@ DecodeStatus DubboCodec::decodeHeader(Buffer::Instance& buffer, MessageMetadata& absl::StrCat("invalid dubbo message serialization type ", static_cast::type>(serialize_type))); } - context->setSerializeType(serialize_type); // Initial basic type of message. MessageType type = @@ -277,7 +275,6 @@ MessageMetadataSharedPtr DirectResponseUtil::heartbeatResponse(MessageMetadata& auto context = std::make_unique(); // Set context. - context->setSerializeType(request_context.serializeType()); context->setMessageType(MessageType::HeartbeatResponse); context->setResponseStatus(ResponseStatus::Ok); context->setRequestId(request_context.requestId()); @@ -299,7 +296,6 @@ MessageMetadataSharedPtr DirectResponseUtil::localResponse(MessageMetadata& requ auto context = std::make_unique(); // Set context. - context->setSerializeType(request_context.serializeType()); if (status != ResponseStatus::Ok) { context->setMessageType(MessageType::Exception); } else if (type.has_value() && @@ -309,16 +305,17 @@ MessageMetadataSharedPtr DirectResponseUtil::localResponse(MessageMetadata& requ } else { context->setMessageType(MessageType::Response); } + context->setResponseStatus(status); context->setRequestId(request_context.requestId()); // Set response. - auto response = std::make_unique(); - if (status == ResponseStatus::Ok && type.has_value()) { + auto response = std::make_unique(); + if (status == ResponseStatus::Ok) { // No response type for non-Ok response. - response->setResponseType(type.value()); + response->setResponseType(type.value_or(RpcResponseType::ResponseWithValue)); } - response->setLocalRawMessage(content); + response->content().initialize(std::make_unique(content), {}); auto metadata = std::make_shared(); metadata->setContext(std::move(context)); diff --git a/source/extensions/common/dubbo/hessian2_serializer_impl.cc b/source/extensions/common/dubbo/hessian2_serializer_impl.cc index 506e315c8d94..0e49a62bd4f8 100644 --- a/source/extensions/common/dubbo/hessian2_serializer_impl.cc +++ b/source/extensions/common/dubbo/hessian2_serializer_impl.cc @@ -8,7 +8,6 @@ #include "source/common/common/macros.h" #include "source/extensions/common/dubbo/hessian2_utils.h" #include "source/extensions/common/dubbo/message.h" -#include "source/extensions/common/dubbo/message_impl.h" #include "source/extensions/common/dubbo/metadata.h" #include "hessian2/object.hpp" @@ -32,7 +31,6 @@ RpcRequestPtr Hessian2SerializerImpl::deserializeRpcRequest(Buffer::Instance& bu ASSERT(context.messageType() == MessageType::Request || context.messageType() == MessageType::Oneway); ASSERT(context.bodySize() <= buffer.length()); - auto request = std::make_unique(); Hessian2::Decoder decoder(std::make_unique(buffer)); @@ -42,9 +40,11 @@ RpcRequestPtr Hessian2SerializerImpl::deserializeRpcRequest(Buffer::Instance& bu auto service_version = decoder.decode(); auto method_name = decoder.decode(); - if (context.bodySize() < decoder.offset()) { - throw EnvoyException(fmt::format("RpcRequest size({}) larger than body size({})", - decoder.offset(), context.bodySize())); + const auto decoded_size = decoder.offset(); + + if (context.bodySize() < decoded_size) { + throw EnvoyException(fmt::format("RpcRequest size({}) larger than body size({})", decoded_size, + context.bodySize())); } if (dubbo_version == nullptr || service_name == nullptr || service_version == nullptr || @@ -52,46 +52,11 @@ RpcRequestPtr Hessian2SerializerImpl::deserializeRpcRequest(Buffer::Instance& bu throw EnvoyException(fmt::format("RpcRequest has no request metadata")); } - request->setServiceName(*service_name); - request->setServiceVersion(*service_version); - request->setMethodName(*method_name); - - // Move original request message to the raw buffer to delay the decoding of complex body. - request->messageBuffer().move(buffer, context.bodySize()); - const size_t parsed_size = decoder.offset(); - auto delayed_decoder = std::make_shared( - std::make_unique(request->messageBuffer(), parsed_size)); - - request->setParametersLazyCallback([delayed_decoder]() -> RpcRequestImpl::ParametersPtr { - auto params = std::make_unique(); - - if (auto types = delayed_decoder->decode(); types != nullptr && !types->empty()) { - uint32_t number = Hessian2Utils::getParametersNumber(*types); - for (uint32_t i = 0; i < number; i++) { - if (auto result = delayed_decoder->decode(); result != nullptr) { - params->push_back(std::move(result)); - } else { - throw EnvoyException("Cannot parse RpcRequest parameter from buffer"); - } - } - } - return params; - }); - - request->setAttachmentLazyCallback([delayed_decoder]() -> RpcRequestImpl::AttachmentPtr { - size_t offset = delayed_decoder->offset(); - - auto result = delayed_decoder->decode(); - if (result != nullptr && result->type() == Hessian2::Object::Type::UntypedMap) { - return std::make_unique( - RpcRequestImpl::Attachment::MapPtr{ - dynamic_cast(result.release())}, - offset); - } else { - return std::make_unique( - std::make_unique(), offset); - } - }); + buffer.drain(decoded_size); + + auto request = std::make_unique(std::move(*dubbo_version), std::move(*service_name), + std::move(*service_version), std::move(*method_name)); + request->content().initialize(buffer, context.bodySize() - decoded_size); return request; } @@ -106,29 +71,29 @@ RpcResponsePtr Hessian2SerializerImpl::deserializeRpcResponse(Buffer::Instance& return nullptr; } + ASSERT(context.hasResponseStatus()); + // Handle normal response or exception response. ASSERT(context.messageType() == MessageType::Response || context.messageType() == MessageType::Exception); - auto response = std::make_unique(); + + ASSERT(context.hasResponseStatus()); + auto response = std::make_unique(); // Non `Ok` response body has no response type info and skip deserialization. if (context.messageType() == MessageType::Exception) { - ASSERT(context.hasResponseStatus()); ASSERT(context.responseStatus() != ResponseStatus::Ok); - response->messageBuffer().move(buffer, context.bodySize()); + response->content().initialize(buffer, context.bodySize()); return response; } - bool has_value = true; - Hessian2::Decoder decoder(std::make_unique(buffer)); auto type_value = decoder.decode(); if (type_value == nullptr) { throw EnvoyException(fmt::format("Cannot parse RpcResponse type from buffer")); } - RpcResponseType type = static_cast(*type_value); - response->setResponseType(type); + const RpcResponseType type = static_cast(*type_value); switch (type) { case RpcResponseType::ResponseWithException: @@ -136,8 +101,6 @@ RpcResponsePtr Hessian2SerializerImpl::deserializeRpcResponse(Buffer::Instance& context.setMessageType(MessageType::Exception); break; case RpcResponseType::ResponseWithNullValue: - has_value = false; - FALLTHRU; case RpcResponseType::ResponseNullValueWithAttachments: case RpcResponseType::ResponseWithValue: case RpcResponseType::ResponseValueWithAttachments: @@ -146,26 +109,25 @@ RpcResponsePtr Hessian2SerializerImpl::deserializeRpcResponse(Buffer::Instance& throw EnvoyException(fmt::format("not supported return type {}", static_cast(type))); } - size_t total_size = decoder.offset(); + const auto decoded_size = decoder.offset(); - if (context.bodySize() < total_size) { - throw EnvoyException(fmt::format("RpcResponse size({}) large than body size({})", total_size, + if (context.bodySize() < decoded_size) { + throw EnvoyException(fmt::format("RpcResponse size({}) large than body size({})", decoded_size, context.bodySize())); } - if (!has_value && context.bodySize() != total_size) { - throw EnvoyException( - fmt::format("RpcResponse is no value, but the rest of the body size({}) not equal 0", - (context.bodySize() - total_size))); - } + buffer.drain(decoded_size); - response->messageBuffer().move(buffer, context.bodySize()); + response->setResponseType(type); + response->content().initialize(buffer, context.bodySize() - decoded_size); return response; } void Hessian2SerializerImpl::serializeRpcResponse(Buffer::Instance& buffer, MessageMetadata& metadata) { ASSERT(metadata.hasContext()); + ASSERT(metadata.context().hasResponseStatus()); + const auto& context = metadata.context(); if (context.heartbeat()) { @@ -173,22 +135,13 @@ void Hessian2SerializerImpl::serializeRpcResponse(Buffer::Instance& buffer, return; } - ASSERT(dynamic_cast(&metadata.mutableResponse()) != nullptr); - auto* typed_response_info = dynamic_cast(&metadata.mutableResponse()); - - if (typed_response_info->localRawMessage().has_value()) { - // Local direct response. - Hessian2::Encoder encoder(std::make_unique(buffer)); - - if (typed_response_info->responseType().has_value()) { - encoder.encode(static_cast(typed_response_info->responseType().value())); - } - encoder.encode(typed_response_info->localRawMessage().value()); - } else { - // Update of dubbo response is not supported for now. And there is no retry for response - // encoding. So, it is safe to move the data directly. - buffer.move(typed_response_info->messageBuffer()); + ASSERT(metadata.hasResponse()); + if (auto type = metadata.response().responseType(); type.has_value()) { + ASSERT(metadata.context().responseStatus() == ResponseStatus::Ok); + buffer.writeByte(0x90 + static_cast(type.value())); } + + buffer.add(metadata.response().content().buffer()); } void Hessian2SerializerImpl::serializeRpcRequest(Buffer::Instance& buffer, @@ -206,25 +159,14 @@ void Hessian2SerializerImpl::serializeRpcRequest(Buffer::Instance& buffer, ASSERT(metadata.context().messageType() == MessageType::Request || metadata.context().messageType() == MessageType::Oneway); - ASSERT(dynamic_cast(&metadata.mutableRequest()) != nullptr); - auto* typed_request_info = dynamic_cast(&metadata.mutableRequest()); + Hessian2::Encoder encoder(std::make_unique(buffer)); - // Create a copy for possible retry. - Buffer::OwnedImpl copy_of_message = typed_request_info->messageBuffer(); + encoder.encode(metadata.request().version()); + encoder.encode(metadata.request().service()); + encoder.encode(metadata.request().serviceVersion()); + encoder.encode(metadata.request().method()); - if (typed_request_info->hasAttachment() && typed_request_info->attachment().attachmentUpdated()) { - const size_t attachment_offset = typed_request_info->attachment().attachmentOffset(); - ASSERT(attachment_offset <= copy_of_message.length()); - - // Move the parameters to buffer. - buffer.move(copy_of_message, attachment_offset); - - // Re-encode the dubbo attachment. - Hessian2::Encoder encoder(std::make_unique(buffer)); - encoder.encode(typed_request_info->attachment().attachment()); - } else { - buffer.move(copy_of_message); - } + buffer.add(metadata.request().content().buffer()); } } // namespace Dubbo diff --git a/source/extensions/common/dubbo/hessian2_serializer_impl.h b/source/extensions/common/dubbo/hessian2_serializer_impl.h index 1f9354ff93d2..974f41ac4dc6 100644 --- a/source/extensions/common/dubbo/hessian2_serializer_impl.h +++ b/source/extensions/common/dubbo/hessian2_serializer_impl.h @@ -1,6 +1,6 @@ #pragma once -#include "source/extensions/common/dubbo/message_impl.h" +#include "source/extensions/common/dubbo/message.h" #include "source/extensions/common/dubbo/serializer.h" namespace Envoy { diff --git a/source/extensions/common/dubbo/message.cc b/source/extensions/common/dubbo/message.cc new file mode 100644 index 000000000000..a85a0d28cb50 --- /dev/null +++ b/source/extensions/common/dubbo/message.cc @@ -0,0 +1,440 @@ +#include "source/extensions/common/dubbo/message.h" + +#include "source/common/common/logger.h" +#include "source/extensions/common/dubbo/hessian2_utils.h" + +namespace Envoy { +namespace Extensions { +namespace Common { +namespace Dubbo { + +void RequestContent::initialize(Buffer::Instance& buffer, uint64_t length) { + ASSERT(content_buffer_.length() == 0, "content buffer has been initialized"); + + content_buffer_.move(buffer, length); + + // Clear the types, arguments and attachments. + types_.clear(); + argvs_.clear(); + attachs_.clear(); + + // Set both decoded and updated to false since the content has been initialized + // by raw buffer. + decoded_ = false; + updated_ = false; +} + +void RequestContent::initialize(std::string&& types, ArgumentVec&& argvs, Attachments&& attachs) { + ASSERT(content_buffer_.length() == 0, "content buffer has been initialized"); + + // Set the types, arguments and attachments. + types_ = std::move(types); + argvs_ = std::move(argvs); + attachs_ = std::move(attachs); + + // Encode the types, arguments and attachments into the content buffer. + encodeEverything(); + + // Set decoded to true since the content has been initialized by types, + // arguments and attachments. + decoded_ = true; + updated_ = false; +} + +const Buffer::Instance& RequestContent::buffer() { + // Ensure the attachments in the buffer is latest. + + if (content_buffer_.length() == 0) { + encodeEverything(); + } else { + encodeAttachments(); + } + + return content_buffer_; +} + +void RequestContent::bufferMoveTo(Buffer::Instance& buffer) { buffer.move(content_buffer_); } + +const ArgumentVec& RequestContent::arguments() { + lazyDecode(); + + return argvs_; +} + +const Attachments& RequestContent::attachments() { + lazyDecode(); + + return attachs_; +} + +void RequestContent::setAttachment(absl::string_view key, absl::string_view val) { + lazyDecode(); + + updated_ = true; + attachs_[key] = val; +} + +void RequestContent::delAttachment(absl::string_view key) { + lazyDecode(); + + updated_ = true; + attachs_.erase(key); +} + +void RequestContent::lazyDecode() { + if (decoded_) { + return; + } + decoded_ = true; + + // Decode the content buffer into types, arguments and attachments. + Hessian2::Decoder decoder(std::make_unique(content_buffer_)); + + // Handle the types and arguments. + if (auto element = decoder.decode(); element != nullptr) { + uint32_t number = 0; + + if (element->type() == Hessian2::Object::Type::Integer) { + ASSERT(element->toInteger().has_value()); + if (int32_t direct_num = element->toInteger().value(); direct_num == -1) { + if (auto types = decoder.decode(); types != nullptr) { + types_ = *types; + number = Hessian2Utils::getParametersNumber(types_); + } else { + ENVOY_LOG(error, "Cannot parse RpcInvocation parameter types from buffer"); + handleBrokenValue(); + return; + } + } else if (direct_num >= 0) { + number = direct_num; + } else { + ENVOY_LOG(error, "Invalid RpcInvocation parameter number {}", direct_num); + handleBrokenValue(); + return; + } + } else if (element->type() == Hessian2::Object::Type::String) { + ASSERT(element->toString().has_value()); + types_ = element->toString().value().get(); + number = Hessian2Utils::getParametersNumber(types_); + } + + for (uint32_t i = 0; i < number; i++) { + if (auto result = decoder.decode(); result != nullptr) { + argvs_.push_back(std::move(result)); + } else { + ENVOY_LOG(error, "Cannot parse RpcInvocation parameter from buffer"); + handleBrokenValue(); + return; + } + } + } else { + ENVOY_LOG(error, "Cannot parse RpcInvocation from buffer"); + handleBrokenValue(); + return; + } + + // Record the size of the arguments in the content buffer. This is useful for + // re-encoding the attachments. + argvs_size_ = decoder.offset(); + + // Handle the attachments. + auto map = decoder.decode(); + if (map == nullptr || map->type() != Hessian2::Object::Type::UntypedMap) { + return; + } + + for (auto& [key, val] : map->toMutableUntypedMap().value().get()) { + if (key->type() != Hessian2::Object::Type::String || + val->type() != Hessian2::Object::Type::String) { + continue; + } + attachs_.emplace(std::move(key->toMutableString().value().get()), + std::move(val->toMutableString().value().get())); + } +} + +void RequestContent::encodeAttachments() { + // Do nothing if the attachments have not been updated. + if (!updated_) { + return; + } + + // Ensure the content has been decoded before re-encoding it. + lazyDecode(); + + const uint64_t buffer_length = content_buffer_.length(); + ASSERT(buffer_length > 0, "content buffer is empty"); + + // The size of arguments will be set when doing lazyDecode() or encodeEverything(). + if (buffer_length < argvs_size_) { + ENVOY_LOG(error, "arguments size {} is larger than content buffer {}", argvs_size_, + buffer_length); + handleBrokenValue(); + return; + } + + // Create a new buffer to hold the re-encoded content. + + Buffer::OwnedImpl new_content_buffer; + // Copy the types and arguments into the new buffer. + new_content_buffer.move(content_buffer_, argvs_size_); + + // Encode the attachments into the new buffer. + Hessian2::Encoder encoder(std::make_unique(new_content_buffer)); + new_content_buffer.writeByte('H'); + for (auto& [key, val] : attachs_) { + encoder.encode(key); + encoder.encode(val); + } + new_content_buffer.writeByte('Z'); + + // Clear the content buffer and move the new buffer into it. + content_buffer_.drain(content_buffer_.length()); + content_buffer_.move(new_content_buffer); + + updated_ = false; +} + +void RequestContent::encodeEverything() { + ASSERT(content_buffer_.length() == 0, "content buffer contains something"); + + // Encode the types, arguments and attachments into the content buffer. + Hessian2::Encoder encoder(std::make_unique(content_buffer_)); + + // Encode the types into the content buffer first. + if (!types_.empty()) { + encoder.encode(types_); + } else if (!argvs_.empty()) { + encoder.encode(static_cast(argvs_.size())); + } else { + encoder.encode(types_); + } + + // Encode the arguments into the content buffer. + for (auto& arg : argvs_) { + encoder.encode(*arg); + } + + // Record the size of the arguments in the content buffer. This is useful for + // re-encoding the attachments. + argvs_size_ = content_buffer_.length(); + + // Encode the attachments into the content buffer. + content_buffer_.writeByte('H'); + for (auto& [key, val] : attachs_) { + encoder.encode(key); + encoder.encode(val); + } + content_buffer_.writeByte('Z'); + + updated_ = false; +} + +void RequestContent::handleBrokenValue() { + // Because the lazy decoding is used, Envoy cannot reject the message with broken + // content. Instead, it will reset the whole content to an empty state. + + // Clear everything. + content_buffer_.drain(content_buffer_.length()); + types_.clear(); + argvs_.clear(); + attachs_.clear(); + + // Encode everything. + encodeEverything(); + + decoded_ = true; + updated_ = false; +} + +void ResponseContent::initialize(Buffer::Instance& buffer, uint64_t length) { + ASSERT(content_buffer_.length() == 0, "content buffer has been initialized"); + content_buffer_.move(buffer, length); + + // Clear the result and attachments. + result_ = nullptr; + attachs_.clear(); + + // Set both decoded and updated to false since the content has been initialized + // by raw buffer. + decoded_ = false; + updated_ = false; +} + +void ResponseContent::initialize(Hessian2::ObjectPtr&& value, Attachments&& attachs) { + ASSERT(content_buffer_.length() == 0, "content buffer has been initialized"); + + // Set the result and attachments. + result_ = std::move(value); + attachs_ = std::move(attachs); + + // Encode the result and attachments into the content buffer. + encodeEverything(); + + // Set decoded to true since the content has been initialized by result and attachments. + decoded_ = true; + updated_ = false; +} + +const Buffer::Instance& ResponseContent::buffer() { + // Ensure the attachments in the buffer is latest. + if (content_buffer_.length() == 0) { + encodeEverything(); + } else { + encodeAttachments(); + } + + return content_buffer_; +} + +void ResponseContent::bufferMoveTo(Buffer::Instance& buffer) { buffer.move(content_buffer_); } + +const Hessian2::Object* ResponseContent::result() { + lazyDecode(); + + return result_.get(); +} + +const Attachments& ResponseContent::attachments() { + lazyDecode(); + + return attachs_; +} + +void ResponseContent::setAttachment(absl::string_view key, absl::string_view val) { + lazyDecode(); + + updated_ = true; + attachs_[key] = val; +} + +void ResponseContent::delAttachment(absl::string_view key) { + lazyDecode(); + + updated_ = true; + attachs_.erase(key); +} + +void ResponseContent::lazyDecode() { + if (decoded_) { + return; + } + decoded_ = true; + + // Decode the content buffer into result and attachments. + Hessian2::Decoder decoder(std::make_unique(content_buffer_)); + + // Handle the result. + result_ = decoder.decode(); + + if (result_ == nullptr) { + ENVOY_LOG(error, "Cannot parse RpcResult from buffer"); + handleBrokenValue(); + return; + } + + // Record the size of the result in the content buffer. This is useful for + // re-encoding the attachments. + result_size_ = decoder.offset(); + + // Handle the attachments. + auto map = decoder.decode(); + if (map == nullptr || map->type() != Hessian2::Object::Type::UntypedMap) { + return; + } + + for (auto& [key, val] : map->toMutableUntypedMap().value().get()) { + if (key->type() != Hessian2::Object::Type::String || + val->type() != Hessian2::Object::Type::String) { + continue; + } + attachs_.emplace(std::move(key->toMutableString().value().get()), + std::move(val->toMutableString().value().get())); + } +} + +void ResponseContent::encodeAttachments() { + if (!updated_) { + return; + } + + // Ensure the content has been decoded before re-encoding it. + lazyDecode(); + + const uint64_t buffer_length = content_buffer_.length(); + ASSERT(buffer_length > 0, "content buffer is empty"); + + if (buffer_length < result_size_) { + ENVOY_LOG(error, "result size {} is larger than content buffer {}", result_size_, + buffer_length); + handleBrokenValue(); + return; + } + + // Create a new buffer to hold the re-encoded content. + Buffer::OwnedImpl new_content_buffer; + + // Copy the result into the new buffer. + new_content_buffer.move(content_buffer_, result_size_); + + // Encode the attachments into the new buffer. + Hessian2::Encoder encoder(std::make_unique(new_content_buffer)); + new_content_buffer.writeByte('H'); + for (auto& [key, val] : attachs_) { + encoder.encode(key); + encoder.encode(val); + } + new_content_buffer.writeByte('Z'); + + // Clear the content buffer and move the new buffer into it. + content_buffer_.drain(content_buffer_.length()); + content_buffer_.move(new_content_buffer); + + updated_ = false; +} + +void ResponseContent::encodeEverything() { + ASSERT(content_buffer_.length() == 0, "content buffer contains something"); + + // Encode the result and attachments into the content buffer. + Hessian2::Encoder encoder(std::make_unique(content_buffer_)); + if (result_ == nullptr) { + content_buffer_.writeByte('N'); + } else { + encoder.encode(*result_); + } + + // Record the size of the result in the content buffer. This is useful for + // re-encoding the attachments. + result_size_ = content_buffer_.length(); + + content_buffer_.writeByte('H'); + for (auto& [key, val] : attachs_) { + encoder.encode(key); + encoder.encode(val); + } + content_buffer_.writeByte('Z'); + + updated_ = false; +} + +void ResponseContent::handleBrokenValue() { + // Because the lazy decoding is used, Envoy cannot reject the message with broken + // content. Instead, it will reset the whole content to an empty state. + + // Clear everything. + content_buffer_.drain(content_buffer_.length()); + result_ = nullptr; + attachs_.clear(); + + // Encode everything. + encodeEverything(); + + decoded_ = true; + updated_ = false; +} + +} // namespace Dubbo +} // namespace Common +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/common/dubbo/message.h b/source/extensions/common/dubbo/message.h index 6eeff49bd189..5808e496a5cd 100644 --- a/source/extensions/common/dubbo/message.h +++ b/source/extensions/common/dubbo/message.h @@ -7,6 +7,7 @@ #include "envoy/common/pure.h" #include "source/common/buffer/buffer_impl.h" +#include "source/extensions/common/dubbo/hessian2_utils.h" #include "absl/container/node_hash_map.h" #include "absl/types/optional.h" @@ -84,29 +85,175 @@ enum class RpcResponseType : uint8_t { ResponseNullValueWithAttachments = 5, }; +using Attachments = absl::flat_hash_map; +using ArgumentVec = absl::InlinedVector; + +class RequestContent : Envoy::Logger::Loggable { +public: + // Initialize the content buffer with the given buffer and length. + void initialize(Buffer::Instance& buffer, uint64_t length); + + // Initialize the content buffer with the given types and arguments and attachments. + // The initialize() call will also encode these types and arguments into the + // content buffer. + void initialize(std::string&& types, ArgumentVec&& argvs, Attachments&& attachs); + + // Underlying content buffer. This may re-encode the result and attachments into the + // content buffer to ensure the returned buffer is up-to-date. + const Buffer::Instance& buffer(); + + // Move the content buffer to the given buffer. This only does the move and does not + // re-encode the result and attachments. + void bufferMoveTo(Buffer::Instance& buffer); + + // Get all the arguments of the request. + const ArgumentVec& arguments(); + + // Get all the attachments of the request. + const Attachments& attachments(); + + // Set the attachment with the given key and value. If the key already exists, the + // value will be updated. Otherwise, a new key-value pair will be added. + void setAttachment(absl::string_view key, absl::string_view val); + + // Remove the attachment with the given key. + void delAttachment(absl::string_view key); + +private: + // Decode the content buffer into types, arguments and attachments. The decoding is + // lazy and will be triggered when the content is accessed. + void lazyDecode(); + + // Re-encode the attachments into the content buffer. + void encodeAttachments(); + + // Re-encode the types, arguments and attachments into the content buffer. + void encodeEverything(); + + // Called when the content is broken. The whole content will be reset to an empty + // state. + void handleBrokenValue(); + + Buffer::OwnedImpl content_buffer_; + + // If the content has been decoded. This ensures the decoding is only performed once. + bool decoded_{false}; + + // If the attachments has been updated. This ensures the re-encoding is only + // when the attachment has been modified. + bool updated_{false}; + + uint64_t argvs_size_{0}; + + std::string types_; + ArgumentVec argvs_; + Attachments attachs_; +}; + /** * RpcRequest represent an rpc call. */ class RpcRequest { public: - virtual ~RpcRequest() = default; + RpcRequest(std::string&& version, std::string&& service, std::string&& service_version, + std::string&& method) + : version_(std::move(version)), service_(std::move(service)), + service_version_(std::move(service_version)), method_(std::move(method)) {} + + absl::string_view version() const { return version_; } + absl::string_view service() const { return service_; } + absl::string_view serviceVersion() const { return service_version_; } + absl::string_view method() const { return method_; } + + RequestContent& content() const { return content_; } - virtual absl::string_view serviceName() const PURE; - virtual absl::string_view methodName() const PURE; - virtual absl::string_view serviceVersion() const PURE; - virtual absl::optional serviceGroup() const PURE; +private: + std::string version_; + std::string service_; + std::string service_version_; + std::string method_; + + mutable RequestContent content_; }; using RpcRequestPtr = std::unique_ptr; +class ResponseContent : public Envoy::Logger::Loggable { +public: + // Initialize the content buffer with the given buffer and length. + void initialize(Buffer::Instance& buffer, uint64_t length); + + // Initialize the content buffer with the given result and attachments. The initialize() + // call will also encode the result and attachments into the content buffer. + void initialize(Hessian2::ObjectPtr&& value, Attachments&& attachs); + + // Underlying content buffer. This may re-encode the result and attachments into the + // content buffer to ensure the returned buffer is up-to-date. + const Buffer::Instance& buffer(); + + // Move the content buffer to the given buffer. This only does the move and does not + // re-encode the result and attachments. + void bufferMoveTo(Buffer::Instance& buffer); + + // Get the result of the response. If the content has not been decoded, the decoding + // will be triggered. + const Hessian2::Object* result(); + + // Get all the attachments of the response. + const Attachments& attachments(); + + // Set the attachment with the given key and value. If the key already exists, the + // value will be updated. Otherwise, a new key-value pair will be added. + void setAttachment(absl::string_view key, absl::string_view val); + + // Remove the attachment with the given key. + void delAttachment(absl::string_view key); + +private: + // Decode the content buffer into value and attachments. The decoding is lazy and will + // be triggered when the content is accessed. + void lazyDecode(); + + // Re-encode the attachments into the content buffer. + void encodeAttachments(); + + // Re-encode the result and attachments into the content buffer. + void encodeEverything(); + + // Called when the content is broken. The whole content will be reset to an empty + // state. + void handleBrokenValue(); + + Buffer::OwnedImpl content_buffer_; + + // If the content has been decoded. This ensures the decoding is only performed once. + bool decoded_{false}; + + // If the attachments has been updated. This ensures the re-encoding is only + // when the attachment has been modified. + bool updated_{false}; + + uint64_t result_size_{0}; + + Hessian2::ObjectPtr result_; + Attachments attachs_; +}; + /** * RpcResponse represent the result of an rpc call. */ class RpcResponse { public: - virtual ~RpcResponse() = default; + RpcResponse() = default; + + void setResponseType(RpcResponseType response_type) { response_type_ = response_type; } + absl::optional responseType() const { return response_type_; } + + ResponseContent& content() const { return content_; } - virtual absl::optional responseType() const PURE; +private: + absl::optional response_type_{}; + mutable ResponseContent content_; }; using RpcResponsePtr = std::unique_ptr; diff --git a/source/extensions/common/dubbo/message_impl.cc b/source/extensions/common/dubbo/message_impl.cc deleted file mode 100644 index 89b30dd92b4b..000000000000 --- a/source/extensions/common/dubbo/message_impl.cc +++ /dev/null @@ -1,95 +0,0 @@ -#include "source/extensions/common/dubbo/message_impl.h" - -#include - -namespace Envoy { -namespace Extensions { -namespace Common { -namespace Dubbo { - -RpcRequestImpl::Attachment::Attachment(MapPtr&& value, size_t offset) - : attachment_(std::move(value)), attachment_offset_(offset) { - ASSERT(attachment_ != nullptr); - ASSERT(attachment_->toMutableUntypedMap().has_value()); -} - -void RpcRequestImpl::Attachment::insert(absl::string_view key, absl::string_view value) { - ASSERT(attachment_->toMutableUntypedMap().has_value()); - - attachment_updated_ = true; - - Hessian2::ObjectPtr key_o = std::make_unique(key); - Hessian2::ObjectPtr val_o = std::make_unique(value); - attachment_->toMutableUntypedMap().value().get().insert_or_assign(std::move(key_o), - std::move(val_o)); -} - -void RpcRequestImpl::Attachment::remove(absl::string_view key) { - ASSERT(attachment_->toMutableUntypedMap().has_value()); - - attachment_updated_ = true; - attachment_->toMutableUntypedMap().value().get().erase(key); -} - -absl::optional RpcRequestImpl::Attachment::lookup(absl::string_view key) const { - ASSERT(attachment_->toMutableUntypedMap().has_value()); - - auto& map = attachment_->toMutableUntypedMap().value().get(); - auto result = map.find(key); - if (result != map.end() && result->second->type() == Hessian2::Object::Type::String) { - ASSERT(result->second->toString().has_value()); - return absl::make_optional(result->second->toString().value().get()); - } - return absl::nullopt; -} - -void RpcRequestImpl::assignParametersIfNeed() const { - ASSERT(parameters_lazy_callback_ != nullptr); - if (parameters_ == nullptr) { - parameters_ = parameters_lazy_callback_(); - } -} - -void RpcRequestImpl::assignAttachmentIfNeed() const { - ASSERT(attachment_lazy_callback_ != nullptr); - if (attachment_ != nullptr) { - return; - } - - assignParametersIfNeed(); - attachment_ = attachment_lazy_callback_(); - - if (auto g = attachment_->lookup("group"); g.has_value()) { - const_cast(this)->group_ = std::string(g.value()); - } -} - -absl::optional RpcRequestImpl::serviceGroup() const { - assignAttachmentIfNeed(); - return RpcRequestBase::serviceGroup(); -} - -const RpcRequestImpl::Attachment& RpcRequestImpl::attachment() const { - assignAttachmentIfNeed(); - return *attachment_; -} - -RpcRequestImpl::AttachmentPtr& RpcRequestImpl::mutableAttachment() const { - assignAttachmentIfNeed(); - return attachment_; -} - -const RpcRequestImpl::Parameters& RpcRequestImpl::parameters() const { - assignParametersIfNeed(); - return *parameters_; -} - -RpcRequestImpl::ParametersPtr& RpcRequestImpl::mutableParameters() const { - assignParametersIfNeed(); - return parameters_; -} - -} // namespace Dubbo -} // namespace Common -} // namespace Extensions -} // namespace Envoy diff --git a/source/extensions/common/dubbo/message_impl.h b/source/extensions/common/dubbo/message_impl.h deleted file mode 100644 index 877cb5dba626..000000000000 --- a/source/extensions/common/dubbo/message_impl.h +++ /dev/null @@ -1,138 +0,0 @@ -#pragma once - -#include - -#include "source/extensions/common/dubbo/hessian2_utils.h" -#include "source/extensions/common/dubbo/message.h" - -namespace Envoy { -namespace Extensions { -namespace Common { -namespace Dubbo { - -class RpcRequestBase : public RpcRequest { -public: - void setServiceName(absl::string_view name) { service_name_ = std::string(name); } - void setMethodName(absl::string_view name) { method_name_ = std::string(name); } - void setServiceVersion(absl::string_view version) { service_version_ = std::string(version); } - void setServiceGroup(absl::string_view group) { group_ = std::string(group); } - - // RpcRequest - absl::string_view serviceName() const override { return service_name_; } - absl::string_view methodName() const override { return method_name_; } - absl::string_view serviceVersion() const override { return service_version_; } - absl::optional serviceGroup() const override { - return group_.has_value() ? absl::make_optional(group_.value()) - : absl::nullopt; - } - -protected: - std::string service_name_; - std::string method_name_; - std::string service_version_; - absl::optional group_; -}; - -class RpcRequestImpl : public RpcRequestBase { -public: - // Each parameter consists of a parameter binary size and Hessian2::Object. - using Parameters = std::vector; - using ParametersPtr = std::unique_ptr; - - class Attachment { - public: - using Map = Hessian2::UntypedMapObject; - using MapPtr = std::unique_ptr; - using String = Hessian2::StringObject; - - Attachment(MapPtr&& value, size_t offset); - - const Map& attachment() const { return *attachment_; } - - void insert(absl::string_view key, absl::string_view value); - void remove(absl::string_view key); - absl::optional lookup(absl::string_view key) const; - - // Whether the attachment should be re-serialized. - bool attachmentUpdated() const { return attachment_updated_; } - - size_t attachmentOffset() const { return attachment_offset_; } - - private: - bool attachment_updated_{false}; - - MapPtr attachment_; - - // The binary offset of attachment in the original message. Retaining this value can help - // subsequent re-serialization of the attachment without re-serializing the parameters. - size_t attachment_offset_{}; - }; - using AttachmentPtr = std::unique_ptr; - - using AttachmentLazyCallback = std::function; - using ParametersLazyCallback = std::function; - - bool hasParameters() const { return parameters_ != nullptr; } - const Parameters& parameters() const; - ParametersPtr& mutableParameters() const; - - bool hasAttachment() const { return attachment_ != nullptr; } - const Attachment& attachment() const; - AttachmentPtr& mutableAttachment() const; - - void setParametersLazyCallback(ParametersLazyCallback&& callback) { - parameters_lazy_callback_ = std::move(callback); - } - - void setAttachmentLazyCallback(AttachmentLazyCallback&& callback) { - attachment_lazy_callback_ = std::move(callback); - } - - absl::optional serviceGroup() const override; - - Buffer::Instance& messageBuffer() { return message_buffer_; } - -private: - void assignParametersIfNeed() const; - void assignAttachmentIfNeed() const; - - // Original request message from downstream. - Buffer::OwnedImpl message_buffer_; - - AttachmentLazyCallback attachment_lazy_callback_; - ParametersLazyCallback parameters_lazy_callback_; - - mutable ParametersPtr parameters_{}; - mutable AttachmentPtr attachment_{}; -}; - -class RpcResponseImpl : public RpcResponse { -public: - void setResponseType(RpcResponseType type) { response_type_ = type; } - - // RpcResponse - absl::optional responseType() const override { return response_type_; } - - Buffer::Instance& messageBuffer() { return message_buffer_; } - - void setLocalRawMessage(absl::string_view val) { local_raw_message_ = std::string(val); } - absl::optional localRawMessage() { - return local_raw_message_.has_value() - ? absl::make_optional(local_raw_message_.value()) - : absl::nullopt; - } - -private: - // Original response message from upstream. - Buffer::OwnedImpl message_buffer_; - - // Optional raw content for local direct response. - absl::optional local_raw_message_; - - absl::optional response_type_; -}; - -} // namespace Dubbo -} // namespace Common -} // namespace Extensions -} // namespace Envoy diff --git a/source/extensions/common/dubbo/metadata.h b/source/extensions/common/dubbo/metadata.h index 774b73054ce1..6c2790c0ce8f 100644 --- a/source/extensions/common/dubbo/metadata.h +++ b/source/extensions/common/dubbo/metadata.h @@ -7,6 +7,7 @@ #include "source/extensions/common/dubbo/message.h" #include "absl/types/optional.h" +#include "hessian2/object.hpp" namespace Envoy { namespace Extensions { @@ -18,9 +19,6 @@ namespace Dubbo { */ class Context { public: - void setSerializeType(SerializeType type) { serialize_type_ = type; } - SerializeType serializeType() const { return serialize_type_; } - void setMessageType(MessageType type) { message_type_ = type; } MessageType messageType() const { return message_type_; } @@ -47,12 +45,10 @@ class Context { } private: - SerializeType serialize_type_{SerializeType::Hessian2}; MessageType message_type_{MessageType::Request}; absl::optional response_status_{}; int64_t request_id_{}; - size_t body_size_{}; }; diff --git a/source/extensions/common/wasm/wasm_vm.cc b/source/extensions/common/wasm/wasm_vm.cc index fc225eb3045b..aaf20e95569a 100644 --- a/source/extensions/common/wasm/wasm_vm.cc +++ b/source/extensions/common/wasm/wasm_vm.cc @@ -79,8 +79,7 @@ bool isWasmEngineAvailable(absl::string_view runtime) { absl::string_view getFirstAvailableWasmEngineName() { constexpr absl::string_view wasm_engines[] = { - "envoy.wasm.runtime.v8", "envoy.wasm.runtime.wasmtime", "envoy.wasm.runtime.wamr", - "envoy.wasm.runtime.wavm"}; + "envoy.wasm.runtime.v8", "envoy.wasm.runtime.wasmtime", "envoy.wasm.runtime.wamr"}; for (const auto wasm_engine : wasm_engines) { if (isWasmEngineAvailable(wasm_engine)) { return wasm_engine; diff --git a/source/extensions/common/wasm/wasm_vm.h b/source/extensions/common/wasm/wasm_vm.h index 139d06c87f69..e347428e1155 100644 --- a/source/extensions/common/wasm/wasm_vm.h +++ b/source/extensions/common/wasm/wasm_vm.h @@ -38,7 +38,8 @@ class WasmException : public EnvoyException { using WasmVmPtr = std::unique_ptr; -// Create a new low-level Wasm VM using runtime of the given type (e.g. "envoy.wasm.runtime.wavm"). +// Create a new low-level Wasm VM using runtime of the given type (e.g. +// "envoy.wasm.runtime.wasmtime"). WasmVmPtr createWasmVm(absl::string_view runtime); /** diff --git a/source/extensions/compression/zstd/common/BUILD b/source/extensions/compression/zstd/common/BUILD index 98b6a3abb62f..a0aa94556434 100644 --- a/source/extensions/compression/zstd/common/BUILD +++ b/source/extensions/compression/zstd/common/BUILD @@ -8,16 +8,6 @@ licenses(["notice"]) # Apache 2 envoy_extension_package() -envoy_cc_library( - name = "zstd_base_lib", - srcs = ["base.cc"], - hdrs = ["base.h"], - external_deps = ["zstd"], - deps = [ - "//source/common/buffer:buffer_lib", - ], -) - envoy_cc_library( name = "zstd_dictionary_manager_lib", hdrs = ["dictionary_manager.h"], diff --git a/source/extensions/compression/zstd/common/dictionary_manager.h b/source/extensions/compression/zstd/common/dictionary_manager.h index 45501345e5ec..43d8c4974281 100644 --- a/source/extensions/compression/zstd/common/dictionary_manager.h +++ b/source/extensions/compression/zstd/common/dictionary_manager.h @@ -44,9 +44,9 @@ template class envoy::config::core::v3::DataSource::SpecifierCase::kFilename) { is_watch_added = true; const auto& filename = source.filename(); - watcher_->addWatch( + THROW_IF_NOT_OK(watcher_->addWatch( filename, Filesystem::Watcher::Events::Modified | Filesystem::Watcher::Events::MovedTo, - [this, id, filename](uint32_t) { onDictionaryUpdate(id, filename); }); + [this, id, filename](uint32_t) { onDictionaryUpdate(id, filename); })); } } diff --git a/source/extensions/compression/zstd/compressor/BUILD b/source/extensions/compression/zstd/compressor/BUILD index e6167ff1a1cb..76c3343a3202 100644 --- a/source/extensions/compression/zstd/compressor/BUILD +++ b/source/extensions/compression/zstd/compressor/BUILD @@ -16,7 +16,8 @@ envoy_cc_library( deps = [ "//envoy/compression/compressor:compressor_interface", "//source/common/buffer:buffer_lib", - "//source/extensions/compression/zstd/common:zstd_base_lib", + "//source/common/compression/zstd/common:zstd_base_lib", + "//source/common/compression/zstd/compressor:compressor_base", "//source/extensions/compression/zstd/common:zstd_dictionary_manager_lib", ], ) diff --git a/source/extensions/compression/zstd/compressor/zstd_compressor_impl.cc b/source/extensions/compression/zstd/compressor/zstd_compressor_impl.cc index 89d3ed754e65..55ece012d086 100644 --- a/source/extensions/compression/zstd/compressor/zstd_compressor_impl.cc +++ b/source/extensions/compression/zstd/compressor/zstd_compressor_impl.cc @@ -1,7 +1,5 @@ #include "source/extensions/compression/zstd/compressor/zstd_compressor_impl.h" -#include "source/common/buffer/buffer_impl.h" - namespace Envoy { namespace Extensions { namespace Compression { @@ -11,15 +9,9 @@ namespace Compressor { ZstdCompressorImpl::ZstdCompressorImpl(uint32_t compression_level, bool enable_checksum, uint32_t strategy, const ZstdCDictManagerPtr& cdict_manager, uint32_t chunk_size) - : Common::Base(chunk_size), cctx_(ZSTD_createCCtx(), &ZSTD_freeCCtx), - cdict_manager_(cdict_manager), compression_level_(compression_level) { + : ZstdCompressorImplBase(compression_level, enable_checksum, strategy, chunk_size), + cdict_manager_(cdict_manager) { size_t result; - result = ZSTD_CCtx_setParameter(cctx_.get(), ZSTD_c_checksumFlag, enable_checksum); - RELEASE_ASSERT(!ZSTD_isError(result), ""); - - result = ZSTD_CCtx_setParameter(cctx_.get(), ZSTD_c_strategy, strategy); - RELEASE_ASSERT(!ZSTD_isError(result), ""); - if (cdict_manager_) { ZSTD_CDict* cdict = cdict_manager_->getFirstDictionary(); result = ZSTD_CCtx_refCDict(cctx_.get(), cdict); @@ -29,36 +21,17 @@ ZstdCompressorImpl::ZstdCompressorImpl(uint32_t compression_level, bool enable_c RELEASE_ASSERT(!ZSTD_isError(result), ""); } -void ZstdCompressorImpl::compress(Buffer::Instance& buffer, - Envoy::Compression::Compressor::State state) { - Buffer::OwnedImpl accumulation_buffer; - for (const Buffer::RawSlice& input_slice : buffer.getRawSlices()) { - if (input_slice.len_ > 0) { - setInput(input_slice); - process(accumulation_buffer, ZSTD_e_continue); - buffer.drain(input_slice.len_); - } - } - - ASSERT(buffer.length() == 0); - buffer.move(accumulation_buffer); +void ZstdCompressorImpl::compressPreprocess(Buffer::Instance&, + Envoy::Compression::Compressor::State) {} - if (state == Envoy::Compression::Compressor::State::Finish) { - process(buffer, ZSTD_e_end); - } +void ZstdCompressorImpl::compressProcess(const Buffer::Instance&, + const Buffer::RawSlice& input_slice, + Buffer::Instance& accumulation_buffer) { + setInput(input_slice); + process(accumulation_buffer, ZSTD_e_continue); } -void ZstdCompressorImpl::process(Buffer::Instance& output_buffer, ZSTD_EndDirective mode) { - bool finished; - do { - const size_t remaining = ZSTD_compressStream2(cctx_.get(), &output_, &input_, mode); - getOutput(output_buffer); - // If we're on the last chunk we're finished when zstd returns 0, - // which means its consumed all the input AND finished the frame. - // Otherwise, we're finished when we've consumed all the input. - finished = (ZSTD_e_end == mode) ? (remaining == 0) : (input_.pos == input_.size); - } while (!finished); -} +void ZstdCompressorImpl::compressPostprocess(Buffer::Instance&) {} } // namespace Compressor } // namespace Zstd diff --git a/source/extensions/compression/zstd/compressor/zstd_compressor_impl.h b/source/extensions/compression/zstd/compressor/zstd_compressor_impl.h index e0adaec7caaf..95da3d1a8e12 100644 --- a/source/extensions/compression/zstd/compressor/zstd_compressor_impl.h +++ b/source/extensions/compression/zstd/compressor/zstd_compressor_impl.h @@ -2,7 +2,8 @@ #include "envoy/compression/compressor/compressor.h" -#include "source/extensions/compression/zstd/common/base.h" +#include "source/common/compression/zstd/common/base.h" +#include "source/common/compression/zstd/compressor/zstd_compressor_impl_base.h" #include "source/extensions/compression/zstd/common/dictionary_manager.h" namespace Envoy { @@ -18,22 +19,21 @@ using ZstdCDictManagerPtr = std::unique_ptr; /** * Implementation of compressor's interface. */ -class ZstdCompressorImpl : public Common::Base, - public Envoy::Compression::Compressor::Compressor, - NonCopyable { +class ZstdCompressorImpl : public Envoy::Compression::Zstd::Compressor::ZstdCompressorImplBase { public: ZstdCompressorImpl(uint32_t compression_level, bool enable_checksum, uint32_t strategy, const ZstdCDictManagerPtr& cdict_manager, uint32_t chunk_size); - // Compression::Compressor::Compressor - void compress(Buffer::Instance& buffer, Envoy::Compression::Compressor::State state) override; - private: - void process(Buffer::Instance& output_buffer, ZSTD_EndDirective mode); + void compressPreprocess(Buffer::Instance& buffer, + Envoy::Compression::Compressor::State state) override; + + void compressProcess(const Buffer::Instance& buffer, const Buffer::RawSlice& input_slice, + Buffer::Instance& accumulation_buffer) override; + + void compressPostprocess(Buffer::Instance& accumulation_buffer) override; - std::unique_ptr cctx_; const ZstdCDictManagerPtr& cdict_manager_; - const uint32_t compression_level_; }; } // namespace Compressor diff --git a/source/extensions/compression/zstd/decompressor/BUILD b/source/extensions/compression/zstd/decompressor/BUILD index 9fc4383b939a..361a3b866f23 100644 --- a/source/extensions/compression/zstd/decompressor/BUILD +++ b/source/extensions/compression/zstd/decompressor/BUILD @@ -18,7 +18,7 @@ envoy_cc_library( "//envoy/stats:stats_interface", "//envoy/stats:stats_macros", "//source/common/buffer:buffer_lib", - "//source/extensions/compression/zstd/common:zstd_base_lib", + "//source/common/compression/zstd/common:zstd_base_lib", "//source/extensions/compression/zstd/common:zstd_dictionary_manager_lib", ], ) diff --git a/source/extensions/compression/zstd/decompressor/zstd_decompressor_impl.cc b/source/extensions/compression/zstd/decompressor/zstd_decompressor_impl.cc index 7a165da1973c..e2a00232c120 100644 --- a/source/extensions/compression/zstd/decompressor/zstd_decompressor_impl.cc +++ b/source/extensions/compression/zstd/decompressor/zstd_decompressor_impl.cc @@ -21,7 +21,7 @@ constexpr uint64_t MaxInflateRatio = 100; ZstdDecompressorImpl::ZstdDecompressorImpl(Stats::Scope& scope, const std::string& stats_prefix, const ZstdDDictManagerPtr& ddict_manager, uint32_t chunk_size) - : Common::Base(chunk_size), dctx_(ZSTD_createDCtx(), &ZSTD_freeDCtx), + : Envoy::Compression::Zstd::Common::Base(chunk_size), dctx_(ZSTD_createDCtx(), &ZSTD_freeDCtx), ddict_manager_(ddict_manager), stats_(generateStats(stats_prefix, scope)) {} void ZstdDecompressorImpl::decompress(const Buffer::Instance& input_buffer, diff --git a/source/extensions/compression/zstd/decompressor/zstd_decompressor_impl.h b/source/extensions/compression/zstd/decompressor/zstd_decompressor_impl.h index 597a8c72ad0e..5f7ed470e3e4 100644 --- a/source/extensions/compression/zstd/decompressor/zstd_decompressor_impl.h +++ b/source/extensions/compression/zstd/decompressor/zstd_decompressor_impl.h @@ -5,7 +5,7 @@ #include "envoy/stats/stats_macros.h" #include "source/common/common/logger.h" -#include "source/extensions/compression/zstd/common/base.h" +#include "source/common/compression/zstd/common/base.h" #include "source/extensions/compression/zstd/common/dictionary_manager.h" #include "zstd_errors.h" @@ -39,7 +39,7 @@ struct ZstdDecompressorStats { /** * Implementation of decompressor's interface. */ -class ZstdDecompressorImpl : public Common::Base, +class ZstdDecompressorImpl : public Envoy::Compression::Zstd::Common::Base, public Envoy::Compression::Decompressor::Decompressor, public Logger::Loggable, NonCopyable { diff --git a/source/extensions/config_subscription/filesystem/filesystem_subscription_impl.cc b/source/extensions/config_subscription/filesystem/filesystem_subscription_impl.cc index bc33cecee33e..b149e552b398 100644 --- a/source/extensions/config_subscription/filesystem/filesystem_subscription_impl.cc +++ b/source/extensions/config_subscription/filesystem/filesystem_subscription_impl.cc @@ -27,11 +27,12 @@ FilesystemSubscriptionImpl::FilesystemSubscriptionImpl( stats_(stats), api_(api), validation_visitor_(validation_visitor) { if (!path_config_source.has_watched_directory()) { file_watcher_ = dispatcher.createFilesystemWatcher(); - file_watcher_->addWatch(path_, Filesystem::Watcher::Events::MovedTo, [this](uint32_t) { - if (started_) { - refresh(); - } - }); + THROW_IF_NOT_OK( + file_watcher_->addWatch(path_, Filesystem::Watcher::Events::MovedTo, [this](uint32_t) { + if (started_) { + refresh(); + } + })); } else { directory_watcher_ = std::make_unique(path_config_source.watched_directory(), dispatcher); diff --git a/source/extensions/config_subscription/grpc/eds_resources_cache_impl.cc b/source/extensions/config_subscription/grpc/eds_resources_cache_impl.cc index b527c2b6b404..bf3709a54172 100644 --- a/source/extensions/config_subscription/grpc/eds_resources_cache_impl.cc +++ b/source/extensions/config_subscription/grpc/eds_resources_cache_impl.cc @@ -1,5 +1,7 @@ #include "source/extensions/config_subscription/grpc/eds_resources_cache_impl.h" +#include + #include "source/common/common/logger.h" namespace Envoy { @@ -49,9 +51,12 @@ void EdsResourcesCacheImpl::removeCallback(absl::string_view resource_name, resource_it != resources_map_.end()) { ENVOY_LOG_MISC(trace, "Removing callback for resource {} from the xDS resource cache", resource_name); - resource_it->second.removal_cbs_.erase(std::remove(resource_it->second.removal_cbs_.begin(), - resource_it->second.removal_cbs_.end(), - removal_cb)); + auto& callbacks = resource_it->second.removal_cbs_; + // Using the Erase-Remove idiom, in which all entries to be removed are + // moved to the end of the vector by the remove() call, and then the erase() call + // will erase these entries from the first element to be removed all the + // way to the end of the vector. + callbacks.erase(std::remove(callbacks.begin(), callbacks.end(), removal_cb), callbacks.end()); } } diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index a3fa5c9831e2..66191fd96be8 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -297,6 +297,7 @@ EXTENSIONS = { "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", + "envoy.transport_sockets.tls": "//source/extensions/transport_sockets/tls:config", "envoy.transport_sockets.internal_upstream": "//source/extensions/transport_sockets/internal_upstream:config", # @@ -348,7 +349,6 @@ EXTENSIONS = { "envoy.wasm.runtime.null": "//source/extensions/wasm_runtime/null:config", "envoy.wasm.runtime.v8": "//source/extensions/wasm_runtime/v8:config", "envoy.wasm.runtime.wamr": "//source/extensions/wasm_runtime/wamr:config", - "envoy.wasm.runtime.wavm": "//source/extensions/wasm_runtime/wavm:config", "envoy.wasm.runtime.wasmtime": "//source/extensions/wasm_runtime/wasmtime:config", # diff --git a/source/extensions/extensions_metadata.yaml b/source/extensions/extensions_metadata.yaml index 771ae0f999c3..88957f155d5e 100644 --- a/source/extensions/extensions_metadata.yaml +++ b/source/extensions/extensions_metadata.yaml @@ -1354,14 +1354,6 @@ envoy.wasm.runtime.wasmtime: # is updated to capture additional Wasm runtimes". security_posture: unknown status: alpha -envoy.wasm.runtime.wavm: - categories: - - envoy.wasm.runtime - # "This may never change from unknown until the threat model at - # https://envoyproxy.io/docs/envoy/latest/intro/arch_overview/security/threat_model#core-and-extensions - # is updated to capture additional Wasm runtimes". - security_posture: unknown - status: alpha envoy.watchdog.profile_action: categories: - envoy.guarddog_actions diff --git a/source/extensions/filters/common/ext_authz/check_request_utils.cc b/source/extensions/filters/common/ext_authz/check_request_utils.cc index 8a62aa1cdc98..0e24523eccb8 100644 --- a/source/extensions/filters/common/ext_authz/check_request_utils.cc +++ b/source/extensions/filters/common/ext_authz/check_request_utils.cc @@ -247,8 +247,9 @@ void CheckRequestUtils::createTcpCheck( MatcherSharedPtr CheckRequestUtils::toRequestMatchers(const envoy::type::matcher::v3::ListStringMatcher& list, - bool add_http_headers) { - std::vector matchers(createStringMatchers(list)); + bool add_http_headers, + Server::Configuration::CommonFactoryContext& context) { + std::vector matchers(createStringMatchers(list, context)); if (add_http_headers) { const std::vector keys{ @@ -259,8 +260,9 @@ CheckRequestUtils::toRequestMatchers(const envoy::type::matcher::v3::ListStringM envoy::type::matcher::v3::StringMatcher matcher; matcher.set_exact(key.get()); matchers.push_back( - std::make_unique>( - matcher)); + std::make_unique< + Matchers::StringMatcherImplWithContext>( + matcher, context)); } } @@ -268,12 +270,14 @@ CheckRequestUtils::toRequestMatchers(const envoy::type::matcher::v3::ListStringM } std::vector -CheckRequestUtils::createStringMatchers(const envoy::type::matcher::v3::ListStringMatcher& list) { +CheckRequestUtils::createStringMatchers(const envoy::type::matcher::v3::ListStringMatcher& list, + Server::Configuration::CommonFactoryContext& context) { std::vector matchers; for (const auto& matcher : list.patterns()) { matchers.push_back( - std::make_unique>( - matcher)); + std::make_unique< + Matchers::StringMatcherImplWithContext>( + matcher, context)); } return matchers; } diff --git a/source/extensions/filters/common/ext_authz/check_request_utils.h b/source/extensions/filters/common/ext_authz/check_request_utils.h index 1390485c0ae0..9f436d2e3857 100644 --- a/source/extensions/filters/common/ext_authz/check_request_utils.h +++ b/source/extensions/filters/common/ext_authz/check_request_utils.h @@ -113,9 +113,11 @@ class CheckRequestUtils { const Protobuf::Map& destination_labels); static MatcherSharedPtr toRequestMatchers(const envoy::type::matcher::v3::ListStringMatcher& list, - bool add_http_headers); + bool add_http_headers, + Server::Configuration::CommonFactoryContext& context); static std::vector - createStringMatchers(const envoy::type::matcher::v3::ListStringMatcher& list); + createStringMatchers(const envoy::type::matcher::v3::ListStringMatcher& list, + Server::Configuration::CommonFactoryContext& context); private: static void setAttrContextPeer(envoy::service::auth::v3::AttributeContext::Peer& peer, diff --git a/source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc b/source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc index bd6bfa0e26bd..101a991dc100 100644 --- a/source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc +++ b/source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc @@ -103,17 +103,20 @@ struct SuccessResponse { // Config ClientConfig::ClientConfig(const envoy::extensions::filters::http::ext_authz::v3::ExtAuthz& config, - uint32_t timeout, absl::string_view path_prefix) + uint32_t timeout, absl::string_view path_prefix, + Server::Configuration::CommonFactoryContext& context) : client_header_matchers_(toClientMatchers( - config.http_service().authorization_response().allowed_client_headers())), + config.http_service().authorization_response().allowed_client_headers(), context)), client_header_on_success_matchers_(toClientMatchersOnSuccess( - config.http_service().authorization_response().allowed_client_headers_on_success())), + config.http_service().authorization_response().allowed_client_headers_on_success(), + context)), to_dynamic_metadata_matchers_(toDynamicMetadataMatchers( - config.http_service().authorization_response().dynamic_metadata_from_headers())), + config.http_service().authorization_response().dynamic_metadata_from_headers(), context)), upstream_header_matchers_(toUpstreamMatchers( - config.http_service().authorization_response().allowed_upstream_headers())), + config.http_service().authorization_response().allowed_upstream_headers(), context)), upstream_header_to_append_matchers_(toUpstreamMatchers( - config.http_service().authorization_response().allowed_upstream_headers_to_append())), + config.http_service().authorization_response().allowed_upstream_headers_to_append(), + context)), cluster_name_(config.http_service().server_uri().cluster()), timeout_(timeout), path_prefix_(path_prefix), tracing_name_(fmt::format("async {} egress", config.http_service().server_uri().cluster())), @@ -122,20 +125,26 @@ ClientConfig::ClientConfig(const envoy::extensions::filters::http::ext_authz::v3 envoy::config::core::v3::HeaderValueOption::OVERWRITE_IF_EXISTS_OR_ADD)) {} MatcherSharedPtr -ClientConfig::toClientMatchersOnSuccess(const envoy::type::matcher::v3::ListStringMatcher& list) { - std::vector matchers(CheckRequestUtils::createStringMatchers(list)); +ClientConfig::toClientMatchersOnSuccess(const envoy::type::matcher::v3::ListStringMatcher& list, + Server::Configuration::CommonFactoryContext& context) { + std::vector matchers( + CheckRequestUtils::createStringMatchers(list, context)); return std::make_shared(std::move(matchers)); } MatcherSharedPtr -ClientConfig::toDynamicMetadataMatchers(const envoy::type::matcher::v3::ListStringMatcher& list) { - std::vector matchers(CheckRequestUtils::createStringMatchers(list)); +ClientConfig::toDynamicMetadataMatchers(const envoy::type::matcher::v3::ListStringMatcher& list, + Server::Configuration::CommonFactoryContext& context) { + std::vector matchers( + CheckRequestUtils::createStringMatchers(list, context)); return std::make_shared(std::move(matchers)); } MatcherSharedPtr -ClientConfig::toClientMatchers(const envoy::type::matcher::v3::ListStringMatcher& list) { - std::vector matchers(CheckRequestUtils::createStringMatchers(list)); +ClientConfig::toClientMatchers(const envoy::type::matcher::v3::ListStringMatcher& list, + Server::Configuration::CommonFactoryContext& context) { + std::vector matchers( + CheckRequestUtils::createStringMatchers(list, context)); // If list is empty, all authorization response headers, except Host, should be added to // the client response. @@ -143,8 +152,9 @@ ClientConfig::toClientMatchers(const envoy::type::matcher::v3::ListStringMatcher envoy::type::matcher::v3::StringMatcher matcher; matcher.set_exact(Http::Headers::get().Host.get()); matchers.push_back( - std::make_unique>( - matcher)); + std::make_unique< + Matchers::StringMatcherImplWithContext>( + matcher, context)); return std::make_shared(std::move(matchers)); } @@ -159,16 +169,18 @@ ClientConfig::toClientMatchers(const envoy::type::matcher::v3::ListStringMatcher envoy::type::matcher::v3::StringMatcher matcher; matcher.set_exact(key.get()); matchers.push_back( - std::make_unique>( - matcher)); + std::make_unique< + Matchers::StringMatcherImplWithContext>( + matcher, context)); } return std::make_shared(std::move(matchers)); } MatcherSharedPtr -ClientConfig::toUpstreamMatchers(const envoy::type::matcher::v3::ListStringMatcher& list) { - return std::make_unique(CheckRequestUtils::createStringMatchers(list)); +ClientConfig::toUpstreamMatchers(const envoy::type::matcher::v3::ListStringMatcher& list, + Server::Configuration::CommonFactoryContext& context) { + return std::make_unique(CheckRequestUtils::createStringMatchers(list, context)); } RawHttpClientImpl::RawHttpClientImpl(Upstream::ClusterManager& cm, ClientConfigSharedPtr config) diff --git a/source/extensions/filters/common/ext_authz/ext_authz_http_impl.h b/source/extensions/filters/common/ext_authz/ext_authz_http_impl.h index db611f4fad73..4211fc9f4ae4 100644 --- a/source/extensions/filters/common/ext_authz/ext_authz_http_impl.h +++ b/source/extensions/filters/common/ext_authz/ext_authz_http_impl.h @@ -25,7 +25,8 @@ namespace ExtAuthz { class ClientConfig { public: ClientConfig(const envoy::extensions::filters::http::ext_authz::v3::ExtAuthz& config, - uint32_t timeout, absl::string_view path_prefix); + uint32_t timeout, absl::string_view path_prefix, + Server::Configuration::CommonFactoryContext& context); /** * Returns the name of the authorization cluster. @@ -87,13 +88,17 @@ class ClientConfig { const Router::HeaderParser& requestHeaderParser() const { return *request_headers_parser_; } private: - static MatcherSharedPtr toClientMatchers(const envoy::type::matcher::v3::ListStringMatcher& list); + static MatcherSharedPtr toClientMatchers(const envoy::type::matcher::v3::ListStringMatcher& list, + Server::Configuration::CommonFactoryContext& context); static MatcherSharedPtr - toClientMatchersOnSuccess(const envoy::type::matcher::v3::ListStringMatcher& list); + toClientMatchersOnSuccess(const envoy::type::matcher::v3::ListStringMatcher& list, + Server::Configuration::CommonFactoryContext& context); static MatcherSharedPtr - toDynamicMetadataMatchers(const envoy::type::matcher::v3::ListStringMatcher& list); + toDynamicMetadataMatchers(const envoy::type::matcher::v3::ListStringMatcher& list, + Server::Configuration::CommonFactoryContext& context); static MatcherSharedPtr - toUpstreamMatchers(const envoy::type::matcher::v3::ListStringMatcher& list); + toUpstreamMatchers(const envoy::type::matcher::v3::ListStringMatcher& list, + Server::Configuration::CommonFactoryContext& context); const MatcherSharedPtr request_header_matchers_; const MatcherSharedPtr client_header_matchers_; diff --git a/source/extensions/filters/common/rbac/engine_impl.cc b/source/extensions/filters/common/rbac/engine_impl.cc index 76d010c562c7..f4b1f9e11bdd 100644 --- a/source/extensions/filters/common/rbac/engine_impl.cc +++ b/source/extensions/filters/common/rbac/engine_impl.cc @@ -42,7 +42,8 @@ void generateLog(StreamInfo::StreamInfo& info, EnforcementMode mode, bool log) { RoleBasedAccessControlEngineImpl::RoleBasedAccessControlEngineImpl( const envoy::config::rbac::v3::RBAC& rules, - ProtobufMessage::ValidationVisitor& validation_visitor, const EnforcementMode mode) + ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context, const EnforcementMode mode) : action_(rules.action()), mode_(mode) { // guard expression builder by presence of a condition in policies for (const auto& policy : rules.policies()) { @@ -54,7 +55,7 @@ RoleBasedAccessControlEngineImpl::RoleBasedAccessControlEngineImpl( for (const auto& policy : rules.policies()) { policies_.emplace(policy.first, std::make_unique(policy.second, builder_.get(), - validation_visitor)); + validation_visitor, context)); } } diff --git a/source/extensions/filters/common/rbac/engine_impl.h b/source/extensions/filters/common/rbac/engine_impl.h index c748142c8a97..7f51648caa47 100644 --- a/source/extensions/filters/common/rbac/engine_impl.h +++ b/source/extensions/filters/common/rbac/engine_impl.h @@ -65,6 +65,7 @@ class RoleBasedAccessControlEngineImpl : public RoleBasedAccessControlEngine, No public: RoleBasedAccessControlEngineImpl(const envoy::config::rbac::v3::RBAC& rules, ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context, const EnforcementMode mode = EnforcementMode::Enforced); bool handleAction(const Network::Connection& connection, diff --git a/source/extensions/filters/common/rbac/matchers.cc b/source/extensions/filters/common/rbac/matchers.cc index 5df6db1bfe37..47ffb5ec57b7 100644 --- a/source/extensions/filters/common/rbac/matchers.cc +++ b/source/extensions/filters/common/rbac/matchers.cc @@ -13,12 +13,13 @@ namespace Common { namespace RBAC { MatcherConstSharedPtr Matcher::create(const envoy::config::rbac::v3::Permission& permission, - ProtobufMessage::ValidationVisitor& validation_visitor) { + ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context) { switch (permission.rule_case()) { case envoy::config::rbac::v3::Permission::RuleCase::kAndRules: - return std::make_shared(permission.and_rules(), validation_visitor); + return std::make_shared(permission.and_rules(), validation_visitor, context); case envoy::config::rbac::v3::Permission::RuleCase::kOrRules: - return std::make_shared(permission.or_rules(), validation_visitor); + return std::make_shared(permission.or_rules(), validation_visitor, context); case envoy::config::rbac::v3::Permission::RuleCase::kHeader: return std::make_shared(permission.header()); case envoy::config::rbac::v3::Permission::RuleCase::kDestinationIp: @@ -33,9 +34,10 @@ MatcherConstSharedPtr Matcher::create(const envoy::config::rbac::v3::Permission& case envoy::config::rbac::v3::Permission::RuleCase::kMetadata: return std::make_shared(permission.metadata()); case envoy::config::rbac::v3::Permission::RuleCase::kNotRule: - return std::make_shared(permission.not_rule(), validation_visitor); + return std::make_shared(permission.not_rule(), validation_visitor, context); case envoy::config::rbac::v3::Permission::RuleCase::kRequestedServerName: - return std::make_shared(permission.requested_server_name()); + return std::make_shared(permission.requested_server_name(), + context); case envoy::config::rbac::v3::Permission::RuleCase::kUrlPath: return std::make_shared(permission.url_path()); case envoy::config::rbac::v3::Permission::RuleCase::kUriTemplate: { @@ -56,14 +58,15 @@ MatcherConstSharedPtr Matcher::create(const envoy::config::rbac::v3::Permission& PANIC_DUE_TO_CORRUPT_ENUM; } -MatcherConstSharedPtr Matcher::create(const envoy::config::rbac::v3::Principal& principal) { +MatcherConstSharedPtr Matcher::create(const envoy::config::rbac::v3::Principal& principal, + Server::Configuration::CommonFactoryContext& context) { switch (principal.identifier_case()) { case envoy::config::rbac::v3::Principal::IdentifierCase::kAndIds: - return std::make_shared(principal.and_ids()); + return std::make_shared(principal.and_ids(), context); case envoy::config::rbac::v3::Principal::IdentifierCase::kOrIds: - return std::make_shared(principal.or_ids()); + return std::make_shared(principal.or_ids(), context); case envoy::config::rbac::v3::Principal::IdentifierCase::kAuthenticated: - return std::make_shared(principal.authenticated()); + return std::make_shared(principal.authenticated(), context); case envoy::config::rbac::v3::Principal::IdentifierCase::kSourceIp: return std::make_shared(principal.source_ip(), IPMatcher::Type::ConnectionRemote); @@ -80,7 +83,7 @@ MatcherConstSharedPtr Matcher::create(const envoy::config::rbac::v3::Principal& case envoy::config::rbac::v3::Principal::IdentifierCase::kMetadata: return std::make_shared(principal.metadata()); case envoy::config::rbac::v3::Principal::IdentifierCase::kNotId: - return std::make_shared(principal.not_id()); + return std::make_shared(principal.not_id(), context); case envoy::config::rbac::v3::Principal::IdentifierCase::kUrlPath: return std::make_shared(principal.url_path()); case envoy::config::rbac::v3::Principal::IdentifierCase::kFilterState: @@ -92,15 +95,17 @@ MatcherConstSharedPtr Matcher::create(const envoy::config::rbac::v3::Principal& } AndMatcher::AndMatcher(const envoy::config::rbac::v3::Permission::Set& set, - ProtobufMessage::ValidationVisitor& validation_visitor) { + ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context) { for (const auto& rule : set.rules()) { - matchers_.push_back(Matcher::create(rule, validation_visitor)); + matchers_.push_back(Matcher::create(rule, validation_visitor, context)); } } -AndMatcher::AndMatcher(const envoy::config::rbac::v3::Principal::Set& set) { +AndMatcher::AndMatcher(const envoy::config::rbac::v3::Principal::Set& set, + Server::Configuration::CommonFactoryContext& context) { for (const auto& id : set.ids()) { - matchers_.push_back(Matcher::create(id)); + matchers_.push_back(Matcher::create(id, context)); } } @@ -117,15 +122,17 @@ bool AndMatcher::matches(const Network::Connection& connection, } OrMatcher::OrMatcher(const Protobuf::RepeatedPtrField& rules, - ProtobufMessage::ValidationVisitor& validation_visitor) { + ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context) { for (const auto& rule : rules) { - matchers_.push_back(Matcher::create(rule, validation_visitor)); + matchers_.push_back(Matcher::create(rule, validation_visitor, context)); } } -OrMatcher::OrMatcher(const Protobuf::RepeatedPtrField& ids) { +OrMatcher::OrMatcher(const Protobuf::RepeatedPtrField& ids, + Server::Configuration::CommonFactoryContext& context) { for (const auto& id : ids) { - matchers_.push_back(Matcher::create(id)); + matchers_.push_back(Matcher::create(id, context)); } } diff --git a/source/extensions/filters/common/rbac/matchers.h b/source/extensions/filters/common/rbac/matchers.h index b5914f397f3f..bc39bc94c97e 100644 --- a/source/extensions/filters/common/rbac/matchers.h +++ b/source/extensions/filters/common/rbac/matchers.h @@ -49,13 +49,15 @@ class Matcher { * proto message. */ static MatcherConstSharedPtr create(const envoy::config::rbac::v3::Permission& permission, - ProtobufMessage::ValidationVisitor& validation_visitor); + ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context); /** * Creates a shared instance of a matcher based off the rules defined in the Principal config * proto message. */ - static MatcherConstSharedPtr create(const envoy::config::rbac::v3::Principal& principal); + static MatcherConstSharedPtr create(const envoy::config::rbac::v3::Principal& principal, + Server::Configuration::CommonFactoryContext& context); }; /** @@ -76,8 +78,10 @@ class AlwaysMatcher : public Matcher { class AndMatcher : public Matcher { public: AndMatcher(const envoy::config::rbac::v3::Permission::Set& rules, - ProtobufMessage::ValidationVisitor& validation_visitor); - AndMatcher(const envoy::config::rbac::v3::Principal::Set& ids); + ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context); + AndMatcher(const envoy::config::rbac::v3::Principal::Set& ids, + Server::Configuration::CommonFactoryContext& context); bool matches(const Network::Connection& connection, const Envoy::Http::RequestHeaderMap& headers, const StreamInfo::StreamInfo&) const override; @@ -93,12 +97,17 @@ class AndMatcher : public Matcher { class OrMatcher : public Matcher { public: OrMatcher(const envoy::config::rbac::v3::Permission::Set& set, - ProtobufMessage::ValidationVisitor& validation_visitor) - : OrMatcher(set.rules(), validation_visitor) {} - OrMatcher(const envoy::config::rbac::v3::Principal::Set& set) : OrMatcher(set.ids()) {} + ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context) + : OrMatcher(set.rules(), validation_visitor, context) {} + OrMatcher(const envoy::config::rbac::v3::Principal::Set& set, + Server::Configuration::CommonFactoryContext& context) + : OrMatcher(set.ids(), context) {} OrMatcher(const Protobuf::RepeatedPtrField& rules, - ProtobufMessage::ValidationVisitor& validation_visitor); - OrMatcher(const Protobuf::RepeatedPtrField& ids); + ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context); + OrMatcher(const Protobuf::RepeatedPtrField& ids, + Server::Configuration::CommonFactoryContext& context); bool matches(const Network::Connection& connection, const Envoy::Http::RequestHeaderMap& headers, const StreamInfo::StreamInfo&) const override; @@ -110,10 +119,12 @@ class OrMatcher : public Matcher { class NotMatcher : public Matcher { public: NotMatcher(const envoy::config::rbac::v3::Permission& permission, - ProtobufMessage::ValidationVisitor& validation_visitor) - : matcher_(Matcher::create(permission, validation_visitor)) {} - NotMatcher(const envoy::config::rbac::v3::Principal& principal) - : matcher_(Matcher::create(principal)) {} + ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context) + : matcher_(Matcher::create(permission, validation_visitor, context)) {} + NotMatcher(const envoy::config::rbac::v3::Principal& principal, + Server::Configuration::CommonFactoryContext& context) + : matcher_(Matcher::create(principal, context)) {} bool matches(const Network::Connection& connection, const Envoy::Http::RequestHeaderMap& headers, const StreamInfo::StreamInfo&) const override; @@ -188,18 +199,19 @@ class PortRangeMatcher : public Matcher { */ class AuthenticatedMatcher : public Matcher { public: - AuthenticatedMatcher(const envoy::config::rbac::v3::Principal::Authenticated& auth) + AuthenticatedMatcher(const envoy::config::rbac::v3::Principal::Authenticated& auth, + Server::Configuration::CommonFactoryContext& context) : matcher_(auth.has_principal_name() - ? absl::make_optional< - Matchers::StringMatcherImpl>( - auth.principal_name()) + ? absl::make_optional>(auth.principal_name(), context) : absl::nullopt) {} bool matches(const Network::Connection& connection, const Envoy::Http::RequestHeaderMap& headers, const StreamInfo::StreamInfo&) const override; private: - const absl::optional> + const absl::optional< + Matchers::StringMatcherImplWithContext> matcher_; }; @@ -211,9 +223,10 @@ class AuthenticatedMatcher : public Matcher { class PolicyMatcher : public Matcher, NonCopyable { public: PolicyMatcher(const envoy::config::rbac::v3::Policy& policy, Expr::Builder* builder, - ProtobufMessage::ValidationVisitor& validation_visitor) - : permissions_(policy.permissions(), validation_visitor), principals_(policy.principals()), - condition_(policy.condition()) { + ProtobufMessage::ValidationVisitor& validation_visitor, + Server::Configuration::CommonFactoryContext& context) + : permissions_(policy.permissions(), validation_visitor, context), + principals_(policy.principals(), context), condition_(policy.condition()) { if (policy.has_condition()) { expr_ = Expr::createExpression(*builder, condition_); } @@ -258,11 +271,12 @@ class FilterStateMatcher : public Matcher { */ class RequestedServerNameMatcher : public Matcher, - Envoy::Matchers::StringMatcherImpl { + Envoy::Matchers::StringMatcherImplWithContext { public: - RequestedServerNameMatcher(const envoy::type::matcher::v3::StringMatcher& requested_server_name) - : Envoy::Matchers::StringMatcherImpl( - requested_server_name) {} + RequestedServerNameMatcher(const envoy::type::matcher::v3::StringMatcher& requested_server_name, + Server::Configuration::CommonFactoryContext& context) + : Envoy::Matchers::StringMatcherImplWithContext( + requested_server_name, context) {} bool matches(const Network::Connection& connection, const Envoy::Http::RequestHeaderMap& headers, const StreamInfo::StreamInfo&) const override; diff --git a/source/extensions/filters/common/rbac/utility.cc b/source/extensions/filters/common/rbac/utility.cc index 710342d7b8fd..cc14523ee1fb 100644 --- a/source/extensions/filters/common/rbac/utility.cc +++ b/source/extensions/filters/common/rbac/utility.cc @@ -10,11 +10,14 @@ namespace Filters { namespace Common { namespace RBAC { -RoleBasedAccessControlFilterStats -generateStats(const std::string& prefix, const std::string& shadow_prefix, Stats::Scope& scope) { +RoleBasedAccessControlFilterStats generateStats(const std::string& prefix, + const std::string& rules_prefix, + const std::string& shadow_rules_prefix, + Stats::Scope& scope) { const std::string final_prefix = Envoy::statPrefixJoin(prefix, "rbac."); - return {ENFORCE_RBAC_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix)) - SHADOW_RBAC_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix + shadow_prefix))}; + return { + ENFORCE_RBAC_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix + rules_prefix)) + SHADOW_RBAC_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix + shadow_rules_prefix))}; } std::string responseDetail(const std::string& policy_id) { diff --git a/source/extensions/filters/common/rbac/utility.h b/source/extensions/filters/common/rbac/utility.h index ac2a339226ca..fe17def3c6e1 100644 --- a/source/extensions/filters/common/rbac/utility.h +++ b/source/extensions/filters/common/rbac/utility.h @@ -34,8 +34,10 @@ struct RoleBasedAccessControlFilterStats { SHADOW_RBAC_FILTER_STATS(GENERATE_COUNTER_STRUCT) }; -RoleBasedAccessControlFilterStats -generateStats(const std::string& prefix, const std::string& shadow_prefix, Stats::Scope& scope); +RoleBasedAccessControlFilterStats generateStats(const std::string& prefix, + const std::string& rules_prefix, + const std::string& shadow_rules_prefix, + Stats::Scope& scope); template std::unique_ptr @@ -51,7 +53,7 @@ createEngine(const ConfigType& config, Server::Configuration::ServerFactoryConte } if (config.has_rules()) { return std::make_unique(config.rules(), validation_visitor, - EnforcementMode::Enforced); + context, EnforcementMode::Enforced); } return nullptr; @@ -71,7 +73,7 @@ createShadowEngine(const ConfigType& config, Server::Configuration::ServerFactor } if (config.has_shadow_rules()) { return std::make_unique( - config.shadow_rules(), validation_visitor, EnforcementMode::Shadow); + config.shadow_rules(), validation_visitor, context, EnforcementMode::Shadow); } return nullptr; diff --git a/source/extensions/filters/http/aws_lambda/config.cc b/source/extensions/filters/http/aws_lambda/config.cc index 947593faab10..bd7a07b4a0b0 100644 --- a/source/extensions/filters/http/aws_lambda/config.cc +++ b/source/extensions/filters/http/aws_lambda/config.cc @@ -51,8 +51,7 @@ absl::StatusOr AwsLambdaFilterFactory::createFilterFactor Extensions::Common::Aws::Utility::fetchMetadata); auto signer = std::make_shared( - service_name, region, std::move(credentials_provider), - server_context.mainThreadDispatcher().timeSource(), + service_name, region, std::move(credentials_provider), server_context, // TODO: extend API to allow specifying header exclusion. ref: // https://github.com/envoyproxy/envoy/pull/18998 Extensions::Common::Aws::AwsSigningHeaderExclusionVector{}); diff --git a/source/extensions/filters/http/aws_request_signing/config.cc b/source/extensions/filters/http/aws_request_signing/config.cc index 4727c0133f10..c0de8b785e58 100644 --- a/source/extensions/filters/http/aws_request_signing/config.cc +++ b/source/extensions/filters/http/aws_request_signing/config.cc @@ -71,9 +71,8 @@ AwsRequestSigningFilterFactory::createFilterFactoryFromProtoTyped( if (config.signing_algorithm() == AwsRequestSigning_SigningAlgorithm_AWS_SIGV4A) { signer = std::make_unique( - config.service_name(), region, credentials_provider, - server_context.mainThreadDispatcher().timeSource(), matcher_config, query_string, - expiration_time); + config.service_name(), region, credentials_provider, server_context, matcher_config, + query_string, expiration_time); } else { // Verify that we have not specified a region set when using sigv4 algorithm if (isARegionSet(region)) { @@ -81,9 +80,8 @@ AwsRequestSigningFilterFactory::createFilterFactoryFromProtoTyped( "can be specified when using signing_algorithm: AWS_SIGV4A."); } signer = std::make_unique( - config.service_name(), region, credentials_provider, - server_context.mainThreadDispatcher().timeSource(), matcher_config, query_string, - expiration_time); + config.service_name(), region, credentials_provider, server_context, matcher_config, + query_string, expiration_time); } auto filter_config = @@ -137,7 +135,7 @@ AwsRequestSigningFilterFactory::createRouteSpecificFilterConfigTyped( AwsRequestSigning_SigningAlgorithm_AWS_SIGV4A) { signer = std::make_unique( per_route_config.aws_request_signing().service_name(), region, credentials_provider, - context.mainThreadDispatcher().timeSource(), matcher_config, query_string, expiration_time); + context, matcher_config, query_string, expiration_time); } else { // Verify that we have not specified a region set when using sigv4 algorithm if (isARegionSet(region)) { @@ -146,7 +144,7 @@ AwsRequestSigningFilterFactory::createRouteSpecificFilterConfigTyped( } signer = std::make_unique( per_route_config.aws_request_signing().service_name(), region, credentials_provider, - context.mainThreadDispatcher().timeSource(), matcher_config, query_string, expiration_time); + context, matcher_config, query_string, expiration_time); } return std::make_shared( diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index a920ac3ddbb2..8628a68e23c9 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -42,10 +42,11 @@ struct CacheResponseCodeDetailValues { using CacheResponseCodeDetails = ConstSingleton; CacheFilter::CacheFilter(const envoy::extensions::filters::http::cache::v3::CacheConfig& config, - const std::string&, Stats::Scope&, TimeSource& time_source, + const std::string&, Stats::Scope&, + Server::Configuration::CommonFactoryContext& context, std::shared_ptr http_cache) - : time_source_(time_source), cache_(http_cache), - vary_allow_list_(config.allowed_vary_headers()) {} + : time_source_(context.timeSource()), cache_(http_cache), + vary_allow_list_(config.allowed_vary_headers(), context) {} void CacheFilter::onDestroy() { filter_state_ = FilterState::Destroyed; diff --git a/source/extensions/filters/http/cache/cache_filter.h b/source/extensions/filters/http/cache/cache_filter.h index cedf5a60ee28..5cac2781fa10 100644 --- a/source/extensions/filters/http/cache/cache_filter.h +++ b/source/extensions/filters/http/cache/cache_filter.h @@ -54,7 +54,8 @@ class CacheFilter : public Http::PassThroughFilter, public std::enable_shared_from_this { public: CacheFilter(const envoy::extensions::filters::http::cache::v3::CacheConfig& config, - const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source, + const std::string& stats_prefix, Stats::Scope& scope, + Server::Configuration::CommonFactoryContext& context, std::shared_ptr http_cache); // Http::StreamFilterBase void onDestroy() override; diff --git a/source/extensions/filters/http/cache/cache_headers_utils.cc b/source/extensions/filters/http/cache/cache_headers_utils.cc index 582746215dc2..5c622462c4e7 100644 --- a/source/extensions/filters/http/cache/cache_headers_utils.cc +++ b/source/extensions/filters/http/cache/cache_headers_utils.cc @@ -284,12 +284,14 @@ CacheHeadersUtils::parseCommaDelimitedHeader(const Http::HeaderMap::GetResult& e } VaryAllowList::VaryAllowList( - const Protobuf::RepeatedPtrField& allow_list) { + const Protobuf::RepeatedPtrField& allow_list, + Server::Configuration::CommonFactoryContext& context) { for (const auto& rule : allow_list) { allow_list_.emplace_back( - std::make_unique>( - rule)); + std::make_unique< + Matchers::StringMatcherImplWithContext>( + rule, context)); } } diff --git a/source/extensions/filters/http/cache/cache_headers_utils.h b/source/extensions/filters/http/cache/cache_headers_utils.h index 440b58843d1a..5f96d24c54e2 100644 --- a/source/extensions/filters/http/cache/cache_headers_utils.h +++ b/source/extensions/filters/http/cache/cache_headers_utils.h @@ -128,7 +128,8 @@ class VaryAllowList { public: // Parses the allow list from the Cache Config into the object's private allow_list_. VaryAllowList( - const Protobuf::RepeatedPtrField& allow_list); + const Protobuf::RepeatedPtrField& allow_list, + Server::Configuration::CommonFactoryContext& context); // Checks if the headers contain an allowed value in the Vary header. bool allowsHeaders(const Http::ResponseHeaderMap& headers) const; diff --git a/source/extensions/filters/http/cache/config.cc b/source/extensions/filters/http/cache/config.cc index bfb64ad4bfb7..e379b4c05d1a 100644 --- a/source/extensions/filters/http/cache/config.cc +++ b/source/extensions/filters/http/cache/config.cc @@ -28,8 +28,8 @@ Http::FilterFactoryCb CacheFilterFactory::createFilterFactoryFromProtoTyped( return [config, stats_prefix, &context, cache](Http::FilterChainFactoryCallbacks& callbacks) -> void { - callbacks.addStreamFilter(std::make_shared( - config, stats_prefix, context.scope(), context.serverFactoryContext().timeSource(), cache)); + callbacks.addStreamFilter(std::make_shared(config, stats_prefix, context.scope(), + context.serverFactoryContext(), cache)); }; } diff --git a/source/extensions/filters/http/csrf/config.cc b/source/extensions/filters/http/csrf/config.cc index a36139e17bb7..425bf3812693 100644 --- a/source/extensions/filters/http/csrf/config.cc +++ b/source/extensions/filters/http/csrf/config.cc @@ -15,7 +15,7 @@ Http::FilterFactoryCb CsrfFilterFactory::createFilterFactoryFromProtoTyped( const envoy::extensions::filters::http::csrf::v3::CsrfPolicy& policy, const std::string& stats_prefix, Server::Configuration::FactoryContext& context) { CsrfFilterConfigSharedPtr config = std::make_shared( - policy, stats_prefix, context.scope(), context.serverFactoryContext().runtime()); + policy, stats_prefix, context.scope(), context.serverFactoryContext()); return [config](Http::FilterChainFactoryCallbacks& callbacks) -> void { callbacks.addStreamDecoderFilter(std::make_shared(config)); }; @@ -25,7 +25,7 @@ Router::RouteSpecificFilterConfigConstSharedPtr CsrfFilterFactory::createRouteSpecificFilterConfigTyped( const envoy::extensions::filters::http::csrf::v3::CsrfPolicy& policy, Server::Configuration::ServerFactoryContext& context, ProtobufMessage::ValidationVisitor&) { - return std::make_shared(policy, context.runtime()); + return std::make_shared(policy, context); } /** diff --git a/source/extensions/filters/http/csrf/csrf_filter.cc b/source/extensions/filters/http/csrf/csrf_filter.cc index ea8a521fd85e..c36c1ceb2816 100644 --- a/source/extensions/filters/http/csrf/csrf_filter.cc +++ b/source/extensions/filters/http/csrf/csrf_filter.cc @@ -76,15 +76,16 @@ static CsrfStats generateStats(const std::string& prefix, Stats::Scope& scope) { static CsrfPolicyPtr generatePolicy(const envoy::extensions::filters::http::csrf::v3::CsrfPolicy& policy, - Runtime::Loader& runtime) { - return std::make_unique(policy, runtime); + Server::Configuration::CommonFactoryContext& context) { + return std::make_unique(policy, context); } } // namespace CsrfFilterConfig::CsrfFilterConfig( const envoy::extensions::filters::http::csrf::v3::CsrfPolicy& policy, - const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime) - : stats_(generateStats(stats_prefix, scope)), policy_(generatePolicy(policy, runtime)) {} + const std::string& stats_prefix, Stats::Scope& scope, + Server::Configuration::CommonFactoryContext& context) + : stats_(generateStats(stats_prefix, scope)), policy_(generatePolicy(policy, context)) {} CsrfFilter::CsrfFilter(const CsrfFilterConfigSharedPtr config) : config_(config) {} diff --git a/source/extensions/filters/http/csrf/csrf_filter.h b/source/extensions/filters/http/csrf/csrf_filter.h index b52b9473a884..fe7bc78a1a29 100644 --- a/source/extensions/filters/http/csrf/csrf_filter.h +++ b/source/extensions/filters/http/csrf/csrf_filter.h @@ -35,12 +35,13 @@ struct CsrfStats { class CsrfPolicy : public Router::RouteSpecificFilterConfig { public: CsrfPolicy(const envoy::extensions::filters::http::csrf::v3::CsrfPolicy& policy, - Runtime::Loader& runtime) - : policy_(policy), runtime_(runtime) { + Server::Configuration::CommonFactoryContext& context) + : policy_(policy), runtime_(context.runtime()) { for (const auto& additional_origin : policy.additional_origins()) { additional_origins_.emplace_back( - std::make_unique>( - additional_origin)); + std::make_unique< + Matchers::StringMatcherImplWithContext>( + additional_origin, context)); } } @@ -78,7 +79,8 @@ using CsrfPolicyPtr = std::unique_ptr; class CsrfFilterConfig { public: CsrfFilterConfig(const envoy::extensions::filters::http::csrf::v3::CsrfPolicy& policy, - const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime); + const std::string& stats_prefix, Stats::Scope& scope, + Server::Configuration::CommonFactoryContext& context); CsrfStats& stats() { return stats_; } const CsrfPolicy* policy() { return policy_.get(); } diff --git a/source/extensions/filters/http/ext_authz/config.cc b/source/extensions/filters/http/ext_authz/config.cc index 881609481e96..87a20cbd12e9 100644 --- a/source/extensions/filters/http/ext_authz/config.cc +++ b/source/extensions/filters/http/ext_authz/config.cc @@ -25,9 +25,8 @@ Http::FilterFactoryCb ExtAuthzFilterConfig::createFilterFactoryFromProtoTyped( const std::string& stats_prefix, Server::Configuration::FactoryContext& context) { auto& server_context = context.serverFactoryContext(); - const auto filter_config = std::make_shared( - proto_config, context.scope(), server_context.runtime(), server_context.httpContext(), - stats_prefix, server_context.bootstrap()); + const auto filter_config = + std::make_shared(proto_config, context.scope(), stats_prefix, server_context); // The callback is created in main thread and executed in worker thread, variables except factory // context must be captured by value into the callback. Http::FilterFactoryCb callback; @@ -38,7 +37,7 @@ Http::FilterFactoryCb ExtAuthzFilterConfig::createFilterFactoryFromProtoTyped( timeout, DefaultTimeout); const auto client_config = std::make_shared( - proto_config, timeout_ms, proto_config.http_service().path_prefix()); + proto_config, timeout_ms, proto_config.http_service().path_prefix(), server_context); callback = [filter_config, client_config, &server_context](Http::FilterChainFactoryCallbacks& callbacks) { auto client = std::make_unique( diff --git a/source/extensions/filters/http/ext_authz/ext_authz.h b/source/extensions/filters/http/ext_authz/ext_authz.h index 7a24e2d84deb..a3d78bd2787c 100644 --- a/source/extensions/filters/http/ext_authz/ext_authz.h +++ b/source/extensions/filters/http/ext_authz/ext_authz.h @@ -56,8 +56,8 @@ class FilterConfig { public: FilterConfig(const envoy::extensions::filters::http::ext_authz::v3::ExtAuthz& config, - Stats::Scope& scope, Runtime::Loader& runtime, Http::Context& http_context, - const std::string& stats_prefix, envoy::config::bootstrap::v3::Bootstrap& bootstrap) + Stats::Scope& scope, const std::string& stats_prefix, + Server::Configuration::ServerFactoryContext& factory_context) : allow_partial_message_(config.with_request_body().allow_partial_message()), failure_mode_allow_(config.failure_mode_allow()), failure_mode_allow_header_add_(config.failure_mode_allow_header_add()), @@ -70,7 +70,7 @@ class FilterConfig { pack_as_bytes_(config.has_http_service() || config.with_request_body().pack_as_bytes()), status_on_error_(toErrorCode(config.status_on_error().code())), scope_(scope), - runtime_(runtime), http_context_(http_context), + runtime_(factory_context.runtime()), http_context_(factory_context.httpContext()), filter_enabled_(config.has_filter_enabled() ? absl::optional( Runtime::FractionalPercent(config.filter_enabled(), runtime_)) @@ -103,6 +103,7 @@ class FilterConfig { ext_authz_error_(pool_.add(createPoolStatName(config.stat_prefix(), "error"))), ext_authz_failure_mode_allowed_( pool_.add(createPoolStatName(config.stat_prefix(), "failure_mode_allowed"))) { + auto bootstrap = factory_context.bootstrap(); auto labels_key_it = bootstrap.node().metadata().fields().find(config.bootstrap_metadata_labels_key()); if (labels_key_it != bootstrap.node().metadata().fields().end()) { @@ -123,14 +124,14 @@ class FilterConfig { // Method, Path and Host). if (config.has_grpc_service() && config.has_allowed_headers()) { request_header_matchers_ = Filters::Common::ExtAuthz::CheckRequestUtils::toRequestMatchers( - config.allowed_headers(), false); + config.allowed_headers(), false, factory_context); } else if (config.has_http_service()) { if (config.http_service().authorization_request().has_allowed_headers()) { request_header_matchers_ = Filters::Common::ExtAuthz::CheckRequestUtils::toRequestMatchers( - config.http_service().authorization_request().allowed_headers(), true); + config.http_service().authorization_request().allowed_headers(), true, factory_context); } else { request_header_matchers_ = Filters::Common::ExtAuthz::CheckRequestUtils::toRequestMatchers( - config.allowed_headers(), true); + config.allowed_headers(), true, factory_context); } } } diff --git a/source/extensions/filters/http/ext_proc/config.cc b/source/extensions/filters/http/ext_proc/config.cc index ee45fc6a73c4..cfbba66b4181 100644 --- a/source/extensions/filters/http/ext_proc/config.cc +++ b/source/extensions/filters/http/ext_proc/config.cc @@ -20,7 +20,7 @@ Http::FilterFactoryCb ExternalProcessingFilterConfig::createFilterFactoryFromPro proto_config, std::chrono::milliseconds(message_timeout_ms), max_message_timeout_ms, context.scope(), stats_prefix, Envoy::Extensions::Filters::Common::Expr::getBuilder(context.serverFactoryContext()), - context.serverFactoryContext().localInfo()); + context.serverFactoryContext()); return [filter_config, grpc_service = proto_config.grpc_service(), &context](Http::FilterChainFactoryCallbacks& callbacks) { @@ -50,8 +50,7 @@ ExternalProcessingFilterConfig::createFilterFactoryFromProtoWithServerContextTyp const auto filter_config = std::make_shared( proto_config, std::chrono::milliseconds(message_timeout_ms), max_message_timeout_ms, server_context.scope(), stats_prefix, - Envoy::Extensions::Filters::Common::Expr::getBuilder(server_context), - server_context.localInfo()); + Envoy::Extensions::Filters::Common::Expr::getBuilder(server_context), server_context); return [filter_config, grpc_service = proto_config.grpc_service(), &server_context](Http::FilterChainFactoryCallbacks& callbacks) { diff --git a/source/extensions/filters/http/ext_proc/ext_proc.h b/source/extensions/filters/http/ext_proc/ext_proc.h index 689b1e322872..45fb789b6d7b 100644 --- a/source/extensions/filters/http/ext_proc/ext_proc.h +++ b/source/extensions/filters/http/ext_proc/ext_proc.h @@ -129,7 +129,7 @@ class FilterConfig { const uint32_t max_message_timeout_ms, Stats::Scope& scope, const std::string& stats_prefix, Extensions::Filters::Common::Expr::BuilderInstanceSharedPtr builder, - const LocalInfo::LocalInfo& local_info) + Server::Configuration::CommonFactoryContext& context) : failure_mode_allow_(config.failure_mode_allow()), disable_clear_route_cache_(config.disable_clear_route_cache()), message_timeout_(message_timeout), max_message_timeout_ms_(max_message_timeout_ms), @@ -138,8 +138,9 @@ class FilterConfig { filter_metadata_(config.filter_metadata()), allow_mode_override_(config.allow_mode_override()), disable_immediate_response_(config.disable_immediate_response()), - allowed_headers_(initHeaderMatchers(config.forward_rules().allowed_headers())), - disallowed_headers_(initHeaderMatchers(config.forward_rules().disallowed_headers())), + allowed_headers_(initHeaderMatchers(config.forward_rules().allowed_headers(), context)), + disallowed_headers_( + initHeaderMatchers(config.forward_rules().disallowed_headers(), context)), untyped_forwarding_namespaces_( config.metadata_options().forwarding_namespaces().untyped().begin(), config.metadata_options().forwarding_namespaces().untyped().end()), @@ -149,7 +150,7 @@ class FilterConfig { untyped_receiving_namespaces_( config.metadata_options().receiving_namespaces().untyped().begin(), config.metadata_options().receiving_namespaces().untyped().end()), - expression_manager_(builder, local_info, config.request_attributes(), + expression_manager_(builder, context.localInfo(), config.request_attributes(), config.response_attributes()) {} bool failureModeAllow() const { return failure_mode_allow_; } diff --git a/source/extensions/filters/http/ext_proc/matching_utils.cc b/source/extensions/filters/http/ext_proc/matching_utils.cc index e778be5ab945..e17c600da533 100644 --- a/source/extensions/filters/http/ext_proc/matching_utils.cc +++ b/source/extensions/filters/http/ext_proc/matching_utils.cc @@ -86,12 +86,14 @@ ExpressionManager::evaluateAttributes(const Filters::Common::Expr::Activation& a } std::vector -initHeaderMatchers(const envoy::type::matcher::v3::ListStringMatcher& header_list) { +initHeaderMatchers(const envoy::type::matcher::v3::ListStringMatcher& header_list, + Server::Configuration::CommonFactoryContext& context) { std::vector header_matchers; for (const auto& matcher : header_list.patterns()) { header_matchers.push_back( - std::make_unique>( - matcher)); + std::make_unique< + Matchers::StringMatcherImplWithContext>( + matcher, context)); } return header_matchers; } diff --git a/source/extensions/filters/http/ext_proc/matching_utils.h b/source/extensions/filters/http/ext_proc/matching_utils.h index f02c51ac2728..efd3a1f522f7 100644 --- a/source/extensions/filters/http/ext_proc/matching_utils.h +++ b/source/extensions/filters/http/ext_proc/matching_utils.h @@ -57,7 +57,8 @@ class ExpressionManager : public Logger::Loggable { }; std::vector -initHeaderMatchers(const envoy::type::matcher::v3::ListStringMatcher& header_list); +initHeaderMatchers(const envoy::type::matcher::v3::ListStringMatcher& header_list, + Server::Configuration::CommonFactoryContext& context); } // namespace ExternalProcessing } // namespace HttpFilters diff --git a/source/extensions/filters/http/json_to_metadata/filter.cc b/source/extensions/filters/http/json_to_metadata/filter.cc index 18c31b1e5736..fe24b2a622d1 100644 --- a/source/extensions/filters/http/json_to_metadata/filter.cc +++ b/source/extensions/filters/http/json_to_metadata/filter.cc @@ -60,6 +60,28 @@ struct JsonValueToProtobufValueConverter { } }; +absl::flat_hash_set generateAllowContentTypes( + const Protobuf::RepeatedPtrField& proto_allow_content_types) { + if (proto_allow_content_types.empty()) { + return {Http::Headers::get().ContentTypeValues.Json}; + } + + absl::flat_hash_set allow_content_types; + for (const auto& request_allowed_content_type : proto_allow_content_types) { + allow_content_types.insert(request_allowed_content_type); + } + return allow_content_types; +} + +Regex::CompiledMatcherPtr generateAllowContentTypeRegexs( + const envoy::type::matcher::v3::RegexMatcher& proto_allow_content_types_regex) { + + Regex::CompiledMatcherPtr allow_content_types_regex; + allow_content_types_regex = Regex::Utility::parseRegex(proto_allow_content_types_regex); + + return allow_content_types_regex; +} + } // anonymous namespace Rule::Rule(const ProtoRule& rule) : rule_(rule) { @@ -96,7 +118,17 @@ FilterConfig::FilterConfig( response_allow_content_types_( generateAllowContentTypes(proto_config.response_rules().allow_content_types())), request_allow_empty_content_type_(proto_config.request_rules().allow_empty_content_type()), - response_allow_empty_content_type_(proto_config.response_rules().allow_empty_content_type()) { + response_allow_empty_content_type_(proto_config.response_rules().allow_empty_content_type()), + request_allow_content_types_regex_( + proto_config.request_rules().has_allow_content_types_regex() + ? generateAllowContentTypeRegexs( + proto_config.request_rules().allow_content_types_regex()) + : nullptr), + response_allow_content_types_regex_( + proto_config.response_rules().has_allow_content_types_regex() + ? generateAllowContentTypeRegexs( + proto_config.response_rules().allow_content_types_regex()) + : nullptr) { if (request_rules_.empty() && response_rules_.empty()) { throw EnvoyException("json_to_metadata_filter: Per filter configs must at least specify " "either request or response rules"); @@ -111,25 +143,14 @@ Rules FilterConfig::generateRules(const ProtobufRepeatedRule& proto_rules) const return rules; } -absl::flat_hash_set FilterConfig::generateAllowContentTypes( - const Protobuf::RepeatedPtrField& proto_allow_content_types) const { - if (proto_allow_content_types.empty()) { - return {Http::Headers::get().ContentTypeValues.Json}; - } - - absl::flat_hash_set allow_content_types; - for (const auto& request_allowed_content_type : proto_allow_content_types) { - allow_content_types.insert(request_allowed_content_type); - } - return allow_content_types; -} - bool FilterConfig::requestContentTypeAllowed(absl::string_view content_type) const { if (content_type.empty()) { return request_allow_empty_content_type_; } - return request_allow_content_types_.contains(content_type); + return request_allow_content_types_.contains(content_type) || + (request_allow_content_types_regex_ && + request_allow_content_types_regex_->match(content_type)); } bool FilterConfig::responseContentTypeAllowed(absl::string_view content_type) const { @@ -137,7 +158,9 @@ bool FilterConfig::responseContentTypeAllowed(absl::string_view content_type) co return response_allow_empty_content_type_; } - return response_allow_content_types_.contains(content_type); + return response_allow_content_types_.contains(content_type) || + (response_allow_content_types_regex_ && + response_allow_content_types_regex_->match(content_type)); } void Filter::applyKeyValue(const std::string& value, const KeyValuePair& keyval, diff --git a/source/extensions/filters/http/json_to_metadata/filter.h b/source/extensions/filters/http/json_to_metadata/filter.h index 99f4feea7047..b7a50e5356e6 100644 --- a/source/extensions/filters/http/json_to_metadata/filter.h +++ b/source/extensions/filters/http/json_to_metadata/filter.h @@ -11,6 +11,7 @@ #include "envoy/stats/stats_macros.h" #include "source/common/buffer/buffer_impl.h" +#include "source/common/common/matchers.h" #include "source/extensions/filters/http/common/pass_through_filter.h" #include "absl/strings/string_view.h" @@ -74,8 +75,6 @@ class FilterConfig { private: using ProtobufRepeatedRule = Protobuf::RepeatedPtrField; Rules generateRules(const ProtobufRepeatedRule& proto_rule) const; - absl::flat_hash_set generateAllowContentTypes( - const Protobuf::RepeatedPtrField& proto_allow_content_types) const; JsonToMetadataStats rqstats_; JsonToMetadataStats respstats_; const Rules request_rules_; @@ -84,6 +83,8 @@ class FilterConfig { const absl::flat_hash_set response_allow_content_types_; const bool request_allow_empty_content_type_; const bool response_allow_empty_content_type_; + const Regex::CompiledMatcherPtr request_allow_content_types_regex_; + const Regex::CompiledMatcherPtr response_allow_content_types_regex_; }; const uint32_t MAX_PAYLOAD_VALUE_LEN = 8 * 1024; diff --git a/source/extensions/filters/http/jwt_authn/authenticator.cc b/source/extensions/filters/http/jwt_authn/authenticator.cc index 3e04ca79bd16..237f147ee5d0 100644 --- a/source/extensions/filters/http/jwt_authn/authenticator.cc +++ b/source/extensions/filters/http/jwt_authn/authenticator.cc @@ -13,6 +13,7 @@ #include "source/common/tracing/http_tracer_impl.h" #include "absl/strings/str_split.h" +#include "absl/time/time.h" #include "jwt_verify_lib/jwt.h" #include "jwt_verify_lib/struct_utils.h" #include "jwt_verify_lib/verify.h" @@ -241,6 +242,25 @@ void AuthenticatorImpl::startVerify() { return; } + const bool sub_allowed = jwks_data_->isSubjectAllowed(jwt_->sub_); + + if (!sub_allowed) { + doneWithStatus(Status::JwtVerificationFail); + return; + } + + absl::optional exp; + if (jwt_->exp_) { + exp = absl::FromUnixSeconds(jwt_->exp_); + } + const bool exp_allowed = jwks_data_->isLifetimeAllowed( + absl::FromChrono(timeSource().systemTime()), exp ? &exp.value() : nullptr); + + if (!exp_allowed) { + doneWithStatus(Status::JwtVerificationFail); + return; + } + if (use_jwt_cache) { handleGoodJwt(/*cache_hit=*/true); return; diff --git a/source/extensions/filters/http/jwt_authn/jwks_cache.cc b/source/extensions/filters/http/jwt_authn/jwks_cache.cc index 01c1daa11493..b5109b639ef6 100644 --- a/source/extensions/filters/http/jwt_authn/jwks_cache.cc +++ b/source/extensions/filters/http/jwt_authn/jwks_cache.cc @@ -8,10 +8,14 @@ #include "envoy/thread_local/thread_local.h" #include "source/common/common/logger.h" +#include "source/common/common/matchers.h" #include "source/common/config/datasource.h" #include "source/common/http/utility.h" #include "absl/container/node_hash_map.h" +#include "absl/strings/string_view.h" +#include "absl/time/time.h" +#include "absl/types/optional.h" #include "jwt_verify_lib/check_audience.h" using envoy::extensions::filters::http::jwt_authn::v3::JwtAuthentication; @@ -57,6 +61,21 @@ class JwksDataImpl : public JwksCache::JwksData, public Logger::Loggable(audiences); + + if (jwt_provider_.has_subjects()) { + sub_matcher_.emplace(jwt_provider_.subjects(), context.serverFactoryContext()); + } + + if (jwt_provider_.require_expiration()) { + max_exp_ = absl::InfiniteDuration(); + } + + if (jwt_provider_.has_max_lifetime()) { + // Intentionally overwrite previous max_exp_. max_lifetime takes precedence. + max_exp_ = absl::Seconds(jwt_provider_.max_lifetime().seconds()) + + absl::Nanoseconds(jwt_provider_.max_lifetime().nanos()); + } + bool enable_jwt_cache = jwt_provider_.has_jwt_cache_config(); const auto& config = jwt_provider_.jwt_cache_config(); tls_.set([enable_jwt_cache, config](Envoy::Event::Dispatcher& dispatcher) { @@ -92,6 +111,35 @@ class JwksDataImpl : public JwksCache::JwksData, public Logger::LoggableareAudiencesAllowed(jwt_audiences); } + bool isSubjectAllowed(const absl::string_view jwt_subject) const override { + if (!sub_matcher_.has_value()) { + return true; + } + + return sub_matcher_->match(jwt_subject); + } + + bool isLifetimeAllowed(const absl::Time& now, const absl::Time* exp) const override { + // This function takes the current time and calculates the remaining lifetime of the JWT. + // Then it compares that with the max lifetime in the config. Using issue time or not before + // claims would be better, but optional according to the spec. + + // Without a max lifetime, any exp is allowed. + if (!max_exp_.has_value()) { + return true; + } + + // If there's no exp field and we have a max set, then this isn't allowed. + if (exp == nullptr) { + return false; + } + + // Take the remaining credential lifetime and return if it's less + // than the max. + absl::Duration lifetime = *exp - now; + return lifetime < *max_exp_; + } + const Jwks* getJwksObj() const override { return tls_->jwks_.get(); } bool isExpired() const override { return time_source_.monotonicTime() >= tls_->expire_; } @@ -141,6 +189,9 @@ class JwksDataImpl : public JwksCache::JwksData, public Logger::Loggable tls_; // async fetcher JwksAsyncFetcherPtr async_fetcher_; + absl::optional> + sub_matcher_; + absl::optional max_exp_; }; using JwksDataImplPtr = std::unique_ptr; diff --git a/source/extensions/filters/http/jwt_authn/jwks_cache.h b/source/extensions/filters/http/jwt_authn/jwks_cache.h index 981fc0ecd59d..9cd0aac6926a 100644 --- a/source/extensions/filters/http/jwt_authn/jwks_cache.h +++ b/source/extensions/filters/http/jwt_authn/jwks_cache.h @@ -12,6 +12,7 @@ #include "source/extensions/filters/http/jwt_authn/jwt_cache.h" #include "source/extensions/filters/http/jwt_authn/stats.h" +#include "absl/strings/string_view.h" #include "jwt_verify_lib/jwks.h" namespace Envoy { @@ -55,6 +56,12 @@ class JwksCache { // Check if a list of audiences are allowed. virtual bool areAudiencesAllowed(const std::vector& audiences) const PURE; + // Check if a subject is allowed. + virtual bool isSubjectAllowed(absl::string_view sub) const PURE; + + // Check if the current credential lifetime is allowed. + virtual bool isLifetimeAllowed(const absl::Time& now, const absl::Time* exp) const PURE; + // Get the cached config: JWT rule. virtual const envoy::extensions::filters::http::jwt_authn::v3::JwtProvider& getJwtProvider() const PURE; diff --git a/source/extensions/filters/http/oauth2/BUILD b/source/extensions/filters/http/oauth2/BUILD index a236dfa7583f..e33ddce15638 100644 --- a/source/extensions/filters/http/oauth2/BUILD +++ b/source/extensions/filters/http/oauth2/BUILD @@ -44,6 +44,9 @@ envoy_cc_library( name = "oauth_lib", srcs = ["filter.cc"], hdrs = ["filter.h"], + external_deps = [ + "jwt_verify_lib", + ], deps = [ ":oauth_client", "//envoy/server:filter_config_interface", diff --git a/source/extensions/filters/http/oauth2/filter.cc b/source/extensions/filters/http/oauth2/filter.cc index 3eefed0e15a0..cda4885331f7 100644 --- a/source/extensions/filters/http/oauth2/filter.cc +++ b/source/extensions/filters/http/oauth2/filter.cc @@ -24,6 +24,10 @@ #include "absl/strings/str_cat.h" #include "absl/strings/str_join.h" #include "absl/strings/str_split.h" +#include "jwt_verify_lib/jwt.h" +#include "jwt_verify_lib/status.h" + +using namespace std::chrono_literals; namespace Envoy { namespace Extensions { @@ -197,7 +201,9 @@ FilterConfig::FilterConfig( cookie_names_(proto_config.credentials().cookie_names()), auth_type_(getAuthType(proto_config.auth_type())), use_refresh_token_(proto_config.use_refresh_token().value()), - default_expires_in_(PROTOBUF_GET_SECONDS_OR_DEFAULT(proto_config, default_expires_in, 0)) { + default_expires_in_(PROTOBUF_GET_SECONDS_OR_DEFAULT(proto_config, default_expires_in, 0)), + default_refresh_token_expires_in_( + PROTOBUF_GET_SECONDS_OR_DEFAULT(proto_config, default_refresh_token_expires_in, 604800)) { if (!cluster_manager.clusters().hasCluster(oauth_token_endpoint_.cluster())) { throw EnvoyException(fmt::format("OAuth2 filter: unknown cluster '{}' in config. Please " "specify which cluster to direct OAuth requests to.", @@ -233,9 +239,7 @@ void OAuth2CookieValidator::setParams(const Http::RequestHeaderMap& headers, secret_.assign(secret.begin(), secret.end()); } -bool OAuth2CookieValidator::canUpdateTokenByRefreshToken() const { - return (!token_.empty() && !refresh_token_.empty()); -} +bool OAuth2CookieValidator::canUpdateTokenByRefreshToken() const { return !refresh_token_.empty(); } bool OAuth2CookieValidator::hmacIsValid() const { return ( @@ -362,6 +366,9 @@ Http::FilterHeadersStatus OAuth2Filter::decodeHeaders(Http::RequestHeaderMap& he // Check if we can update the access token via a refresh token. if (config_->useRefreshToken() && validator_->canUpdateTokenByRefreshToken()) { + + ENVOY_LOG(debug, "Trying to update the access token using the refresh token"); + // try to update access token by refresh token oauth_client_->asyncRefreshAccessToken(validator_->refreshToken(), config_->clientId(), config_->clientSecret(), config_->authType()); @@ -533,6 +540,7 @@ void OAuth2Filter::updateTokens(const std::string& access_token, const std::stri id_token_ = id_token; refresh_token_ = refresh_token; expires_in_ = std::to_string(expires_in.count()); + expires_refresh_token_in_ = getExpiresTimeForRefreshToken(refresh_token, expires_in); const auto new_epoch = time_source_.systemTime() + expires_in; new_expires_ = std::to_string( @@ -552,6 +560,34 @@ std::string OAuth2Filter::getEncodedToken() const { return encoded_token; } +std::string +OAuth2Filter::getExpiresTimeForRefreshToken(const std::string& refresh_token, + const std::chrono::seconds& expires_in) const { + if (config_->useRefreshToken()) { + ::google::jwt_verify::Jwt jwt; + if (jwt.parseFromString(refresh_token) == ::google::jwt_verify::Status::Ok && jwt.exp_ != 0) { + const std::chrono::seconds expirationFromJwt = std::chrono::seconds{jwt.exp_}; + const std::chrono::seconds now = + std::chrono::time_point_cast(time_source_.systemTime()) + .time_since_epoch(); + + if (now < expirationFromJwt) { + const auto expiration_epoch = expirationFromJwt - now; + return std::to_string(expiration_epoch.count()); + } else { + ENVOY_LOG(debug, "An expiration time in the refresh token is less of the current time"); + return "0"; + } + } + ENVOY_LOG(debug, "The refresh token is not JWT or exp claim is ommited. The lifetime of the " + "refresh token is taken from filter configuration"); + const std::chrono::seconds default_refresh_token_expires_in = + config_->defaultRefreshTokenExpiresIn(); + return std::to_string(default_refresh_token_expires_in.count()); + } + return std::to_string(expires_in.count()); +} + void OAuth2Filter::onGetAccessTokenSuccess(const std::string& access_code, const std::string& id_token, const std::string& refresh_token, @@ -608,7 +644,7 @@ void OAuth2Filter::finishRefreshAccessTokenFlow() { } std::string new_cookies(absl::StrJoin(cookies, "; ", absl::PairFormatter("="))); - request_headers_->addReferenceKey(Http::Headers::get().Cookie, new_cookies); + request_headers_->setReferenceKey(Http::Headers::get().Cookie, new_cookies); if (config_->forwardBearerToken() && !access_token_.empty()) { setBearerToken(*request_headers_, access_token_); } @@ -669,9 +705,12 @@ void OAuth2Filter::addResponseCookies(Http::ResponseHeaderMap& headers, } if (!refresh_token_.empty()) { + const std::string refresh_token_cookie_tail_http_only = + fmt::format(CookieTailHttpOnlyFormatString, expires_refresh_token_in_); + headers.addReferenceKey(Http::Headers::get().SetCookie, absl::StrCat(cookie_names.refresh_token_, "=", refresh_token_, - cookie_attribute_httponly)); + refresh_token_cookie_tail_http_only)); } } } diff --git a/source/extensions/filters/http/oauth2/filter.h b/source/extensions/filters/http/oauth2/filter.h index f593f9b9b4aa..ca5153b9d144 100644 --- a/source/extensions/filters/http/oauth2/filter.h +++ b/source/extensions/filters/http/oauth2/filter.h @@ -143,6 +143,9 @@ class FilterConfig { const AuthType& authType() const { return auth_type_; } bool useRefreshToken() const { return use_refresh_token_; } std::chrono::seconds defaultExpiresIn() const { return default_expires_in_; } + std::chrono::seconds defaultRefreshTokenExpiresIn() const { + return default_refresh_token_expires_in_; + } private: static FilterStats generateStats(const std::string& prefix, Stats::Scope& scope); @@ -167,6 +170,7 @@ class FilterConfig { const AuthType auth_type_; const bool use_refresh_token_{}; const std::chrono::seconds default_expires_in_; + const std::chrono::seconds default_refresh_token_expires_in_; }; using FilterConfigSharedPtr = std::shared_ptr; @@ -265,6 +269,7 @@ class OAuth2Filter : public Http::PassThroughFilter, std::string id_token_; std::string refresh_token_; std::string expires_in_; + std::string expires_refresh_token_in_; std::string new_expires_; absl::string_view host_; std::string state_; @@ -284,6 +289,8 @@ class OAuth2Filter : public Http::PassThroughFilter, Http::FilterHeadersStatus signOutUser(const Http::RequestHeaderMap& headers); std::string getEncodedToken() const; + std::string getExpiresTimeForRefreshToken(const std::string& refresh_token, + const std::chrono::seconds& expires_in) const; void addResponseCookies(Http::ResponseHeaderMap& headers, const std::string& encoded_token) const; const std::string& bearerPrefix() const; }; diff --git a/source/extensions/filters/http/rbac/rbac_filter.cc b/source/extensions/filters/http/rbac/rbac_filter.cc index bb6950942611..f1ee12283824 100644 --- a/source/extensions/filters/http/rbac/rbac_filter.cc +++ b/source/extensions/filters/http/rbac/rbac_filter.cc @@ -61,7 +61,7 @@ RoleBasedAccessControlFilterConfig::RoleBasedAccessControlFilterConfig( const std::string& stats_prefix, Stats::Scope& scope, Server::Configuration::ServerFactoryContext& context, ProtobufMessage::ValidationVisitor& validation_visitor) - : stats_(Filters::Common::RBAC::generateStats(stats_prefix, + : stats_(Filters::Common::RBAC::generateStats(stats_prefix, proto_config.rules_stat_prefix(), proto_config.shadow_rules_stat_prefix(), scope)), shadow_rules_stat_prefix_(proto_config.shadow_rules_stat_prefix()), engine_(Filters::Common::RBAC::createEngine(proto_config, context, validation_visitor, diff --git a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc index 334236048f21..b58abc50dfc1 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc +++ b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.cc @@ -166,8 +166,9 @@ ParameterRouteEntryImpl::ParameterData::ParameterData(uint32_t index, } MethodRouteEntryImpl::MethodRouteEntryImpl( - const envoy::extensions::filters::network::dubbo_proxy::v3::Route& route) - : RouteEntryImplBase(route), method_name_(route.match().method().name()) { + const envoy::extensions::filters::network::dubbo_proxy::v3::Route& route, + Server::Configuration::CommonFactoryContext& context) + : RouteEntryImplBase(route), method_name_(route.match().method().name(), context) { if (route.match().method().params_match_size() != 0) { parameter_route_ = std::make_shared(route); } @@ -206,12 +207,12 @@ RouteConstSharedPtr MethodRouteEntryImpl::matches(const MessageMetadata& metadat } SingleRouteMatcherImpl::SingleRouteMatcherImpl(const RouteConfig& config, - Server::Configuration::ServerFactoryContext&) + Server::Configuration::ServerFactoryContext& context) : interface_matcher_(config.interface()), group_(config.group()), version_(config.version()) { using envoy::extensions::filters::network::dubbo_proxy::v3::RouteMatch; for (const auto& route : config.routes()) { - routes_.emplace_back(std::make_shared(route)); + routes_.emplace_back(std::make_shared(route, context)); } ENVOY_LOG(debug, "dubbo route matcher: routes list size {}", routes_.size()); } diff --git a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h index 8b37eb2a2f23..b5fbcb2c7004 100644 --- a/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h +++ b/source/extensions/filters/network/dubbo_proxy/router/route_matcher.h @@ -118,7 +118,8 @@ class ParameterRouteEntryImpl : public RouteEntryImplBase { class MethodRouteEntryImpl : public RouteEntryImplBase { public: - MethodRouteEntryImpl(const envoy::extensions::filters::network::dubbo_proxy::v3::Route& route); + MethodRouteEntryImpl(const envoy::extensions::filters::network::dubbo_proxy::v3::Route& route, + Server::Configuration::CommonFactoryContext& context); ~MethodRouteEntryImpl() override; // RoutEntryImplBase @@ -126,7 +127,8 @@ class MethodRouteEntryImpl : public RouteEntryImplBase { uint64_t random_value) const override; private: - const Matchers::StringMatcherImpl method_name_; + const Matchers::StringMatcherImplWithContext + method_name_; std::shared_ptr parameter_route_; }; diff --git a/source/extensions/filters/network/http_connection_manager/config.cc b/source/extensions/filters/network/http_connection_manager/config.cc index a016f5058240..458c3eeb3e92 100644 --- a/source/extensions/filters/network/http_connection_manager/config.cc +++ b/source/extensions/filters/network/http_connection_manager/config.cc @@ -72,9 +72,11 @@ FilterFactoryMap::const_iterator findUpgradeCaseInsensitive(const FilterFactoryM std::unique_ptr createInternalAddressConfig( const envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& - config) { + config, + absl::Status& creation_status) { if (config.has_internal_address_config()) { - return std::make_unique(config.internal_address_config()); + return std::make_unique(config.internal_address_config(), + creation_status); } return std::make_unique(); @@ -128,7 +130,8 @@ envoy::extensions::filters::network::http_connection_manager::v3::HttpConnection Http::HeaderValidatorFactoryPtr createHeaderValidatorFactory([[maybe_unused]] const envoy::extensions::filters::network:: http_connection_manager::v3::HttpConnectionManager& config, - [[maybe_unused]] Server::Configuration::FactoryContext& context) { + [[maybe_unused]] Server::Configuration::FactoryContext& context, + absl::Status& creation_status) { Http::HeaderValidatorFactoryPtr header_validator_factory; #ifdef ENVOY_ENABLE_UHV @@ -172,27 +175,31 @@ createHeaderValidatorFactory([[maybe_unused]] const envoy::extensions::filters:: auto* factory = Envoy::Config::Utility::getFactory( header_validator_config); if (!factory) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( fmt::format("Header validator extension not found: '{}'", header_validator_config.name())); + return nullptr; } header_validator_factory = factory->createFromProto(header_validator_config.typed_config(), context.serverFactoryContext()); if (!header_validator_factory) { - throwEnvoyExceptionOrPanic(fmt::format("Header validator extension could not be created: '{}'", - header_validator_config.name())); + creation_status = absl::InvalidArgumentError(fmt::format( + "Header validator extension could not be created: '{}'", header_validator_config.name())); + return nullptr; } #else if (config.has_typed_header_validation_config()) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( fmt::format("This Envoy binary does not support header validator extensions.: '{}'", config.typed_header_validation_config().name())); + return nullptr; } if (Runtime::runtimeFeatureEnabled( "envoy.reloadable_features.enable_universal_header_validator")) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( "Header validator can not be enabled since this Envoy binary does not support it."); + return nullptr; } #endif return header_validator_factory; @@ -239,7 +246,7 @@ Utility::Singletons Utility::createSingletons(Server::Configuration::FactoryCont tracer_manager, filter_config_provider_manager}; } -std::shared_ptr Utility::createConfig( +absl::StatusOr> Utility::createConfig( const envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& proto_config, Server::Configuration::FactoryContext& context, Http::DateProvider& date_provider, @@ -247,9 +254,13 @@ std::shared_ptr Utility::createConfig( Config::ConfigProviderManager& scoped_routes_config_provider_manager, Tracing::TracerManager& tracer_manager, FilterConfigProviderManager& filter_config_provider_manager) { - return std::make_shared( + absl::Status creation_status = absl::OkStatus(); + auto config = std::make_shared( proto_config, context, date_provider, route_config_provider_manager, - scoped_routes_config_provider_manager, tracer_manager, filter_config_provider_manager); + scoped_routes_config_provider_manager, tracer_manager, filter_config_provider_manager, + creation_status); + RETURN_IF_NOT_OK(creation_status); + return config; } Network::FilterFactoryCb @@ -267,10 +278,13 @@ HttpConnectionManagerFilterConfigFactory::createFilterFactoryFromProtoAndHopByHo Server::Configuration::FactoryContext& context, bool clear_hop_by_hop_headers) { Utility::Singletons singletons = Utility::createSingletons(context); - auto filter_config = Utility::createConfig( - proto_config, context, *singletons.date_provider_, *singletons.route_config_provider_manager_, - *singletons.scoped_routes_config_provider_manager_, *singletons.tracer_manager_, - *singletons.filter_config_provider_manager_); + auto filter_config = THROW_OR_RETURN_VALUE( + Utility::createConfig(proto_config, context, *singletons.date_provider_, + *singletons.route_config_provider_manager_, + *singletons.scoped_routes_config_provider_manager_, + *singletons.tracer_manager_, + *singletons.filter_config_provider_manager_), + std::shared_ptr); // This lambda captures the shared_ptrs created above, thus preserving the // reference count. @@ -310,10 +324,11 @@ LEGACY_REGISTER_FACTORY(HttpConnectionManagerFilterConfigFactory, InternalAddressConfig::InternalAddressConfig( const envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager:: - InternalAddressConfig& config) + InternalAddressConfig& config, + absl::Status& creation_status) : unix_sockets_(config.unix_sockets()) { auto list_or_error = Network::Address::IpList::create(config.cidr_ranges()); - THROW_IF_STATUS_NOT_OK(list_or_error, throw); + SET_AND_RETURN_IF_NOT_OK(list_or_error.status(), creation_status); cidr_ranges_ = std::move(list_or_error.value()); } @@ -324,13 +339,13 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( Router::RouteConfigProviderManager& route_config_provider_manager, Config::ConfigProviderManager& scoped_routes_config_provider_manager, Tracing::TracerManager& tracer_manager, - FilterConfigProviderManager& filter_config_provider_manager) + FilterConfigProviderManager& filter_config_provider_manager, absl::Status& creation_status) : context_(context), stats_prefix_(fmt::format("http.{}.", config.stat_prefix())), stats_(Http::ConnectionManagerImpl::generateStats(stats_prefix_, context_.scope())), tracing_stats_( Http::ConnectionManagerImpl::generateTracingStats(stats_prefix_, context_.scope())), use_remote_address_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, use_remote_address, false)), - internal_address_config_(createInternalAddressConfig(config)), + internal_address_config_(createInternalAddressConfig(config, creation_status)), xff_num_trusted_hops_(config.xff_num_trusted_hops()), skip_xff_append_(config.skip_xff_append()), via_(config.via()), route_config_provider_manager_(route_config_provider_manager), @@ -397,16 +412,19 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( ? std::make_unique( config.proxy_status_config()) : nullptr), - header_validator_factory_(createHeaderValidatorFactory(config, context)), + header_validator_factory_(createHeaderValidatorFactory(config, context, creation_status)), append_local_overload_(config.append_local_overload()), append_x_forwarded_port_(config.append_x_forwarded_port()), add_proxy_protocol_connection_state_( PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, add_proxy_protocol_connection_state, true)) { + if (!creation_status.ok()) { + return; + } auto options_or_error = Http2::Utility::initializeAndValidateOptions( config.http2_protocol_options(), config.has_stream_error_on_invalid_http_message(), config.stream_error_on_invalid_http_message()); - THROW_IF_STATUS_NOT_OK(options_or_error, throw); + SET_AND_RETURN_IF_NOT_OK(options_or_error.status(), creation_status); http2_options_ = options_or_error.value(); if (!idle_timeout_) { idle_timeout_ = std::chrono::hours(1); @@ -415,8 +433,9 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( } if (config.strip_any_host_port() && config.strip_matching_host_port()) { - throwEnvoyExceptionOrPanic(fmt::format( + creation_status = absl::InvalidArgumentError(fmt::format( "Error: Only one of `strip_matching_host_port` or `strip_any_host_port` can be set.")); + return; } if (config.strip_any_host_port()) { @@ -438,7 +457,7 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( envoy::extensions::request_id::uuid::v3::UuidRequestIdConfig()); } auto extension_or_error = Http::RequestIDExtensionFactory::fromProto(final_rid_config, context_); - THROW_IF_STATUS_NOT_OK(extension_or_error, throw); + SET_AND_RETURN_IF_NOT_OK(extension_or_error.status(), creation_status); request_id_extension_ = extension_or_error.value(); // Check if IP detection extensions were configured, otherwise fall back to XFF. @@ -452,13 +471,15 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( extension->mutable_typed_config()->PackFrom(xff_config); } else { if (use_remote_address_) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( "Original IP detection extensions and use_remote_address may not be mixed"); + return; } if (xff_num_trusted_hops_ > 0) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( "Original IP detection extensions and xff_num_trusted_hops may not be mixed"); + return; } } @@ -467,14 +488,16 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( auto* factory = Envoy::Config::Utility::getFactory(extension_config); if (!factory) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( fmt::format("Original IP detection extension not found: '{}'", extension_config.name())); + return; } auto extension = factory->createExtension(extension_config.typed_config(), context_); if (!extension) { - throwEnvoyExceptionOrPanic(fmt::format( + creation_status = absl::InvalidArgumentError(fmt::format( "Original IP detection extension could not be created: '{}'", extension_config.name())); + return; } original_ip_detection_extensions_.push_back(extension); } @@ -485,14 +508,16 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( auto* factory = Envoy::Config::Utility::getFactory(extension_config); if (!factory) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( fmt::format("Early header mutation extension not found: '{}'", extension_config.name())); + return; } auto extension = factory->createExtension(extension_config.typed_config(), context_); if (!extension) { - throwEnvoyExceptionOrPanic(fmt::format( + creation_status = absl::InvalidArgumentError(fmt::format( "Early header mutation extension could not be created: '{}'", extension_config.name())); + return; } early_header_mutation_extensions_.push_back(std::move(extension)); } @@ -579,13 +604,15 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( if (config.has_access_log_options()) { if (config.flush_access_log_on_new_request() /* deprecated */) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( "Only one of flush_access_log_on_new_request or access_log_options can be specified."); + return; } if (config.has_access_log_flush_interval()) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( "Only one of access_log_flush_interval or access_log_options can be specified."); + return; } flush_access_log_on_new_request_ = @@ -637,41 +664,46 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( #ifdef ENVOY_ENABLE_QUIC codec_type_ = CodecType::HTTP3; if (!context_.listenerInfo().isQuic()) { - throwEnvoyExceptionOrPanic("HTTP/3 codec configured on non-QUIC listener."); + creation_status = absl::InvalidArgumentError("HTTP/3 codec configured on non-QUIC listener."); + return; } #else - throwEnvoyExceptionOrPanic("HTTP3 configured but not enabled in the build."); + creation_status = absl::InvalidArgumentError("HTTP3 configured but not enabled in the build."); + return; #endif break; } if (codec_type_ != CodecType::HTTP3 && context_.listenerInfo().isQuic()) { - throwEnvoyExceptionOrPanic("Non-HTTP/3 codec configured on QUIC listener."); + creation_status = absl::InvalidArgumentError("Non-HTTP/3 codec configured on QUIC listener."); + return; } Http::FilterChainHelper helper(filter_config_provider_manager_, context_.serverFactoryContext(), context_.serverFactoryContext().clusterManager(), context_, stats_prefix_); - THROW_IF_NOT_OK(helper.processFilters(config.http_filters(), "http", "http", filter_factories_)); + SET_AND_RETURN_IF_NOT_OK( + helper.processFilters(config.http_filters(), "http", "http", filter_factories_), + creation_status); for (const auto& upgrade_config : config.upgrade_configs()) { const std::string& name = upgrade_config.upgrade_type(); const bool enabled = upgrade_config.has_enabled() ? upgrade_config.enabled().value() : true; if (findUpgradeCaseInsensitive(upgrade_filter_factories_, name) != upgrade_filter_factories_.end()) { - throwEnvoyExceptionOrPanic( + creation_status = absl::InvalidArgumentError( fmt::format("Error: multiple upgrade configs with the same name: '{}'", name)); + return; } if (!upgrade_config.filters().empty()) { std::unique_ptr factories = std::make_unique(); Http::DependencyManager upgrade_dependency_manager; - THROW_IF_NOT_OK( - helper.processFilters(upgrade_config.filters(), name, "http upgrade", *factories)); + SET_AND_RETURN_IF_NOT_OK( + helper.processFilters(upgrade_config.filters(), name, "http upgrade", *factories), + creation_status); // TODO(auni53): Validate encode dependencies too. auto status = upgrade_dependency_manager.validDecodeDependencies(); - if (!status.ok()) { - throwEnvoyExceptionOrPanic(std::string(status.message())); - } + SET_AND_RETURN_IF_NOT_OK(status, creation_status); upgrade_filter_factories_.emplace( std::make_pair(name, FilterConfig{std::move(factories), enabled})); @@ -803,10 +835,13 @@ HttpConnectionManagerFactory::createHttpConnectionManagerFactoryFromProto( Server::Configuration::FactoryContext& context, bool clear_hop_by_hop_headers) { Utility::Singletons singletons = Utility::createSingletons(context); - auto filter_config = Utility::createConfig( - proto_config, context, *singletons.date_provider_, *singletons.route_config_provider_manager_, - *singletons.scoped_routes_config_provider_manager_, *singletons.tracer_manager_, - *singletons.filter_config_provider_manager_); + auto filter_config = THROW_OR_RETURN_VALUE( + Utility::createConfig(proto_config, context, *singletons.date_provider_, + *singletons.route_config_provider_manager_, + *singletons.scoped_routes_config_provider_manager_, + *singletons.tracer_manager_, + *singletons.filter_config_provider_manager_), + std::shared_ptr); // This lambda captures the shared_ptrs created above, thus preserving the // reference count. diff --git a/source/extensions/filters/network/http_connection_manager/config.h b/source/extensions/filters/network/http_connection_manager/config.h index b03d65e22c1c..b18e396a8367 100644 --- a/source/extensions/filters/network/http_connection_manager/config.h +++ b/source/extensions/filters/network/http_connection_manager/config.h @@ -100,7 +100,8 @@ DECLARE_FACTORY(MobileHttpConnectionManagerFilterConfigFactory); class InternalAddressConfig : public Http::InternalAddressConfig { public: InternalAddressConfig(const envoy::extensions::filters::network::http_connection_manager::v3:: - HttpConnectionManager::InternalAddressConfig& config); + HttpConnectionManager::InternalAddressConfig& config, + absl::Status& creation_status); bool isInternalAddress(const Network::Address::Instance& address) const override { if (address.type() == Network::Address::Type::Pipe) { @@ -134,7 +135,7 @@ class HttpConnectionManagerConfig : Logger::Loggable, Router::RouteConfigProviderManager& route_config_provider_manager, Config::ConfigProviderManager& scoped_routes_config_provider_manager, Tracing::TracerManager& tracer_manager, - FilterConfigProviderManager& filter_config_provider_manager); + FilterConfigProviderManager& filter_config_provider_manager, absl::Status& creation_status); // Http::FilterChainFactory bool createFilterChain( @@ -406,9 +407,9 @@ class Utility { * @param date_provider the singleton used in config creation. * @param route_config_provider_manager the singleton used in config creation. * @param scoped_routes_config_provider_manager the singleton used in config creation. - * @return a shared_ptr to the created config object. + * @return a shared_ptr to the created config object or a creation error */ - static std::shared_ptr createConfig( + static absl::StatusOr> createConfig( const envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& proto_config, Server::Configuration::FactoryContext& context, Http::DateProvider& date_provider, diff --git a/source/extensions/filters/network/rbac/rbac_filter.cc b/source/extensions/filters/network/rbac/rbac_filter.cc index 500a0c2f617e..7d07de0f6d18 100644 --- a/source/extensions/filters/network/rbac/rbac_filter.cc +++ b/source/extensions/filters/network/rbac/rbac_filter.cc @@ -56,7 +56,7 @@ RoleBasedAccessControlFilterConfig::RoleBasedAccessControlFilterConfig( const envoy::extensions::filters::network::rbac::v3::RBAC& proto_config, Stats::Scope& scope, Server::Configuration::ServerFactoryContext& context, ProtobufMessage::ValidationVisitor& validation_visitor) - : stats_(Filters::Common::RBAC::generateStats(proto_config.stat_prefix(), + : stats_(Filters::Common::RBAC::generateStats(proto_config.stat_prefix(), "", proto_config.shadow_rules_stat_prefix(), scope)), shadow_rules_stat_prefix_(proto_config.shadow_rules_stat_prefix()), engine_(Filters::Common::RBAC::createEngine(proto_config, context, validation_visitor, 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 7e8ba256ebc5..2b0e416a9eba 100644 --- a/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.cc +++ b/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.cc @@ -399,7 +399,8 @@ void UdpProxyFilter::UdpActiveSession::onReadReady() { uint32_t packets_dropped = 0; const Api::IoErrorPtr result = Network::Utility::readPacketsFromSocket( udp_socket_->ioHandle(), *addresses_.local_, *this, cluster_.filter_.config_->timeSource(), - cluster_.filter_.config_->upstreamSocketConfig().prefer_gro_, packets_dropped); + cluster_.filter_.config_->upstreamSocketConfig().prefer_gro_, /*allow_mmsg=*/true, + packets_dropped); if (result == nullptr) { udp_socket_->ioHandle().activateFileEvents(Event::FileReadyType::Read); return; diff --git a/source/extensions/grpc_credentials/aws_iam/config.cc b/source/extensions/grpc_credentials/aws_iam/config.cc index 4e8127a19749..fc9c11a68dfb 100644 --- a/source/extensions/grpc_credentials/aws_iam/config.cc +++ b/source/extensions/grpc_credentials/aws_iam/config.cc @@ -22,11 +22,12 @@ namespace GrpcCredentials { namespace AwsIam { std::shared_ptr AwsIamGrpcCredentialsFactory::getChannelCredentials( - const envoy::config::core::v3::GrpcService& grpc_service_config, Api::Api& api) { + const envoy::config::core::v3::GrpcService& grpc_service_config, + Server::Configuration::CommonFactoryContext& context) { const auto& google_grpc = grpc_service_config.google_grpc(); std::shared_ptr creds = - Grpc::CredsUtility::defaultSslChannelCredentials(grpc_service_config, api); + Grpc::CredsUtility::defaultSslChannelCredentials(grpc_service_config, context.api()); std::shared_ptr call_creds; for (const auto& credential : google_grpc.call_credentials()) { @@ -66,10 +67,10 @@ std::shared_ptr AwsIamGrpcCredentialsFactory::getChann // usage of AWS credentials common utils. Until then we are setting nullopt for server // factory context. auto credentials_provider = std::make_shared( - api, absl::nullopt /*Empty factory context*/, region, + context.api(), absl::nullopt /*Empty factory context*/, region, Common::Aws::Utility::fetchMetadata); auto signer = std::make_unique( - config.service_name(), region, credentials_provider, api.timeSource(), + config.service_name(), region, credentials_provider, context, // TODO: extend API to allow specifying header exclusion. ref: // https://github.com/envoyproxy/envoy/pull/18998 Common::Aws::AwsSigningHeaderExclusionVector{}); diff --git a/source/extensions/grpc_credentials/aws_iam/config.h b/source/extensions/grpc_credentials/aws_iam/config.h index 5997f61903de..16efbcead7c6 100644 --- a/source/extensions/grpc_credentials/aws_iam/config.h +++ b/source/extensions/grpc_credentials/aws_iam/config.h @@ -20,7 +20,7 @@ class AwsIamGrpcCredentialsFactory : public Grpc::GoogleGrpcCredentialsFactory { public: std::shared_ptr getChannelCredentials(const envoy::config::core::v3::GrpcService& grpc_service_config, - Api::Api& api) override; + Server::Configuration::CommonFactoryContext& context) override; Envoy::ProtobufTypes::MessagePtr createEmptyConfigProto() { return std::make_unique(); diff --git a/source/extensions/grpc_credentials/example/config.cc b/source/extensions/grpc_credentials/example/config.cc index 2645fb07deea..18cebbe34ef0 100644 --- a/source/extensions/grpc_credentials/example/config.cc +++ b/source/extensions/grpc_credentials/example/config.cc @@ -13,10 +13,11 @@ namespace Example { std::shared_ptr AccessTokenExampleGrpcCredentialsFactory::getChannelCredentials( - const envoy::config::core::v3::GrpcService& grpc_service_config, Api::Api& api) { + const envoy::config::core::v3::GrpcService& grpc_service_config, + Server::Configuration::CommonFactoryContext& context) { const auto& google_grpc = grpc_service_config.google_grpc(); std::shared_ptr creds = - Grpc::CredsUtility::defaultSslChannelCredentials(grpc_service_config, api); + Grpc::CredsUtility::defaultSslChannelCredentials(grpc_service_config, context.api()); std::shared_ptr call_creds = nullptr; for (const auto& credential : google_grpc.call_credentials()) { switch (credential.credential_specifier_case()) { diff --git a/source/extensions/grpc_credentials/example/config.h b/source/extensions/grpc_credentials/example/config.h index 33cd85e6f425..28c860adcfaf 100644 --- a/source/extensions/grpc_credentials/example/config.h +++ b/source/extensions/grpc_credentials/example/config.h @@ -28,7 +28,7 @@ class AccessTokenExampleGrpcCredentialsFactory : public Grpc::GoogleGrpcCredenti public: std::shared_ptr getChannelCredentials(const envoy::config::core::v3::GrpcService& grpc_service_config, - Api::Api& api) override; + Server::Configuration::CommonFactoryContext& context) override; std::string name() const override { return "envoy.grpc_credentials.access_token_example"; } }; diff --git a/source/extensions/grpc_credentials/file_based_metadata/config.cc b/source/extensions/grpc_credentials/file_based_metadata/config.cc index 266adbf96443..ba02ec399fd1 100644 --- a/source/extensions/grpc_credentials/file_based_metadata/config.cc +++ b/source/extensions/grpc_credentials/file_based_metadata/config.cc @@ -19,10 +19,11 @@ namespace FileBasedMetadata { std::shared_ptr FileBasedMetadataGrpcCredentialsFactory::getChannelCredentials( - const envoy::config::core::v3::GrpcService& grpc_service_config, Api::Api& api) { + const envoy::config::core::v3::GrpcService& grpc_service_config, + Server::Configuration::CommonFactoryContext& context) { const auto& google_grpc = grpc_service_config.google_grpc(); std::shared_ptr creds = - Grpc::CredsUtility::defaultSslChannelCredentials(grpc_service_config, api); + Grpc::CredsUtility::defaultSslChannelCredentials(grpc_service_config, context.api()); std::shared_ptr call_creds = nullptr; for (const auto& credential : google_grpc.call_credentials()) { switch (credential.credential_specifier_case()) { @@ -39,8 +40,9 @@ FileBasedMetadataGrpcCredentialsFactory::getChannelCredentials( const auto& file_based_metadata_config = Envoy::MessageUtil::downcastAndValidate< const envoy::config::grpc_credential::v3::FileBasedMetadataConfig&>( *file_based_metadata_config_message, ProtobufMessage::getNullValidationVisitor()); - std::shared_ptr new_call_creds = grpc::MetadataCredentialsFromPlugin( - std::make_unique(file_based_metadata_config, api)); + std::shared_ptr new_call_creds = + grpc::MetadataCredentialsFromPlugin(std::make_unique( + file_based_metadata_config, context.api())); if (call_creds == nullptr) { call_creds = new_call_creds; } else { diff --git a/source/extensions/grpc_credentials/file_based_metadata/config.h b/source/extensions/grpc_credentials/file_based_metadata/config.h index 2ff1c54a161f..778ad34db9a1 100644 --- a/source/extensions/grpc_credentials/file_based_metadata/config.h +++ b/source/extensions/grpc_credentials/file_based_metadata/config.h @@ -24,7 +24,7 @@ class FileBasedMetadataGrpcCredentialsFactory : public Grpc::GoogleGrpcCredentia public: std::shared_ptr getChannelCredentials(const envoy::config::core::v3::GrpcService& grpc_service_config, - Api::Api& api) override; + Server::Configuration::CommonFactoryContext& context) override; Envoy::ProtobufTypes::MessagePtr createEmptyConfigProto() { return std::make_unique(); diff --git a/source/extensions/health_checkers/common/health_checker_base_impl.cc b/source/extensions/health_checkers/common/health_checker_base_impl.cc index f774af6f9aca..262ad36ba947 100644 --- a/source/extensions/health_checkers/common/health_checker_base_impl.cc +++ b/source/extensions/health_checkers/common/health_checker_base_impl.cc @@ -18,8 +18,8 @@ HealthCheckerImplBase::HealthCheckerImplBase(const Cluster& cluster, Random::RandomGenerator& random, HealthCheckEventLoggerPtr&& event_logger) : always_log_health_check_failures_(config.always_log_health_check_failures()), - cluster_(cluster), dispatcher_(dispatcher), - timeout_(PROTOBUF_GET_MS_REQUIRED(config, timeout)), + always_log_health_check_success_(config.always_log_health_check_success()), cluster_(cluster), + dispatcher_(dispatcher), timeout_(PROTOBUF_GET_MS_REQUIRED(config, timeout)), unhealthy_threshold_(PROTOBUF_GET_WRAPPED_REQUIRED(config, unhealthy_threshold)), healthy_threshold_(PROTOBUF_GET_WRAPPED_REQUIRED(config, healthy_threshold)), stats_(generateStats(cluster.info()->statsScope())), runtime_(runtime), random_(random), @@ -305,6 +305,11 @@ void HealthCheckerImplBase::ActiveHealthCheckSession::handleSuccess(bool degrade host_->setLastHcPassTime(time_source_.monotonicTime()); } + if (changed_state != HealthTransition::Changed && parent_.always_log_health_check_success_ && + parent_.event_logger_) { + parent_.event_logger_->logSuccessfulHealthCheck(parent_.healthCheckerType(), host_); + } + changed_state = clearPendingFlag(changed_state); if (degraded != host_->healthFlagGet(Host::HealthFlag::DEGRADED_ACTIVE_HC)) { diff --git a/source/extensions/health_checkers/common/health_checker_base_impl.h b/source/extensions/health_checkers/common/health_checker_base_impl.h index 1e7e308348cd..4527e1440a0e 100644 --- a/source/extensions/health_checkers/common/health_checker_base_impl.h +++ b/source/extensions/health_checkers/common/health_checker_base_impl.h @@ -104,6 +104,7 @@ class HealthCheckerImplBase : public HealthChecker, virtual envoy::data::core::v3::HealthCheckerType healthCheckerType() const PURE; const bool always_log_health_check_failures_; + const bool always_log_health_check_success_; const Cluster& cluster_; Event::Dispatcher& dispatcher_; const std::chrono::milliseconds timeout_; 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 23e7ab516d33..bb64744cda07 100644 --- a/source/extensions/resource_monitors/injected_resource/injected_resource_monitor.cc +++ b/source/extensions/resource_monitors/injected_resource/injected_resource_monitor.cc @@ -17,8 +17,8 @@ InjectedResourceMonitor::InjectedResourceMonitor( Server::Configuration::ResourceMonitorFactoryContext& context) : filename_(config.filename()), watcher_(context.mainThreadDispatcher().createFilesystemWatcher()), api_(context.api()) { - watcher_->addWatch(filename_, Filesystem::Watcher::Events::MovedTo, - [this](uint32_t) { onFileChanged(); }); + THROW_IF_NOT_OK(watcher_->addWatch(filename_, Filesystem::Watcher::Events::MovedTo, + [this](uint32_t) { onFileChanged(); })); } void InjectedResourceMonitor::onFileChanged() { file_changed_ = true; } diff --git a/source/extensions/string_matcher/lua/match.cc b/source/extensions/string_matcher/lua/match.cc index fd24f17f81f0..c6ee81e7022c 100644 --- a/source/extensions/string_matcher/lua/match.cc +++ b/source/extensions/string_matcher/lua/match.cc @@ -77,12 +77,11 @@ bool LuaStringMatcher::match(const absl::string_view value) const { // Lua state is not thread safe, so a state needs to be stored in thread local storage. class LuaStringMatcherThreadWrapper : public Matchers::StringMatcher { public: - LuaStringMatcherThreadWrapper(const std::string& code) { + LuaStringMatcherThreadWrapper(const std::string& code, ThreadLocal::SlotAllocator& tls) { // Validate that there are no errors while creating on the main thread. LuaStringMatcher validator(code); - tls_slot_ = ThreadLocal::TypedSlot::makeUnique( - *InjectableSingleton::getExisting()); + tls_slot_ = ThreadLocal::TypedSlot::makeUnique(tls); tls_slot_->set([code](Event::Dispatcher&) -> std::shared_ptr { return std::make_shared(code); }); @@ -95,18 +94,18 @@ class LuaStringMatcherThreadWrapper : public Matchers::StringMatcher { }; Matchers::StringMatcherPtr -LuaStringMatcherFactory::createStringMatcher(const ProtobufWkt::Any& message) { +LuaStringMatcherFactory::createStringMatcher(const ProtobufWkt::Any& message, + ThreadLocal::SlotAllocator& tls, Api::Api& api) { ::envoy::extensions::string_matcher::lua::v3::Lua config; Config::Utility::translateOpaqueConfig(message, ProtobufMessage::getStrictValidationVisitor(), config); - Api::Api* api = InjectableSingleton::getExisting(); absl::StatusOr result = Config::DataSource::read( - config.source_code(), false /* allow_empty */, *api, 0 /* max_size */); + config.source_code(), false /* allow_empty */, api, 0 /* max_size */); if (!result.ok()) { throw EnvoyException( fmt::format("Failed to get lua string matcher code from source: {}", result.status())); } - return std::make_unique(*result); + return std::make_unique(*result, tls); } ProtobufTypes::MessagePtr LuaStringMatcherFactory::createEmptyConfigProto() { diff --git a/source/extensions/string_matcher/lua/match.h b/source/extensions/string_matcher/lua/match.h index 8452a24b0832..2777bcf76220 100644 --- a/source/extensions/string_matcher/lua/match.h +++ b/source/extensions/string_matcher/lua/match.h @@ -29,7 +29,9 @@ class LuaStringMatcher : public Matchers::StringMatcher, public ThreadLocal::Thr class LuaStringMatcherFactory : public Matchers::StringMatcherExtensionFactory { public: - Matchers::StringMatcherPtr createStringMatcher(const ProtobufWkt::Any& message) override; + Matchers::StringMatcherPtr createStringMatcher(const ProtobufWkt::Any& message, + ThreadLocal::SlotAllocator& tls, + Api::Api& api) override; std::string name() const override { return "envoy.string_matcher.lua"; } ProtobufTypes::MessagePtr createEmptyConfigProto() override; }; diff --git a/source/extensions/tracers/opencensus/config.cc b/source/extensions/tracers/opencensus/config.cc index bf33e76de248..d8f5283068a2 100644 --- a/source/extensions/tracers/opencensus/config.cc +++ b/source/extensions/tracers/opencensus/config.cc @@ -26,8 +26,7 @@ Tracing::DriverSharedPtr OpenCensusTracerFactory::createTracerDriverTyped( } } - driver_ = std::make_shared(proto_config, context.serverFactoryContext().localInfo(), - context.serverFactoryContext().api()); + driver_ = std::make_shared(proto_config, context.serverFactoryContext()); config_ = proto_config; return driver_; } diff --git a/source/extensions/tracers/opencensus/opencensus_tracer_impl.cc b/source/extensions/tracers/opencensus/opencensus_tracer_impl.cc index 5c4ea0b4dfba..8bde7883a704 100644 --- a/source/extensions/tracers/opencensus/opencensus_tracer_impl.cc +++ b/source/extensions/tracers/opencensus/opencensus_tracer_impl.cc @@ -253,8 +253,8 @@ void Span::setSampled(bool sampled) { span_.AddAnnotation("setSampled", {{"sampl } // namespace Driver::Driver(const envoy::config::trace::v3::OpenCensusConfig& oc_config, - const LocalInfo::LocalInfo& localinfo, Api::Api& api) - : oc_config_(oc_config), local_info_(localinfo) { + Server::Configuration::CommonFactoryContext& context) + : oc_config_(oc_config), local_info_(context.localInfo()) { // To give user a chance to correct initially invalid configuration and try to apply it once again // without a need to restart Envoy, validation checks must be done prior to any side effects. if (oc_config.stackdriver_exporter_enabled() && oc_config.has_stackdriver_grpc_service() && @@ -289,7 +289,7 @@ Driver::Driver(const envoy::config::trace::v3::OpenCensusConfig& oc_config, // address will be used. stackdriver_service.mutable_google_grpc()->set_target_uri(GoogleStackdriverTraceAddress); } - auto channel = Envoy::Grpc::GoogleGrpcUtils::createChannel(stackdriver_service, api); + auto channel = Envoy::Grpc::GoogleGrpcUtils::createChannel(stackdriver_service, context); // TODO(bianpengyuan): add tests for trace_service_stub and initial_metadata options with mock // stubs. opts.trace_service_stub = ::google::devtools::cloudtrace::v2::TraceService::NewStub(channel); @@ -322,7 +322,7 @@ Driver::Driver(const envoy::config::trace::v3::OpenCensusConfig& oc_config, #ifdef ENVOY_GOOGLE_GRPC const envoy::config::core::v3::GrpcService& ocagent_service = oc_config.ocagent_grpc_service(); - auto channel = Envoy::Grpc::GoogleGrpcUtils::createChannel(ocagent_service, api); + auto channel = Envoy::Grpc::GoogleGrpcUtils::createChannel(ocagent_service, context); opts.trace_service_stub = ::opencensus::proto::agent::trace::v1::TraceService::NewStub(channel); #else diff --git a/source/extensions/tracers/opencensus/opencensus_tracer_impl.h b/source/extensions/tracers/opencensus/opencensus_tracer_impl.h index db3da7856a86..3ca29cb71d30 100644 --- a/source/extensions/tracers/opencensus/opencensus_tracer_impl.h +++ b/source/extensions/tracers/opencensus/opencensus_tracer_impl.h @@ -1,8 +1,7 @@ #pragma once -#include "envoy/api/api.h" #include "envoy/config/trace/v3/opencensus.pb.h" -#include "envoy/local_info/local_info.h" +#include "envoy/server/factory_context.h" #include "envoy/tracing/trace_driver.h" #include "source/common/common/logger.h" @@ -18,7 +17,7 @@ namespace OpenCensus { class Driver : public Tracing::Driver, Logger::Loggable { public: Driver(const envoy::config::trace::v3::OpenCensusConfig& oc_config, - const LocalInfo::LocalInfo& localinfo, Api::Api& api); + Server::Configuration::CommonFactoryContext& context); // Tracing::Driver Tracing::SpanPtr startSpan(const Tracing::Config& config, Tracing::TraceContext& trace_context, diff --git a/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config.cc b/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config.cc index b3a6f2b91c45..eee907c002f4 100644 --- a/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config.cc +++ b/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config.cc @@ -8,18 +8,19 @@ namespace Extensions { namespace Tracers { namespace OpenTelemetry { -void SamplerConfig::parse(const std::string& json) { +bool SamplerConfig::parse(const std::string& json) { const auto result = Envoy::Json::Factory::loadFromStringNoThrow(json); if (result.ok()) { const auto& obj = result.value(); if (obj->hasObject("rootSpansPerMinute")) { const auto value = obj->getInteger("rootSpansPerMinute", default_root_spans_per_minute_); root_spans_per_minute_.store(value); - return; + return true; } } // Didn't get a value, reset to default root_spans_per_minute_.store(default_root_spans_per_minute_); + return false; } } // namespace OpenTelemetry diff --git a/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config.h b/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config.h index 6f576b2d5afb..3763de3f7a25 100644 --- a/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config.h +++ b/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config.h @@ -22,12 +22,18 @@ class SamplerConfig { ? default_root_spans_per_minute : ROOT_SPANS_PER_MINUTE_DEFAULT), root_spans_per_minute_(default_root_spans_per_minute_) {} + + SamplerConfig(const SamplerConfig&) = delete; + SamplerConfig& operator=(const SamplerConfig&) = delete; + /** * @brief Parses a json string containing the expected root spans per minute. * * @param json A string containing the configuration. + * + * @return true if parsing was successful, false otherwise */ - void parse(const std::string& json); + bool parse(const std::string& json); /** * @brief Returns wanted root spans per minute diff --git a/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider.cc b/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider.cc index 465cb9e1bd5f..badc4458c131 100644 --- a/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider.cc +++ b/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider.cc @@ -1,5 +1,7 @@ #include "source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider.h" +#include + #include "source/common/common/enum_to_int.h" #include "source/common/http/utility.h" @@ -8,10 +10,92 @@ namespace Extensions { namespace Tracers { namespace OpenTelemetry { +static constexpr std::chrono::seconds INITIAL_TIMER_DURATION{10}; +static constexpr std::chrono::minutes TIMER_INTERVAL{5}; + +namespace { + +bool reEnableTimer(Http::Code response_code) { + switch (response_code) { + case Http::Code::OK: + case Http::Code::TooManyRequests: + case Http::Code::InternalServerError: + case Http::Code::BadGateway: + case Http::Code::ServiceUnavailable: + case Http::Code::GatewayTimeout: + return true; + default: + return false; + } +} + +} // namespace + SamplerConfigProviderImpl::SamplerConfigProviderImpl( - Server::Configuration::TracerFactoryContext& /*context*/, + Server::Configuration::TracerFactoryContext& context, const envoy::extensions::tracers::opentelemetry::samplers::v3::DynatraceSamplerConfig& config) - : sampler_config_(config.root_spans_per_minute()) {} + : cluster_manager_(context.serverFactoryContext().clusterManager()), + http_uri_(config.http_uri()), + authorization_header_value_(absl::StrCat("Api-Token ", config.token())), + sampler_config_(config.root_spans_per_minute()) { + + timer_ = context.serverFactoryContext().mainThreadDispatcher().createTimer([this]() -> void { + const auto thread_local_cluster = cluster_manager_.getThreadLocalCluster(http_uri_.cluster()); + if (thread_local_cluster == nullptr) { + ENVOY_LOG(error, "SamplerConfigProviderImpl failed: [cluster = {}] is not configured", + http_uri_.cluster()); + } else { + Http::RequestMessagePtr message = Http::Utility::prepareHeaders(http_uri_); + message->headers().setReferenceMethod(Http::Headers::get().MethodValues.Get); + message->headers().setReference(Http::CustomHeaders::get().Authorization, + authorization_header_value_); + active_request_ = thread_local_cluster->httpAsyncClient().send( + std::move(message), *this, + Http::AsyncClient::RequestOptions().setTimeout(std::chrono::milliseconds( + DurationUtil::durationToMilliseconds(http_uri_.timeout())))); + } + }); + + timer_->enableTimer(std::chrono::seconds(INITIAL_TIMER_DURATION)); +} + +SamplerConfigProviderImpl::~SamplerConfigProviderImpl() { + if (active_request_) { + active_request_->cancel(); + } +} + +void SamplerConfigProviderImpl::onSuccess(const Http::AsyncClient::Request& /*request*/, + Http::ResponseMessagePtr&& http_response) { + active_request_ = nullptr; + const auto response_code = Http::Utility::getResponseStatus(http_response->headers()); + bool json_valid = false; + if (response_code == enumToInt(Http::Code::OK)) { + ENVOY_LOG(debug, "Received sampling configuration from Dynatrace: {}", + http_response->bodyAsString()); + json_valid = sampler_config_.parse(http_response->bodyAsString()); + if (!json_valid) { + ENVOY_LOG(warn, "Failed to parse sampling configuration received from Dynatrace: {}", + http_response->bodyAsString()); + } + } else { + ENVOY_LOG(warn, "Failed to get sampling configuration from Dynatrace: {}", response_code); + } + + if (json_valid || reEnableTimer(static_cast(response_code))) { + timer_->enableTimer(std::chrono::seconds(TIMER_INTERVAL)); + } else { + ENVOY_LOG(error, "Stopped to query sampling configuration from Dynatrace."); + } +} + +void SamplerConfigProviderImpl::onFailure(const Http::AsyncClient::Request& /*request*/, + Http::AsyncClient::FailureReason reason) { + active_request_ = nullptr; + timer_->enableTimer(std::chrono::seconds(TIMER_INTERVAL)); + ENVOY_LOG(warn, "Failed to get sampling configuration from Dynatrace. Reason {}", + enumToInt(reason)); +} const SamplerConfig& SamplerConfigProviderImpl::getSamplerConfig() const { return sampler_config_; } diff --git a/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider.h b/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider.h index 6dd464d4f6c7..ddb42ac5fb6a 100644 --- a/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider.h +++ b/source/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider.h @@ -40,18 +40,33 @@ class SamplerConfigProvider { }; class SamplerConfigProviderImpl : public SamplerConfigProvider, - public Logger::Loggable { + public Logger::Loggable, + public Http::AsyncClient::Callbacks { public: SamplerConfigProviderImpl( Server::Configuration::TracerFactoryContext& context, const envoy::extensions::tracers::opentelemetry::samplers::v3::DynatraceSamplerConfig& config); + void onSuccess(const Http::AsyncClient::Request& request, + Http::ResponseMessagePtr&& response) override; + + void onFailure(const Http::AsyncClient::Request& request, + Http::AsyncClient::FailureReason reason) override; + + void onBeforeFinalizeUpstreamSpan(Envoy::Tracing::Span& /*span*/, + const Http::ResponseHeaderMap* /*response_headers*/) override{}; + const SamplerConfig& getSamplerConfig() const override; - ~SamplerConfigProviderImpl() override = default; + ~SamplerConfigProviderImpl() override; private: + Event::TimerPtr timer_; + Upstream::ClusterManager& cluster_manager_; + envoy::config::core::v3::HttpUri http_uri_; + const std::string authorization_header_value_; + Http::AsyncClient::Request* active_request_{}; SamplerConfig sampler_config_; }; 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 4c6f6fb3dc30..13f5011c7c01 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 @@ -31,8 +31,9 @@ namespace Tls { using SPIFFEConfig = envoy::extensions::transport_sockets::tls::v3::SPIFFECertValidatorConfig; SPIFFEValidator::SPIFFEValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, - SslStats& stats, TimeSource& time_source) - : stats_(stats), time_source_(time_source) { + SslStats& stats, + Server::Configuration::CommonFactoryContext& context) + : stats_(stats), time_source_(context.timeSource()) { ASSERT(config != nullptr); allow_expired_certificate_ = config->allowExpiredCertificate(); @@ -48,7 +49,7 @@ SPIFFEValidator::SPIFFEValidator(const Envoy::Ssl::CertificateValidationContextC // 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)); + subject_alt_name_matchers_.emplace_back(createStringSanMatcher(matcher, context)); } } } @@ -310,9 +311,10 @@ Envoy::Ssl::CertificateDetailsPtr SPIFFEValidator::getCaCertInformation() const class SPIFFEValidatorFactory : public CertValidatorFactory { public: - CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, - SslStats& stats, TimeSource& time_source) override { - return std::make_unique(config, stats, time_source); + CertValidatorPtr + createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, + Server::Configuration::CommonFactoryContext& context) override { + return std::make_unique(config, stats, context); } std::string name() const override { return "envoy.tls.cert_validator.spiffe"; } 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 4bb140dbe63b..0c8a93a80f16 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 @@ -35,7 +35,7 @@ class SPIFFEValidator : public CertValidator { SPIFFEValidator(SslStats& stats, TimeSource& time_source) : stats_(stats), time_source_(time_source){}; SPIFFEValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - TimeSource& time_source); + Server::Configuration::CommonFactoryContext& context); ~SPIFFEValidator() override = default; // Tls::CertValidator diff --git a/source/extensions/transport_sockets/tls/config.cc b/source/extensions/transport_sockets/tls/config.cc index ec2f39b60aed..d89789c6e2ad 100644 --- a/source/extensions/transport_sockets/tls/config.cc +++ b/source/extensions/transport_sockets/tls/config.cc @@ -51,14 +51,6 @@ ProtobufTypes::MessagePtr DownstreamSslSocketFactory::createEmptyConfigProto() { LEGACY_REGISTER_FACTORY(DownstreamSslSocketFactory, Server::Configuration::DownstreamTransportSocketConfigFactory, "tls"); -Ssl::ContextManagerPtr SslContextManagerFactory::createContextManager(TimeSource& time_source) { - return std::make_unique(time_source); -} - -static Envoy::Registry::RegisterInternalFactory - ssl_manager_registered; - } // namespace Tls } // namespace TransportSockets } // namespace Extensions diff --git a/source/extensions/transport_sockets/tls/config.h b/source/extensions/transport_sockets/tls/config.h index b9aa1f86ba9e..3e39adc38bfb 100644 --- a/source/extensions/transport_sockets/tls/config.h +++ b/source/extensions/transport_sockets/tls/config.h @@ -42,13 +42,6 @@ class DownstreamSslSocketFactory DECLARE_FACTORY(DownstreamSslSocketFactory); -class SslContextManagerFactory : public Ssl::ContextManagerFactory { -public: - Ssl::ContextManagerPtr createContextManager(TimeSource& time_source) override; -}; - -DECLARE_FACTORY(SslContextManagerFactory); - } // namespace Tls } // namespace TransportSockets } // namespace Extensions diff --git a/source/extensions/upstreams/http/udp/upstream_request.cc b/source/extensions/upstreams/http/udp/upstream_request.cc index 1fd5de949251..fdba4ecc5962 100644 --- a/source/extensions/upstreams/http/udp/upstream_request.cc +++ b/source/extensions/upstreams/http/udp/upstream_request.cc @@ -113,7 +113,7 @@ void UdpUpstream::onSocketReadReady() { uint32_t packets_dropped = 0; const Api::IoErrorPtr result = Network::Utility::readPacketsFromSocket( socket_->ioHandle(), *socket_->connectionInfoProvider().localAddress(), *this, - dispatcher_.timeSource(), /*prefer_gro=*/true, packets_dropped); + dispatcher_.timeSource(), /*allow_gro=*/true, /*allow_mmsg=*/true, packets_dropped); if (result == nullptr) { socket_->ioHandle().activateFileEvents(Event::FileReadyType::Read); return; diff --git a/source/extensions/wasm_runtime/wavm/BUILD b/source/extensions/wasm_runtime/wavm/BUILD deleted file mode 100644 index fa65a029b17b..000000000000 --- a/source/extensions/wasm_runtime/wavm/BUILD +++ /dev/null @@ -1,21 +0,0 @@ -load( - "//bazel:envoy_build_system.bzl", - "envoy_cc_extension", - "envoy_extension_package", -) -load("//bazel:envoy_select.bzl", "envoy_select_wasm_wavm") - -licenses(["notice"]) # Apache 2 - -envoy_extension_package() - -envoy_cc_extension( - name = "config", - srcs = envoy_select_wasm_wavm(["config.cc"]), - deps = envoy_select_wasm_wavm([ - "//envoy/registry", - "//source/extensions/common/wasm:wasm_runtime_factory_interface", - "@proxy_wasm_cpp_host//:base_lib", - "@proxy_wasm_cpp_host//:wavm_lib", - ]), -) diff --git a/source/extensions/wasm_runtime/wavm/config.cc b/source/extensions/wasm_runtime/wavm/config.cc deleted file mode 100644 index c2b3d93a29e4..000000000000 --- a/source/extensions/wasm_runtime/wavm/config.cc +++ /dev/null @@ -1,26 +0,0 @@ -#include "envoy/registry/registry.h" - -#include "source/extensions/common/wasm/wasm_runtime_factory.h" - -#include "include/proxy-wasm/wavm.h" - -namespace Envoy { -namespace Extensions { -namespace Common { -namespace Wasm { - -class WavmRuntimeFactory : public WasmRuntimeFactory { -public: - WasmVmPtr createWasmVm() override { return proxy_wasm::createWavmVm(); } - - std::string name() const override { return "envoy.wasm.runtime.wavm"; } -}; - -#if defined(PROXY_WASM_HAS_RUNTIME_WAVM) -REGISTER_FACTORY(WavmRuntimeFactory, WasmRuntimeFactory); -#endif - -} // namespace Wasm -} // namespace Common -} // namespace Extensions -} // namespace Envoy diff --git a/source/server/BUILD b/source/server/BUILD index f656303df03e..6fa2605b1efd 100644 --- a/source/server/BUILD +++ b/source/server/BUILD @@ -54,10 +54,10 @@ envoy_cc_library( "//source/common/network:socket_option_lib", "//source/common/network:utility_lib", "//source/common/protobuf:utility_lib", - "//source/extensions/access_loggers/common:file_access_log_lib", "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", "@envoy_api//envoy/config/metrics/v3:pkg_cc_proto", "@envoy_api//envoy/config/trace/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/access_loggers/file/v3:pkg_cc_proto", ], ) @@ -397,6 +397,7 @@ envoy_cc_library( hdrs = ["listener_manager_factory.h"], deps = [ "//envoy/server:factory_context_interface", + "//envoy/server:instance_interface", "//envoy/server:listener_manager_interface", "//envoy/server:worker_interface", "//source/common/quic:quic_stat_names_lib", @@ -417,7 +418,6 @@ envoy_cc_library( ":listener_hooks_lib", ":listener_manager_factory_lib", ":regex_engine_lib", - ":ssl_context_manager_lib", ":utils_lib", ":worker_lib", "//envoy/event:dispatcher_interface", @@ -462,6 +462,7 @@ envoy_cc_library( "//source/common/signal:fatal_error_handler_lib", "//source/common/singleton:manager_impl_lib", "//source/common/stats:thread_local_store_lib", + "//source/common/tls:context_lib", "//source/common/upstream:cluster_manager_lib", "//source/common/upstream:health_discovery_service_lib", "//source/common/version:version_lib", @@ -483,16 +484,6 @@ envoy_cc_library( ], ) -envoy_cc_library( - name = "ssl_context_manager_lib", - srcs = ["ssl_context_manager.cc"], - hdrs = ["ssl_context_manager.h"], - deps = [ - "//envoy/registry", - "//envoy/ssl:context_manager_interface", - ], -) - envoy_cc_library( name = "listener_hooks_lib", hdrs = ["listener_hooks.h"], diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index 3913191068d4..1c453e712a8e 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -388,6 +388,7 @@ envoy_cc_library( name = "utils_lib", srcs = ["utils.cc"], hdrs = ["utils.h"], + visibility = ["//visibility:public"], deps = [ "//envoy/init:manager_interface", "//source/common/common:enum_to_int", diff --git a/source/server/admin/admin.cc b/source/server/admin/admin.cc index 07f3b271381a..0b21be7fef7f 100644 --- a/source/server/admin/admin.cc +++ b/source/server/admin/admin.cc @@ -293,7 +293,7 @@ bool AdminImpl::createNetworkFilterChain(Network::Connection& connection, bool AdminImpl::createFilterChain(Http::FilterChainManager& manager, bool, const Http::FilterChainOptions&) const { Http::FilterFactoryCb factory = [this](Http::FilterChainFactoryCallbacks& callbacks) { - callbacks.addStreamFilter(std::make_shared(createRequestFunction())); + callbacks.addStreamFilter(std::make_shared(*this)); }; manager.applyFilterFactoryCb({}, factory); return true; @@ -494,7 +494,7 @@ bool AdminImpl::removeHandler(const std::string& prefix) { Http::Code AdminImpl::request(absl::string_view path_and_query, absl::string_view method, Http::ResponseHeaderMap& response_headers, std::string& body) { - AdminFilter filter(createRequestFunction()); + AdminFilter filter(*this); auto request_headers = Http::RequestHeaderMapImpl::create(); request_headers->setMethod(method); diff --git a/source/server/admin/admin.h b/source/server/admin/admin.h index 86c6ab7f57d3..ef0cde79927b 100644 --- a/source/server/admin/admin.h +++ b/source/server/admin/admin.h @@ -216,9 +216,6 @@ class AdminImpl : public Admin, void closeSocket() override; void addListenerToHandler(Network::ConnectionHandler* handler) override; - GenRequestFn createRequestFunction() const { - return [this](AdminStream& admin_stream) -> RequestPtr { return makeRequest(admin_stream); }; - } uint64_t maxRequestsPerConnection() const override { return 0; } const HttpConnectionManagerProto::ProxyStatusConfig* proxyStatusConfig() const override { return proxy_status_config_.get(); @@ -246,10 +243,7 @@ class AdminImpl : public Admin, ::Envoy::Http::HeaderValidatorStats& getHeaderValidatorStats(Http::Protocol protocol); #endif - /** - * Creates a Request from the request in the admin stream. - */ - RequestPtr makeRequest(AdminStream& admin_stream) const; + RequestPtr makeRequest(AdminStream& admin_stream) const override; /** * Creates a UrlHandler structure from a non-chunked callback. diff --git a/source/server/admin/admin_filter.cc b/source/server/admin/admin_filter.cc index 7a11c251dc88..d9dee2576612 100644 --- a/source/server/admin/admin_filter.cc +++ b/source/server/admin/admin_filter.cc @@ -5,8 +5,7 @@ namespace Envoy { namespace Server { -AdminFilter::AdminFilter(Admin::GenRequestFn admin_handler_fn) - : admin_handler_fn_(admin_handler_fn) {} +AdminFilter::AdminFilter(const Admin& admin) : admin_(admin) {} Http::FilterHeadersStatus AdminFilter::decodeHeaders(Http::RequestHeaderMap& headers, bool end_stream) { @@ -87,12 +86,13 @@ void AdminFilter::onComplete() { auto header_map = Http::ResponseHeaderMapImpl::create(); RELEASE_ASSERT(request_headers_, ""); - Admin::RequestPtr handler = admin_handler_fn_(*this); + Admin::RequestPtr handler = admin_.makeRequest(*this); Http::Code code = handler->start(*header_map); Utility::populateFallbackResponseHeaders(code, *header_map); decoder_callbacks_->encodeHeaders(std::move(header_map), false, StreamInfo::ResponseCodeDetails::get().AdminFilterResponse); + // TODO(#31087): use high/lower watermarks to apply flow-control to the admin http port. bool more_data; do { Buffer::OwnedImpl response; diff --git a/source/server/admin/admin_filter.h b/source/server/admin/admin_filter.h index e163029b2283..2dbe6d01df94 100644 --- a/source/server/admin/admin_filter.h +++ b/source/server/admin/admin_filter.h @@ -28,7 +28,13 @@ class AdminFilter : public Http::PassThroughFilter, absl::string_view path_and_query, Http::ResponseHeaderMap& response_headers, Buffer::OwnedImpl& response, AdminFilter& filter)>; - AdminFilter(Admin::GenRequestFn admin_handler_func); + /** + * Instantiates an AdminFilter. + * + * @param admin the admin context from which to create the filter. This is used + * to create a request object based on the path. + */ + AdminFilter(const Admin& admin); // Http::StreamFilterBase // Handlers relying on the reference should use addOnDestroyCallback() @@ -58,7 +64,7 @@ class AdminFilter : public Http::PassThroughFilter, * Called when an admin request has been completely received. */ void onComplete(); - Admin::GenRequestFn admin_handler_fn_; + const Admin& admin_; Http::RequestHeaderMap* request_headers_{}; std::list> on_destroy_callbacks_; bool end_stream_on_complete_ = true; diff --git a/source/server/admin/clusters_handler.cc b/source/server/admin/clusters_handler.cc index 5b05de26a27c..abf3f63430c6 100644 --- a/source/server/admin/clusters_handler.cc +++ b/source/server/admin/clusters_handler.cc @@ -100,7 +100,9 @@ void setHealthFlag(Upstream::Host::HealthFlag flag, const Upstream::Host& host, host.healthFlagGet(Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT)); break; case Upstream::Host::HealthFlag::EDS_STATUS_DRAINING: - health_status.set_eds_health_status(envoy::config::core::v3::DRAINING); + if (host.healthFlagGet(Upstream::Host::HealthFlag::EDS_STATUS_DRAINING)) { + health_status.set_eds_health_status(envoy::config::core::v3::DRAINING); + } break; } } diff --git a/source/server/admin/clusters_handler.h b/source/server/admin/clusters_handler.h index c03623edb36b..14bc570dedb6 100644 --- a/source/server/admin/clusters_handler.h +++ b/source/server/admin/clusters_handler.h @@ -1,5 +1,6 @@ #pragma once +#include "envoy/admin/v3/clusters.pb.h" #include "envoy/buffer/buffer.h" #include "envoy/http/codes.h" #include "envoy/http/header_map.h" @@ -13,6 +14,16 @@ namespace Envoy { namespace Server { +/** + * A utility to set admin health status from a specified host and health flag. + * + * @param healthFlag The specific health status to be checked. + * @param host The target host. + * @param health_status A proto reference representing the admin health status. + */ +void setHealthFlag(Upstream::Host::HealthFlag flag, const Upstream::Host& host, + envoy::admin::v3::HostHealthStatus& health_status); + class ClustersHandler : public HandlerContextBase { public: diff --git a/source/server/admin/runtime_handler.cc b/source/server/admin/runtime_handler.cc index 7cb9c2531e5a..456146cb8e9c 100644 --- a/source/server/admin/runtime_handler.cc +++ b/source/server/admin/runtime_handler.cc @@ -90,7 +90,7 @@ Http::Code RuntimeHandler::handlerRuntimeModify(Http::ResponseHeaderMap&, for (const auto& it : params.data()) { overrides.insert({it.first, it.second[0]}); } - TRY_ASSERT_MAIN_THREAD { server_.runtime().mergeValues(overrides); } + TRY_ASSERT_MAIN_THREAD { THROW_IF_NOT_OK(server_.runtime().mergeValues(overrides)); } END_TRY catch (const EnvoyException& e) { ENVOY_LOG_MISC(error, "{}", e.what()); diff --git a/source/server/config_validation/BUILD b/source/server/config_validation/BUILD index eba39b9bc6d2..f917244c0268 100644 --- a/source/server/config_validation/BUILD +++ b/source/server/config_validation/BUILD @@ -50,7 +50,6 @@ envoy_cc_library( hdrs = [ "connection.h", "dispatcher.h", - "dns.h", ], deps = [ "//envoy/event:dispatcher_interface", @@ -60,16 +59,6 @@ envoy_cc_library( ], ) -envoy_cc_library( - name = "dns_lib", - srcs = ["dns.cc"], - hdrs = ["dns.h"], - deps = [ - "//envoy/event:dispatcher_interface", - "//envoy/network:dns_interface", - ], -) - envoy_cc_library( name = "server_lib", srcs = ["server.cc"], @@ -79,7 +68,6 @@ envoy_cc_library( ":admin_lib", ":api_lib", ":cluster_manager_lib", - ":dns_lib", "//envoy/server:drain_manager_interface", "//envoy/server:instance_interface", "//envoy/ssl:context_manager_interface", @@ -100,6 +88,7 @@ envoy_cc_library( "//source/common/stats:stats_lib", "//source/common/thread_local:thread_local_lib", "//source/common/version:version_lib", + "//source/extensions/transport_sockets/tls:config", "//source/server:configuration_lib", "//source/server:hot_restart_nop_lib", "//source/server:overload_manager_lib", diff --git a/source/server/config_validation/admin.h b/source/server/config_validation/admin.h index 173afb29d355..8e75607e3f8f 100644 --- a/source/server/config_validation/admin.h +++ b/source/server/config_validation/admin.h @@ -39,6 +39,7 @@ class ValidationAdmin : public Admin { void addListenerToHandler(Network::ConnectionHandler* handler) override; uint32_t concurrency() const override { return 1; } void closeSocket() override {} + RequestPtr makeRequest(AdminStream&) const override { return nullptr; } private: ConfigTrackerImpl config_tracker_; diff --git a/source/server/config_validation/cluster_manager.cc b/source/server/config_validation/cluster_manager.cc index 719919f25874..cc1834be90a9 100644 --- a/source/server/config_validation/cluster_manager.cc +++ b/source/server/config_validation/cluster_manager.cc @@ -11,7 +11,7 @@ namespace Upstream { ClusterManagerPtr ValidationClusterManagerFactory::clusterManagerFromProto( const envoy::config::bootstrap::v3::Bootstrap& bootstrap) { auto cluster_manager = std::unique_ptr{new ValidationClusterManager( - bootstrap, *this, stats_, tls_, context_.runtime(), context_.localInfo(), + bootstrap, *this, context_, stats_, tls_, context_.runtime(), context_.localInfo(), context_.accessLogManager(), context_.mainThreadDispatcher(), context_.admin(), context_.messageValidationContext(), context_.api(), http_context_, context_.grpcContext(), context_.routerContext(), server_)}; diff --git a/source/server/config_validation/dispatcher.h b/source/server/config_validation/dispatcher.h index 33d3c39ed200..bd2cd87f38d2 100644 --- a/source/server/config_validation/dispatcher.h +++ b/source/server/config_validation/dispatcher.h @@ -3,7 +3,6 @@ #include "envoy/event/dispatcher.h" #include "source/common/event/dispatcher_impl.h" -#include "source/server/config_validation/dns.h" namespace Envoy { namespace Event { diff --git a/source/server/config_validation/dns.cc b/source/server/config_validation/dns.cc deleted file mode 100644 index c590bf181133..000000000000 --- a/source/server/config_validation/dns.cc +++ /dev/null @@ -1,13 +0,0 @@ -#include "source/server/config_validation/dns.h" - -namespace Envoy { -namespace Network { - -ActiveDnsQuery* ValidationDnsResolver::resolve(const std::string&, DnsLookupFamily, - ResolveCb callback) { - callback(DnsResolver::ResolutionStatus::Success, {}); - return nullptr; -} - -} // namespace Network -} // namespace Envoy diff --git a/source/server/config_validation/dns.h b/source/server/config_validation/dns.h deleted file mode 100644 index 3777256579b0..000000000000 --- a/source/server/config_validation/dns.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "envoy/event/dispatcher.h" -#include "envoy/network/dns.h" - -namespace Envoy { -namespace Network { - -/** - * DnsResolver to be used in config validation runs. Every DNS query immediately fails to resolve, - * since we never need DNS information to validate a config. (If a config contains an unresolvable - * name, it still passes validation -- for example, we might be running validation in a test - * environment, while the name resolves fine in prod.) - */ -class ValidationDnsResolver : public DnsResolver { -public: - // Network::DnsResolver - ActiveDnsQuery* resolve(const std::string& dns_name, DnsLookupFamily dns_lookup_family, - ResolveCb callback) override; -}; - -} // namespace Network -} // namespace Envoy diff --git a/source/server/config_validation/server.cc b/source/server/config_validation/server.cc index 054cb3f11b24..79065df65da0 100644 --- a/source/server/config_validation/server.cc +++ b/source/server/config_validation/server.cc @@ -13,12 +13,12 @@ #include "source/common/local_info/local_info_impl.h" #include "source/common/protobuf/utility.h" #include "source/common/singleton/manager_impl.h" +#include "source/common/tls/context_manager_impl.h" #include "source/common/version/version.h" #include "source/server/admin/admin_factory_context.h" #include "source/server/listener_manager_factory.h" #include "source/server/overload_manager_impl.h" #include "source/server/regex_engine.h" -#include "source/server/ssl_context_manager.h" #include "source/server/utils.h" namespace Envoy { @@ -88,8 +88,8 @@ void ValidationInstance::initialize(const Options& options, // If we get all the way through that stripped-down initialization flow, to the point where we'd // be ready to serve, then the config has passed validation. // Handle configuration that needs to take place prior to the main configuration load. - InstanceUtil::loadBootstrapConfig(bootstrap_, options, - messageValidationContext().staticValidationVisitor(), *api_); + THROW_IF_NOT_OK(InstanceUtil::loadBootstrapConfig( + bootstrap_, options, messageValidationContext().staticValidationVisitor(), *api_)); if (bootstrap_.has_application_log_config()) { THROW_IF_NOT_OK( @@ -98,7 +98,7 @@ void ValidationInstance::initialize(const Options& options, } // Inject regex engine to singleton. - Regex::EnginePtr regex_engine = createRegexEngine( + regex_engine_ = createRegexEngine( bootstrap_, messageValidationContext().staticValidationVisitor(), serverFactoryContext()); Config::StatsUtility::createTagProducer(bootstrap_, options_.statsTags()); @@ -132,7 +132,9 @@ void ValidationInstance::initialize(const Options& options, "Component factory should not return nullptr from createDrainManager()"); secret_manager_ = std::make_unique(admin()->getConfigTracker()); - ssl_context_manager_ = createContextManager("ssl_context_manager", api_->timeSource()); + ssl_context_manager_ = + std::make_unique(server_contexts_); + cluster_manager_factory_ = std::make_unique( server_contexts_, stats(), threadLocal(), http_context_, [this]() -> Network::DnsResolverSharedPtr { return this->dnsResolver(); }, diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h index fc301751e57d..777b5a1ff6d2 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -27,7 +27,6 @@ #include "source/server/config_validation/admin.h" #include "source/server/config_validation/api.h" #include "source/server/config_validation/cluster_manager.h" -#include "source/server/config_validation/dns.h" #include "source/server/hot_restart_nop_impl.h" #include "source/server/server.h" @@ -123,6 +122,7 @@ class ValidationInstance final : Logger::Loggable, bool enableReusePortDefault() override { return true; } Configuration::StatsConfig& statsConfig() override { return config_.statsConfig(); } + Regex::Engine& regexEngine() override { return *regex_engine_; } envoy::config::bootstrap::v3::Bootstrap& bootstrap() override { return bootstrap_; } Configuration::ServerFactoryContext& serverFactoryContext() override { return server_contexts_; } Configuration::TransportSocketFactoryContext& transportSocketFactoryContext() override { @@ -194,6 +194,7 @@ class ValidationInstance final : Logger::Loggable, Filter::TcpListenerFilterConfigProviderManagerImpl tcp_listener_config_provider_manager_; Server::DrainManagerPtr drain_manager_; HotRestartNopImpl nop_hot_restart_; + Regex::EnginePtr regex_engine_; }; } // namespace Server diff --git a/source/server/configuration_impl.cc b/source/server/configuration_impl.cc index b5c01e101646..7070baf2097d 100644 --- a/source/server/configuration_impl.cc +++ b/source/server/configuration_impl.cc @@ -10,6 +10,7 @@ #include "envoy/config/bootstrap/v3/bootstrap.pb.h" #include "envoy/config/metrics/v3/stats.pb.h" #include "envoy/config/trace/v3/http_tracer.pb.h" +#include "envoy/extensions/access_loggers/file/v3/file.pb.h" #include "envoy/network/connection.h" #include "envoy/runtime/runtime.h" #include "envoy/server/instance.h" @@ -23,7 +24,6 @@ #include "source/common/config/utility.h" #include "source/common/network/socket_option_factory.h" #include "source/common/protobuf/utility.h" -#include "source/extensions/access_loggers/common/file_access_log_impl.h" namespace Envoy { namespace Server { @@ -276,11 +276,13 @@ void InitialImpl::initAdminAccessLog(const envoy::config::bootstrap::v3::Bootstr } if (!admin.access_log_path().empty()) { - Filesystem::FilePathAndType file_info{Filesystem::DestinationType::File, - admin.access_log_path()}; - admin_.access_logs_.emplace_back(new Extensions::AccessLoggers::File::FileAccessLog( - file_info, {}, Formatter::HttpSubstitutionFormatUtils::defaultSubstitutionFormatter(), - factory_context.serverFactoryContext().accessLogManager())); + envoy::extensions::access_loggers::file::v3::FileAccessLog config; + config.mutable_format(); + config.set_path(admin.access_log_path()); + + auto factory = Config::Utility::getFactoryByName( + "envoy.file_access_log"); + admin_.access_logs_.emplace_back(factory->createAccessLogInstance(config, {}, factory_context)); } } diff --git a/source/server/server.cc b/source/server/server.cc index 0b9863428a95..4df8def7c43e 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -50,13 +50,13 @@ #include "source/common/stats/stats_matcher_impl.h" #include "source/common/stats/thread_local_store.h" #include "source/common/stats/timespan_impl.h" +#include "source/common/tls/context_manager_impl.h" #include "source/common/upstream/cluster_manager_impl.h" #include "source/common/version/version.h" #include "source/server/configuration_impl.h" #include "source/server/listener_hooks.h" #include "source/server/listener_manager_factory.h" #include "source/server/regex_engine.h" -#include "source/server/ssl_context_manager.h" #include "source/server/utils.h" namespace Envoy { @@ -364,17 +364,16 @@ registerCustomInlineHeadersFromBootstrap(const envoy::config::bootstrap::v3::Boo } // namespace -void InstanceUtil::loadBootstrapConfig(envoy::config::bootstrap::v3::Bootstrap& bootstrap, - const Options& options, - ProtobufMessage::ValidationVisitor& validation_visitor, - Api::Api& api) { +absl::Status InstanceUtil::loadBootstrapConfig( + envoy::config::bootstrap::v3::Bootstrap& bootstrap, const Options& options, + ProtobufMessage::ValidationVisitor& validation_visitor, Api::Api& api) { const std::string& config_path = options.configPath(); const std::string& config_yaml = options.configYaml(); const envoy::config::bootstrap::v3::Bootstrap& config_proto = options.configProto(); // One of config_path and config_yaml or bootstrap should be specified. if (config_path.empty() && config_yaml.empty() && config_proto.ByteSizeLong() == 0) { - throwEnvoyExceptionOrPanic( + return absl::InvalidArgumentError( "At least one of --config-path or --config-yaml or Options::configProto() " "should be non-empty"); } @@ -384,7 +383,7 @@ void InstanceUtil::loadBootstrapConfig(envoy::config::bootstrap::v3::Bootstrap& MessageUtil::loadFromFile(config_path, bootstrap, validation_visitor, api); #else if (!config_path.empty()) { - throwEnvoyExceptionOrPanic("Cannot load from file with YAML disabled\n"); + return absl::InvalidArgumentError("Cannot load from file with YAML disabled\n"); } UNREFERENCED_PARAMETER(api); #endif @@ -396,13 +395,14 @@ void InstanceUtil::loadBootstrapConfig(envoy::config::bootstrap::v3::Bootstrap& // TODO(snowp): The fact that we do a merge here doesn't seem to be covered under test. bootstrap.MergeFrom(bootstrap_override); #else - throwEnvoyExceptionOrPanic("Cannot load from YAML with YAML disabled\n"); + return absl::InvalidArgumentError("Cannot load from YAML with YAML disabled\n"); #endif } if (config_proto.ByteSizeLong() != 0) { bootstrap.MergeFrom(config_proto); } MessageUtil::validate(bootstrap, validation_visitor); + return absl::OkStatus(); } void InstanceBase::initialize(Network::Address::InstanceConstSharedPtr local_address, @@ -455,8 +455,8 @@ absl::Status InstanceBase::initializeOrThrow(Network::Address::InstanceConstShar } // Handle configuration that needs to take place prior to the main configuration load. - InstanceUtil::loadBootstrapConfig(bootstrap_, options_, - messageValidationContext().staticValidationVisitor(), *api_); + RETURN_IF_NOT_OK(InstanceUtil::loadBootstrapConfig( + bootstrap_, options_, messageValidationContext().staticValidationVisitor(), *api_)); bootstrap_config_update_time_ = time_source_.systemTime(); if (bootstrap_.has_application_log_config()) { @@ -749,7 +749,8 @@ absl::Status InstanceBase::initializeOrThrow(Network::Address::InstanceConstShar } // Once we have runtime we can initialize the SSL context manager. - ssl_context_manager_ = createContextManager("ssl_context_manager", time_source_); + ssl_context_manager_ = + std::make_unique(server_contexts_); cluster_manager_factory_ = std::make_unique( serverFactoryContext(), stats_store_, thread_local_, http_context_, @@ -826,7 +827,7 @@ void InstanceBase::onRuntimeReady() { if (bootstrap_.has_hds_config()) { const auto& hds_config = bootstrap_.hds_config(); async_client_manager_ = std::make_unique( - *config_.clusterManager(), thread_local_, time_source_, *api_, grpc_context_.statNames(), + *config_.clusterManager(), thread_local_, server_contexts_, grpc_context_.statNames(), bootstrap_.grpc_async_client_manager_config()); TRY_ASSERT_MAIN_THREAD { THROW_IF_NOT_OK(Config::Utility::checkTransportVersion(hds_config)); @@ -879,10 +880,13 @@ Runtime::LoaderPtr InstanceUtil::createRuntime(Instance& server, #ifdef ENVOY_ENABLE_YAML ENVOY_LOG(info, "runtime: {}", MessageUtil::getYamlStringFromMessage(config.runtime())); #endif - return std::make_unique( + absl::Status creation_status; + auto loader = std::make_unique( server.dispatcher(), server.threadLocal(), config.runtime(), server.localInfo(), server.stats(), server.api().randomGenerator(), - server.messageValidationContext().dynamicValidationVisitor(), server.api()); + server.messageValidationContext().dynamicValidationVisitor(), server.api(), creation_status); + THROW_IF_NOT_OK(creation_status); + return loader; } void InstanceBase::loadServerFlags(const absl::optional& flags_path) { diff --git a/source/server/server.h b/source/server/server.h index 3afd1348aaba..ae9b1ea95bc0 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -140,10 +140,10 @@ class InstanceUtil : Logger::Loggable { * @param validation_visitor message validation visitor instance. * @param api reference to the Api object */ - static void loadBootstrapConfig(envoy::config::bootstrap::v3::Bootstrap& bootstrap, - const Options& options, - ProtobufMessage::ValidationVisitor& validation_visitor, - Api::Api& api); + static absl::Status loadBootstrapConfig(envoy::config::bootstrap::v3::Bootstrap& bootstrap, + const Options& options, + ProtobufMessage::ValidationVisitor& validation_visitor, + Api::Api& api); }; /** @@ -197,6 +197,7 @@ class ServerFactoryContextImpl : public Configuration::ServerFactoryContext, ProcessContextOptRef processContext() override { return server_.processContext(); } Envoy::Server::DrainManager& drainManager() override { return server_.drainManager(); } ServerLifecycleNotifier& lifecycleNotifier() override { return server_.lifecycleNotifier(); } + Regex::Engine& regexEngine() override { return server_.regexEngine(); } Configuration::StatsConfig& statsConfig() override { return server_.statsConfig(); } envoy::config::bootstrap::v3::Bootstrap& bootstrap() override { return server_.bootstrap(); } OverloadManager& overloadManager() override { return server_.overloadManager(); } @@ -292,6 +293,7 @@ class InstanceBase : Logger::Loggable, TimeSource& timeSource() override { return time_source_; } void flushStats() override; Configuration::StatsConfig& statsConfig() override { return config_.statsConfig(); } + Regex::Engine& regexEngine() override { return *regex_engine_; } envoy::config::bootstrap::v3::Bootstrap& bootstrap() override { return bootstrap_; } Configuration::ServerFactoryContext& serverFactoryContext() override { return server_contexts_; } Configuration::TransportSocketFactoryContext& transportSocketFactoryContext() override { diff --git a/source/server/ssl_context_manager.cc b/source/server/ssl_context_manager.cc deleted file mode 100644 index 8a9fec347dee..000000000000 --- a/source/server/ssl_context_manager.cc +++ /dev/null @@ -1,64 +0,0 @@ -#include "source/server/ssl_context_manager.h" - -#include - -#include "envoy/common/exception.h" -#include "envoy/registry/registry.h" - -namespace Envoy { -namespace Server { - -/** - * A stub that provides a SSL context manager capable of reporting on - * certificates' data in case there's no TLS implementation built - * into Envoy. - */ -class SslContextManagerNoTlsStub final : public Envoy::Ssl::ContextManager { - Ssl::ClientContextSharedPtr - createSslClientContext(Stats::Scope& /* scope */, - const Envoy::Ssl::ClientContextConfig& /* config */) override { - throwException(); - } - - Ssl::ServerContextSharedPtr createSslServerContext( - Stats::Scope& /* scope */, const Envoy::Ssl::ServerContextConfig& /* config */, - const std::vector& /* server_names */, Ssl::ContextAdditionalInitFunc) override { - throwException(); - } - - absl::optional daysUntilFirstCertExpires() const override { - return absl::make_optional(std::numeric_limits::max()); - } - absl::optional secondsUntilFirstOcspResponseExpires() const override { - return absl::nullopt; - } - - void iterateContexts(std::function /* callback */) override{}; - - Ssl::PrivateKeyMethodManager& privateKeyMethodManager() override { throwException(); } - - void removeContext(const Envoy::Ssl::ContextSharedPtr& old_context) override { - if (old_context) { - throwEnvoyExceptionOrPanic("SSL is not supported in this configuration"); - } - } - -private: - [[noreturn]] void throwException() { - throwEnvoyExceptionOrPanic("SSL is not supported in this configuration"); - } -}; - -Ssl::ContextManagerPtr createContextManager(const std::string& factory_name, - TimeSource& time_source) { - Ssl::ContextManagerFactory* factory = - Registry::FactoryRegistry::getFactory(factory_name); - if (factory != nullptr) { - return factory->createContextManager(time_source); - } - - return std::make_unique(); -} - -} // namespace Server -} // namespace Envoy diff --git a/source/server/ssl_context_manager.h b/source/server/ssl_context_manager.h deleted file mode 100644 index 4b618e6e64f4..000000000000 --- a/source/server/ssl_context_manager.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "envoy/common/time.h" -#include "envoy/ssl/context_manager.h" - -namespace Envoy { -namespace Server { - -Ssl::ContextManagerPtr createContextManager(const std::string& factory_name, - TimeSource& time_source); - -} // namespace Server -} // namespace Envoy diff --git a/test/common/common/backoff_strategy_test.cc b/test/common/common/backoff_strategy_test.cc index a5111809bf44..6dcb6a04aeaf 100644 --- a/test/common/common/backoff_strategy_test.cc +++ b/test/common/common/backoff_strategy_test.cc @@ -114,4 +114,29 @@ TEST(FixedBackOffStrategyTest, FixedBackOffBasicReset) { EXPECT_EQ(20, fixed_back_off.nextBackOffMs()); } +TEST(BackOffStrategyUtilsTest, InvalidConfig) { + { + // Valid config. + envoy::config::core::v3::BackoffStrategy backoff_strategy; + backoff_strategy.mutable_base_interval()->set_seconds(2); + backoff_strategy.mutable_max_interval()->set_seconds(3); + EXPECT_TRUE(BackOffStrategyUtils::validateBackOffStrategyConfig(backoff_strategy, 1, 10).ok()); + } + + { + // Max interval is lower than base interval. + envoy::config::core::v3::BackoffStrategy backoff_strategy; + backoff_strategy.mutable_base_interval()->set_seconds(3); + backoff_strategy.mutable_max_interval()->set_seconds(2); + EXPECT_TRUE(!BackOffStrategyUtils::validateBackOffStrategyConfig(backoff_strategy, 1, 10).ok()); + } + + { + // Max interval is lower than base interval. + envoy::config::core::v3::BackoffStrategy backoff_strategy; + backoff_strategy.mutable_max_interval()->set_nanos(2000000); + EXPECT_TRUE(!BackOffStrategyUtils::validateBackOffStrategyConfig(backoff_strategy, 3, 10).ok()); + } +} + } // namespace Envoy diff --git a/test/common/common/thread_test.cc b/test/common/common/thread_test.cc index 8c8a28f7d3d9..7ac215195ed5 100644 --- a/test/common/common/thread_test.cc +++ b/test/common/common/thread_test.cc @@ -257,10 +257,11 @@ TEST(PosixThreadTest, PThreadId) { auto thread = thread_factory->createThread([&]() { thread_id = thread_factory->currentPthreadId(); }, /* options= */ absl::nullopt, /* crash_on_failure= */ false); + auto threadId = thread->pthreadId(); thread->join(); - EXPECT_EQ(thread->pthreadId(), thread_id); - EXPECT_NE(thread->pthreadId(), thread_factory->currentThreadId()); + EXPECT_EQ(threadId, thread_id); + EXPECT_NE(threadId, thread_factory->currentThreadId()); } TEST(PosixThreadTest, Joinable) { diff --git a/test/common/config/watched_directory_test.cc b/test/common/config/watched_directory_test.cc index b22dd7dbb983..6ae512b7b481 100644 --- a/test/common/config/watched_directory_test.cc +++ b/test/common/config/watched_directory_test.cc @@ -7,6 +7,7 @@ #include "gtest/gtest.h" +using testing::DoAll; using testing::Return; using testing::SaveArg; @@ -21,7 +22,7 @@ TEST(WatchedDirectory, All) { EXPECT_CALL(dispatcher, createFilesystemWatcher_()).WillOnce(Return(watcher)); Filesystem::Watcher::OnChangedCb cb; EXPECT_CALL(*watcher, addWatch("foo/bar/", Filesystem::Watcher::Events::MovedTo, _)) - .WillOnce(SaveArg<2>(&cb)); + .WillOnce(DoAll(SaveArg<2>(&cb), Return(absl::OkStatus()))); WatchedDirectory wd(config, dispatcher); bool called = false; wd.setCallback([&called] { called = true; }); diff --git a/test/common/filesystem/watcher_impl_test.cc b/test/common/filesystem/watcher_impl_test.cc index d110ad4b8f25..29282ff84949 100644 --- a/test/common/filesystem/watcher_impl_test.cc +++ b/test/common/filesystem/watcher_impl_test.cc @@ -47,11 +47,14 @@ TEST_F(WatcherImplTest, All) { WatchCallback callback; EXPECT_CALL(callback, called(Watcher::Events::MovedTo)).Times(2); - watcher->addWatch(TestEnvironment::temporaryPath("envoy_test/watcher_link"), - Watcher::Events::MovedTo, [&](uint32_t events) -> void { - callback.called(events); - dispatcher_->exit(); - }); + ASSERT_TRUE(watcher + ->addWatch(TestEnvironment::temporaryPath("envoy_test/watcher_link"), + Watcher::Events::MovedTo, + [&](uint32_t events) -> void { + callback.called(events); + dispatcher_->exit(); + }) + .ok()); TestEnvironment::renameFile(TestEnvironment::temporaryPath("envoy_test/watcher_new_link"), TestEnvironment::temporaryPath("envoy_test/watcher_link")); dispatcher_->run(Event::Dispatcher::RunType::Block); @@ -75,11 +78,14 @@ TEST_F(WatcherImplTest, Create) { { std::ofstream file(TestEnvironment::temporaryPath("envoy_test/watcher_target")); } WatchCallback callback; - watcher->addWatch(TestEnvironment::temporaryPath("envoy_test/watcher_link"), - Watcher::Events::MovedTo, [&](uint32_t events) -> void { - callback.called(events); - dispatcher_->exit(); - }); + ASSERT_TRUE(watcher + ->addWatch(TestEnvironment::temporaryPath("envoy_test/watcher_link"), + Watcher::Events::MovedTo, + [&](uint32_t events) -> void { + callback.called(events); + dispatcher_->exit(); + }) + .ok()); { std::ofstream file(TestEnvironment::temporaryPath("envoy_test/other_file")); } dispatcher_->run(Event::Dispatcher::RunType::NonBlock); @@ -99,11 +105,14 @@ TEST_F(WatcherImplTest, Modify) { std::ofstream file(TestEnvironment::temporaryPath("envoy_test/watcher_target")); WatchCallback callback; - watcher->addWatch(TestEnvironment::temporaryPath("envoy_test/watcher_target"), - Watcher::Events::Modified, [&](uint32_t events) -> void { - callback.called(events); - dispatcher_->exit(); - }); + ASSERT_TRUE(watcher + ->addWatch(TestEnvironment::temporaryPath("envoy_test/watcher_target"), + Watcher::Events::Modified, + [&](uint32_t events) -> void { + callback.called(events); + dispatcher_->exit(); + }) + .ok()); dispatcher_->run(Event::Dispatcher::RunType::NonBlock); file << "text" << std::flush; @@ -115,13 +124,14 @@ TEST_F(WatcherImplTest, Modify) { TEST_F(WatcherImplTest, BadPath) { Filesystem::WatcherPtr watcher = dispatcher_->createFilesystemWatcher(); - EXPECT_THROW( - watcher->addWatch("this_is_not_a_file", Watcher::Events::MovedTo, [&](uint32_t) -> void {}), - EnvoyException); + EXPECT_FALSE( + watcher->addWatch("this_is_not_a_file", Watcher::Events::MovedTo, [&](uint32_t) -> void {}) + .ok()); - EXPECT_THROW(watcher->addWatch("this_is_not_a_dir/file", Watcher::Events::MovedTo, - [&](uint32_t) -> void {}), - EnvoyException); + EXPECT_FALSE( + watcher + ->addWatch("this_is_not_a_dir/file", Watcher::Events::MovedTo, [&](uint32_t) -> void {}) + .ok()); } TEST_F(WatcherImplTest, ParentDirectoryRemoved) { @@ -132,9 +142,11 @@ TEST_F(WatcherImplTest, ParentDirectoryRemoved) { WatchCallback callback; EXPECT_CALL(callback, called(testing::_)).Times(0); - watcher->addWatch(TestEnvironment::temporaryPath("envoy_test_empty/watcher_link"), - Watcher::Events::MovedTo, - [&](uint32_t events) -> void { callback.called(events); }); + ASSERT_TRUE(watcher + ->addWatch(TestEnvironment::temporaryPath("envoy_test_empty/watcher_link"), + Watcher::Events::MovedTo, + [&](uint32_t events) -> void { callback.called(events); }) + .ok()); int rc = rmdir(TestEnvironment::temporaryPath("envoy_test_empty").c_str()); EXPECT_EQ(0, rc); @@ -146,9 +158,9 @@ TEST_F(WatcherImplTest, RootDirectoryPath) { Filesystem::WatcherPtr watcher = dispatcher_->createFilesystemWatcher(); #ifndef WIN32 - EXPECT_NO_THROW(watcher->addWatch("/", Watcher::Events::MovedTo, [&](uint32_t) -> void {})); + EXPECT_TRUE(watcher->addWatch("/", Watcher::Events::MovedTo, [&](uint32_t) -> void {}).ok()); #else - EXPECT_NO_THROW(watcher->addWatch("c:\\", Watcher::Events::MovedTo, [&](uint32_t) -> void {})); + EXPECT_TRUE(watcher->addWatch("c:\\", Watcher::Events::MovedTo, [&](uint32_t) -> void {}).ok()); #endif } @@ -169,11 +181,14 @@ TEST_F(WatcherImplTest, SymlinkAtomicRename) { WatchCallback callback; EXPECT_CALL(callback, called(Watcher::Events::MovedTo)); - watcher->addWatch(TestEnvironment::temporaryPath("envoy_test/"), Watcher::Events::MovedTo, - [&](uint32_t events) -> void { - callback.called(events); - dispatcher_->exit(); - }); + ASSERT_TRUE(watcher + ->addWatch(TestEnvironment::temporaryPath("envoy_test/"), + Watcher::Events::MovedTo, + [&](uint32_t events) -> void { + callback.called(events); + dispatcher_->exit(); + }) + .ok()); TestEnvironment::createPath(TestEnvironment::temporaryPath("envoy_test/..timestamp2")); { std::ofstream file(TestEnvironment::temporaryPath("envoy_test/..timestamp2/watched_file")); } diff --git a/test/common/formatter/substitution_format_string_test.cc b/test/common/formatter/substitution_format_string_test.cc index 4f012f7aa4e8..6d74620d2a0e 100644 --- a/test/common/formatter/substitution_format_string_test.cc +++ b/test/common/formatter/substitution_format_string_test.cc @@ -212,5 +212,111 @@ TEST_F(SubstitutionFormatStringUtilsTest, TestFromProtoConfigJsonWithMultipleExt EXPECT_TRUE(TestUtility::jsonStringEqual(out_json, expected)); } +TEST_F(SubstitutionFormatStringUtilsTest, TestParseFormattersWithUnknownExtension) { + const std::string yaml = R"EOF( + name: envoy.formatter.TestFormatterUnknown + typed_config: + "@type": type.googleapis.com/google.protobuf.Any + )EOF"; + + SubstitutionFormatStringUtils::FormattersConfig config; + auto* entry1 = config.Add(); + envoy::config::core::v3::TypedExtensionConfig proto; + TestUtility::loadFromYaml(yaml, proto); + *entry1 = proto; + + EXPECT_THROW_WITH_MESSAGE(SubstitutionFormatStringUtils::parseFormatters(config, context_), + EnvoyException, + "Formatter not found: envoy.formatter.TestFormatterUnknown"); +} + +TEST_F(SubstitutionFormatStringUtilsTest, TestParseFormattersWithInvalidFormatter) { + FailCommandFactory fail_factory; + Registry::InjectFactory command_register(fail_factory); + + const std::string yaml = R"EOF( + name: envoy.formatter.FailFormatter + typed_config: + "@type": type.googleapis.com/google.protobuf.UInt64Value + )EOF"; + + SubstitutionFormatStringUtils::FormattersConfig config; + auto* entry1 = config.Add(); + envoy::config::core::v3::TypedExtensionConfig proto; + TestUtility::loadFromYaml(yaml, proto); + *entry1 = proto; + + EXPECT_THROW_WITH_MESSAGE(SubstitutionFormatStringUtils::parseFormatters(config, context_), + EnvoyException, + "Failed to create command parser: envoy.formatter.FailFormatter"); +} + +TEST_F(SubstitutionFormatStringUtilsTest, TestParseFormattersWithSingleExtension) { + TestCommandFactory factory; + Registry::InjectFactory command_register(factory); + + const std::string yaml = R"EOF( + name: envoy.formatter.TestFormatter + typed_config: + "@type": type.googleapis.com/google.protobuf.StringValue + )EOF"; + + SubstitutionFormatStringUtils::FormattersConfig config; + auto* entry1 = config.Add(); + envoy::config::core::v3::TypedExtensionConfig proto; + TestUtility::loadFromYaml(yaml, proto); + *entry1 = proto; + + auto commands = SubstitutionFormatStringUtils::parseFormatters(config, context_); + ASSERT_EQ(1, commands.size()); + + absl::optional max_length = {}; + ASSERT_TRUE(commands[0] != nullptr); + auto provider = commands[0]->parse("COMMAND_EXTENSION", "", max_length); + ASSERT_TRUE(provider != nullptr); +} + +TEST_F(SubstitutionFormatStringUtilsTest, TestParseFormattersWithMultipleExtensions) { + TestCommandFactory factory; + Registry::InjectFactory command_register(factory); + AdditionalCommandFactory additional_factory; + Registry::InjectFactory additional_command_register(additional_factory); + + const std::string test_command_yaml = R"EOF( + name: envoy.formatter.TestFormatter + typed_config: + "@type": type.googleapis.com/google.protobuf.StringValue + )EOF"; + + const std::string additional_command_yaml = R"EOF( + name: envoy.formatter.AdditionalFormatter + typed_config: + "@type": type.googleapis.com/google.protobuf.UInt32Value + )EOF"; + + SubstitutionFormatStringUtils::FormattersConfig config; + + auto* entry1 = config.Add(); + envoy::config::core::v3::TypedExtensionConfig test_command_proto; + TestUtility::loadFromYaml(test_command_yaml, test_command_proto); + *entry1 = test_command_proto; + + auto* entry2 = config.Add(); + envoy::config::core::v3::TypedExtensionConfig additional_command_proto; + TestUtility::loadFromYaml(additional_command_yaml, additional_command_proto); + *entry2 = additional_command_proto; + + auto commands = SubstitutionFormatStringUtils::parseFormatters(config, context_); + ASSERT_EQ(2, commands.size()); + + absl::optional max_length = {}; + ASSERT_TRUE(commands[0] != nullptr); + auto test_command_provider = commands[0]->parse("COMMAND_EXTENSION", "", max_length); + ASSERT_TRUE(test_command_provider != nullptr); + ASSERT_TRUE(commands[1] != nullptr); + auto additional_command_provider = commands[1]->parse("ADDITIONAL_EXTENSION", "", max_length); + ASSERT_TRUE(additional_command_provider != nullptr); +} + } // namespace Formatter } // namespace Envoy diff --git a/test/common/grpc/BUILD b/test/common/grpc/BUILD index cbf20b2ab3c4..ec28b9cea693 100644 --- a/test/common/grpc/BUILD +++ b/test/common/grpc/BUILD @@ -34,6 +34,7 @@ envoy_cc_test( deps = [ "//source/common/api:api_lib", "//source/common/grpc:async_client_manager_lib", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/stats:stats_mocks", "//test/mocks/thread_local:thread_local_mocks", "//test/mocks/upstream:cluster_manager_mocks", @@ -113,6 +114,7 @@ envoy_cc_test( "//source/common/stats:stats_lib", "//source/common/tracing:http_tracer_lib", "//test/mocks/grpc:grpc_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/tracing:tracing_mocks", "//test/proto:helloworld_proto_cc_proto", "//test/test_common:test_time_lib", @@ -227,6 +229,7 @@ envoy_cc_benchmark_binary( deps = [ "//source/common/api:api_lib", "//source/common/grpc:async_client_manager_lib", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/stats:stats_mocks", "//test/mocks/thread_local:thread_local_mocks", "//test/mocks/upstream:cluster_manager_mocks", diff --git a/test/common/grpc/async_client_manager_benchmark.cc b/test/common/grpc/async_client_manager_benchmark.cc index 5b62f6c270b5..05c2b74b829a 100644 --- a/test/common/grpc/async_client_manager_benchmark.cc +++ b/test/common/grpc/async_client_manager_benchmark.cc @@ -9,6 +9,7 @@ #include "source/common/grpc/async_client_manager_impl.h" #include "test/benchmark/main.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/stats/mocks.h" #include "test/mocks/thread_local/mocks.h" #include "test/mocks/upstream/cluster_manager.h" @@ -27,20 +28,20 @@ namespace { class AsyncClientManagerImplTest { public: - AsyncClientManagerImplTest() - : api_(Api::createApiForTest()), stat_names_(scope_.symbolTable()), - async_client_manager_( - cm_, tls_, test_time_.timeSystem(), *api_, stat_names_, - envoy::config::bootstrap::v3::Bootstrap::GrpcAsyncClientManagerConfig()) {} + AsyncClientManagerImplTest() : api_(Api::createApiForTest()), stat_names_(scope_.symbolTable()) { + ON_CALL(context_, api()).WillByDefault(testing::ReturnRef(*api_)); + async_client_manager_ = std::make_unique( + cm_, context_.threadLocal(), context_, stat_names_, + envoy::config::bootstrap::v3::Bootstrap::GrpcAsyncClientManagerConfig()); + } Upstream::MockClusterManager cm_; - NiceMock tls_; + NiceMock context_; Stats::MockStore store_; Stats::MockScope& scope_{store_.mockScope()}; - DangerousDeprecatedTestTime test_time_; Api::ApiPtr api_; StatNames stat_names_; - AsyncClientManagerImpl async_client_manager_; + std::unique_ptr async_client_manager_; }; void testGetOrCreateAsyncClientWithConfig(::benchmark::State& state) { @@ -54,7 +55,7 @@ void testGetOrCreateAsyncClientWithConfig(::benchmark::State& state) { for (int i = 0; i < 1000; i++) { RawAsyncClientSharedPtr foo_client0 = async_client_man_test.async_client_manager_ - .getOrCreateRawAsyncClient(grpc_service, async_client_man_test.scope_, true) + ->getOrCreateRawAsyncClient(grpc_service, async_client_man_test.scope_, true) .value(); } } @@ -72,8 +73,8 @@ void testGetOrCreateAsyncClientWithHashConfig(::benchmark::State& state) { for (int i = 0; i < 1000; i++) { RawAsyncClientSharedPtr foo_client0 = async_client_man_test.async_client_manager_ - .getOrCreateRawAsyncClientWithHashKey(config_with_hash_key_a, - async_client_man_test.scope_, true) + ->getOrCreateRawAsyncClientWithHashKey(config_with_hash_key_a, + async_client_man_test.scope_, true) .value(); } } diff --git a/test/common/grpc/async_client_manager_impl_test.cc b/test/common/grpc/async_client_manager_impl_test.cc index 8cbcd82c5c5d..13581c6571f5 100644 --- a/test/common/grpc/async_client_manager_impl_test.cc +++ b/test/common/grpc/async_client_manager_impl_test.cc @@ -9,6 +9,7 @@ #include "source/common/event/dispatcher_impl.h" #include "source/common/grpc/async_client_manager_impl.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/stats/mocks.h" #include "test/mocks/thread_local/mocks.h" #include "test/mocks/upstream/cluster_manager.h" @@ -202,21 +203,26 @@ class AsyncClientManagerImplTest : public testing::Test { : api_(Api::createApiForTest(time_system_)), dispatcher_(api_->allocateDispatcher("test_grpc_manager")), stat_names_(scope_.symbolTable()) { - tls_.setDispatcher(dispatcher_.get()); + context_.thread_local_.setDispatcher(dispatcher_.get()); } void initialize(absl::optional config = absl::nullopt) { + ON_CALL(context_, clusterManager()).WillByDefault(testing::ReturnRef(cm_)); + ON_CALL(context_, mainThreadDispatcher()).WillByDefault(testing::ReturnRef(*dispatcher_)); + ON_CALL(context_, timeSource()).WillByDefault(testing::ReturnRef(time_system_)); + ON_CALL(context_, api()).WillByDefault(testing::ReturnRef(*api_)); if (config.has_value()) { async_client_manager_ = std::make_unique( - cm_, tls_, time_system_, *api_, stat_names_, config.value()); + cm_, context_.threadLocal(), context_, stat_names_, config.value()); } else { async_client_manager_ = std::make_unique( - cm_, tls_, time_system_, *api_, stat_names_, Bootstrap::GrpcAsyncClientManagerConfig()); + cm_, context_.threadLocal(), context_, stat_names_, + Bootstrap::GrpcAsyncClientManagerConfig()); } } + NiceMock context_; Upstream::MockClusterManager cm_; - NiceMock tls_; Stats::MockStore store_; Stats::MockScope& scope_{store_.mockScope()}; Event::SimulatedTimeSystem time_system_; diff --git a/test/common/grpc/google_async_client_impl_test.cc b/test/common/grpc/google_async_client_impl_test.cc index d0d3f7d5e67b..7e8f48655393 100644 --- a/test/common/grpc/google_async_client_impl_test.cc +++ b/test/common/grpc/google_async_client_impl_test.cc @@ -9,6 +9,7 @@ #include "source/common/stream_info/stream_info_impl.h" #include "test/mocks/grpc/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/tracing/mocks.h" #include "test/proto/helloworld.pb.h" #include "test/test_common/test_time.h" @@ -57,6 +58,7 @@ class EnvoyGoogleAsyncClientImplTest : public testing::Test { method_descriptor_(helloworld::Greeter::descriptor()->FindMethodByName("SayHello")), stat_names_(scope_->symbolTable()) { + ON_CALL(context_, api()).WillByDefault(testing::ReturnRef(*api_)); auto* google_grpc = config_.mutable_google_grpc(); google_grpc->set_target_uri("fake_address"); google_grpc->set_stat_prefix("test_cluster"); @@ -70,7 +72,7 @@ class EnvoyGoogleAsyncClientImplTest : public testing::Test { virtual void initialize() { grpc_client_ = std::make_unique(*dispatcher_, *tls_, stub_factory_, - scope_, config_, *api_, stat_names_); + scope_, config_, context_, stat_names_); } envoy::config::core::v3::GrpcService config_; @@ -78,6 +80,7 @@ class EnvoyGoogleAsyncClientImplTest : public testing::Test { Stats::IsolatedStoreImpl stats_store_; Api::ApiPtr api_; Event::DispatcherPtr dispatcher_; + NiceMock context_; Stats::ScopeSharedPtr scope_; GoogleAsyncClientThreadLocalPtr tls_; MockStubFactory stub_factory_; @@ -180,7 +183,7 @@ class EnvoyGoogleLessMockedAsyncClientImplTest : public EnvoyGoogleAsyncClientIm public: void initialize() override { grpc_client_ = std::make_unique(*dispatcher_, *tls_, real_stub_factory_, - scope_, config_, *api_, stat_names_); + scope_, config_, context_, stat_names_); } GoogleGenericStubFactory real_stub_factory_; diff --git a/test/common/grpc/grpc_client_integration_test_harness.h b/test/common/grpc/grpc_client_integration_test_harness.h index 27baab111f4f..f6a3363eb24b 100644 --- a/test/common/grpc/grpc_client_integration_test_harness.h +++ b/test/common/grpc/grpc_client_integration_test_harness.h @@ -368,9 +368,9 @@ class GrpcClientIntegrationTest : public GrpcClientIntegrationParamTest { #ifdef ENVOY_GOOGLE_GRPC google_tls_ = std::make_unique(*api_); GoogleGenericStubFactory stub_factory; - return std::make_unique(*dispatcher_, *google_tls_, stub_factory, - stats_scope_, createGoogleGrpcConfig(), *api_, - google_grpc_stat_names_); + return std::make_unique( + *dispatcher_, *google_tls_, stub_factory, stats_scope_, createGoogleGrpcConfig(), + server_factory_context_, google_grpc_stat_names_); #else PANIC("reached unexpected code"); #endif @@ -510,7 +510,8 @@ class GrpcClientIntegrationTest : public GrpcClientIntegrationParamTest { Upstream::MockClusterManager cm_; NiceMock local_info_; Runtime::MockLoader runtime_; - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{test_time_.timeSystem()}; + testing::NiceMock server_factory_context_; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{server_factory_context_}; NiceMock random_; Http::AsyncClientPtr http_async_client_; Http::ConnectionPool::InstancePtr http_conn_pool_; @@ -532,6 +533,7 @@ class GrpcSslClientIntegrationTest : public GrpcClientIntegrationTest { public: GrpcSslClientIntegrationTest() { ON_CALL(factory_context_.server_context_, api()).WillByDefault(ReturnRef(*api_)); + ON_CALL(server_factory_context_, api()).WillByDefault(ReturnRef(*api_)); } void TearDown() override { // Reset some state in the superclass before we destruct context_manager_ in our destructor, it diff --git a/test/common/http/conn_pool_grid_test.cc b/test/common/http/conn_pool_grid_test.cc index d9b1fff1fdf6..1a44c6f8a5ac 100644 --- a/test/common/http/conn_pool_grid_test.cc +++ b/test/common/http/conn_pool_grid_test.cc @@ -1033,7 +1033,7 @@ TEST_F(ConnectivityGridTest, ConnectionCloseDuringAysnConnect) { ASSERT_EQ(0, Api::OsSysCallsSingleton::get().getifaddrs(interfaces).return_value_); } - Api::MockOsSysCalls os_sys_calls; + NiceMock os_sys_calls; TestThreadsafeSingletonInjector os_calls(&os_sys_calls); EXPECT_CALL(os_sys_calls, supportsGetifaddrs()).WillOnce(Return(supports_getifaddrs)); if (supports_getifaddrs) { diff --git a/test/common/http/http3_status_tracker_impl_test.cc b/test/common/http/http3_status_tracker_impl_test.cc index 8d6ce3b88d82..19c6a582f090 100644 --- a/test/common/http/http3_status_tracker_impl_test.cc +++ b/test/common/http/http3_status_tracker_impl_test.cc @@ -33,7 +33,7 @@ TEST_F(Http3StatusTrackerImplTest, Initialized) { TEST_F(Http3StatusTrackerImplTest, MarkBroken) { EXPECT_CALL(*timer_, enabled()).WillOnce(Return(false)); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(5 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1 * 1000), nullptr)); tracker_.markHttp3Broken(); EXPECT_TRUE(tracker_.isHttp3Broken()); EXPECT_FALSE(tracker_.isHttp3Confirmed()); @@ -42,7 +42,7 @@ TEST_F(Http3StatusTrackerImplTest, MarkBroken) { TEST_F(Http3StatusTrackerImplTest, MarkBrokenRepeatedly) { EXPECT_CALL(*timer_, enabled()).WillOnce(Return(false)); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(5 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1 * 1000), nullptr)); tracker_.markHttp3Broken(); EXPECT_TRUE(tracker_.isHttp3Broken()); EXPECT_FALSE(tracker_.isHttp3Confirmed()); @@ -55,7 +55,7 @@ TEST_F(Http3StatusTrackerImplTest, MarkBrokenRepeatedly) { TEST_F(Http3StatusTrackerImplTest, MarkBrokenThenExpires) { EXPECT_CALL(*timer_, enabled()).WillOnce(Return(false)); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(5 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1 * 1000), nullptr)); tracker_.markHttp3Broken(); timer_->invokeCallback(); @@ -67,19 +67,19 @@ TEST_F(Http3StatusTrackerImplTest, MarkBrokenThenExpires) { TEST_F(Http3StatusTrackerImplTest, MarkBrokenWithBackoff) { EXPECT_CALL(*timer_, enabled()).WillRepeatedly(Return(false)); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(5 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1 * 1000), nullptr)); tracker_.markHttp3Broken(); timer_->invokeCallback(); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(10 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(2 * 1000), nullptr)); tracker_.markHttp3Broken(); timer_->invokeCallback(); EXPECT_FALSE(tracker_.isHttp3Broken()); EXPECT_FALSE(tracker_.isHttp3Confirmed()); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(20 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(4 * 1000), nullptr)); tracker_.markHttp3Broken(); EXPECT_TRUE(tracker_.isHttp3Broken()); EXPECT_FALSE(tracker_.isHttp3Confirmed()); @@ -88,7 +88,7 @@ TEST_F(Http3StatusTrackerImplTest, MarkBrokenWithBackoff) { EXPECT_FALSE(tracker_.isHttp3Broken()); EXPECT_FALSE(tracker_.isHttp3Confirmed()); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(40 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(8 * 1000), nullptr)); tracker_.markHttp3Broken(); EXPECT_TRUE(tracker_.isHttp3Broken()); EXPECT_FALSE(tracker_.isHttp3Confirmed()); @@ -101,51 +101,31 @@ TEST_F(Http3StatusTrackerImplTest, MarkBrokenWithBackoff) { TEST_F(Http3StatusTrackerImplTest, MarkBrokenWithBackoffMax) { EXPECT_CALL(*timer_, enabled()).WillRepeatedly(Return(false)); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(5 * 60 * 1000), nullptr)); - tracker_.markHttp3Broken(); - timer_->invokeCallback(); - - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(10 * 60 * 1000), nullptr)); - tracker_.markHttp3Broken(); - timer_->invokeCallback(); - - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(20 * 60 * 1000), nullptr)); - tracker_.markHttp3Broken(); - timer_->invokeCallback(); - - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(40 * 60 * 1000), nullptr)); - tracker_.markHttp3Broken(); - timer_->invokeCallback(); - - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(80 * 60 * 1000), nullptr)); - tracker_.markHttp3Broken(); - timer_->invokeCallback(); - - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(160 * 60 * 1000), nullptr)); - tracker_.markHttp3Broken(); - timer_->invokeCallback(); - - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(320 * 60 * 1000), nullptr)); - tracker_.markHttp3Broken(); - timer_->invokeCallback(); - - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(640 * 60 * 1000), nullptr)); - tracker_.markHttp3Broken(); - timer_->invokeCallback(); + for (int i = 0; i < 17; ++i) { + EXPECT_CALL( + *timer_, + enableTimer(std::chrono::milliseconds(1 * static_cast(pow(2, i)) * 1000), nullptr)); + tracker_.markHttp3Broken(); + timer_->invokeCallback(); + } - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1280 * 60 * 1000), nullptr)); + EXPECT_CALL( + *timer_, + enableTimer(std::chrono::milliseconds(1 * static_cast(pow(2, 17)) * 1000), nullptr)); tracker_.markHttp3Broken(); timer_->invokeCallback(); // Broken period no longer increases. - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1280 * 60 * 1000), nullptr)); + EXPECT_CALL( + *timer_, + enableTimer(std::chrono::milliseconds(1 * static_cast(pow(2, 17)) * 1000), nullptr)); tracker_.markHttp3Broken(); timer_->invokeCallback(); } TEST_F(Http3StatusTrackerImplTest, MarkBrokenThenExpiresThenConfirmedThenBroken) { EXPECT_CALL(*timer_, enabled()).WillOnce(Return(false)); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(5 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1 * 1000), nullptr)); tracker_.markHttp3Broken(); timer_->invokeCallback(); @@ -157,7 +137,7 @@ TEST_F(Http3StatusTrackerImplTest, MarkBrokenThenExpiresThenConfirmedThenBroken) // markConfirmed will have reset the timeout back to the initial value. EXPECT_CALL(*timer_, enabled()).WillOnce(Return(false)); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(5 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1 * 1000), nullptr)); tracker_.markHttp3Broken(); EXPECT_TRUE(tracker_.isHttp3Broken()); @@ -167,7 +147,7 @@ TEST_F(Http3StatusTrackerImplTest, MarkBrokenThenExpiresThenConfirmedThenBroken) TEST_F(Http3StatusTrackerImplTest, MarkBrokenThenConfirmed) { EXPECT_CALL(*timer_, enabled()).WillOnce(Return(false)); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(5 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1 * 1000), nullptr)); tracker_.markHttp3Broken(); timer_->invokeCallback(); @@ -185,7 +165,7 @@ TEST_F(Http3StatusTrackerImplTest, MarkFailedRecentlyAndThenBroken) { EXPECT_FALSE(tracker_.isHttp3Confirmed()); EXPECT_CALL(*timer_, enabled()).WillOnce(Return(false)); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(5 * 60 * 1000), nullptr)); + EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1 * 1000), nullptr)); tracker_.markHttp3Broken(); EXPECT_TRUE(tracker_.isHttp3Broken()); diff --git a/test/common/network/udp_listener_impl_test.cc b/test/common/network/udp_listener_impl_test.cc index 6df91c150355..cc4818cfd43a 100644 --- a/test/common/network/udp_listener_impl_test.cc +++ b/test/common/network/udp_listener_impl_test.cc @@ -516,7 +516,7 @@ TEST_P(UdpListenerImplTest, UdpListenerRecvMsgError) { // Inject mocked OsSysCalls implementation to mock a read failure. Api::MockOsSysCalls os_sys_calls; TestThreadsafeSingletonInjector os_calls(&os_sys_calls); - EXPECT_CALL(os_sys_calls, supportsMmsg()).Times((2u)); + EXPECT_CALL(os_sys_calls, supportsMmsg()).Times((1u)); EXPECT_CALL(os_sys_calls, recvmsg(_, _, _)) .WillOnce(Return(Api::SysCallSizeResult{-1, SOCKET_ERROR_NOT_SUP})); diff --git a/test/common/network/utility_test.cc b/test/common/network/utility_test.cc index f4e3e3886d79..765ab7c4eafb 100644 --- a/test/common/network/utility_test.cc +++ b/test/common/network/utility_test.cc @@ -829,7 +829,12 @@ TEST(PacketLoss, LossTest) { NiceMock processor; MonotonicTime time(std::chrono::seconds(0)); uint32_t packets_dropped = 0; - Utility::readFromSocket(handle, *address, processor, time, false, &packets_dropped); + UdpRecvMsgMethod recv_msg_method = UdpRecvMsgMethod::RecvMsg; + if (Api::OsSysCallsSingleton::get().supportsMmsg()) { + recv_msg_method = UdpRecvMsgMethod::RecvMmsg; + } + + Utility::readFromSocket(handle, *address, processor, time, recv_msg_method, &packets_dropped); EXPECT_EQ(1, packets_dropped); // Send another packet. @@ -837,7 +842,7 @@ TEST(PacketLoss, LossTest) { reinterpret_cast(&storage), sizeof(storage))); // Make sure the drop count is now 2. - Utility::readFromSocket(handle, *address, processor, time, false, &packets_dropped); + Utility::readFromSocket(handle, *address, processor, time, recv_msg_method, &packets_dropped); EXPECT_EQ(2, packets_dropped); } #endif diff --git a/test/common/quic/BUILD b/test/common/quic/BUILD index 6c0fff13ff16..19437fb2be92 100644 --- a/test/common/quic/BUILD +++ b/test/common/quic/BUILD @@ -64,6 +64,7 @@ envoy_cc_test( "//source/common/quic:envoy_quic_proof_verifier_lib", "//source/common/tls:context_config_lib", "//test/mocks/network:network_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/ssl:ssl_mocks", "//test/test_common:test_runtime_lib", "@com_github_google_quiche//:quic_core_versions_lib", @@ -108,6 +109,7 @@ envoy_cc_test( "//test/common/config:dummy_config_proto_cc_proto", "//test/common/tls/cert_validator:timed_cert_validator", "//test/mocks/event:event_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/ssl:ssl_mocks", "@com_github_google_quiche//:quic_test_tools_test_certificates_lib", ], @@ -190,6 +192,7 @@ envoy_cc_test( deps = [ ":test_utils_lib", "//envoy/stats:stats_macros", + "//source/common/api:os_sys_calls_lib", "//source/common/quic:client_codec_lib", "//source/common/quic:envoy_quic_alarm_factory_lib", "//source/common/quic:envoy_quic_client_connection_lib", @@ -202,6 +205,8 @@ envoy_cc_test( "//test/mocks/stats:stats_mocks", "//test/test_common:logging_lib", "//test/test_common:simulated_time_system_lib", + "//test/test_common:test_runtime_lib", + "//test/test_common:threadsafe_singleton_injector_lib", "@com_github_google_quiche//:quic_test_tools_session_peer_lib", ], ) diff --git a/test/common/quic/envoy_quic_client_session_test.cc b/test/common/quic/envoy_quic_client_session_test.cc index 1a97687d2118..6d1701e7256c 100644 --- a/test/common/quic/envoy_quic_client_session_test.cc +++ b/test/common/quic/envoy_quic_client_session_test.cc @@ -1,5 +1,6 @@ #include "envoy/stats/stats_macros.h" +#include "source/common/api/os_sys_calls_impl.h" #include "source/common/network/transport_socket_options_impl.h" #include "source/common/quic/client_codec_impl.h" #include "source/common/quic/envoy_quic_alarm_factory.h" @@ -18,6 +19,8 @@ #include "test/test_common/logging.h" #include "test/test_common/network_utility.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 "gmock/gmock.h" #include "gtest/gtest.h" @@ -29,6 +32,7 @@ using testing::_; using testing::Invoke; +using testing::Return; namespace Envoy { namespace Quic { @@ -45,7 +49,7 @@ class TestEnvoyQuicClientConnection : public EnvoyQuicClientConnection { quic::ConnectionIdGeneratorInterface& generator) : EnvoyQuicClientConnection(server_connection_id, helper, alarm_factory, &writer, false, supported_versions, dispatcher, std::move(connection_socket), - generator) { + generator, /*prefer_gro=*/true) { SetEncrypter(quic::ENCRYPTION_FORWARD_SECURE, std::make_unique(quic::ENCRYPTION_FORWARD_SECURE)); InstallDecrypter(quic::ENCRYPTION_FORWARD_SECURE, @@ -73,45 +77,50 @@ class EnvoyQuicClientSessionTest : public testing::TestWithParam( quic::test::crypto_test_utils::ProofVerifierForTesting())), quic_stat_names_(store_.symbolTable()), transport_socket_options_(std::make_shared()), - envoy_quic_session_( - quic_config_, quic_version_, - std::unique_ptr(quic_connection_), - quic::QuicServerId("example.com", 443, false), crypto_config_, *dispatcher_, - /*send_buffer_limit*/ 1024 * 1024, crypto_stream_factory_, quic_stat_names_, {}, - *store_.rootScope(), transport_socket_options_, {}), stats_({ALL_HTTP3_CODEC_STATS(POOL_COUNTER_PREFIX(store_, "http3."), POOL_GAUGE_PREFIX(store_, "http3."))}) { http3_options_.mutable_quic_protocol_options() ->mutable_num_timeouts_to_trigger_port_migration() ->set_value(1); + } + + void SetUp() override { + quic_connection_ = new TestEnvoyQuicClientConnection( + quic::test::TestConnectionId(), connection_helper_, alarm_factory_, writer_, quic_version_, + *dispatcher_, createConnectionSocket(peer_addr_, self_addr_, nullptr, /*prefer_gro=*/true), + connection_id_generator_); + + OptRef cache; + OptRef uts_factory; + envoy_quic_session_ = std::make_unique( + quic_config_, quic_version_, + std::unique_ptr(quic_connection_), + quic::QuicServerId("example.com", 443, false), crypto_config_, *dispatcher_, + /*send_buffer_limit*/ 1024 * 1024, crypto_stream_factory_, quic_stat_names_, cache, + *store_.rootScope(), transport_socket_options_, uts_factory); + http_connection_ = std::make_unique( - envoy_quic_session_, http_connection_callbacks_, stats_, http3_options_, 64 * 1024, 100); - EXPECT_EQ(time_system_.systemTime(), envoy_quic_session_.streamInfo().startTime()); - EXPECT_EQ(EMPTY_STRING, envoy_quic_session_.nextProtocol()); + *envoy_quic_session_, http_connection_callbacks_, stats_, http3_options_, 64 * 1024, 100); + EXPECT_EQ(time_system_.systemTime(), envoy_quic_session_->streamInfo().startTime()); + EXPECT_EQ(EMPTY_STRING, envoy_quic_session_->nextProtocol()); EXPECT_EQ(Http::Protocol::Http3, http_connection_->protocol()); time_system_.advanceTimeWait(std::chrono::milliseconds(1)); ON_CALL(writer_, WritePacket(_, _, _, _, _, _)) .WillByDefault(testing::Return(quic::WriteResult(quic::WRITE_STATUS_OK, 1))); - } - void SetUp() override { - envoy_quic_session_.Initialize(); - setQuicConfigWithDefaultValues(envoy_quic_session_.config()); + envoy_quic_session_->Initialize(); + setQuicConfigWithDefaultValues(envoy_quic_session_->config()); quic::test::QuicConfigPeer::SetReceivedStatelessResetToken( - envoy_quic_session_.config(), + envoy_quic_session_->config(), quic::QuicUtils::GenerateStatelessResetToken(quic::test::TestConnectionId())); - envoy_quic_session_.OnConfigNegotiated(); - envoy_quic_session_.addConnectionCallbacks(network_connection_callbacks_); - envoy_quic_session_.setConnectionStats( + envoy_quic_session_->OnConfigNegotiated(); + envoy_quic_session_->addConnectionCallbacks(network_connection_callbacks_); + envoy_quic_session_->setConnectionStats( {read_total_, read_current_, write_total_, write_current_, nullptr, nullptr}); EXPECT_EQ(&read_total_, &quic_connection_->connectionStats().read_total_); } @@ -121,7 +130,7 @@ class EnvoyQuicClientSessionTest : public testing::TestWithParamclose(Network::ConnectionCloseType::NoFlush); } peer_socket_->close(); } @@ -163,7 +172,7 @@ class EnvoyQuicClientSessionTest : public testing::TestWithParam envoy_quic_session_; Network::MockConnectionCallbacks network_connection_callbacks_; Http::MockServerConnectionCallbacks http_connection_callbacks_; testing::StrictMock read_total_; @@ -199,8 +208,8 @@ TEST_P(EnvoyQuicClientSessionTest, NewStream) { 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()); + EXPECT_EQ(0, envoy_quic_session_->GetNumActiveStreams()); + EXPECT_EQ(16, envoy_quic_session_->numPacketsExpectedPerEventLoop()); NiceMock response_decoder; NiceMock stream_callbacks; @@ -217,8 +226,8 @@ TEST_P(EnvoyQuicClientSessionTest, PacketLimits) { })); stream.OnStreamHeaderList(/*fin=*/false, headers.uncompressed_header_bytes(), headers); // With one stream, still read 16 packets. - EXPECT_EQ(1, envoy_quic_session_.GetNumActiveStreams()); - EXPECT_EQ(16, envoy_quic_session_.numPacketsExpectedPerEventLoop()); + EXPECT_EQ(1, envoy_quic_session_->GetNumActiveStreams()); + EXPECT_EQ(16, envoy_quic_session_->numPacketsExpectedPerEventLoop()); EnvoyQuicClientStream& stream2 = sendGetRequest(response_decoder, stream_callbacks); EXPECT_CALL(response_decoder, decodeHeaders_(_, /*end_stream=*/false)) @@ -227,13 +236,13 @@ TEST_P(EnvoyQuicClientSessionTest, PacketLimits) { })); stream2.OnStreamHeaderList(/*fin=*/false, headers.uncompressed_header_bytes(), headers); // With 2 streams, read 32 packets. - EXPECT_EQ(2, envoy_quic_session_.GetNumActiveStreams()); - EXPECT_EQ(32, envoy_quic_session_.numPacketsExpectedPerEventLoop()); + EXPECT_EQ(2, envoy_quic_session_->GetNumActiveStreams()); + EXPECT_EQ(32, envoy_quic_session_->numPacketsExpectedPerEventLoop()); EXPECT_CALL(*quic_connection_, SendConnectionClosePacket(quic::QUIC_NO_ERROR, _, "Closed by application")); EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::LocalClose)); - envoy_quic_session_.close(Network::ConnectionCloseType::NoFlush); + envoy_quic_session_->close(Network::ConnectionCloseType::NoFlush); } TEST_P(EnvoyQuicClientSessionTest, OnResetFrame) { @@ -246,7 +255,7 @@ TEST_P(EnvoyQuicClientSessionTest, OnResetFrame) { quic::QuicRstStreamFrame rst1(/*control_frame_id=*/1u, stream_id, quic::QUIC_ERROR_PROCESSING_STREAM, /*bytes_written=*/0u); EXPECT_CALL(stream_callbacks, onResetStream(Http::StreamResetReason::RemoteReset, _)); - envoy_quic_session_.OnRstStream(rst1); + envoy_quic_session_->OnRstStream(rst1); EXPECT_EQ( 1U, TestUtility::findCounter( @@ -263,7 +272,7 @@ TEST_P(EnvoyQuicClientSessionTest, SendResetFrame) { quic::QuicStreamId stream_id = stream.id(); EXPECT_CALL(stream_callbacks, onResetStream(Http::StreamResetReason::LocalReset, _)); EXPECT_CALL(*quic_connection_, SendControlFrame(_)); - envoy_quic_session_.ResetStream(stream_id, quic::QUIC_ERROR_PROCESSING_STREAM); + envoy_quic_session_->ResetStream(stream_id, quic::QUIC_ERROR_PROCESSING_STREAM); EXPECT_EQ( 1U, TestUtility::findCounter( @@ -276,7 +285,7 @@ TEST_P(EnvoyQuicClientSessionTest, OnGoAwayFrame) { Http::MockStreamCallbacks stream_callbacks; EXPECT_CALL(http_connection_callbacks_, onGoAway(Http::GoAwayErrorCode::NoError)); - envoy_quic_session_.OnHttp3GoAway(4u); + envoy_quic_session_->OnHttp3GoAway(4u); } TEST_P(EnvoyQuicClientSessionTest, ConnectionClose) { @@ -288,8 +297,8 @@ TEST_P(EnvoyQuicClientSessionTest, ConnectionClose) { EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::RemoteClose)); quic_connection_->OnConnectionCloseFrame(frame); 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()); + envoy_quic_session_->transportFailureReason()); + EXPECT_EQ(Network::Connection::State::Closed, envoy_quic_session_->state()); EXPECT_EQ( 1U, TestUtility::findCounter( @@ -305,8 +314,8 @@ TEST_P(EnvoyQuicClientSessionTest, ConnectionCloseWithActiveStream) { SendConnectionClosePacket(quic::QUIC_NO_ERROR, _, "Closed by application")); EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::LocalClose)); EXPECT_CALL(stream_callbacks, onResetStream(Http::StreamResetReason::ConnectionTermination, _)); - envoy_quic_session_.close(Network::ConnectionCloseType::NoFlush); - EXPECT_EQ(Network::Connection::State::Closed, envoy_quic_session_.state()); + envoy_quic_session_->close(Network::ConnectionCloseType::NoFlush); + EXPECT_EQ(Network::Connection::State::Closed, envoy_quic_session_->state()); EXPECT_TRUE(stream.write_side_closed() && stream.reading_stopped()); EXPECT_EQ(1U, TestUtility::findCounter( store_, "http3.upstream.tx.quic_connection_close_error_code_QUIC_NO_ERROR") @@ -321,8 +330,8 @@ TEST_P(EnvoyQuicClientSessionTest, HandshakeTimesOutWithActiveStream) { SendConnectionClosePacket(quic::QUIC_HANDSHAKE_FAILED, _, "fake handshake time out")); EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::LocalClose)); EXPECT_CALL(stream_callbacks, onResetStream(Http::StreamResetReason::LocalConnectionFailure, _)); - envoy_quic_session_.OnStreamError(quic::QUIC_HANDSHAKE_FAILED, "fake handshake time out"); - EXPECT_EQ(Network::Connection::State::Closed, envoy_quic_session_.state()); + envoy_quic_session_->OnStreamError(quic::QUIC_HANDSHAKE_FAILED, "fake handshake time out"); + EXPECT_EQ(Network::Connection::State::Closed, envoy_quic_session_->state()); EXPECT_TRUE(stream.write_side_closed() && stream.reading_stopped()); EXPECT_EQ(1U, TestUtility::findCounter( @@ -339,8 +348,8 @@ TEST_P(EnvoyQuicClientSessionTest, ConnectionClosePopulatesQuicVersionStats) { EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::RemoteClose)); quic_connection_->OnConnectionCloseFrame(frame); 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()); + envoy_quic_session_->transportFailureReason()); + EXPECT_EQ(Network::Connection::State::Closed, envoy_quic_session_->state()); std::string quic_version_stat_name; switch (GetParam().transport_version) { case quic::QUIC_VERSION_IETF_DRAFT_29: @@ -360,8 +369,8 @@ TEST_P(EnvoyQuicClientSessionTest, ConnectionClosePopulatesQuicVersionStats) { TEST_P(EnvoyQuicClientSessionTest, IncomingUnidirectionalReadStream) { quic::QuicStreamId stream_id = 1u; quic::QuicStreamFrame stream_frame(stream_id, false, 0, "aaa"); - envoy_quic_session_.OnStreamFrame(stream_frame); - EXPECT_FALSE(quic::test::QuicSessionPeer::IsStreamCreated(&envoy_quic_session_, stream_id)); + envoy_quic_session_->OnStreamFrame(stream_frame); + EXPECT_FALSE(quic::test::QuicSessionPeer::IsStreamCreated(envoy_quic_session_.get(), stream_id)); // IETF stream 3 is server initiated uni-directional stream. stream_id = 3u; auto payload = std::make_unique(8); @@ -371,17 +380,18 @@ TEST_P(EnvoyQuicClientSessionTest, IncomingUnidirectionalReadStream) { EXPECT_CALL(*quic_connection_, SendConnectionClosePacket(quic::QUIC_HTTP_RECEIVE_SERVER_PUSH, _, "Received server push stream")); quic::QuicStreamFrame stream_frame2(stream_id, false, 0, absl::string_view(payload.get(), 1)); - envoy_quic_session_.OnStreamFrame(stream_frame2); + envoy_quic_session_->OnStreamFrame(stream_frame2); } TEST_P(EnvoyQuicClientSessionTest, GetRttAndCwnd) { - EXPECT_GT(envoy_quic_session_.lastRoundTripTime().value(), std::chrono::microseconds(0)); + EXPECT_GT(envoy_quic_session_->lastRoundTripTime().value(), std::chrono::microseconds(0)); // Just make sure the CWND is non-zero. We don't want to make strong assertions on what the value // should be in this test, that is the job the congestion controllers' tests. - EXPECT_GT(envoy_quic_session_.congestionWindowInBytes().value(), 500); + EXPECT_GT(envoy_quic_session_->congestionWindowInBytes().value(), 500); - envoy_quic_session_.configureInitialCongestionWindow(8000000, std::chrono::microseconds(1000000)); - EXPECT_GT(envoy_quic_session_.congestionWindowInBytes().value(), + envoy_quic_session_->configureInitialCongestionWindow(8000000, + std::chrono::microseconds(1000000)); + EXPECT_GT(envoy_quic_session_->congestionWindowInBytes().value(), quic::kInitialCongestionWindow * quic::kDefaultTCPMSS); } @@ -463,7 +473,7 @@ TEST_P(EnvoyQuicClientSessionTest, StatelessResetOnProbingSocket) { // Trigger port migration. quic_connection_->OnPathDegradingDetected(); - EXPECT_TRUE(envoy_quic_session_.HasPendingPathValidation()); + EXPECT_TRUE(envoy_quic_session_->HasPendingPathValidation()); auto* path_validation_context = dynamic_cast( quic_connection_->GetPathValidationContext()); @@ -486,7 +496,7 @@ TEST_P(EnvoyQuicClientSessionTest, StatelessResetOnProbingSocket) { // fail the probing. EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::RemoteClose)) .Times(0); - while (envoy_quic_session_.HasPendingPathValidation()) { + while (envoy_quic_session_->HasPendingPathValidation()) { // Running event loop to receive the STATELESS_RESET and following socket reads shouldn't cause // crash. dispatcher_->run(Event::Dispatcher::RunType::NonBlock); @@ -494,5 +504,172 @@ TEST_P(EnvoyQuicClientSessionTest, StatelessResetOnProbingSocket) { EXPECT_EQ(self_addr_->asString(), quic_connection_->self_address().ToString()); } +class MockOsSysCallsImpl : public Api::OsSysCallsImpl { +public: + MOCK_METHOD(Api::SysCallSizeResult, recvmsg, (os_fd_t socket, msghdr* msg, int flags), + (override)); + MOCK_METHOD(Api::SysCallIntResult, recvmmsg, + (os_fd_t socket, struct mmsghdr* msgvec, unsigned int vlen, int flags, + struct timespec* timeout), + (override)); + MOCK_METHOD(bool, supportsUdpGro, (), (const)); +}; + +// Ensures that the Network::Utility::readFromSocket function uses GRO. +// Only Linux platforms support GRO. +TEST_P(EnvoyQuicClientSessionTest, UsesUdpGro) { + if (!Api::OsSysCallsSingleton::get().supportsUdpGro()) { + GTEST_SKIP() << "Platform doesn't support GRO."; + } + + NiceMock os_sys_calls; + TestThreadsafeSingletonInjector singleton_injector{&os_sys_calls}; + + // Have to connect the QUIC session, so that the socket is set up so we can do I/O on it. + envoy_quic_session_->connect(); + + std::string write_data = "abc"; + Buffer::RawSlice slice; + slice.mem_ = write_data.data(); + slice.len_ = write_data.length(); + + // Make sure the option for GRO is set on the socket. +// Windows doesn't have the GRO socket options. +#if !defined(WIN32) + int sock_opt; + socklen_t sock_len = sizeof(int); + EXPECT_EQ(0, quic_connection_->connectionSocket() + ->getSocketOption(SOL_UDP, UDP_GRO, &sock_opt, &sock_len) + .return_value_); + EXPECT_EQ(1, sock_opt); +#endif + + // GRO uses `recvmsg`, not `recvmmsg`. + EXPECT_CALL(os_sys_calls, supportsUdpGro()).WillRepeatedly(Return(true)); + EXPECT_CALL(os_sys_calls, recvmmsg(_, _, _, _, _)).Times(0); + EXPECT_CALL(os_sys_calls, recvmsg(_, _, _)) + .WillOnce( + Invoke([&](os_fd_t /*socket*/, msghdr* /*msg*/, int /*flags*/) -> Api::SysCallSizeResult { + dispatcher_->exit(); + // Return an error so IoSocketHandleImpl::recvmsg() exits early, instead of trying to + // use the msghdr that would normally have been populated by recvmsg but is not + // populated by this mock. + return {-1, SOCKET_ERROR_AGAIN}; + })); + + peer_socket_->ioHandle().sendmsg(&slice, 1, 0, peer_addr_->ip(), *self_addr_); + + EXPECT_LOG_CONTAINS("trace", "starting gro recvmsg with max", + dispatcher_->run(Event::Dispatcher::RunType::RunUntilExit)); +} + +class EnvoyQuicClientSessionDisallowMmsgTest : public EnvoyQuicClientSessionTest { +public: + void SetUp() override { + EXPECT_CALL(os_sys_calls_, supportsUdpGro()).WillRepeatedly(Return(false)); + EnvoyQuicClientSessionTest::SetUp(); + } + +protected: + NiceMock os_sys_calls_; + +private: + TestThreadsafeSingletonInjector singleton_injector_{&os_sys_calls_}; +}; + +INSTANTIATE_TEST_SUITE_P(EnvoyQuicClientSessionDisallowMmsgTests, + EnvoyQuicClientSessionDisallowMmsgTest, + testing::ValuesIn(quic::CurrentSupportedHttp3Versions())); + +// Ensures that the Network::Utility::readFromSocket function uses `recvmsg` for client QUIC +// connections when GRO is not supported. +TEST_P(EnvoyQuicClientSessionDisallowMmsgTest, UsesRecvMsgWhenNoGro) { + // Have to connect the QUIC session, so that the socket is set up so we can do I/O on it. + envoy_quic_session_->connect(); + + std::string write_data = "abc"; + Buffer::RawSlice slice; + slice.mem_ = write_data.data(); + slice.len_ = write_data.length(); + +// Windows doesn't have the GRO socket options. +#if !defined(WIN32) + // Make sure the option for GRO is *not* set on the socket. + int sock_opt; + socklen_t sock_len = sizeof(int); + EXPECT_EQ(0, quic_connection_->connectionSocket() + ->getSocketOption(SOL_UDP, UDP_GRO, &sock_opt, &sock_len) + .return_value_); + EXPECT_EQ(0, sock_opt); +#endif + + // Uses `recvmsg`, not `recvmmsg`. + EXPECT_CALL(os_sys_calls_, recvmmsg(_, _, _, _, _)).Times(0); + EXPECT_CALL(os_sys_calls_, recvmsg(_, _, _)) + .WillOnce( + Invoke([&](os_fd_t /*socket*/, msghdr* /*msg*/, int /*flags*/) -> Api::SysCallSizeResult { + dispatcher_->exit(); + // Return an error so IoSocketHandleImpl::recvmsg() exits early, instead of trying to + // use the msghdr that would normally have been populated by recvmsg but is not + // populated by this mock. + return {-1, SOCKET_ERROR_AGAIN}; + })); + + peer_socket_->ioHandle().sendmsg(&slice, 1, 0, peer_addr_->ip(), *self_addr_); + + EXPECT_LOG_CONTAINS("trace", "starting recvmsg with max", + dispatcher_->run(Event::Dispatcher::RunType::RunUntilExit)); +} + +class EnvoyQuicClientSessionAllowMmsgTest : public EnvoyQuicClientSessionTest { +public: + void SetUp() override { + EXPECT_CALL(os_sys_calls_, supportsUdpGro()).WillRepeatedly(Return(false)); + + scoped_runtime_.mergeValues( + {{"envoy.reloadable_features.disallow_quic_client_udp_mmsg", "false"}}); + EnvoyQuicClientSessionTest::SetUp(); + } + +protected: + NiceMock os_sys_calls_; + +private: + TestScopedRuntime scoped_runtime_; + TestThreadsafeSingletonInjector singleton_injector_{&os_sys_calls_}; +}; + +INSTANTIATE_TEST_SUITE_P(EnvoyQuicClientSessionAllowMmsgTests, EnvoyQuicClientSessionAllowMmsgTest, + testing::ValuesIn(quic::CurrentSupportedHttp3Versions())); + +TEST_P(EnvoyQuicClientSessionAllowMmsgTest, UsesRecvMmsgWhenNoGroAndMmsgAllowed) { + if (!Api::OsSysCallsSingleton::get().supportsMmsg()) { + GTEST_SKIP() << "Platform doesn't support recvmmsg."; + } + + // Have to connect the QUIC session, so that the socket is set up so we can do I/O on it. + envoy_quic_session_->connect(); + + std::string write_data = "abc"; + Buffer::RawSlice slice; + slice.mem_ = write_data.data(); + slice.len_ = write_data.length(); + + // Make sure recvmmsg is used when GRO isn't supported. + EXPECT_CALL(os_sys_calls_, supportsUdpGro()).WillRepeatedly(Return(false)); + EXPECT_CALL(os_sys_calls_, recvmsg(_, _, _)).Times(0); + EXPECT_CALL(os_sys_calls_, recvmmsg(_, _, _, _, _)) + .WillRepeatedly(Invoke([&](os_fd_t, struct mmsghdr*, unsigned int, int, + struct timespec*) -> Api::SysCallIntResult { + dispatcher_->exit(); + return {0, SOCKET_ERROR_AGAIN}; + })); + + peer_socket_->ioHandle().sendmsg(&slice, 1, 0, peer_addr_->ip(), *self_addr_); + + EXPECT_LOG_CONTAINS("trace", "starting recvmmsg with packets", + dispatcher_->run(Event::Dispatcher::RunType::RunUntilExit)); +} + } // namespace Quic } // namespace Envoy diff --git a/test/common/quic/envoy_quic_client_stream_test.cc b/test/common/quic/envoy_quic_client_stream_test.cc index b579c6962130..4ba077ead61d 100644 --- a/test/common/quic/envoy_quic_client_stream_test.cc +++ b/test/common/quic/envoy_quic_client_stream_test.cc @@ -46,7 +46,8 @@ class EnvoyQuicClientStreamTest : public testing::Test { quic_connection_(new MockEnvoyQuicClientConnection( quic::test::TestConnectionId(), connection_helper_, alarm_factory_, &writer_, /*owns_writer=*/false, {quic_version_}, *dispatcher_, - createConnectionSocket(peer_addr_, self_addr_, nullptr), connection_id_generator_)), + createConnectionSocket(peer_addr_, self_addr_, nullptr, /*prefer_gro=*/true), + connection_id_generator_)), quic_session_(quic_config_, {quic_version_}, std::unique_ptr(quic_connection_), *dispatcher_, quic_config_.GetInitialStreamFlowControlWindowToSend() * 2, diff --git a/test/common/quic/envoy_quic_proof_source_test.cc b/test/common/quic/envoy_quic_proof_source_test.cc index a6814a40c52d..a6c6bcb2d4f3 100644 --- a/test/common/quic/envoy_quic_proof_source_test.cc +++ b/test/common/quic/envoy_quic_proof_source_test.cc @@ -9,6 +9,7 @@ #include "test/common/quic/test_utils.h" #include "test/mocks/network/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/ssl/mocks.h" #include "test/test_common/test_runtime.h" @@ -17,6 +18,7 @@ #include "quiche/quic/core/crypto/certificate_view.h" #include "quiche/quic/test_tools/test_certificates.h" +using testing::HasSubstr; using testing::Invoke; using testing::Return; using testing::ReturnRef; @@ -71,7 +73,7 @@ class SignatureVerifier { const absl::optional nullopt = absl::nullopt; ON_CALL(cert_validation_ctx_config_, customValidatorConfig()).WillByDefault(ReturnRef(nullopt)); auto context = std::make_shared( - *store_.rootScope(), client_context_config_, time_system_); + *store_.rootScope(), client_context_config_, server_factory_context_); ON_CALL(verify_context_, dispatcher()).WillByDefault(ReturnRef(dispatcher_)); ON_CALL(verify_context_, transportSocketOptions()) .WillByDefault(ReturnRef(transport_socket_options_)); @@ -105,6 +107,7 @@ class SignatureVerifier { NiceMock store_; Event::GlobalTimeSystem time_system_; NiceMock client_context_config_; + NiceMock server_factory_context_; NiceMock cert_validation_ctx_config_; std::unique_ptr verifier_; NiceMock tls_context_manager_; @@ -224,7 +227,8 @@ class EnvoyQuicProofSourceTest : public ::testing::Test { Network::MockFilterChainManager filter_chain_manager_; Network::MockListenSocket listen_socket_; testing::NiceMock listener_config_; - Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{time_system_}; + Server::Configuration::MockServerFactoryContext factory_context_; + Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{factory_context_}; Ssl::MockServerContextConfig* mock_context_config_; std::function secret_update_callback_; std::unique_ptr transport_socket_factory_; @@ -306,10 +310,10 @@ f/lOd5zz2e7Tu2pUtx1sX1tlKph1D0ANpJwxRV78R2hjmynLSl7h4Ual9NMubqkD x96rVeUbRJ/qU4//nNM/XQa9vIAIcTZ0jFhmb0c3R4rmoqqC3vkSDwtaE5yuS5T4 GUy+n0vQNB0cXGzgcGI= -----END CERTIFICATE-----)"}; - EXPECT_THROW_WITH_MESSAGE(expectCertChainAndPrivateKey(cert_with_rsa_1024, false, true), - EnvoyException, - "Failed to load certificate chain from , only RSA certificates with " - "2048-bit or larger keys are supported"); + EXPECT_THAT_THROWS_MESSAGE( + expectCertChainAndPrivateKey(cert_with_rsa_1024, false, true), EnvoyException, + HasSubstr("Failed to load certificate chain from , only RSA certificates with " + "2048-bit")); } TEST_F(EnvoyQuicProofSourceTest, ComputeSignatureFailNoFilterChain) { @@ -391,7 +395,9 @@ class LegacyEnvoyQuicProofSourceTest : public ::testing::Test { Network::MockFilterChainManager filter_chain_manager_; Network::MockListenSocket listen_socket_; testing::NiceMock listener_config_; - Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{time_system_}; + testing::NiceMock server_factory_context_; + Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{ + server_factory_context_}; Ssl::MockServerContextConfig* mock_context_config_; std::unique_ptr transport_socket_factory_; Ssl::MockTlsCertificateConfig tls_cert_config_; diff --git a/test/common/quic/envoy_quic_proof_verifier_test.cc b/test/common/quic/envoy_quic_proof_verifier_test.cc index ab12465385eb..97b5bd52724a 100644 --- a/test/common/quic/envoy_quic_proof_verifier_test.cc +++ b/test/common/quic/envoy_quic_proof_verifier_test.cc @@ -9,6 +9,7 @@ #include "test/common/quic/test_utils.h" #include "test/common/tls/cert_validator/timed_cert_validator.h" #include "test/mocks/event/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/ssl/mocks.h" #include "test/mocks/stats/mocks.h" #include "test/test_common/test_time.h" @@ -80,7 +81,7 @@ class EnvoyQuicProofVerifierTest : public testing::Test { EXPECT_CALL(cert_validation_ctx_config_, customValidatorConfig()) .WillRepeatedly(ReturnRef(custom_validator_config_)); auto context = std::make_shared( - *store_.rootScope(), client_context_config_, time_system_); + *store_.rootScope(), client_context_config_, factory_context_); verifier_ = std::make_unique(std::move(context)); } @@ -98,7 +99,7 @@ class EnvoyQuicProofVerifierTest : public testing::Test { absl::optional custom_validator_config_{ absl::nullopt}; NiceMock store_; - Event::GlobalTimeSystem time_system_; + Server::Configuration::MockServerFactoryContext factory_context_; NiceMock client_context_config_; Ssl::MockCertificateValidationContextConfig cert_validation_ctx_config_; std::unique_ptr verifier_; diff --git a/test/common/quic/test_utils.h b/test/common/quic/test_utils.h index 0a1a5938eab4..fdf3a1d0e587 100644 --- a/test/common/quic/test_utils.h +++ b/test/common/quic/test_utils.h @@ -75,7 +75,7 @@ class MockEnvoyQuicClientConnection : public EnvoyQuicClientConnection { quic::ConnectionIdGeneratorInterface& generator) : EnvoyQuicClientConnection(server_connection_id, helper, alarm_factory, writer, owns_writer, supported_versions, dispatcher, std::move(connection_socket), - generator) {} + generator, /*prefer_gro=*/true) {} MOCK_METHOD(quic::MessageStatus, SendMessage, (quic::QuicMessageId, absl::Span, bool)); diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index e867650b01e9..22a22de713e0 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -9180,6 +9180,79 @@ TEST_F(RouteConfigurationV2, BadConnectConfig) { "Non-CONNECT upgrade type Websocket has ConnectConfig"); } +TEST_F(RouteConfigurationV2, ConnectProxy) { + const std::string yaml = R"EOF( +virtual_hosts: + - name: connect-proxy + domains: ["*"] + routes: + - match: + connect_matcher: {} + route: + cluster: some-cluster + upgrade_configs: + - upgrade_type: CONNECT + )EOF"; + + factory_context_.cluster_manager_.initializeClusters({"some-cluster"}, {}); + Http::TestRequestHeaderMapImpl headers = genPathlessHeaders("example.com", "CONNECT"); + + { + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.http_route_connect_proxy_by_default", "true"}}); + + TestConfigImpl config(parseRouteConfigurationFromYaml(yaml), factory_context_, true); + EXPECT_FALSE(config.route(headers, 0)->routeEntry()->connectConfig()); + } + + { + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.http_route_connect_proxy_by_default", "false"}}); + + TestConfigImpl config(parseRouteConfigurationFromYaml(yaml), factory_context_, true); + EXPECT_TRUE(config.route(headers, 0)->routeEntry()->connectConfig()); + } +} + +TEST_F(RouteConfigurationV2, ConnectTerminate) { + const std::string yaml = R"EOF( +virtual_hosts: + - name: connect-terminate + domains: ["*"] + routes: + - match: + connect_matcher: {} + route: + cluster: some-cluster + upgrade_configs: + - upgrade_type: CONNECT + connect_config: {} + )EOF"; + + factory_context_.cluster_manager_.initializeClusters({"some-cluster"}, {}); + Http::TestRequestHeaderMapImpl headers = genPathlessHeaders("example.com", "CONNECT"); + + { + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.http_route_connect_proxy_by_default", "true"}}); + + TestConfigImpl config(parseRouteConfigurationFromYaml(yaml), factory_context_, true); + EXPECT_TRUE(config.route(headers, 0)->routeEntry()->connectConfig()); + } + + { + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.http_route_connect_proxy_by_default", "false"}}); + + TestConfigImpl config(parseRouteConfigurationFromYaml(yaml), factory_context_, true); + EXPECT_TRUE(config.route(headers, 0)->routeEntry()->connectConfig()); + } +} + // Verifies that we're creating a new instance of the retry plugins on each call instead of // always returning the same one. TEST_F(RouteConfigurationV2, RetryPluginsAreNotReused) { diff --git a/test/common/router/header_parser_corpus/wrong_upstream_address b/test/common/router/header_parser_corpus/wrong_upstream_address new file mode 100644 index 000000000000..b45638c9ab38 --- /dev/null +++ b/test/common/router/header_parser_corpus/wrong_upstream_address @@ -0,0 +1,8 @@ +stream_info { + upstream_local_address { + socket_address { + address: "s" + named_port: "" + } + } +} diff --git a/test/common/router/route_corpus/clusterfuzz-testcase-minimized-route_fuzz_test-6160522024124416 b/test/common/router/route_corpus/clusterfuzz-testcase-minimized-route_fuzz_test-6160522024124416 new file mode 100644 index 000000000000..4ebb1b44c22e --- /dev/null +++ b/test/common/router/route_corpus/clusterfuzz-testcase-minimized-route_fuzz_test-6160522024124416 @@ -0,0 +1 @@ +config { e: " " virtual_hosts { name: " " domains: " " matcher { matcher_list { matchers { predicate { single_predicate { input { name: " " typed_config { type_url: " /envoy.type.matcher.v3.HttpRequestHeaderMatchInput" e: " " } } value_match { custom { name: " " typed_config { l: " " e: " " } } } } } on_match { action { name: " " typed_config { type_url: "/envoy.config.route.v3.Route" value: "\n\002b\000\032 " } } } } } on_no_match { action { name: " " typed_config { type_url: "/envoy.config.route.v3.Route" value: "\n\002b\000\032 " } } } } } } e: 4 \ No newline at end of file diff --git a/test/common/router/route_corpus/testcase-6001880544444416 b/test/common/router/route_corpus/testcase-6001880544444416 new file mode 100644 index 000000000000..657bdff08b77 --- /dev/null +++ b/test/common/router/route_corpus/testcase-6001880544444416 @@ -0,0 +1,29 @@ +config { + virtual_hosts { + name: " " + domains: "" + matcher { + matcher_list { + matchers { + on_match { + action { + name: "route" + typed_config { + type_url: "type.googleapis.com/envoy.config.route.v3.Route" + } + } + } + } + } + on_no_match { + action { + name: "route" + typed_config { + type_url: "type.googleapis.com/envoy.config.route.v3.Route" + } + } + } + } + } +} +random_value: 8388608 diff --git a/test/common/router/route_fuzz_test.cc b/test/common/router/route_fuzz_test.cc index 93d545f9b58e..184795a864d6 100644 --- a/test/common/router/route_fuzz_test.cc +++ b/test/common/router/route_fuzz_test.cc @@ -140,6 +140,10 @@ DEFINE_PROTO_FUZZER(const test::common::router::RouteTestCase& input) { static NiceMock stream_info; static NiceMock factory_context; static ScopedInjectableLoader engine(std::make_unique()); + static ScopedInjectableLoader tls_inject( + std::make_unique()); + static ScopedInjectableLoader api_inject(std::make_unique()); + try { if (!validateConfig(input)) { return; diff --git a/test/common/runtime/runtime_impl_test.cc b/test/common/runtime/runtime_impl_test.cc index e8913f451a0a..aabbd377ba4c 100644 --- a/test/common/runtime/runtime_impl_test.cc +++ b/test/common/runtime/runtime_impl_test.cc @@ -29,7 +29,7 @@ #include "gtest/gtest.h" #ifdef ENVOY_ENABLE_QUIC -#include "source/common/quic/envoy_quic_utils.h" +#include "quiche/common/platform/api/quiche_flags.h" #endif using testing::_; @@ -55,6 +55,7 @@ class LoaderImplTest : public testing::Test { Invoke([this](absl::string_view path, uint32_t, Filesystem::Watcher::OnChangedCb cb) { EXPECT_EQ(path, expected_watch_root_); on_changed_cbs_.emplace_back(cb); + return absl::OkStatus(); })); return mock_watcher; })); @@ -94,8 +95,10 @@ class DiskLoaderImplTest : public LoaderImplTest { envoy::config::bootstrap::v3::LayeredRuntime layered_runtime; Config::translateRuntime(runtime, layered_runtime); + absl::Status creation_status; loader_ = std::make_unique(dispatcher_, tls_, layered_runtime, local_info_, store_, - generator_, validation_visitor_, *api_); + generator_, validation_visitor_, *api_, creation_status); + THROW_IF_NOT_OK(creation_status); } void write(const std::string& path, const std::string& value) { @@ -287,7 +290,7 @@ TEST_F(DiskLoaderImplTest, GetLayers) { EXPECT_NE(nullptr, dynamic_cast(layers.back().get())); EXPECT_TRUE(layers[3]->values().empty()); - loader_->mergeValues({{"foo", "bar"}}); + ASSERT_TRUE(loader_->mergeValues({{"foo", "bar"}}).ok()); // The old snapshot and its layers should have been invalidated. Refetch. const auto& new_layers = loader_->snapshot().getLayers(); EXPECT_EQ("bar", new_layers[3]->values().find("foo")->second.raw_string_value_); @@ -328,6 +331,19 @@ TEST_F(DiskLoaderImplTest, OverrideFolderDoesNotExist) { EXPECT_EQ(1, store_.counter("runtime.override_dir_not_exists").value()); } +TEST_F(DiskLoaderImplTest, FileDoesNotExist) { + EXPECT_CALL(dispatcher_, createFilesystemWatcher_()).WillRepeatedly(InvokeWithoutArgs([] { + Filesystem::MockWatcher* mock_watcher = new NiceMock(); + EXPECT_CALL(*mock_watcher, addWatch(_, Filesystem::Watcher::Events::MovedTo, _)) + .WillRepeatedly(Return(absl::InvalidArgumentError("file does not exist"))); + return mock_watcher; + })); + + EXPECT_THROW_WITH_MESSAGE( + run("test/common/runtime/test_data/current", "envoy_override_does_not_exist"), EnvoyException, + "file does not exist"); +} + TEST_F(DiskLoaderImplTest, PercentHandling) { setup(); run("test/common/runtime/test_data/current", "envoy_override"); @@ -336,7 +352,7 @@ TEST_F(DiskLoaderImplTest, PercentHandling) { // Smoke test integer value of 0, should be interpreted as 0% { - loader_->mergeValues({{"foo", "0"}}); + ASSERT_TRUE(loader_->mergeValues({{"foo", "0"}}).ok()); EXPECT_FALSE(loader_->snapshot().featureEnabled("foo", default_value, 0)); EXPECT_FALSE(loader_->snapshot().featureEnabled("foo", default_value, 5)); @@ -344,7 +360,7 @@ TEST_F(DiskLoaderImplTest, PercentHandling) { // Smoke test integer value of 5, should be interpreted as 5% { - loader_->mergeValues({{"foo", "5"}}); + ASSERT_TRUE(loader_->mergeValues({{"foo", "5"}}).ok()); EXPECT_TRUE(loader_->snapshot().featureEnabled("foo", default_value, 0)); EXPECT_TRUE(loader_->snapshot().featureEnabled("foo", default_value, 4)); EXPECT_FALSE(loader_->snapshot().featureEnabled("foo", default_value, 5)); @@ -361,7 +377,7 @@ TEST_F(DiskLoaderImplTest, PercentHandling) { // not the uint32 conversion is handled properly. uint64_t high_value = 1ULL << 60; std::string high_value_str = std::to_string(high_value); - loader_->mergeValues({{"foo", high_value_str}}); + ASSERT_TRUE(loader_->mergeValues({{"foo", high_value_str}}).ok()); EXPECT_TRUE(loader_->snapshot().featureEnabled("foo", default_value, 0)); EXPECT_TRUE(loader_->snapshot().featureEnabled("foo", default_value, 50)); EXPECT_TRUE(loader_->snapshot().featureEnabled("foo", default_value, 100)); @@ -375,32 +391,32 @@ void testNewOverrides(Loader& loader, Stats::TestUtil::TestStore& store) { store.gauge("runtime.admin_overrides_active", Stats::Gauge::ImportMode::NeverImport); // New string. - loader.mergeValues({{"foo", "bar"}}); + ASSERT_TRUE(loader.mergeValues({{"foo", "bar"}}).ok()); EXPECT_EQ("bar", loader.snapshot().get("foo").value().get()); EXPECT_EQ(1, admin_overrides_active.value()); // Remove new string. - loader.mergeValues({{"foo", ""}}); + ASSERT_TRUE(loader.mergeValues({{"foo", ""}}).ok()); EXPECT_FALSE(loader.snapshot().get("foo").has_value()); EXPECT_EQ(0, admin_overrides_active.value()); // New integer. - loader.mergeValues({{"baz", "42"}}); + ASSERT_TRUE(loader.mergeValues({{"baz", "42"}}).ok()); EXPECT_EQ(42, loader.snapshot().getInteger("baz", 0)); EXPECT_EQ(1, admin_overrides_active.value()); // Remove new integer. - loader.mergeValues({{"baz", ""}}); + ASSERT_TRUE(loader.mergeValues({{"baz", ""}}).ok()); EXPECT_EQ(0, loader.snapshot().getInteger("baz", 0)); EXPECT_EQ(0, admin_overrides_active.value()); // New double. - loader.mergeValues({{"beep", "42.1"}}); + ASSERT_TRUE(loader.mergeValues({{"beep", "42.1"}}).ok()); EXPECT_EQ(42.1, loader.snapshot().getDouble("beep", 1.2)); EXPECT_EQ(1, admin_overrides_active.value()); // Remove new double. - loader.mergeValues({{"beep", ""}}); + ASSERT_TRUE(loader.mergeValues({{"beep", ""}}).ok()); EXPECT_EQ(1.2, loader.snapshot().getDouble("beep", 1.2)); EXPECT_EQ(0, admin_overrides_active.value()); } @@ -413,42 +429,42 @@ TEST_F(DiskLoaderImplTest, MergeValues) { store_.gauge("runtime.admin_overrides_active", Stats::Gauge::ImportMode::NeverImport); // Override string - loader_->mergeValues({{"file2", "new world"}}); + ASSERT_TRUE(loader_->mergeValues({{"file2", "new world"}}).ok()); EXPECT_EQ("new world", loader_->snapshot().get("file2").value().get()); EXPECT_EQ(1, admin_overrides_active.value()); // Remove overridden string - loader_->mergeValues({{"file2", ""}}); + ASSERT_TRUE(loader_->mergeValues({{"file2", ""}}).ok()); EXPECT_EQ("world", loader_->snapshot().get("file2").value().get()); EXPECT_EQ(0, admin_overrides_active.value()); // Override integer - loader_->mergeValues({{"file3", "42"}}); + ASSERT_TRUE(loader_->mergeValues({{"file3", "42"}}).ok()); EXPECT_EQ(42, loader_->snapshot().getInteger("file3", 1)); EXPECT_EQ(1, admin_overrides_active.value()); // Remove overridden integer - loader_->mergeValues({{"file3", ""}}); + ASSERT_TRUE(loader_->mergeValues({{"file3", ""}}).ok()); EXPECT_EQ(2, loader_->snapshot().getInteger("file3", 1)); EXPECT_EQ(0, admin_overrides_active.value()); // Override double - loader_->mergeValues({{"file_with_double", "42.1"}}); + ASSERT_TRUE(loader_->mergeValues({{"file_with_double", "42.1"}}).ok()); EXPECT_EQ(42.1, loader_->snapshot().getDouble("file_with_double", 1.1)); EXPECT_EQ(1, admin_overrides_active.value()); // Remove overridden double - loader_->mergeValues({{"file_with_double", ""}}); + ASSERT_TRUE(loader_->mergeValues({{"file_with_double", ""}}).ok()); EXPECT_EQ(23.2, loader_->snapshot().getDouble("file_with_double", 1.1)); EXPECT_EQ(0, admin_overrides_active.value()); // Override override string - loader_->mergeValues({{"file1", "hello overridden override"}}); + ASSERT_TRUE(loader_->mergeValues({{"file1", "hello overridden override"}}).ok()); EXPECT_EQ("hello overridden override", loader_->snapshot().get("file1").value().get()); EXPECT_EQ(1, admin_overrides_active.value()); // Remove overridden override string - loader_->mergeValues({{"file1", ""}}); + ASSERT_TRUE(loader_->mergeValues({{"file1", ""}}).ok()); EXPECT_EQ("hello override", loader_->snapshot().get("file1").value().get()); EXPECT_EQ(0, admin_overrides_active.value()); EXPECT_EQ(0, store_.gauge("runtime.admin_overrides_active", Stats::Gauge::ImportMode::NeverImport) @@ -472,14 +488,14 @@ TEST_F(DiskLoaderImplTest, LayersOverride) { EXPECT_EQ("thing", loader_->snapshot().get("some").value().get()); EXPECT_EQ("thang", loader_->snapshot().get("other").value().get()); // Admin overrides disk and bootstrap. - loader_->mergeValues({{"file2", "pluto"}, {"some", "day soon"}}); + ASSERT_TRUE(loader_->mergeValues({{"file2", "pluto"}, {"some", "day soon"}}).ok()); EXPECT_EQ("pluto", loader_->snapshot().get("file2").value().get()); EXPECT_EQ("day soon", loader_->snapshot().get("some").value().get()); EXPECT_EQ("thang", loader_->snapshot().get("other").value().get()); // Admin overrides stick over filesystem updates. EXPECT_EQ("Layer cake", loader_->snapshot().get("file14").value().get()); EXPECT_EQ("Cheese cake", loader_->snapshot().get("file15").value().get()); - loader_->mergeValues({{"file14", "Mega layer cake"}}); + ASSERT_TRUE(loader_->mergeValues({{"file14", "Mega layer cake"}}).ok()); EXPECT_EQ("Mega layer cake", loader_->snapshot().get("file14").value().get()); EXPECT_EQ("Cheese cake", loader_->snapshot().get("file15").value().get()); write("test/common/runtime/test_data/current/envoy/file14", "Sad cake"); @@ -503,11 +519,12 @@ TEST_F(DiskLoaderImplTest, MultipleAdminLayersFail) { layer->set_name("admin_1"); layer->mutable_admin_layer(); } - EXPECT_THROW_WITH_MESSAGE( + absl::Status creation_status; + auto loader = std::make_unique(dispatcher_, tls_, layered_runtime, local_info_, store_, - generator_, validation_visitor_, *api_), - EnvoyException, - "Too many admin layers specified in LayeredRuntime, at most one may be specified"); + generator_, validation_visitor_, *api_, creation_status); + EXPECT_EQ(creation_status.message(), + "Too many admin layers specified in LayeredRuntime, at most one may be specified"); } class StaticLoaderImplTest : public LoaderImplTest { @@ -525,8 +542,10 @@ class StaticLoaderImplTest : public LoaderImplTest { layer->set_name("admin"); layer->mutable_admin_layer(); } + absl::Status creation_status; loader_ = std::make_unique(dispatcher_, tls_, layered_runtime, local_info_, store_, - generator_, validation_visitor_, *api_); + generator_, validation_visitor_, *api_, creation_status); + THROW_IF_NOT_OK(creation_status); } ProtobufWkt::Struct base_; @@ -544,13 +563,13 @@ TEST_F(StaticLoaderImplTest, All) { #ifdef ENVOY_ENABLE_QUIC TEST_F(StaticLoaderImplTest, QuicheReloadableFlags) { - EXPECT_TRUE(GetQuicReloadableFlag(quic_testonly_default_true)); - EXPECT_FALSE(GetQuicReloadableFlag(quic_testonly_default_false)); + EXPECT_TRUE(GetQuicheReloadableFlag(quic_testonly_default_true)); + EXPECT_FALSE(GetQuicheReloadableFlag(quic_testonly_default_false)); - SetQuicheReloadableFlag(spdy, quic_testonly_default_true, false); + SetQuicheReloadableFlag(quic_testonly_default_true, false); - EXPECT_FALSE(GetQuicReloadableFlag(quic_testonly_default_true)); - EXPECT_FALSE(GetQuicReloadableFlag(quic_testonly_default_false)); + EXPECT_FALSE(GetQuicheReloadableFlag(quic_testonly_default_true)); + EXPECT_FALSE(GetQuicheReloadableFlag(quic_testonly_default_false)); // Test that Quiche flags can be overwritten via Envoy runtime config. base_ = TestUtility::parseYaml( @@ -558,8 +577,8 @@ TEST_F(StaticLoaderImplTest, QuicheReloadableFlags) { "true"); setup(); - EXPECT_TRUE(GetQuicReloadableFlag(quic_testonly_default_true)); - EXPECT_FALSE(GetQuicReloadableFlag(quic_testonly_default_false)); + EXPECT_TRUE(GetQuicheReloadableFlag(quic_testonly_default_true)); + EXPECT_FALSE(GetQuicheReloadableFlag(quic_testonly_default_false)); // Test that Quiche flags can be overwritten again. base_ = TestUtility::parseYaml( @@ -567,8 +586,8 @@ TEST_F(StaticLoaderImplTest, QuicheReloadableFlags) { "false"); setup(); - EXPECT_FALSE(GetQuicReloadableFlag(quic_testonly_default_true)); - EXPECT_FALSE(GetQuicReloadableFlag(quic_testonly_default_false)); + EXPECT_FALSE(GetQuicheReloadableFlag(quic_testonly_default_true)); + EXPECT_FALSE(GetQuicheReloadableFlag(quic_testonly_default_false)); } #endif @@ -807,7 +826,7 @@ TEST_F(StaticLoaderImplTest, RuntimeFromNonWorkerThreads) { setup(); // Set up foo -> bar - loader_->mergeValues({{"foo", "bar"}}); + ASSERT_TRUE(loader_->mergeValues({{"foo", "bar"}}).ok()); EXPECT_EQ("bar", loader_->threadsafeSnapshot()->get("foo").value().get()); const Snapshot* original_snapshot_pointer = loader_->threadsafeSnapshot().get(); @@ -844,7 +863,7 @@ TEST_F(StaticLoaderImplTest, RuntimeFromNonWorkerThreads) { if (!read_bar) { foo_read.wait(mutex); } - loader_->mergeValues({{"foo", "eep"}}); + ASSERT_TRUE(loader_->mergeValues({{"foo", "eep"}}).ok()); updated_eep = true; } @@ -875,21 +894,23 @@ class DiskLayerTest : public testing::Test { }; TEST_F(DiskLayerTest, IllegalPath) { + absl::Status creation_status; #ifdef WIN32 - EXPECT_THROW_WITH_MESSAGE(DiskLayer("test", R"EOF(\\.\)EOF", *api_), EnvoyException, - R"EOF(Invalid path: \\.\)EOF"); + DiskLayer layer("test", R"EOF(\\.\)EOF", *api_, creation_status); + EXPECT_EQ(creation_status.message(), R"EOF(Invalid path: \\.\)EOF"); #else - EXPECT_THROW_WITH_MESSAGE(DiskLayer("test", "/dev", *api_), EnvoyException, "Invalid path: /dev"); + DiskLayer layer("test", "/dev", *api_, creation_status); + EXPECT_EQ(creation_status.message(), "Invalid path: /dev"); #endif } // Validate that we catch recursion that goes too deep in the runtime filesystem // walk. TEST_F(DiskLayerTest, Loop) { - EXPECT_THROW_WITH_MESSAGE( - DiskLayer("test", TestEnvironment::temporaryPath("test/common/runtime/test_data/loop"), - *api_), - EnvoyException, "Walk recursion depth exceeded 16"); + absl::Status creation_status; + DiskLayer layer("test", TestEnvironment::temporaryPath("test/common/runtime/test_data/loop"), + *api_, creation_status); + EXPECT_EQ(creation_status.message(), "Walk recursion depth exceeded 16"); } TEST(NoRuntime, FeatureEnabled) { @@ -936,8 +957,10 @@ class RtdsLoaderImplTest : public LoaderImplTest { rtds_callbacks_.push_back(&callbacks); return ret; })); + absl::Status creation_status; loader_ = std::make_unique(dispatcher_, tls_, config, local_info_, store_, - generator_, validation_visitor_, *api_); + generator_, validation_visitor_, *api_, creation_status); + THROW_IF_NOT_OK(creation_status); loader_->initialize(cm_); for (auto* sub : rtds_subscriptions_) { EXPECT_CALL(*sub, start(_)); @@ -1271,8 +1294,9 @@ TEST_F(RtdsLoaderImplTest, BadConfigSource) { rtds_layer->mutable_rtds_config(); EXPECT_CALL(cm_, subscriptionFactory()); + absl::Status creation_status; LoaderImpl loader(dispatcher_, tls_, config, local_info_, store_, generator_, validation_visitor_, - *api_); + *api_, creation_status); EXPECT_THROW_WITH_MESSAGE(loader.initialize(cm_), EnvoyException, "bad config"); } diff --git a/test/common/secret/sds_api_test.cc b/test/common/secret/sds_api_test.cc index edc3327cbfb9..8d5e933f6e46 100644 --- a/test/common/secret/sds_api_test.cc +++ b/test/common/secret/sds_api_test.cc @@ -248,6 +248,7 @@ class TlsCertificateSdsRotationApiTest : public testing::TestWithParam, .WillOnce( Invoke([this](absl::string_view, uint32_t, Filesystem::Watcher::OnChangedCb cb) { watch_cbs_.push_back(cb); + return absl::OkStatus(); })); EXPECT_CALL(filesystem_, fileReadToEnd(cert_path_)).WillOnce(Return(cert_value)); EXPECT_CALL(filesystem_, fileReadToEnd(key_path_)).WillOnce(Return(key_value)); @@ -262,6 +263,7 @@ class TlsCertificateSdsRotationApiTest : public testing::TestWithParam, .WillRepeatedly( Invoke([this](absl::string_view, uint32_t, Filesystem::Watcher::OnChangedCb cb) { watch_cbs_.push_back(cb); + return absl::OkStatus(); })); } EXPECT_TRUE( @@ -318,10 +320,12 @@ class CertificateValidationContextSdsRotationApiTest : public testing::TestWithP EXPECT_CALL(*watcher, addWatch(watch_path, Filesystem::Watcher::Events::MovedTo, _)) .WillOnce(Invoke([this](absl::string_view, uint32_t, Filesystem::Watcher::OnChangedCb cb) { watch_cbs_.push_back(cb); + return absl::OkStatus(); })); EXPECT_CALL(*watcher, addWatch(watch_path, Filesystem::Watcher::Events::MovedTo, _)) .WillOnce(Invoke([this](absl::string_view, uint32_t, Filesystem::Watcher::OnChangedCb cb) { watch_cbs_.push_back(cb); + return absl::OkStatus(); })); EXPECT_TRUE( subscription_factory_.callbacks_->onConfigUpdate(decoded_resources.refvec_, "").ok()); diff --git a/test/common/stats/tag_extractor_impl_test.cc b/test/common/stats/tag_extractor_impl_test.cc index 43e74080a685..747f9daca0c1 100644 --- a/test/common/stats/tag_extractor_impl_test.cc +++ b/test/common/stats/tag_extractor_impl_test.cc @@ -450,6 +450,17 @@ TEST(TagExtractorTest, DefaultTagExtractors) { rbac_prefix.name_ = tag_names.RBAC_PREFIX; rbac_prefix.value_ = "my_rbac_prefix"; regex_tester.testRegex("my_rbac_prefix.rbac.allowed", "rbac.allowed", {rbac_prefix}); + + // RBAC HTTP Filter Prefix + Tag rbac_http_hcm_prefix; + rbac_http_hcm_prefix.name_ = tag_names.HTTP_CONN_MANAGER_PREFIX; + rbac_http_hcm_prefix.value_ = "hcm_prefix"; + + Tag rbac_http_prefix; + rbac_http_prefix.name_ = tag_names.RBAC_HTTP_PREFIX; + rbac_http_prefix.value_ = "prefix"; + regex_tester.testRegex("http.hcm_prefix.rbac.prefix.allowed", "http.rbac.allowed", + {rbac_http_hcm_prefix, rbac_http_prefix}); } TEST(TagExtractorTest, ExtAuthzTagExtractors) { diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index 36c0ea4bf253..23a3c599f0f3 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -2161,9 +2161,11 @@ class HistogramParameterisedTest : public HistogramTest, layer->set_name("admin"); layer->mutable_admin_layer(); } - loader_ = - std::make_unique(dispatcher_, tls_, layered_runtime, local_info_, - *store_, generator_, validation_visitor_, *api_); + absl::Status creation_status; + loader_ = std::make_unique(dispatcher_, tls_, layered_runtime, local_info_, + *store_, generator_, validation_visitor_, *api_, + creation_status); + THROW_IF_NOT_OK(creation_status); } Event::MockDispatcher dispatcher_; diff --git a/test/common/tls/cert_validator/BUILD b/test/common/tls/cert_validator/BUILD index f0b243a980d3..76c9d63dc831 100644 --- a/test/common/tls/cert_validator/BUILD +++ b/test/common/tls/cert_validator/BUILD @@ -21,6 +21,7 @@ envoy_cc_test( "//source/common/tls/cert_validator:cert_validator_lib", "//test/common/tls:ssl_test_utils", "//test/common/tls/cert_validator:test_common", + "//test/mocks/server:server_factory_context_mocks", "//test/test_common:environment_lib", "//test/test_common:test_runtime_lib", ], @@ -56,6 +57,7 @@ envoy_cc_test( deps = [ "//source/common/protobuf:utility_lib", "//source/common/tls/cert_validator:cert_validator_lib", + "//test/mocks/server:server_factory_context_mocks", "//test/test_common:utility_lib", "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", ], diff --git a/test/common/tls/cert_validator/default_validator_integration_test.cc b/test/common/tls/cert_validator/default_validator_integration_test.cc index a76e87ed04fd..ac5b3232863f 100644 --- a/test/common/tls/cert_validator/default_validator_integration_test.cc +++ b/test/common/tls/cert_validator/default_validator_integration_test.cc @@ -14,8 +14,8 @@ namespace Ssl { void SslCertValidatorIntegrationTest::initialize() { HttpIntegrationTest::initialize(); - context_manager_ = - std::make_unique(timeSystem()); + context_manager_ = std::make_unique( + factory_context_.serverFactoryContext()); registerTestServerPorts({"http"}); test_server_->counter(listenerStatPrefix("ssl.fail_verify_error"))->reset(); diff --git a/test/common/tls/cert_validator/default_validator_test.cc b/test/common/tls/cert_validator/default_validator_test.cc index 0fa44d2d6053..aff9ed58697d 100644 --- a/test/common/tls/cert_validator/default_validator_test.cc +++ b/test/common/tls/cert_validator/default_validator_test.cc @@ -6,6 +6,7 @@ #include "test/common/tls/cert_validator/test_common.h" #include "test/common/tls/ssl_test_utility.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/environment.h" #include "test/test_common/test_runtime.h" #include "test/test_common/utility.h" @@ -34,28 +35,34 @@ TEST(DefaultCertValidatorTest, TestVerifySubjectAltNameDNSMatched) { } TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameDNSMatched) { + NiceMock context; + bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/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_DNS, matcher)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); EXPECT_TRUE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameIncorrectTypeMatched) { + NiceMock context; + bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/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)}); + SanMatcherPtr{std::make_unique(GEN_URI, matcher, context)}); EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameWildcardDNSMatched) { + NiceMock context; + bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir " "}}/test/common/tls/test_data/san_multiple_dns_cert.pem")); @@ -63,11 +70,13 @@ TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameWildcardDNSMatched) { matcher.set_exact("api.example.com"); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); EXPECT_TRUE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } TEST(DefaultCertValidatorTest, TestMultiLevelMatch) { + NiceMock context; + // san_multiple_dns_cert matches *.example.com bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir " @@ -76,7 +85,7 @@ TEST(DefaultCertValidatorTest, TestMultiLevelMatch) { matcher.set_exact("foo.api.example.com"); std::vector subject_alt_name_matchers; subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } @@ -98,13 +107,15 @@ TEST(DefaultCertValidatorTest, TestVerifySubjectAltMultiDomain) { } TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameURIMatched) { + NiceMock context; + bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/san_uri_cert.pem")); envoy::type::matcher::v3::StringMatcher 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)}); + SanMatcherPtr{std::make_unique(GEN_URI, matcher, context)}); EXPECT_TRUE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } @@ -117,37 +128,40 @@ TEST(DefaultCertValidatorTest, TestVerifySubjectAltNameNotMatched) { } TEST(DefaultCertValidatorTest, TestMatchSubjectAltNameNotMatched) { + NiceMock context; + bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/san_dns_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)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_IPADD, matcher)}); + SanMatcherPtr{std::make_unique(GEN_IPADD, matcher, context)}); subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_URI, matcher)}); + SanMatcherPtr{std::make_unique(GEN_URI, matcher, context)}); subject_alt_name_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_EMAIL, matcher)}); + SanMatcherPtr{std::make_unique(GEN_EMAIL, matcher, context)}); EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } TEST(DefaultCertValidatorTest, TestCertificateVerificationWithSANMatcher) { + NiceMock context; Stats::TestUtil::TestStore test_store; SslStats stats = generateSslStats(*test_store.rootScope()); // Create the default validator object. auto default_validator = std::make_unique( - /*CertificateValidationContextConfig=*/nullptr, stats, - Event::GlobalTimeSystem().timeSystem()); + /*CertificateValidationContextConfig=*/nullptr, stats, context); bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/san_dns_cert.pem")); envoy::type::matcher::v3::StringMatcher 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)}); + san_matchers.push_back( + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); // Verify the certificate with correct SAN regex matcher. EXPECT_EQ(default_validator->verifyCertificate(cert.get(), /*verify_san_list=*/{}, san_matchers, nullptr, nullptr), @@ -157,7 +171,7 @@ TEST(DefaultCertValidatorTest, TestCertificateVerificationWithSANMatcher) { matcher.MergeFrom(TestUtility::createExactMatcher("hello.example.com")); std::vector invalid_san_matchers; invalid_san_matchers.push_back( - SanMatcherPtr{std::make_unique(GEN_DNS, matcher)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); std::string error; // Verify the certificate with incorrect SAN exact matcher. EXPECT_EQ(default_validator->verifyCertificate(cert.get(), /*verify_san_list=*/{}, @@ -167,13 +181,13 @@ TEST(DefaultCertValidatorTest, TestCertificateVerificationWithSANMatcher) { } TEST(DefaultCertValidatorTest, TestCertificateVerificationWithNoValidationContext) { + NiceMock context; Stats::TestUtil::TestStore test_store; SslStats stats = generateSslStats(*test_store.rootScope()); // Create the default validator object. auto default_validator = std::make_unique( - /*CertificateValidationContextConfig=*/nullptr, stats, - Event::GlobalTimeSystem().timeSystem()); + /*CertificateValidationContextConfig=*/nullptr, stats, context); EXPECT_EQ(default_validator->verifyCertificate(/*cert=*/nullptr, /*verify_san_list=*/{}, /*subject_alt_name_matchers=*/{}, nullptr, @@ -191,13 +205,13 @@ TEST(DefaultCertValidatorTest, TestCertificateVerificationWithNoValidationContex } TEST(DefaultCertValidatorTest, TestCertificateVerificationWithEmptyCertChain) { + NiceMock context; Stats::TestUtil::TestStore test_store; SslStats stats = generateSslStats(*test_store.rootScope()); // Create the default validator object. auto default_validator = std::make_unique( - /*CertificateValidationContextConfig=*/nullptr, stats, - Event::GlobalTimeSystem().timeSystem()); + /*CertificateValidationContextConfig=*/nullptr, stats, context); SSLContextPtr ssl_ctx = SSL_CTX_new(TLS_method()); bssl::UniquePtr cert_chain(sk_X509_new_null()); @@ -210,18 +224,20 @@ TEST(DefaultCertValidatorTest, TestCertificateVerificationWithEmptyCertChain) { } TEST(DefaultCertValidatorTest, NoSanInCert) { + NiceMock context; + bssl::UniquePtr cert = readCertFromFile( TestEnvironment::substitute("{{ test_rundir }}/test/common/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)}); + SanMatcherPtr{std::make_unique(GEN_DNS, matcher, context)}); EXPECT_FALSE(DefaultCertValidator::matchSubjectAltName(cert.get(), subject_alt_name_matchers)); } TEST(DefaultCertValidatorTest, WithVerifyDepth) { - + NiceMock context; Stats::TestUtil::TestStore test_store; SslStats stats = generateSslStats(*test_store.rootScope()); envoy::config::core::v3::TypedExtensionConfig typed_conf; @@ -245,8 +261,8 @@ TEST(DefaultCertValidatorTest, WithVerifyDepth) { std::make_unique(typed_conf, false, san_matchers, ca_cert_str, 2); auto default_validator = - std::make_unique( - test_config.get(), stats, Event::GlobalTimeSystem().timeSystem()); + std::make_unique(test_config.get(), + stats, context); STACK_OF(X509)* intermediates = cert_chain.get(); SSLContextPtr ssl_ctx = SSL_CTX_new(TLS_method()); @@ -266,7 +282,7 @@ TEST(DefaultCertValidatorTest, WithVerifyDepth) { test_config = std::make_unique(typed_conf, false, san_matchers, ca_cert_str); default_validator = std::make_unique( - test_config.get(), stats, Event::GlobalTimeSystem().timeSystem()); + test_config.get(), stats, context); // Re-initialize context ssl_ctx = SSL_CTX_new(TLS_method()); @@ -321,6 +337,8 @@ class MockCertificateValidationContextConfig : public Ssl::CertificateValidation }; TEST(DefaultCertValidatorTest, TestUnexpectedSanMatcherType) { + NiceMock context; + auto mock_context_config = std::make_unique(); EXPECT_CALL(*mock_context_config.get(), trustChainVerification()) .WillRepeatedly(testing::Return(envoy::extensions::transport_sockets::tls::v3:: @@ -329,14 +347,16 @@ TEST(DefaultCertValidatorTest, TestUnexpectedSanMatcherType) { std::vector(); Stats::TestUtil::TestStore store; auto ssl_stats = generateSslStats(*store.rootScope()); - auto validator = std::make_unique(mock_context_config.get(), ssl_stats, - Event::GlobalTimeSystem().timeSystem()); + auto validator = + std::make_unique(mock_context_config.get(), ssl_stats, context); auto ctx = std::vector(); EXPECT_THROW_WITH_REGEX(validator->initializeSslContexts(ctx, false), EnvoyException, "Failed to create string SAN matcher of type.*"); } TEST(DefaultCertValidatorTest, TestInitializeSslContextFailure) { + NiceMock context; + auto mock_context_config = std::make_unique( "-----BEGIN CERTIFICATE-----\nincomplete payload"); EXPECT_CALL(*mock_context_config.get(), trustChainVerification()) @@ -345,8 +365,8 @@ TEST(DefaultCertValidatorTest, TestInitializeSslContextFailure) { Stats::TestUtil::TestStore store; auto ssl_stats = generateSslStats(*store.rootScope()); - auto validator = std::make_unique(mock_context_config.get(), ssl_stats, - Event::GlobalTimeSystem().timeSystem()); + auto validator = + std::make_unique(mock_context_config.get(), ssl_stats, context); auto ctx = std::vector(); EXPECT_THROW_WITH_REGEX(validator->initializeSslContexts(ctx, false), EnvoyException, "Failed to load trusted CA certificates from.*"); diff --git a/test/common/tls/cert_validator/san_matcher_test.cc b/test/common/tls/cert_validator/san_matcher_test.cc index 11f67e467119..3b330866a379 100644 --- a/test/common/tls/cert_validator/san_matcher_test.cc +++ b/test/common/tls/cert_validator/san_matcher_test.cc @@ -4,6 +4,7 @@ #include "source/common/protobuf/utility.h" #include "source/common/tls/cert_validator/san_matcher.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/utility.h" #include "gmock/gmock.h" @@ -16,6 +17,8 @@ namespace Tls { // Verify that we get a valid string san matcher for all valid san types. TEST(SanMatcherConfigTest, TestValidSanType) { + NiceMock context; + // 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; @@ -29,9 +32,9 @@ TEST(SanMatcherConfigTest, TestValidSanType) { san_matcher.set_san_type(san_type); if (san_type == envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher:: SAN_TYPE_UNSPECIFIED) { - EXPECT_DEATH(createStringSanMatcher(san_matcher), "unhandled value"); + EXPECT_DEATH(createStringSanMatcher(san_matcher, context), "unhandled value"); } else { - const SanMatcherPtr matcher = createStringSanMatcher(san_matcher); + const SanMatcherPtr matcher = createStringSanMatcher(san_matcher, context); EXPECT_NE(matcher.get(), nullptr); // Verify that the message is valid. TestUtility::validate(san_matcher); @@ -40,6 +43,7 @@ TEST(SanMatcherConfigTest, TestValidSanType) { } TEST(SanMatcherConfigTest, UnspecifiedSanType) { + NiceMock context; envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher san_matcher; san_matcher.mutable_matcher()->set_exact("foo.example"); // Do not set san_type @@ -54,7 +58,7 @@ TEST(SanMatcherConfigTest, UnspecifiedSanType) { static_cast( static_cast(123)); san_matcher.set_san_type(san_type); - EXPECT_EQ(createStringSanMatcher(san_matcher), nullptr); + EXPECT_EQ(createStringSanMatcher(san_matcher, context), nullptr); } } // namespace Tls diff --git a/test/common/tls/cert_validator/timed_cert_validator.h b/test/common/tls/cert_validator/timed_cert_validator.h index 45a57fc5eda2..2ea320d87af2 100644 --- a/test/common/tls/cert_validator/timed_cert_validator.h +++ b/test/common/tls/cert_validator/timed_cert_validator.h @@ -18,8 +18,9 @@ class TimedCertValidator : public DefaultCertValidator { public: TimedCertValidator(std::chrono::milliseconds validation_time_out_ms, const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, - TimeSource& time_source, absl::optional expected_host_name) - : DefaultCertValidator(config, stats, time_source), + Server::Configuration::CommonFactoryContext& context, + absl::optional expected_host_name) + : DefaultCertValidator(config, stats, context), validation_time_out_ms_(validation_time_out_ms), expected_host_name_(expected_host_name) {} ValidationResults @@ -49,10 +50,11 @@ class TimedCertValidator : public DefaultCertValidator { class TimedCertValidatorFactory : public CertValidatorFactory { public: - CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, - SslStats& stats, TimeSource& time_source) override { + CertValidatorPtr + createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, + Server::Configuration::CommonFactoryContext& context) override { auto validator = std::make_unique(validation_time_out_ms_, config, stats, - time_source, expected_host_name_); + context, expected_host_name_); if (expected_peer_address_.has_value()) { validator->setExpectedPeerAddress(expected_peer_address_.value()); } diff --git a/test/common/tls/context_impl_test.cc b/test/common/tls/context_impl_test.cc index dadbae2c6f7b..faa1414733d7 100644 --- a/test/common/tls/context_impl_test.cc +++ b/test/common/tls/context_impl_test.cc @@ -118,8 +118,8 @@ class SslContextImplTest : public SslCertsTest { } protected: - Event::SimulatedTimeSystem time_system_; - ContextManagerImpl manager_{time_system_}; + NiceMock server_factory_context_; + ContextManagerImpl manager_{server_factory_context_}; }; TEST_F(SslContextImplTest, TestCipherSuites) { @@ -1157,8 +1157,8 @@ class ClientContextConfigImplTest : public SslCertsTest { }}; } - Event::SimulatedTimeSystem time_system_; - ContextManagerImpl manager_{time_system_}; + NiceMock server_factory_context_; + ContextManagerImpl manager_{server_factory_context_}; }; // Validate that empty SNI (according to C string rules) fails config validation. @@ -1288,8 +1288,7 @@ TEST_F(ClientContextConfigImplTest, RSA3072Cert) { 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); + ContextManagerImpl manager(server_factory_context_); Stats::IsolatedStoreImpl store; auto context = manager_.createSslClientContext(*store.rootScope(), client_context_config); auto cleanup = cleanUpHelper(context); @@ -1765,7 +1764,10 @@ TEST_F(ClientContextConfigImplTest, MissingStaticCertificateValidationContext) { "Unknown static certificate validation context: missing"); } -class ServerContextConfigImplTest : public SslCertsTest {}; +class ServerContextConfigImplTest : public SslCertsTest { +public: + NiceMock server_factory_context_; +}; // Multiple TLS certificates are supported. TEST_F(ServerContextConfigImplTest, MultipleTlsCertificates) { @@ -1896,8 +1898,7 @@ TEST_F(ServerContextConfigImplTest, TlsCertificateNonEmpty) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; tls_context.mutable_common_tls_context()->add_tls_certificates(); ServerContextConfigImpl client_context_config(tls_context, factory_context_); - Event::SimulatedTimeSystem time_system; - ContextManagerImpl manager(time_system); + ContextManagerImpl manager(server_factory_context_); Stats::IsolatedStoreImpl store; EXPECT_THROW_WITH_MESSAGE( Envoy::Ssl::ServerContextSharedPtr server_ctx(manager.createSslServerContext( @@ -2002,8 +2003,7 @@ TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadFailureNoMethod) { NiceMock private_key_method_manager; auto private_key_method_provider_ptr = std::make_shared>(); - Event::SimulatedTimeSystem time_system; - ContextManagerImpl manager(time_system); + ContextManagerImpl manager(server_factory_context_); EXPECT_CALL(factory_context_, sslContextManager()).WillOnce(ReturnRef(context_manager)); EXPECT_CALL(context_manager, privateKeyMethodManager()) .WillOnce(ReturnRef(private_key_method_manager)); @@ -2201,8 +2201,8 @@ TEST_F(ServerContextConfigImplTest, Pkcs12LoadFailureBothPkcs12AndCertChain) { class TestContextImpl : public ContextImpl { public: TestContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, - TimeSource& time_source) - : ContextImpl(scope, config, time_source, nullptr), pool_(scope.symbolTable()), + Server::Configuration::ServerFactoryContext& factory_context) + : ContextImpl(scope, config, factory_context, nullptr), pool_(scope.symbolTable()), fallback_(pool_.add("fallback")) {} void incCounter(absl::string_view name, absl::string_view value) { @@ -2220,7 +2220,7 @@ class SslContextStatsTest : public SslContextImplTest { client_context_config_ = std::make_unique(tls_context_, factory_context_); context_ = std::make_unique(*store_.rootScope(), *client_context_config_, - time_system_); + server_factory_context_); } Stats::TestUtil::TestStore store_; diff --git a/test/common/tls/handshaker_factory_test.cc b/test/common/tls/handshaker_factory_test.cc index f60113708590..e161b5eea047 100644 --- a/test/common/tls/handshaker_factory_test.cc +++ b/test/common/tls/handshaker_factory_test.cc @@ -92,8 +92,8 @@ class HandshakerFactoryImplForTest class HandshakerFactoryTest : public testing::Test { protected: HandshakerFactoryTest() - : context_manager_( - std::make_unique(time_system_)), + : context_manager_(std::make_unique( + server_factory_context_)), registered_factory_(handshaker_factory_) { scoped_runtime_.mergeValues( {{"envoy.reloadable_features.no_extension_lookup_by_name", "false"}}); @@ -111,7 +111,7 @@ class HandshakerFactoryTest : public testing::Test { return SSL_get_SSL_CTX(ssl); } - Event::GlobalTimeSystem time_system_; + NiceMock server_factory_context_; Stats::IsolatedStoreImpl stats_store_; std::unique_ptr context_manager_; HandshakerFactoryImplForTest handshaker_factory_; @@ -248,8 +248,8 @@ class HandshakerFactoryImplForDownstreamTest class HandshakerFactoryDownstreamTest : public testing::Test { protected: HandshakerFactoryDownstreamTest() - : context_manager_( - std::make_unique(time_system_)) { + : context_manager_(std::make_unique( + server_factory_context_)) { scoped_runtime_.mergeValues( {{"envoy.reloadable_features.no_extension_lookup_by_name", "false"}}); } @@ -262,7 +262,7 @@ class HandshakerFactoryDownstreamTest : public testing::Test { return SSL_get_SSL_CTX(ssl); } - Event::GlobalTimeSystem time_system_; + NiceMock server_factory_context_; Stats::IsolatedStoreImpl stats_store_; std::unique_ptr context_manager_; envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context_; diff --git a/test/common/tls/integration/BUILD b/test/common/tls/integration/BUILD index a8dc565d34cb..9ecd12407c87 100644 --- a/test/common/tls/integration/BUILD +++ b/test/common/tls/integration/BUILD @@ -3,7 +3,6 @@ load( "envoy_cc_test", "envoy_cc_test_library", "envoy_package", - "envoy_select_admin_functionality", ) licenses(["notice"]) # Apache 2 @@ -12,7 +11,16 @@ envoy_package() envoy_cc_test_library( name = "ssl_integration_test_lib", - hdrs = ["ssl_integration_test.h"], + srcs = [ + "ssl_integration_test_base.cc", + ], + hdrs = ["ssl_integration_test_base.h"], + deps = [ + "//test/common/config:dummy_config_proto_cc_proto", + "//test/integration:http_integration_lib", + "//test/mocks/secret:secret_mocks", + "//test/test_common:utility_lib", + ], ) envoy_cc_test( @@ -37,7 +45,6 @@ envoy_cc_test( "//source/extensions/transport_sockets/tls:config", "//test/common/config:dummy_config_proto_cc_proto", "//test/common/tls/cert_validator:timed_cert_validator", - "//test/integration:http_integration_lib", "//test/integration/filters:stream_info_to_headers_filter_lib", "//test/mocks/secret:secret_mocks", "//test/test_common:test_runtime_lib", @@ -45,14 +52,7 @@ envoy_cc_test( "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", - "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", - ] + envoy_select_admin_functionality([ - "//source/extensions/transport_sockets/tap:config", - "//test/extensions/common/tap:common", - "@envoy_api//envoy/config/tap/v3:pkg_cc_proto", - "@envoy_api//envoy/data/tap/v3:pkg_cc_proto", - "@envoy_api//envoy/extensions/transport_sockets/tap/v3:pkg_cc_proto", - ]), + ], ) envoy_cc_test_library( diff --git a/test/common/tls/integration/ssl_integration_test.cc b/test/common/tls/integration/ssl_integration_test.cc index 885494ce1f33..27de146cd5fe 100644 --- a/test/common/tls/integration/ssl_integration_test.cc +++ b/test/common/tls/integration/ssl_integration_test.cc @@ -1,5 +1,3 @@ -#include "ssl_integration_test.h" - #include #include @@ -19,6 +17,7 @@ #include "test/common/config/dummy_config.pb.h" #include "test/common/tls/cert_validator/timed_cert_validator.h" +#include "test/common/tls/integration/ssl_integration_test_base.h" #include "test/integration/autonomous_upstream.h" #include "test/integration/integration.h" #include "test/integration/ssl_utility.h" @@ -33,14 +32,6 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#ifdef ENVOY_ADMIN_FUNCTIONALITY -#include "envoy/config/tap/v3/common.pb.h" -#include "envoy/data/tap/v3/wrapper.pb.h" -#include "envoy/extensions/transport_sockets/tap/v3/tap.pb.h" -#include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h" -#include "test/extensions/common/tap/common.h" -#endif - using testing::StartsWith; namespace Envoy { @@ -49,155 +40,11 @@ using Extensions::TransportSockets::Tls::ContextImplPeer; namespace Ssl { -void SslIntegrationTestBase::initialize() { - config_helper_.addSslConfig(ConfigHelper::ServerSslOptions() - .setRsaCert(server_rsa_cert_) - .setRsaCertOcspStaple(server_rsa_cert_ocsp_staple_) - .setEcdsaCert(server_ecdsa_cert_) - .setEcdsaCertOcspStaple(server_ecdsa_cert_ocsp_staple_) - .setOcspStapleRequired(ocsp_staple_required_) - .setTlsV13(server_tlsv1_3_) - .setCurves(server_curves_) - .setExpectClientEcdsaCert(client_ecdsa_cert_) - .setTlsKeyLogFilter(keylog_local_, keylog_remote_, - keylog_local_negative_, - keylog_remote_negative_, keylog_path_, - keylog_multiple_ips_, version_)); - - HttpIntegrationTest::initialize(); - - context_manager_ = - std::make_unique(timeSystem()); - - registerTestServerPorts({"http"}); -} - -void SslIntegrationTestBase::TearDown() { - HttpIntegrationTest::cleanupUpstreamAndDownstream(); - codec_client_.reset(); - context_manager_.reset(); -} - -Network::ClientConnectionPtr -SslIntegrationTestBase::makeSslClientConnection(const ClientSslTransportOptions& options) { - Network::Address::InstanceConstSharedPtr address = getSslAddress(version_, lookupPort("http")); - if (debug_with_s_client_) { - const std::string s_client_cmd = TestEnvironment::substitute( - "openssl s_client -connect " + address->asString() + - " -showcerts -debug -msg -CAfile " - "{{ test_rundir }}/test/config/integration/certs/cacert.pem " - "-servername lyft.com -cert " - "{{ test_rundir }}/test/config/integration/certs/clientcert.pem " - "-key " - "{{ test_rundir }}/test/config/integration/certs/clientkey.pem ", - version_); - ENVOY_LOG_MISC(debug, "Executing {}", s_client_cmd); - RELEASE_ASSERT(::system(s_client_cmd.c_str()) == 0, ""); - } - auto client_transport_socket_factory_ptr = - createClientSslTransportSocketFactory(options, *context_manager_, *api_); - return dispatcher_->createClientConnection( - address, Network::Address::InstanceConstSharedPtr(), - client_transport_socket_factory_ptr->createTransportSocket({}, nullptr), nullptr, nullptr); -} - -void SslIntegrationTestBase::checkStats() { - const uint32_t expected_handshakes = debug_with_s_client_ ? 2 : 1; - Stats::CounterSharedPtr counter = test_server_->counter(listenerStatPrefix("ssl.handshake")); - EXPECT_EQ(expected_handshakes, counter->value()); - counter->reset(); -} - -class SslKeyLogTest : public SslIntegrationTest { +class SslIntegrationTest : public testing::TestWithParam, + public SslIntegrationTestBase { public: - void setLogPath() { - keylog_path_ = TestEnvironment::temporaryPath(TestUtility::uniqueFilename()); - } - void setLocalFilter() { - keylog_local_ = true; - keylog_remote_ = false; - keylog_local_negative_ = false; - keylog_remote_negative_ = false; - } - void setRemoteFilter() { - keylog_remote_ = true; - keylog_local_ = false; - keylog_local_negative_ = false; - keylog_remote_negative_ = false; - } - void setBothLocalAndRemoteFilter() { - keylog_local_ = true; - keylog_remote_ = true; - keylog_local_negative_ = false; - keylog_remote_negative_ = false; - } - void setNeitherLocalNorRemoteFilter() { - keylog_remote_ = false; - keylog_local_ = false; - keylog_local_negative_ = false; - keylog_remote_negative_ = false; - } - void setNegative() { - keylog_local_ = true; - keylog_remote_ = true; - keylog_local_negative_ = true; - keylog_remote_negative_ = true; - } - void setLocalNegative() { - keylog_local_ = true; - keylog_remote_ = true; - keylog_local_negative_ = false; - keylog_remote_negative_ = true; - } - void setRemoteNegative() { - keylog_local_ = true; - keylog_remote_ = true; - keylog_local_negative_ = true; - keylog_remote_negative_ = false; - } - void setMultipleIps() { - keylog_local_ = true; - keylog_remote_ = true; - keylog_local_negative_ = false; - keylog_remote_negative_ = false; - keylog_multiple_ips_ = true; - } - void logCheck() { - EXPECT_TRUE(api_->fileSystem().fileExists(keylog_path_)); - std::string log = waitForAccessLog(keylog_path_); - if (server_tlsv1_3_) { - /** The key log for TLS1.3 is as follows: - * CLIENT_HANDSHAKE_TRAFFIC_SECRET - c62fe86cb3a714451abc7496062251e16862ca3dfc1487c97ab4b291b83a1787 - b335f2ce9079d824a7d2f5ef9af6572d43942d6803bac1ae9de1e840c15c993ae4efdf4ac087877031d1936d5bb858e3 - SERVER_HANDSHAKE_TRAFFIC_SECRET - c62fe86cb3a714451abc7496062251e16862ca3dfc1487c97ab4b291b83a1787 - f498c03446c936d8a17f31669dd54cee2d9bc8d5b7e1a658f677b5cd6e0965111c2331fcc337c01895ec9a0ed12be34a - CLIENT_TRAFFIC_SECRET_0 c62fe86cb3a714451abc7496062251e16862ca3dfc1487c97ab4b291b83a1787 - 0bbbb2056f3d35a3b610c5cc8ae0b9b63a120912ff25054ee52b853fefc59e12e9fdfebc409347c737394457bfd36bde - SERVER_TRAFFIC_SECRET_0 c62fe86cb3a714451abc7496062251e16862ca3dfc1487c97ab4b291b83a1787 - bd3e1757174d82c308515a0c02b981084edda53e546df551ddcf78043bff831c07ff93c7ab3e8ef9e2206c8319c25331 - EXPORTER_SECRET c62fe86cb3a714451abc7496062251e16862ca3dfc1487c97ab4b291b83a1787 - 6bd19fbdd12e6710159bcb406fd42a580c41236e2d53072dba3064f9b3ff214662081f023e9b22325e31fee5bb11b172 - */ - EXPECT_THAT(log, testing::HasSubstr("CLIENT_TRAFFIC_SECRET")); - EXPECT_THAT(log, testing::HasSubstr("SERVER_TRAFFIC_SECRET")); - EXPECT_THAT(log, testing::HasSubstr("CLIENT_HANDSHAKE_TRAFFIC_SECRET")); - EXPECT_THAT(log, testing::HasSubstr("SERVER_HANDSHAKE_TRAFFIC_SECRET")); - EXPECT_THAT(log, testing::HasSubstr("EXPORTER_SECRET")); - } else { - /** The key log for TLS1.1/1.2 is as follows: - * CLIENT_RANDOM 5a479a50fe3e85295840b84e298aeb184cecc34ced22d963e16b01dc48c9530f - d6840f8100e4ceeb282946cdd72fe403b8d0724ee816ab2d0824b6d6b5033d333ec4b2e77f515226f5d829e137855ef1 - */ - EXPECT_THAT(log, testing::HasSubstr("CLIENT_RANDOM")); - } - } - void negativeCheck() { - EXPECT_TRUE(api_->fileSystem().fileExists(keylog_path_)); - auto size = api_->fileSystem().fileSize(keylog_path_); - EXPECT_EQ(size, 0); - } + SslIntegrationTest() : SslIntegrationTestBase(GetParam()) {} + void TearDown() override { SslIntegrationTestBase::TearDown(); }; }; INSTANTIATE_TEST_SUITE_P(IpVersions, SslIntegrationTest, @@ -1096,320 +943,98 @@ TEST_P(SslCertficateIntegrationTest, BothEcdsaAndRsaWithOcspResponseStaplingRequ checkStats(); } -#ifdef ENVOY_ADMIN_FUNCTIONALITY -// TODO(zuercher): write an additional OCSP integration test that validates behavior with an -// expired OCSP response. (Requires OCSP client-side support in upstream TLS.) - -// TODO(mattklein123): Move this into a dedicated integration test for the tap transport socket as -// well as add more tests. -class SslTapIntegrationTest : public SslIntegrationTest { +class SslKeyLogTest : public SslIntegrationTest { public: - void initialize() override { - // TODO(mattklein123): Merge/use the code in ConfigHelper::setTapTransportSocket(). - config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { - // The test supports tapping either the downstream or upstream connection, but not both. - if (upstream_tap_) { - setupUpstreamTap(bootstrap); - } else { - setupDownstreamTap(bootstrap); - } - }); - SslIntegrationTest::initialize(); - // This confuses our socket counting. - debug_with_s_client_ = false; + void setLogPath() { + keylog_path_ = TestEnvironment::temporaryPath(TestUtility::uniqueFilename()); } - - void setupUpstreamTap(envoy::config::bootstrap::v3::Bootstrap& bootstrap) { - auto* transport_socket = - bootstrap.mutable_static_resources()->mutable_clusters(0)->mutable_transport_socket(); - transport_socket->set_name("envoy.transport_sockets.tap"); - envoy::config::core::v3::TransportSocket raw_transport_socket; - raw_transport_socket.set_name("envoy.transport_sockets.raw_buffer"); - envoy::extensions::transport_sockets::tap::v3::Tap tap_config = - createTapConfig(raw_transport_socket); - tap_config.mutable_transport_socket()->MergeFrom(raw_transport_socket); - transport_socket->mutable_typed_config()->PackFrom(tap_config); + void setLocalFilter() { + keylog_local_ = true; + keylog_remote_ = false; + keylog_local_negative_ = false; + keylog_remote_negative_ = false; } - - void setupDownstreamTap(envoy::config::bootstrap::v3::Bootstrap& bootstrap) { - auto* filter_chain = - bootstrap.mutable_static_resources()->mutable_listeners(0)->mutable_filter_chains(0); - // Configure inner SSL transport socket based on existing config. - envoy::config::core::v3::TransportSocket ssl_transport_socket; - auto* transport_socket = filter_chain->mutable_transport_socket(); - ssl_transport_socket.Swap(transport_socket); - // Configure outer tap transport socket. - transport_socket->set_name("envoy.transport_sockets.tap"); - envoy::extensions::transport_sockets::tap::v3::Tap tap_config = - createTapConfig(ssl_transport_socket); - tap_config.mutable_transport_socket()->MergeFrom(ssl_transport_socket); - transport_socket->mutable_typed_config()->PackFrom(tap_config); + void setRemoteFilter() { + keylog_remote_ = true; + keylog_local_ = false; + keylog_local_negative_ = false; + keylog_remote_negative_ = false; } - - envoy::extensions::transport_sockets::tap::v3::Tap - createTapConfig(const envoy::config::core::v3::TransportSocket& inner_transport) { - envoy::extensions::transport_sockets::tap::v3::Tap tap_config; - tap_config.mutable_common_config()->mutable_static_config()->mutable_match()->set_any_match( - true); - auto* output_config = - tap_config.mutable_common_config()->mutable_static_config()->mutable_output_config(); - if (max_rx_bytes_.has_value()) { - output_config->mutable_max_buffered_rx_bytes()->set_value(max_rx_bytes_.value()); - } - if (max_tx_bytes_.has_value()) { - output_config->mutable_max_buffered_tx_bytes()->set_value(max_tx_bytes_.value()); + void setBothLocalAndRemoteFilter() { + keylog_local_ = true; + keylog_remote_ = true; + keylog_local_negative_ = false; + keylog_remote_negative_ = false; + } + void setNeitherLocalNorRemoteFilter() { + keylog_remote_ = false; + keylog_local_ = false; + keylog_local_negative_ = false; + keylog_remote_negative_ = false; + } + void setNegative() { + keylog_local_ = true; + keylog_remote_ = true; + keylog_local_negative_ = true; + keylog_remote_negative_ = true; + } + void setLocalNegative() { + keylog_local_ = true; + keylog_remote_ = true; + keylog_local_negative_ = false; + keylog_remote_negative_ = true; + } + void setRemoteNegative() { + keylog_local_ = true; + keylog_remote_ = true; + keylog_local_negative_ = true; + keylog_remote_negative_ = false; + } + void setMultipleIps() { + keylog_local_ = true; + keylog_remote_ = true; + keylog_local_negative_ = false; + keylog_remote_negative_ = false; + keylog_multiple_ips_ = true; + } + void logCheck() { + EXPECT_TRUE(api_->fileSystem().fileExists(keylog_path_)); + std::string log = waitForAccessLog(keylog_path_); + if (server_tlsv1_3_) { + /** The key log for TLS1.3 is as follows: + * CLIENT_HANDSHAKE_TRAFFIC_SECRET + c62fe86cb3a714451abc7496062251e16862ca3dfc1487c97ab4b291b83a1787 + b335f2ce9079d824a7d2f5ef9af6572d43942d6803bac1ae9de1e840c15c993ae4efdf4ac087877031d1936d5bb858e3 + SERVER_HANDSHAKE_TRAFFIC_SECRET + c62fe86cb3a714451abc7496062251e16862ca3dfc1487c97ab4b291b83a1787 + f498c03446c936d8a17f31669dd54cee2d9bc8d5b7e1a658f677b5cd6e0965111c2331fcc337c01895ec9a0ed12be34a + CLIENT_TRAFFIC_SECRET_0 c62fe86cb3a714451abc7496062251e16862ca3dfc1487c97ab4b291b83a1787 + 0bbbb2056f3d35a3b610c5cc8ae0b9b63a120912ff25054ee52b853fefc59e12e9fdfebc409347c737394457bfd36bde + SERVER_TRAFFIC_SECRET_0 c62fe86cb3a714451abc7496062251e16862ca3dfc1487c97ab4b291b83a1787 + bd3e1757174d82c308515a0c02b981084edda53e546df551ddcf78043bff831c07ff93c7ab3e8ef9e2206c8319c25331 + EXPORTER_SECRET c62fe86cb3a714451abc7496062251e16862ca3dfc1487c97ab4b291b83a1787 + 6bd19fbdd12e6710159bcb406fd42a580c41236e2d53072dba3064f9b3ff214662081f023e9b22325e31fee5bb11b172 + */ + EXPECT_THAT(log, testing::HasSubstr("CLIENT_TRAFFIC_SECRET")); + EXPECT_THAT(log, testing::HasSubstr("SERVER_TRAFFIC_SECRET")); + EXPECT_THAT(log, testing::HasSubstr("CLIENT_HANDSHAKE_TRAFFIC_SECRET")); + EXPECT_THAT(log, testing::HasSubstr("SERVER_HANDSHAKE_TRAFFIC_SECRET")); + EXPECT_THAT(log, testing::HasSubstr("EXPORTER_SECRET")); + } else { + /** The key log for TLS1.1/1.2 is as follows: + * CLIENT_RANDOM 5a479a50fe3e85295840b84e298aeb184cecc34ced22d963e16b01dc48c9530f + d6840f8100e4ceeb282946cdd72fe403b8d0724ee816ab2d0824b6d6b5033d333ec4b2e77f515226f5d829e137855ef1 + */ + EXPECT_THAT(log, testing::HasSubstr("CLIENT_RANDOM")); } - output_config->set_streaming(streaming_tap_); - - auto* output_sink = output_config->mutable_sinks()->Add(); - output_sink->set_format(format_); - output_sink->mutable_file_per_tap()->set_path_prefix(path_prefix_); - tap_config.mutable_transport_socket()->MergeFrom(inner_transport); - return tap_config; } - - std::string path_prefix_ = TestEnvironment::temporaryPath("ssl_trace"); - envoy::config::tap::v3::OutputSink::Format format_{ - envoy::config::tap::v3::OutputSink::PROTO_BINARY}; - absl::optional max_rx_bytes_; - absl::optional max_tx_bytes_; - bool upstream_tap_{}; - bool streaming_tap_{}; + void negativeCheck() { + EXPECT_TRUE(api_->fileSystem().fileExists(keylog_path_)); + auto size = api_->fileSystem().fileSize(keylog_path_); + EXPECT_EQ(size, 0); + } }; -INSTANTIATE_TEST_SUITE_P(IpVersions, SslTapIntegrationTest, - testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), - TestUtility::ipTestParamsToString); - -// Validate two back-to-back requests with binary proto output. -TEST_P(SslTapIntegrationTest, TwoRequestsWithBinaryProto) { - initialize(); - ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { - return makeSslClientConnection({}); - }; - - // First request (ID will be +1 since the client will also bump). - const uint64_t first_id = Network::ConnectionImpl::nextGlobalIdForTest() + 1; - codec_client_ = makeHttpConnection(creator()); - Http::TestRequestHeaderMapImpl post_request_headers{ - {":method", "POST"}, {":path", "/test/long/url"}, - {":scheme", "http"}, {":authority", "sni.lyft.com"}, - {"x-lyft-user-id", "123"}, {"x-forwarded-for", "10.0.0.1"}}; - auto response = - sendRequestAndWaitForResponse(post_request_headers, 128, default_response_headers_, 256); - EXPECT_TRUE(upstream_request_->complete()); - EXPECT_EQ(128, upstream_request_->bodyLength()); - ASSERT_TRUE(response->complete()); - EXPECT_EQ("200", response->headers().getStatusValue()); - EXPECT_EQ(256, response->body().size()); - checkStats(); - envoy::config::core::v3::Address expected_local_address; - Network::Utility::addressToProtobufAddress( - *codec_client_->connection()->connectionInfoProvider().remoteAddress(), - expected_local_address); - envoy::config::core::v3::Address expected_remote_address; - Network::Utility::addressToProtobufAddress( - *codec_client_->connection()->connectionInfoProvider().localAddress(), - expected_remote_address); - codec_client_->close(); - test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 1); - envoy::data::tap::v3::TraceWrapper trace; - TestUtility::loadFromFile(fmt::format("{}_{}.pb", path_prefix_, first_id), trace, *api_); - // Validate general expected properties in the trace. - EXPECT_EQ(first_id, trace.socket_buffered_trace().trace_id()); - EXPECT_THAT(expected_local_address, - ProtoEq(trace.socket_buffered_trace().connection().local_address())); - EXPECT_THAT(expected_remote_address, - ProtoEq(trace.socket_buffered_trace().connection().remote_address())); - ASSERT_GE(trace.socket_buffered_trace().events().size(), 2); - EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(0).read().data().as_bytes(), - "POST /test/long/url HTTP/1.1")); - EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(1).write().data().as_bytes(), - "HTTP/1.1 200 OK")); - EXPECT_FALSE(trace.socket_buffered_trace().read_truncated()); - EXPECT_FALSE(trace.socket_buffered_trace().write_truncated()); - - // Verify a second request hits a different file. - const uint64_t second_id = Network::ConnectionImpl::nextGlobalIdForTest() + 1; - codec_client_ = makeHttpConnection(creator()); - Http::TestRequestHeaderMapImpl get_request_headers{ - {":method", "GET"}, {":path", "/test/long/url"}, - {":scheme", "http"}, {":authority", "sni.lyft.com"}, - {"x-lyft-user-id", "123"}, {"x-forwarded-for", "10.0.0.1"}}; - response = - sendRequestAndWaitForResponse(get_request_headers, 128, default_response_headers_, 256); - EXPECT_TRUE(upstream_request_->complete()); - EXPECT_EQ(128, upstream_request_->bodyLength()); - ASSERT_TRUE(response->complete()); - EXPECT_EQ("200", response->headers().getStatusValue()); - EXPECT_EQ(256, response->body().size()); - checkStats(); - codec_client_->close(); - test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 2); - TestUtility::loadFromFile(fmt::format("{}_{}.pb", path_prefix_, second_id), trace, *api_); - // Validate second connection ID. - EXPECT_EQ(second_id, trace.socket_buffered_trace().trace_id()); - ASSERT_GE(trace.socket_buffered_trace().events().size(), 2); - EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(0).read().data().as_bytes(), - "GET /test/long/url HTTP/1.1")); - EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(1).write().data().as_bytes(), - "HTTP/1.1 200 OK")); - EXPECT_FALSE(trace.socket_buffered_trace().read_truncated()); - EXPECT_FALSE(trace.socket_buffered_trace().write_truncated()); -} - -// Verify that truncation works correctly across multiple transport socket frames. -TEST_P(SslTapIntegrationTest, TruncationWithMultipleDataFrames) { - max_rx_bytes_ = 4; - max_tx_bytes_ = 5; - - initialize(); - ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { - return makeSslClientConnection({}); - }; - - const uint64_t id = Network::ConnectionImpl::nextGlobalIdForTest() + 1; - codec_client_ = makeHttpConnection(creator()); - const Http::TestRequestHeaderMapImpl request_headers{{":method", "GET"}, - {":path", "/test/long/url"}, - {":scheme", "http"}, - {":authority", "sni.lyft.com"}}; - auto result = codec_client_->startRequest(request_headers); - auto response = std::move(result.second); - Buffer::OwnedImpl data1("one"); - result.first.encodeData(data1, false); - Buffer::OwnedImpl data2("two"); - result.first.encodeData(data2, true); - waitForNextUpstreamRequest(); - const Http::TestResponseHeaderMapImpl response_headers{{":status", "200"}}; - upstream_request_->encodeHeaders(response_headers, false); - Buffer::OwnedImpl data3("three"); - upstream_request_->encodeData(data3, false); - response->waitForBodyData(5); - Buffer::OwnedImpl data4("four"); - upstream_request_->encodeData(data4, true); - ASSERT_TRUE(response->waitForEndStream()); - - checkStats(); - codec_client_->close(); - test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 1); - - envoy::data::tap::v3::TraceWrapper trace; - TestUtility::loadFromFile(fmt::format("{}_{}.pb", path_prefix_, id), trace, *api_); - - ASSERT_EQ(trace.socket_buffered_trace().events().size(), 2); - EXPECT_TRUE(trace.socket_buffered_trace().events(0).read().data().truncated()); - EXPECT_TRUE(trace.socket_buffered_trace().events(1).write().data().truncated()); - EXPECT_TRUE(trace.socket_buffered_trace().read_truncated()); - EXPECT_TRUE(trace.socket_buffered_trace().write_truncated()); -} - -// Validate a single request with text proto output. -TEST_P(SslTapIntegrationTest, RequestWithTextProto) { - format_ = envoy::config::tap::v3::OutputSink::PROTO_TEXT; - ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { - return makeSslClientConnection({}); - }; - - // Disable for this test because it uses connection IDs, which disrupts the accounting below - // leading to the wrong path for the `pb_text` being used. - skip_tag_extraction_rule_check_ = true; - - const uint64_t id = Network::ConnectionImpl::nextGlobalIdForTest() + 1; - testRouterRequestAndResponseWithBody(1024, 512, false, false, &creator); - checkStats(); - codec_client_->close(); - test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 1); - envoy::data::tap::v3::TraceWrapper trace; - TestUtility::loadFromFile(fmt::format("{}_{}.pb_text", path_prefix_, id), trace, *api_); - // Test some obvious properties. - EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(0).read().data().as_bytes(), - "GET /test/long/url HTTP/1.1")); - EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(1).write().data().as_bytes(), - "HTTP/1.1 200 OK")); - EXPECT_TRUE(trace.socket_buffered_trace().read_truncated()); - EXPECT_FALSE(trace.socket_buffered_trace().write_truncated()); -} - -// Validate a single request with JSON (body as string) output. This test uses an upstream tap. -TEST_P(SslTapIntegrationTest, RequestWithJsonBodyAsStringUpstreamTap) { - upstream_tap_ = true; - max_rx_bytes_ = 5; - max_tx_bytes_ = 4; - - format_ = envoy::config::tap::v3::OutputSink::JSON_BODY_AS_STRING; - ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { - return makeSslClientConnection({}); - }; - - // Disable for this test because it uses connection IDs, which disrupts the accounting below - // leading to the wrong path for the `pb_text` being used. - skip_tag_extraction_rule_check_ = true; - - const uint64_t id = Network::ConnectionImpl::nextGlobalIdForTest() + 2; - testRouterRequestAndResponseWithBody(512, 1024, false, false, &creator); - checkStats(); - codec_client_->close(); - test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 1); - test_server_.reset(); - - // This must be done after server shutdown so that connection pool connections are closed and - // the tap written. - envoy::data::tap::v3::TraceWrapper trace; - TestUtility::loadFromFile(fmt::format("{}_{}.json", path_prefix_, id), trace, *api_); - - // Test some obvious properties. - EXPECT_EQ(trace.socket_buffered_trace().events(0).write().data().as_string(), "GET "); - EXPECT_EQ(trace.socket_buffered_trace().events(1).read().data().as_string(), "HTTP/"); - EXPECT_TRUE(trace.socket_buffered_trace().read_truncated()); - EXPECT_TRUE(trace.socket_buffered_trace().write_truncated()); -} - -// Validate a single request with length delimited binary proto output. This test uses an upstream -// tap. -TEST_P(SslTapIntegrationTest, RequestWithStreamingUpstreamTap) { - upstream_tap_ = true; - streaming_tap_ = true; - max_rx_bytes_ = 5; - max_tx_bytes_ = 4; - - format_ = envoy::config::tap::v3::OutputSink::PROTO_BINARY_LENGTH_DELIMITED; - ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { - return makeSslClientConnection({}); - }; - - // Disable for this test because it uses connection IDs, which disrupts the accounting below - // leading to the wrong path for the `pb_text` being used. - skip_tag_extraction_rule_check_ = true; - - const uint64_t id = Network::ConnectionImpl::nextGlobalIdForTest() + 2; - testRouterRequestAndResponseWithBody(512, 1024, false, false, &creator); - checkStats(); - codec_client_->close(); - test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 1); - test_server_.reset(); - - // This must be done after server shutdown so that connection pool connections are closed and - // the tap written. - std::vector traces = - Extensions::Common::Tap::readTracesFromFile( - fmt::format("{}_{}.pb_length_delimited", path_prefix_, id)); - ASSERT_GE(traces.size(), 4); - - // The initial connection message has no local address, but has a remote address (not connected - // yet). - EXPECT_TRUE(traces[0].socket_streamed_trace_segment().has_connection()); - EXPECT_FALSE(traces[0].socket_streamed_trace_segment().connection().has_local_address()); - EXPECT_TRUE(traces[0].socket_streamed_trace_segment().connection().has_remote_address()); - - // Verify truncated request/response data. - EXPECT_EQ(traces[1].socket_streamed_trace_segment().event().write().data().as_bytes(), "GET "); - EXPECT_TRUE(traces[1].socket_streamed_trace_segment().event().write().data().truncated()); - EXPECT_EQ(traces[2].socket_streamed_trace_segment().event().read().data().as_bytes(), "HTTP/"); - EXPECT_TRUE(traces[2].socket_streamed_trace_segment().event().read().data().truncated()); -} -#endif - INSTANTIATE_TEST_SUITE_P(IpVersions, SslKeyLogTest, testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), TestUtility::ipTestParamsToString); diff --git a/test/common/tls/integration/ssl_integration_test_base.cc b/test/common/tls/integration/ssl_integration_test_base.cc new file mode 100644 index 000000000000..2a89ffee0972 --- /dev/null +++ b/test/common/tls/integration/ssl_integration_test_base.cc @@ -0,0 +1,66 @@ +#include "ssl_integration_test_base.h" + +namespace Envoy { +namespace Ssl { + +void SslIntegrationTestBase::initialize() { + config_helper_.addSslConfig(ConfigHelper::ServerSslOptions() + .setRsaCert(server_rsa_cert_) + .setRsaCertOcspStaple(server_rsa_cert_ocsp_staple_) + .setEcdsaCert(server_ecdsa_cert_) + .setEcdsaCertOcspStaple(server_ecdsa_cert_ocsp_staple_) + .setOcspStapleRequired(ocsp_staple_required_) + .setTlsV13(server_tlsv1_3_) + .setCurves(server_curves_) + .setExpectClientEcdsaCert(client_ecdsa_cert_) + .setTlsKeyLogFilter(keylog_local_, keylog_remote_, + keylog_local_negative_, + keylog_remote_negative_, keylog_path_, + keylog_multiple_ips_, version_)); + + HttpIntegrationTest::initialize(); + + context_manager_ = std::make_unique( + server_factory_context_); + + registerTestServerPorts({"http"}); +} + +void SslIntegrationTestBase::TearDown() { + HttpIntegrationTest::cleanupUpstreamAndDownstream(); + codec_client_.reset(); + context_manager_.reset(); +} + +Network::ClientConnectionPtr +SslIntegrationTestBase::makeSslClientConnection(const ClientSslTransportOptions& options) { + Network::Address::InstanceConstSharedPtr address = getSslAddress(version_, lookupPort("http")); + if (debug_with_s_client_) { + const std::string s_client_cmd = TestEnvironment::substitute( + "openssl s_client -connect " + address->asString() + + " -showcerts -debug -msg -CAfile " + "{{ test_rundir }}/test/config/integration/certs/cacert.pem " + "-servername lyft.com -cert " + "{{ test_rundir }}/test/config/integration/certs/clientcert.pem " + "-key " + "{{ test_rundir }}/test/config/integration/certs/clientkey.pem ", + version_); + ENVOY_LOG_MISC(debug, "Executing {}", s_client_cmd); + RELEASE_ASSERT(::system(s_client_cmd.c_str()) == 0, ""); + } + auto client_transport_socket_factory_ptr = + createClientSslTransportSocketFactory(options, *context_manager_, *api_); + return dispatcher_->createClientConnection( + address, Network::Address::InstanceConstSharedPtr(), + client_transport_socket_factory_ptr->createTransportSocket({}, nullptr), nullptr, nullptr); +} + +void SslIntegrationTestBase::checkStats() { + const uint32_t expected_handshakes = debug_with_s_client_ ? 2 : 1; + Stats::CounterSharedPtr counter = test_server_->counter(listenerStatPrefix("ssl.handshake")); + EXPECT_EQ(expected_handshakes, counter->value()); + counter->reset(); +} + +} // namespace Ssl +} // namespace Envoy diff --git a/test/common/tls/integration/ssl_integration_test.h b/test/common/tls/integration/ssl_integration_test_base.h similarity index 84% rename from test/common/tls/integration/ssl_integration_test.h rename to test/common/tls/integration/ssl_integration_test_base.h index b2bd345a4288..63366d2d1ff9 100644 --- a/test/common/tls/integration/ssl_integration_test.h +++ b/test/common/tls/integration/ssl_integration_test_base.h @@ -49,12 +49,5 @@ class SslIntegrationTestBase : public HttpIntegrationTest { std::unique_ptr context_manager_; }; -class SslIntegrationTest : public testing::TestWithParam, - public SslIntegrationTestBase { -public: - SslIntegrationTest() : SslIntegrationTestBase(GetParam()) {} - void TearDown() override { SslIntegrationTestBase::TearDown(); }; -}; - } // namespace Ssl } // namespace Envoy diff --git a/test/common/tls/ssl_socket_test.cc b/test/common/tls/ssl_socket_test.cc index b72f7ad6596b..dc231f41eaad 100644 --- a/test/common/tls/ssl_socket_test.cc +++ b/test/common/tls/ssl_socket_test.cc @@ -47,6 +47,7 @@ #include "test/mocks/network/mocks.h" #include "test/mocks/runtime/mocks.h" #include "test/mocks/secret/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/server/transport_socket_factory_context.h" #include "test/mocks/ssl/mocks.h" #include "test/mocks/stats/mocks.h" @@ -346,8 +347,9 @@ void testUtil(const TestUtilOptions& options) { Api::ApiPtr server_api = Api::createApiForTest(server_stats_store, time_system); NiceMock runtime; testing::NiceMock - server_factory_context; - ON_CALL(server_factory_context.server_context_, api()).WillByDefault(ReturnRef(*server_api)); + transport_socket_factory_context; + ON_CALL(transport_socket_factory_context.server_context_, api()) + .WillByDefault(ReturnRef(*server_api)); // For private key method testing. NiceMock context_manager; @@ -356,7 +358,7 @@ void testUtil(const TestUtilOptions& options) { test_private_key_method_factory(test_factory); PrivateKeyMethodManagerImpl private_key_method_manager; if (options.expectedPrivateKeyMethod()) { - EXPECT_CALL(server_factory_context, sslContextManager()) + EXPECT_CALL(transport_socket_factory_context, sslContextManager()) .WillOnce(ReturnRef(context_manager)) .WillRepeatedly(ReturnRef(context_manager)); EXPECT_CALL(context_manager, privateKeyMethodManager()) @@ -367,9 +369,10 @@ void testUtil(const TestUtilOptions& options) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(options.serverCtxYaml()), server_tls_context); - auto server_cfg = - std::make_unique(server_tls_context, server_factory_context); - ContextManagerImpl manager(*time_system); + auto server_cfg = std::make_unique(server_tls_context, + transport_socket_factory_context); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Event::DispatcherPtr dispatcher = server_api->allocateDispatcher("test_thread"); ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -707,7 +710,10 @@ class TestUtilOptionsV2 : public TestUtilOptionsBase { void testUtilV2(const TestUtilOptionsV2& options) { Event::SimulatedTimeSystem time_system; - ContextManagerImpl manager(*time_system); + NiceMock + transport_socket_factory_context; + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); // SNI-based selection logic isn't happening in SslSocket anymore. ASSERT(options.listener().filter_chains().size() == 1); @@ -716,10 +722,9 @@ void testUtilV2(const TestUtilOptionsV2& options) { filter_chain.filter_chain_match().server_names().end()); Stats::TestUtil::TestStore server_stats_store; Api::ApiPtr server_api = Api::createApiForTest(server_stats_store, time_system); - testing::NiceMock - server_factory_context; NiceMock runtime; - ON_CALL(server_factory_context.server_context_, api()).WillByDefault(ReturnRef(*server_api)); + ON_CALL(transport_socket_factory_context.server_context_, api()) + .WillByDefault(ReturnRef(*server_api)); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; const envoy::config::core::v3::TransportSocket& transport_socket = @@ -727,7 +732,8 @@ void testUtilV2(const TestUtilOptionsV2& options) { ASSERT(transport_socket.has_typed_config()); transport_socket.typed_config().UnpackTo(&tls_context); - auto server_cfg = std::make_unique(tls_context, server_factory_context); + auto server_cfg = + std::make_unique(tls_context, transport_socket_factory_context); ServerSslSocketFactory server_ssl_socket_factory(std::move(server_cfg), manager, *server_stats_store.rootScope(), server_names); @@ -1016,7 +1022,8 @@ TEST_P(SslSocketTest, ServerTransportSocketOptions) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), tls_context); auto server_cfg = std::make_unique(tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); auto ssl_socket = server_ssl_socket_factory.createDownstreamTransportSocket(); @@ -3065,7 +3072,8 @@ TEST_P(SslSocketTest, FlushCloseDuringHandshake) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), tls_context); auto server_cfg = std::make_unique(tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -3124,7 +3132,8 @@ TEST_P(SslSocketTest, HalfClose) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); auto server_cfg = std::make_unique(server_tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -3209,7 +3218,8 @@ TEST_P(SslSocketTest, ShutdownWithCloseNotify) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); auto server_cfg = std::make_unique(server_tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -3300,7 +3310,8 @@ TEST_P(SslSocketTest, ShutdownWithoutCloseNotify) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); auto server_cfg = std::make_unique(server_tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -3407,7 +3418,8 @@ TEST_P(SslSocketTest, ClientAuthMultipleCAs) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); auto server_cfg = std::make_unique(server_tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -3489,24 +3501,26 @@ void testTicketSessionResumption(const std::string& server_ctx_yaml1, const Network::Address::IpVersion ip_version, const uint32_t expected_lifetime_hint = 0) { Event::SimulatedTimeSystem time_system; - ContextManagerImpl manager(*time_system); + NiceMock + transport_socket_factory_context; + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; Api::ApiPtr server_api = Api::createApiForTest(server_stats_store, time_system); NiceMock runtime; - testing::NiceMock - server_factory_context; - ON_CALL(server_factory_context.server_context_, api()).WillByDefault(ReturnRef(*server_api)); + ON_CALL(transport_socket_factory_context.server_context_, api()) + .WillByDefault(ReturnRef(*server_api)); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context1; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml1), server_tls_context1); - auto server_cfg1 = - std::make_unique(server_tls_context1, server_factory_context); + auto server_cfg1 = std::make_unique(server_tls_context1, + transport_socket_factory_context); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context2; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml2), server_tls_context2); - auto server_cfg2 = - std::make_unique(server_tls_context2, server_factory_context); + auto server_cfg2 = std::make_unique(server_tls_context2, + transport_socket_factory_context); ServerSslSocketFactory server_ssl_socket_factory1(std::move(server_cfg1), manager, *server_stats_store.rootScope(), server_names1); ServerSslSocketFactory server_ssl_socket_factory2(std::move(server_cfg2), manager, @@ -3647,19 +3661,21 @@ void testSupportForSessionResumption(const std::string& server_ctx_yaml, bool expect_stateful, const Network::Address::IpVersion ip_version) { Event::SimulatedTimeSystem time_system; - ContextManagerImpl manager(*time_system); + NiceMock + transport_socket_factory_context; + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::IsolatedStoreImpl server_stats_store; Api::ApiPtr server_api = Api::createApiForTest(server_stats_store, time_system); NiceMock runtime; - testing::NiceMock - server_factory_context; - ON_CALL(server_factory_context.server_context_, api()).WillByDefault(ReturnRef(*server_api)); + ON_CALL(transport_socket_factory_context.server_context_, api()) + .WillByDefault(ReturnRef(*server_api)); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); - auto server_cfg = - std::make_unique(server_tls_context, server_factory_context); + auto server_cfg = std::make_unique(server_tls_context, + transport_socket_factory_context); ServerSslSocketFactory server_ssl_socket_factory(std::move(server_cfg), manager, *server_stats_store.rootScope(), {}); @@ -4305,7 +4321,8 @@ TEST_P(SslSocketTest, ClientAuthCrossListenerSessionResumption) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context2; TestUtility::loadFromYaml(TestEnvironment::substitute(server2_ctx_yaml), tls_context2); auto server2_cfg = std::make_unique(tls_context2, factory_context_); - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -4422,18 +4439,20 @@ void SslSocketTest::testClientSessionResumption(const std::string& server_ctx_ya const Network::Address::IpVersion version) { InSequence s; - ContextManagerImpl manager(time_system_); + NiceMock server_factory_context; + ContextManagerImpl manager(server_factory_context); Stats::TestUtil::TestStore server_stats_store; Api::ApiPtr server_api = Api::createApiForTest(server_stats_store, time_system_); testing::NiceMock - server_factory_context; - ON_CALL(server_factory_context.server_context_, api()).WillByDefault(ReturnRef(*server_api)); + transport_socket_factory_context; + ON_CALL(transport_socket_factory_context.server_context_, api()) + .WillByDefault(ReturnRef(*server_api)); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_ctx_proto; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_ctx_proto); auto server_cfg = - std::make_unique(server_ctx_proto, server_factory_context); + std::make_unique(server_ctx_proto, transport_socket_factory_context); ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -4698,7 +4717,7 @@ TEST_P(SslSocketTest, SslError) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), tls_context); auto server_cfg = std::make_unique(tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + ContextManagerImpl manager(factory_context_.serverFactoryContext()); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -5230,7 +5249,7 @@ TEST_P(SslSocketTest, SetSignatureAlgorithms) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext server_tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(server_ctx_yaml), server_tls_context); auto server_cfg = std::make_unique(server_tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + ContextManagerImpl manager(factory_context_.serverFactoryContext()); Stats::TestUtil::TestStore server_stats_store; ServerSslSocketFactory server_ssl_socket_factory( std::move(server_cfg), manager, *server_stats_store.rootScope(), std::vector{}); @@ -5845,7 +5864,7 @@ TEST_P(SslSocketTest, DownstreamNotReadySslSocket) { EXPECT_TRUE(server_cfg->tlsCertificates().empty()); EXPECT_FALSE(server_cfg->isReady()); - ContextManagerImpl manager(time_system_); + ContextManagerImpl manager(factory_context_.serverFactoryContext()); ServerSslSocketFactory server_ssl_socket_factory(std::move(server_cfg), manager, *factory_context_.store_.rootScope(), std::vector{}); @@ -5883,7 +5902,7 @@ TEST_P(SslSocketTest, UpstreamNotReadySslSocket) { EXPECT_TRUE(client_cfg->tlsCertificates().empty()); EXPECT_FALSE(client_cfg->isReady()); - ContextManagerImpl manager(time_system_); + ContextManagerImpl manager(factory_context_.serverFactoryContext()); ClientSslSocketFactory client_ssl_socket_factory(std::move(client_cfg), manager, *factory_context_.store_.rootScope()); auto transport_socket = client_ssl_socket_factory.createTransportSocket(nullptr, nullptr); @@ -5911,7 +5930,7 @@ TEST_P(SslSocketTest, TestTransportSocketCallback) { envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext tls_context; auto client_cfg = std::make_unique(tls_context, factory_context_); - ContextManagerImpl manager(time_system_); + ContextManagerImpl manager(factory_context_.serverFactoryContext()); ClientSslSocketFactory client_ssl_socket_factory(std::move(client_cfg), manager, *factory_context_.store_.rootScope()); @@ -5935,7 +5954,7 @@ class SslReadBufferLimitTest : public SslSocketTest { downstream_tls_context_); auto server_cfg = std::make_unique(downstream_tls_context_, factory_context_); - manager_ = std::make_unique(time_system_); + manager_ = std::make_unique(factory_context_.serverFactoryContext()); server_ssl_socket_factory_ = std::make_unique( std::move(server_cfg), *manager_, *server_stats_store_.rootScope(), std::vector{}); diff --git a/test/common/upstream/cluster_manager_impl_test.cc b/test/common/upstream/cluster_manager_impl_test.cc index 04b65bd5c1db..17644f883a1c 100644 --- a/test/common/upstream/cluster_manager_impl_test.cc +++ b/test/common/upstream/cluster_manager_impl_test.cc @@ -105,20 +105,18 @@ class MockedUpdatedClusterManagerImpl : public TestClusterManagerImpl { public: using TestClusterManagerImpl::TestClusterManagerImpl; - MockedUpdatedClusterManagerImpl(const envoy::config::bootstrap::v3::Bootstrap& bootstrap, - ClusterManagerFactory& factory, Stats::Store& stats, - ThreadLocal::Instance& tls, Runtime::Loader& runtime, - const LocalInfo::LocalInfo& local_info, - AccessLog::AccessLogManager& log_manager, - Event::Dispatcher& main_thread_dispatcher, Server::Admin& admin, - ProtobufMessage::ValidationContext& validation_context, - Api::Api& api, MockLocalClusterUpdate& local_cluster_update, - MockLocalHostsRemoved& local_hosts_removed, - Http::Context& http_context, Grpc::Context& grpc_context, - Router::Context& router_context, Server::Instance& server) - : TestClusterManagerImpl(bootstrap, factory, stats, tls, runtime, local_info, log_manager, - main_thread_dispatcher, admin, validation_context, api, http_context, - grpc_context, router_context, server), + MockedUpdatedClusterManagerImpl( + const envoy::config::bootstrap::v3::Bootstrap& bootstrap, ClusterManagerFactory& factory, + Server::Configuration::CommonFactoryContext& factory_context, Stats::Store& stats, + ThreadLocal::Instance& tls, Runtime::Loader& runtime, const LocalInfo::LocalInfo& local_info, + AccessLog::AccessLogManager& log_manager, Event::Dispatcher& main_thread_dispatcher, + Server::Admin& admin, ProtobufMessage::ValidationContext& validation_context, Api::Api& api, + MockLocalClusterUpdate& local_cluster_update, MockLocalHostsRemoved& local_hosts_removed, + Http::Context& http_context, Grpc::Context& grpc_context, Router::Context& router_context, + Server::Instance& server) + : TestClusterManagerImpl(bootstrap, factory, factory_context, stats, tls, runtime, local_info, + log_manager, main_thread_dispatcher, admin, validation_context, api, + http_context, grpc_context, router_context, server), local_cluster_update_(local_cluster_update), local_hosts_removed_(local_hosts_removed) {} protected: @@ -161,6 +159,7 @@ class MockGrpcMuxFactory : public Config::MuxFactory { class UpdateOverrideClusterManagerImpl : public TestClusterManagerImpl { public: UpdateOverrideClusterManagerImpl(const Bootstrap& bootstrap, ClusterManagerFactory& factory, + Server::Configuration::CommonFactoryContext& factory_context, Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager, @@ -169,9 +168,9 @@ class UpdateOverrideClusterManagerImpl : public TestClusterManagerImpl { Api::Api& api, Http::Context& http_context, Grpc::Context& grpc_context, Router::Context& router_context, Server::Instance& server) - : TestClusterManagerImpl(bootstrap, factory, stats, tls, runtime, local_info, log_manager, - main_thread_dispatcher, admin, validation_context, api, http_context, - grpc_context, router_context, server) {} + : TestClusterManagerImpl(bootstrap, factory, factory_context, stats, tls, runtime, local_info, + log_manager, main_thread_dispatcher, admin, validation_context, api, + http_context, grpc_context, router_context, server) {} protected: void postThreadLocalClusterUpdate(ClusterManagerCluster& cluster, @@ -208,9 +207,10 @@ class ClusterManagerImplTest : public testing::Test { virtual void create(const Bootstrap& bootstrap) { cluster_manager_ = TestClusterManagerImpl::createAndInit( - bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, - factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, validation_context_, - *factory_.api_, http_context_, grpc_context_, router_context_, server_); + bootstrap, factory_, factory_.server_context_, factory_.stats_, factory_.tls_, + factory_.runtime_, factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, + validation_context_, *factory_.api_, http_context_, grpc_context_, router_context_, + server_); cluster_manager_->setPrimaryClustersInitializedCb([this, bootstrap]() { THROW_IF_NOT_OK(cluster_manager_->initializeSecondaryClusters(bootstrap)); }); @@ -275,18 +275,19 @@ class ClusterManagerImplTest : public testing::Test { const auto& bootstrap = parseBootstrapFromV3Yaml(yaml); cluster_manager_ = std::make_unique( - bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, - factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, validation_context_, - *factory_.api_, local_cluster_update_, local_hosts_removed_, http_context_, grpc_context_, - router_context_, server_); + bootstrap, factory_, factory_.server_context_, factory_.stats_, factory_.tls_, + factory_.runtime_, factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, + validation_context_, *factory_.api_, local_cluster_update_, local_hosts_removed_, + http_context_, grpc_context_, router_context_, server_); THROW_IF_NOT_OK(cluster_manager_->init(bootstrap)); } void createWithUpdateOverrideClusterManager(const Bootstrap& bootstrap) { cluster_manager_ = std::make_unique( - bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, - factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, validation_context_, - *factory_.api_, http_context_, grpc_context_, router_context_, server_); + bootstrap, factory_, factory_.server_context_, factory_.stats_, factory_.tls_, + factory_.runtime_, factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, + validation_context_, *factory_.api_, http_context_, grpc_context_, router_context_, + server_); THROW_IF_NOT_OK(cluster_manager_->init(bootstrap)); } diff --git a/test/common/upstream/deferred_cluster_initialization_test.cc b/test/common/upstream/deferred_cluster_initialization_test.cc index a9701fc77c25..12fb3373e7cc 100644 --- a/test/common/upstream/deferred_cluster_initialization_test.cc +++ b/test/common/upstream/deferred_cluster_initialization_test.cc @@ -63,9 +63,10 @@ class DeferredClusterInitializationTest : public testing::TestWithParam { void create(const envoy::config::bootstrap::v3::Bootstrap& bootstrap) { cluster_manager_ = TestClusterManagerImpl::createAndInit( - bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, - factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, validation_context_, - *factory_.api_, http_context_, grpc_context_, router_context_, server_); + bootstrap, factory_, factory_.server_context_, factory_.stats_, factory_.tls_, + factory_.runtime_, factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, + validation_context_, *factory_.api_, http_context_, grpc_context_, router_context_, + server_); cluster_manager_->setPrimaryClustersInitializedCb([this, bootstrap]() { THROW_IF_NOT_OK(cluster_manager_->initializeSecondaryClusters(bootstrap)); }); diff --git a/test/common/upstream/hds_test.cc b/test/common/upstream/hds_test.cc index 82acb9d72e8d..c58700be552a 100644 --- a/test/common/upstream/hds_test.cc +++ b/test/common/upstream/hds_test.cc @@ -61,8 +61,7 @@ class HdsTest : public testing::Test { HdsTest() : retry_timer_(new Event::MockTimer()), server_response_timer_(new Event::MockTimer()), async_client_(new Grpc::MockAsyncClient()), - api_(Api::createApiForTest(stats_store_, random_)), - ssl_context_manager_(api_->timeSource()) { + api_(Api::createApiForTest(stats_store_, random_)), ssl_context_manager_(server_context_) { ON_CALL(server_context_, api()).WillByDefault(ReturnRef(*api_)); node_.set_id("hds-node"); } diff --git a/test/common/upstream/health_checker_impl_test.cc b/test/common/upstream/health_checker_impl_test.cc index 9558e88d7e39..a3349b98667e 100644 --- a/test/common/upstream/health_checker_impl_test.cc +++ b/test/common/upstream/health_checker_impl_test.cc @@ -309,6 +309,25 @@ class HttpHealthCheckerImplTest : public Event::TestUsingSimulatedTime, addCompletionCallback(); } + void setupNoServiceValidationHCAlwaysLogSuccess() { + const std::string yaml = R"EOF( + timeout: 1s + interval: 1s + no_traffic_interval: 5s + interval_jitter: 1s + unhealthy_threshold: 2 + healthy_threshold: 1 + http_health_check: + service_name_matcher: + prefix: locations + path: /healthcheck + always_log_health_check_success: true + )EOF"; + + allocHealthChecker(yaml); + addCompletionCallback(); + } + void setupNoServiceValidationNoReuseConnectionHC() { std::string yaml = R"EOF( timeout: 1s @@ -2348,6 +2367,42 @@ TEST_F(HttpHealthCheckerImplTest, HttpFailLogError) { cluster_->prioritySet().getMockHostSet(0)->hosts_[0]->coarseHealth()); } +TEST_F(HttpHealthCheckerImplTest, HttpAlwaysLogSuccess) { + setupNoServiceValidationHCAlwaysLogSuccess(); + cluster_->prioritySet().getMockHostSet(0)->hosts_ = { + makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", simTime())}; + cluster_->prioritySet().getMockHostSet(0)->hosts_[0]->healthFlagSet( + Host::HealthFlag::FAILED_ACTIVE_HC); + cluster_->prioritySet().getMockHostSet(0)->hosts_[0]->healthFlagSet( + Host::HealthFlag::PENDING_ACTIVE_HC); + expectSessionCreate(); + expectStreamCreate(0); + EXPECT_CALL(*test_sessions_[0]->timeout_timer_, enableTimer(_, _)); + health_checker_->start(); + + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Changed)); + EXPECT_CALL(event_logger_, logAddHealthy(_, _, true)); + EXPECT_CALL(event_logger_, logSuccessfulHealthCheck(_, _)).Times(0); + EXPECT_CALL(*test_sessions_[0]->interval_timer_, enableTimer(_, _)); + EXPECT_CALL(*test_sessions_[0]->timeout_timer_, disableTimer()); + respond(0, "200", false); + EXPECT_EQ(Host::Health::Healthy, + cluster_->prioritySet().getMockHostSet(0)->hosts_[0]->coarseHealth()); + + EXPECT_CALL(*test_sessions_[0]->timeout_timer_, enableTimer(_, _)); + expectStreamCreate(0); + test_sessions_[0]->interval_timer_->invokeCallback(); + + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)); + EXPECT_CALL(event_logger_, logAddHealthy(_, _, _)).Times(0); + EXPECT_CALL(event_logger_, logSuccessfulHealthCheck(_, _)); + EXPECT_CALL(*test_sessions_[0]->interval_timer_, enableTimer(_, _)); + EXPECT_CALL(*test_sessions_[0]->timeout_timer_, disableTimer()); + respond(0, "200", false); + EXPECT_EQ(Host::Health::Healthy, + cluster_->prioritySet().getMockHostSet(0)->hosts_[0]->coarseHealth()); +} + TEST_F(HttpHealthCheckerImplTest, Disconnect) { setupNoServiceValidationHC(); EXPECT_CALL(event_logger_, logUnhealthy(_, _, _, true)); @@ -6429,6 +6484,19 @@ TEST(HealthCheckEventLoggerImplTest, All) { "{\"region\":\"\",\"zone\":\"\",\"sub_zone\":\"\"}}\n"})); event_logger.logAddHealthy(envoy::data::core::v3::HTTP, host, false); + EXPECT_CALL(*file, write(absl::string_view{ + "{\"health_checker_type\":\"HTTP\",\"host\":{\"socket_address\":{" + "\"protocol\":\"TCP\",\"address\":\"10.0.0.1\",\"port_value\":443," + "\"resolver_name\":\"\"," + "\"ipv4_compat\":false}},\"cluster_name\":\"fake_" + "cluster\"," + "\"timestamp\":\"2009-02-13T23:31:31.234Z\"," + "\"metadata\":" + "{\"filter_metadata\":{},\"typed_filter_metadata\":{}},\"locality\":" + "{\"region\":\"\",\"zone\":\"\",\"sub_zone\":\"\"}," + "\"successful_health_check_event\":{}}\n"})); + event_logger.logSuccessfulHealthCheck(envoy::data::core::v3::HTTP, host); + EXPECT_CALL(*file, write(absl::string_view{ "{\"health_checker_type\":\"HTTP\",\"host\":{\"socket_address\":{" "\"protocol\":\"TCP\",\"address\":\"10.0.0.1\",\"port_value\":443," @@ -6516,6 +6584,17 @@ TEST(HealthCheckEventLoggerImplTest, OneEventLogger) { "{\"filter_metadata\":{},\"typed_filter_metadata\":{}},\"locality\":" "{\"region\":\"\",\"zone\":\"\",\"sub_zone\":\"\"}}\n"); + event_logger.logSuccessfulHealthCheck(envoy::data::core::v3::HTTP, host); + EXPECT_EQ( + file_log_data.value(), + "{\"health_checker_type\":\"HTTP\",\"host\":{\"socket_address\":{" + "\"protocol\":\"TCP\",\"address\":\"10.0.0.1\",\"port_value\":443,\"resolver_name\":\"\"," + "\"ipv4_compat\":false}},\"cluster_name\":\"fake_" + "cluster\",\"timestamp\":\"2009-02-13T23:31:31.234Z\"," + "\"metadata\":" + "{\"filter_metadata\":{},\"typed_filter_metadata\":{}},\"locality\":" + "{\"region\":\"\",\"zone\":\"\",\"sub_zone\":\"\"},\"successful_health_check_event\":{}}\n"); + event_logger.logUnhealthy(envoy::data::core::v3::HTTP, host, envoy::data::core::v3::ACTIVE, false); EXPECT_EQ( diff --git a/test/common/upstream/test_cluster_manager.h b/test/common/upstream/test_cluster_manager.h index eb110f9d672e..d05bfc493376 100644 --- a/test/common/upstream/test_cluster_manager.h +++ b/test/common/upstream/test_cluster_manager.h @@ -146,8 +146,7 @@ class TestClusterManagerFactory : public ClusterManagerFactory { new NiceMock}; NiceMock& runtime_ = server_context_.runtime_loader_; NiceMock& dispatcher_ = server_context_.dispatcher_; - Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{ - dispatcher_.timeSource()}; + Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_{server_context_}; NiceMock& local_info_ = server_context_.local_info_; NiceMock& admin_ = server_context_.admin_; NiceMock secret_manager_; @@ -163,17 +162,18 @@ class TestClusterManagerFactory : public ClusterManagerFactory { // clusters, which is necessary in order to call updateHosts on the priority set. class TestClusterManagerImpl : public ClusterManagerImpl { public: - static std::unique_ptr - createAndInit(const envoy::config::bootstrap::v3::Bootstrap& bootstrap, - ClusterManagerFactory& factory, Stats::Store& stats, ThreadLocal::Instance& tls, - Runtime::Loader& runtime, const LocalInfo::LocalInfo& local_info, - AccessLog::AccessLogManager& log_manager, Event::Dispatcher& main_thread_dispatcher, - Server::Admin& admin, ProtobufMessage::ValidationContext& validation_context, - Api::Api& api, Http::Context& http_context, Grpc::Context& grpc_context, - Router::Context& router_context, Server::Instance& server) { - auto cluster_manager = std::unique_ptr{new TestClusterManagerImpl( - bootstrap, factory, stats, tls, runtime, local_info, log_manager, main_thread_dispatcher, - admin, validation_context, api, http_context, grpc_context, router_context, server)}; + static std::unique_ptr createAndInit( + const envoy::config::bootstrap::v3::Bootstrap& bootstrap, ClusterManagerFactory& factory, + Server::Configuration::CommonFactoryContext& context, Stats::Store& stats, + ThreadLocal::Instance& tls, Runtime::Loader& runtime, const LocalInfo::LocalInfo& local_info, + AccessLog::AccessLogManager& log_manager, Event::Dispatcher& main_thread_dispatcher, + Server::Admin& admin, ProtobufMessage::ValidationContext& validation_context, Api::Api& api, + Http::Context& http_context, Grpc::Context& grpc_context, Router::Context& router_context, + Server::Instance& server) { + auto cluster_manager = std::unique_ptr{ + new TestClusterManagerImpl(bootstrap, factory, context, stats, tls, runtime, local_info, + log_manager, main_thread_dispatcher, admin, validation_context, + api, http_context, grpc_context, router_context, server)}; THROW_IF_NOT_OK(cluster_manager->init(bootstrap)); return cluster_manager; } @@ -206,7 +206,8 @@ class TestClusterManagerImpl : public ClusterManagerImpl { using ClusterManagerImpl::ClusterManagerImpl; TestClusterManagerImpl(const envoy::config::bootstrap::v3::Bootstrap& bootstrap, - ClusterManagerFactory& factory, Stats::Store& stats, + ClusterManagerFactory& factory, + Server::Configuration::CommonFactoryContext& context, Stats::Store& stats, ThreadLocal::Instance& tls, Runtime::Loader& runtime, const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager, @@ -214,9 +215,9 @@ class TestClusterManagerImpl : public ClusterManagerImpl { ProtobufMessage::ValidationContext& validation_context, Api::Api& api, Http::Context& http_context, Grpc::Context& grpc_context, Router::Context& router_context, Server::Instance& server) - : ClusterManagerImpl(bootstrap, factory, stats, tls, runtime, local_info, log_manager, - main_thread_dispatcher, admin, validation_context, api, http_context, - grpc_context, router_context, server) {} + : ClusterManagerImpl(bootstrap, factory, context, stats, tls, runtime, local_info, + log_manager, main_thread_dispatcher, admin, validation_context, api, + http_context, grpc_context, router_context, server) {} }; } // namespace Upstream diff --git a/test/config/utility.cc b/test/config/utility.cc index 43f40bd93aef..5c7b4fcac21f 100644 --- a/test/config/utility.cc +++ b/test/config/utility.cc @@ -832,9 +832,10 @@ void ConfigHelper::setConnectConfig( match->mutable_connect_matcher(); } + auto* upgrade = route->mutable_route()->add_upgrade_configs(); + upgrade->set_upgrade_type("CONNECT"); + if (terminate_connect) { - auto* upgrade = route->mutable_route()->add_upgrade_configs(); - upgrade->set_upgrade_type("CONNECT"); auto* config = upgrade->mutable_connect_config(); if (allow_post) { config->set_allow_post(true); @@ -862,9 +863,11 @@ void ConfigHelper::setConnectUdpConfig( match->Clear(); match->mutable_connect_matcher(); + auto* upgrade = route->mutable_route()->add_upgrade_configs(); + upgrade->set_upgrade_type("connect-udp"); + if (terminate_connect) { - auto* upgrade = route->mutable_route()->add_upgrade_configs(); - upgrade->set_upgrade_type("connect-udp"); + upgrade->mutable_connect_config(); } hcm.add_upgrade_configs()->set_upgrade_type("connect-udp"); diff --git a/test/config_test/config_test.cc b/test/config_test/config_test.cc index c1e484675815..78b7969328fc 100644 --- a/test/config_test/config_test.cc +++ b/test/config_test/config_test.cc @@ -97,8 +97,10 @@ class ConfigTest { .Times(AtLeast(0)); envoy::config::bootstrap::v3::Bootstrap bootstrap; - Server::InstanceUtil::loadBootstrapConfig( - bootstrap, options_, server_.messageValidationContext().staticValidationVisitor(), *api_); + EXPECT_TRUE(Server::InstanceUtil::loadBootstrapConfig( + bootstrap, options_, + server_.messageValidationContext().staticValidationVisitor(), *api_) + .ok()); absl::Status creation_status; Server::Configuration::InitialImpl initial_config(bootstrap, creation_status); THROW_IF_NOT_OK_REF(creation_status); @@ -213,8 +215,9 @@ void testMerge() { OptionsImpl options(Server::createTestOptionsImpl("envoyproxy_io_proxy.yaml", overlay, Network::Address::IpVersion::v6)); envoy::config::bootstrap::v3::Bootstrap bootstrap; - Server::InstanceUtil::loadBootstrapConfig(bootstrap, options, - ProtobufMessage::getStrictValidationVisitor(), *api); + ASSERT_TRUE(Server::InstanceUtil::loadBootstrapConfig( + bootstrap, options, ProtobufMessage::getStrictValidationVisitor(), *api) + .ok()); EXPECT_EQ(2, bootstrap.static_resources().clusters_size()); } @@ -243,8 +246,9 @@ uint32_t run(const std::string& directory) { Envoy::Server::createTestOptionsImpl(filename, "", Network::Address::IpVersion::v6)); ConfigTest test1(options); envoy::config::bootstrap::v3::Bootstrap bootstrap; - Server::InstanceUtil::loadBootstrapConfig( - bootstrap, options, ProtobufMessage::getStrictValidationVisitor(), *api); + EXPECT_TRUE(Server::InstanceUtil::loadBootstrapConfig( + bootstrap, options, ProtobufMessage::getStrictValidationVisitor(), *api) + .ok()); ENVOY_LOG_MISC(info, "testing {} as yaml.", filename); OptionsImpl config = asConfigYaml(options, *api); ConfigTest test2(config); diff --git a/test/exe/BUILD b/test/exe/BUILD index 7be94770af65..065b80e97014 100644 --- a/test/exe/BUILD +++ b/test/exe/BUILD @@ -1,6 +1,7 @@ load( "//bazel:envoy_build_system.bzl", "envoy_cc_test", + "envoy_cc_test_library", "envoy_package", "envoy_select_admin_functionality", "envoy_sh_test", @@ -60,13 +61,50 @@ envoy_sh_test( ], ) +envoy_cc_test_library( + name = "main_common_test_base_lib", + srcs = ["main_common_test_base.cc"], + hdrs = ["main_common_test_base.h"], + data = [ + "//test/config/integration:google_com_proxy_port_0", + ], + deps = [ + "//source/common/api:api_lib", + "//source/common/stats:isolated_store_lib", + "//source/exe:envoy_main_common_with_core_extensions_lib", + "//source/exe:platform_impl_lib", + "//source/extensions/clusters/logical_dns:logical_dns_cluster_lib", + "//test/mocks/runtime:runtime_mocks", + "//test/test_common:contention_lib", + "//test/test_common:environment_lib", + "//test/test_common:thread_factory_for_test_lib", + ], +) + envoy_cc_test( name = "main_common_test", srcs = envoy_select_admin_functionality(["main_common_test.cc"]), data = [ "//test/config/integration:google_com_proxy_port_0", ], - deps = [ + deps = envoy_select_admin_functionality([":main_common_test_base_lib"]) + [ + "//source/common/api:api_lib", + "//source/exe:envoy_main_common_with_core_extensions_lib", + "//source/exe:platform_impl_lib", + "//source/extensions/clusters/logical_dns:logical_dns_cluster_lib", + "//test/mocks/runtime:runtime_mocks", + "//test/test_common:contention_lib", + "//test/test_common:environment_lib", + ], +) + +envoy_cc_test( + name = "admin_response_test", + srcs = envoy_select_admin_functionality(["admin_response_test.cc"]), + data = [ + "//test/config/integration:google_com_proxy_port_0", + ], + deps = envoy_select_admin_functionality([":main_common_test_base_lib"]) + [ "//source/common/api:api_lib", "//source/exe:envoy_main_common_with_core_extensions_lib", "//source/exe:platform_impl_lib", diff --git a/test/exe/admin_response_test.cc b/test/exe/admin_response_test.cc new file mode 100644 index 000000000000..33a7f10248e5 --- /dev/null +++ b/test/exe/admin_response_test.cc @@ -0,0 +1,351 @@ +#include "test/exe/main_common_test_base.h" + +#include "gtest/gtest.h" + +namespace Envoy { + +class AdminStreamingTest : public AdminRequestTestBase, public testing::Test { +protected: + static constexpr absl::string_view StreamingEndpoint = "/stream"; + + class StreamingAdminRequest : public Envoy::Server::Admin::Request { + public: + static constexpr uint64_t NumChunks = 10; + static constexpr uint64_t BytesPerChunk = 10000; + + StreamingAdminRequest(std::function& get_headers_hook, + std::function& next_chunk_hook) + : chunk_(BytesPerChunk, 'a'), get_headers_hook_(get_headers_hook), + next_chunk_hook_(next_chunk_hook) {} + Http::Code start(Http::ResponseHeaderMap&) override { + get_headers_hook_(); + return Http::Code::OK; + } + bool nextChunk(Buffer::Instance& response) override { + next_chunk_hook_(); + response.add(chunk_); + return --chunks_remaining_ > 0; + } + + private: + const std::string chunk_; + uint64_t chunks_remaining_{NumChunks}; + std::function& get_headers_hook_; + std::function& next_chunk_hook_; + }; + + AdminStreamingTest() : AdminRequestTestBase(Network::Address::IpVersion::v4) { + startEnvoy(); + started_.WaitForNotification(); + Server::Admin& admin = *main_common_->server()->admin(); + admin.addStreamingHandler( + std::string(StreamingEndpoint), "streaming api", + [this](Server::AdminStream&) -> Server::Admin::RequestPtr { + return std::make_unique(get_headers_hook_, next_chunk_hook_); + }, + true, false); + } + + struct ResponseData { + uint64_t num_chunks_{0}; + uint64_t num_bytes_{0}; + Http::Code code_; + std::string content_type_; + }; + + ResponseData runStreamingRequest(AdminResponseSharedPtr response, + std::function chunk_hook = nullptr) { + absl::Notification done; + std::vector out; + absl::Notification headers_notify; + ResponseData response_data; + response->getHeaders( + [&headers_notify, &response_data](Http::Code code, Http::ResponseHeaderMap& headers) { + response_data.code_ = code; + response_data.content_type_ = headers.getContentTypeValue(); + headers_notify.Notify(); + }); + headers_notify.WaitForNotification(); + bool cont = true; + while (cont && !response->cancelled()) { + absl::Notification chunk_notify; + response->nextChunk( + [&chunk_notify, &response_data, &cont](Buffer::Instance& chunk, bool more) { + cont = more; + response_data.num_bytes_ += chunk.length(); + chunk.drain(chunk.length()); + ++response_data.num_chunks_; + chunk_notify.Notify(); + }); + chunk_notify.WaitForNotification(); + if (chunk_hook != nullptr) { + chunk_hook(); + } + } + + return response_data; + } + + /** + * @return a streaming response to a GET of StreamingEndpoint. + */ + AdminResponseSharedPtr streamingResponse() { + return main_common_->adminRequest(StreamingEndpoint, "GET"); + } + + /** + * In order to trigger certain early-exit criteria in a test, we can exploit + * the fact that all the admin responses are delivered on the main thread. + * So we can pause those by blocking the main thread indefinitely. + * + * The provided lambda runs in the main thread, between two notifications + * controlled by this function. + * + * @param fn function to run in the main thread, before interlockMainThread returns. + */ + void interlockMainThread(std::function fn) { + main_common_->dispatcherForTest().post([this, fn] { + resume_.WaitForNotification(); + fn(); + pause_point_.Notify(); + }); + resume_.Notify(); + pause_point_.WaitForNotification(); + } + + /** + * Requests the headers and waits until the headers have been sent. + * + * @param response the response from which to get headers. + */ + void waitForHeaders(AdminResponseSharedPtr response) { + absl::Notification headers_notify; + response->getHeaders( + [&headers_notify](Http::Code, Http::ResponseHeaderMap&) { headers_notify.Notify(); }); + headers_notify.WaitForNotification(); + } + + /** + * Initiates a '/quitquitquit' call and requests the headers for that call, + * but does not wait for the call to complete. We avoid waiting in order to + * trigger a potential race to ensure that MainCommon handles it properly. + */ + void quitAndRequestHeaders() { + AdminResponseSharedPtr quit_response = main_common_->adminRequest("/quitquitquit", "POST"); + quit_response->getHeaders([](Http::Code, Http::ResponseHeaderMap&) {}); + } + + // This variable provides a hook to allow a test method to specify a hook to + // run when nextChunk() is called. This is currently used only for one test, + // CancelAfterAskingForChunk, that initiates a cancel() from within the chunk + // handler. + std::function get_headers_hook_ = []() {}; + std::function next_chunk_hook_ = []() {}; +}; + +TEST_F(AdminStreamingTest, RequestGetStatsAndQuit) { + AdminResponseSharedPtr response = streamingResponse(); + ResponseData response_data = runStreamingRequest(response); + EXPECT_EQ(StreamingAdminRequest::NumChunks, response_data.num_chunks_); + EXPECT_EQ(StreamingAdminRequest::NumChunks * StreamingAdminRequest::BytesPerChunk, + response_data.num_bytes_); + EXPECT_EQ(Http::Code::OK, response_data.code_); + EXPECT_EQ("text/plain; charset=UTF-8", response_data.content_type_); + EXPECT_TRUE(quitAndWait()); +} + +TEST_F(AdminStreamingTest, QuitDuringChunks) { + int quit_counter = 0; + static constexpr int chunks_to_send_before_quitting = 3; + AdminResponseSharedPtr response = streamingResponse(); + ResponseData response_data = runStreamingRequest(response, [&quit_counter, this]() { + if (++quit_counter == chunks_to_send_before_quitting) { + EXPECT_TRUE(quitAndWait()); + } + }); + EXPECT_EQ(4, response_data.num_chunks_); + EXPECT_EQ(chunks_to_send_before_quitting * StreamingAdminRequest::BytesPerChunk, + response_data.num_bytes_); + EXPECT_EQ(Http::Code::OK, response_data.code_); + EXPECT_EQ("text/plain; charset=UTF-8", response_data.content_type_); +} + +TEST_F(AdminStreamingTest, CancelDuringChunks) { + int quit_counter = 0; + static constexpr int chunks_to_send_before_quitting = 3; + AdminResponseSharedPtr response = streamingResponse(); + ResponseData response_data = runStreamingRequest(response, [response, &quit_counter]() { + if (++quit_counter == chunks_to_send_before_quitting) { + response->cancel(); + } + }); + EXPECT_EQ(3, response_data.num_chunks_); // no final call to the chunk handler after cancel. + EXPECT_EQ(chunks_to_send_before_quitting * StreamingAdminRequest::BytesPerChunk, + response_data.num_bytes_); + EXPECT_EQ(Http::Code::OK, response_data.code_); + EXPECT_EQ("text/plain; charset=UTF-8", response_data.content_type_); + EXPECT_TRUE(quitAndWait()); +} + +TEST_F(AdminStreamingTest, CancelBeforeAskingForHeader) { + AdminResponseSharedPtr response = streamingResponse(); + interlockMainThread([response]() { response->cancel(); }); + int header_calls = 0; + + // After 'cancel', the headers function will not be called. + response->getHeaders([&header_calls](Http::Code, Http::ResponseHeaderMap&) { ++header_calls; }); + EXPECT_TRUE(quitAndWait()); + EXPECT_EQ(0, header_calls); +} + +TEST_F(AdminStreamingTest, CancelAfterAskingForHeader1) { + int header_calls = 0; + AdminResponseSharedPtr response = streamingResponse(); + interlockMainThread([&header_calls, response]() { + response->getHeaders([&header_calls](Http::Code, Http::ResponseHeaderMap&) { ++header_calls; }); + response->cancel(); + }); + EXPECT_TRUE(quitAndWait()); + EXPECT_EQ(0, header_calls); +} + +TEST_F(AdminStreamingTest, CancelAfterAskingForHeader2) { + int header_calls = 0; + AdminResponseSharedPtr response = streamingResponse(); + get_headers_hook_ = [&response]() { response->cancel(); }; + response->getHeaders([&header_calls](Http::Code, Http::ResponseHeaderMap&) { ++header_calls; }); + EXPECT_TRUE(quitAndWait()); + EXPECT_EQ(0, header_calls); +} + +TEST_F(AdminStreamingTest, DeleteAfterAskingForHeader1) { + int header_calls = 0; + AdminResponseSharedPtr response = streamingResponse(); + interlockMainThread([&response, &header_calls]() { + response->getHeaders([&header_calls](Http::Code, Http::ResponseHeaderMap&) { ++header_calls; }); + response.reset(); + }); + EXPECT_TRUE(quitAndWait()); + EXPECT_EQ(1, header_calls); +} + +TEST_F(AdminStreamingTest, DeleteAfterAskingForHeader2) { + int header_calls = 0; + AdminResponseSharedPtr response = streamingResponse(); + get_headers_hook_ = [&response]() { response.reset(); }; + response->getHeaders([&header_calls](Http::Code, Http::ResponseHeaderMap&) { ++header_calls; }); + EXPECT_TRUE(quitAndWait()); + EXPECT_EQ(1, header_calls); +} + +TEST_F(AdminStreamingTest, CancelBeforeAskingForChunk1) { + AdminResponseSharedPtr response = streamingResponse(); + waitForHeaders(response); + response->cancel(); + int chunk_calls = 0; + response->nextChunk([&chunk_calls](Buffer::Instance&, bool) { ++chunk_calls; }); + EXPECT_TRUE(quitAndWait()); + EXPECT_EQ(0, chunk_calls); +} + +TEST_F(AdminStreamingTest, CancelBeforeAskingForChunk2) { + AdminResponseSharedPtr response = streamingResponse(); + waitForHeaders(response); + int chunk_calls = 0; + interlockMainThread([&response, &chunk_calls]() { + response->nextChunk([&chunk_calls](Buffer::Instance&, bool) { ++chunk_calls; }); + response->cancel(); + }); + EXPECT_TRUE(quitAndWait()); + EXPECT_EQ(0, chunk_calls); +} + +TEST_F(AdminStreamingTest, CancelAfterAskingForChunk) { + AdminResponseSharedPtr response = streamingResponse(); + waitForHeaders(response); + int chunk_calls = 0; + + // Cause the /streaming handler to pause while yielding the next chunk, to hit + // an early exit in requestNextChunk. + next_chunk_hook_ = [response]() { response->cancel(); }; + + interlockMainThread([&chunk_calls, response]() { + response->nextChunk([&chunk_calls](Buffer::Instance&, bool) { ++chunk_calls; }); + }); + + EXPECT_TRUE(quitAndWait()); + EXPECT_EQ(0, chunk_calls); +} + +TEST_F(AdminStreamingTest, QuitBeforeHeaders) { + AdminResponseSharedPtr response = streamingResponse(); + EXPECT_TRUE(quitAndWait()); + ResponseData response_data = runStreamingRequest(response); + EXPECT_EQ(1, response_data.num_chunks_); + EXPECT_EQ(0, response_data.num_bytes_); + EXPECT_EQ(Http::Code::InternalServerError, response_data.code_); + EXPECT_EQ("text/plain; charset=UTF-8", response_data.content_type_); +} + +TEST_F(AdminStreamingTest, QuitDeleteRace1) { + AdminResponseSharedPtr response = streamingResponse(); + // Initiates a streaming quit on the main thread, but do not wait for it. + quitAndRequestHeaders(); + response.reset(); // Races with the quitquitquit + EXPECT_TRUE(waitForEnvoyToExit()); +} + +TEST_F(AdminStreamingTest, QuitDeleteRace2) { + AdminResponseSharedPtr response = streamingResponse(); + adminRequest("/quitquitquit", "POST"); + response.reset(); + EXPECT_TRUE(waitForEnvoyToExit()); +} + +TEST_F(AdminStreamingTest, QuitCancelRace) { + AdminResponseSharedPtr response = streamingResponse(); + quitAndRequestHeaders(); + response->cancel(); // Races with the quitquitquit + EXPECT_TRUE(waitForEnvoyToExit()); +} + +TEST_F(AdminStreamingTest, QuitBeforeCreatingResponse) { + // Initiates a streaming quit on the main thread, and wait for headers, which + // will trigger the termination of the event loop, and subsequent nulling of + // main_common_. However we can pause the test infrastructure after the quit + // takes hold leaving main_common_ in tact, to reproduce a potential race. + pause_after_run_ = true; + adminRequest("/quitquitquit", "POST"); + pause_point_.WaitForNotification(); // run() finished, but main_common_ still exists. + AdminResponseSharedPtr response = streamingResponse(); + ResponseData response_data = runStreamingRequest(response); + EXPECT_EQ(1, response_data.num_chunks_); + EXPECT_EQ(0, response_data.num_bytes_); + EXPECT_EQ(Http::Code::InternalServerError, response_data.code_); + EXPECT_EQ("text/plain; charset=UTF-8", response_data.content_type_); + resume_.Notify(); + EXPECT_TRUE(waitForEnvoyToExit()); + response.reset(); +} + +TEST_F(AdminStreamingTest, TimeoutGettingResponse) { + absl::Notification got_headers; + AdminResponseSharedPtr response = streamingResponse(); + + // Mimics a slow admin response by adding a blocking notification in front + // of a call to initiate an admin request. + main_common_->dispatcherForTest().post([this, response, &got_headers] { + resume_.WaitForNotification(); + response->getHeaders( + [&got_headers](Http::Code, Http::ResponseHeaderMap&) { got_headers.Notify(); }); + pause_point_.Notify(); + }); + + ENVOY_LOG_MISC(info, "Blocking for 5 seconds to test timeout functionality..."); + ASSERT_FALSE(got_headers.WaitForNotificationWithTimeout(absl::Seconds(5))); + resume_.Notify(); + pause_point_.WaitForNotification(); + EXPECT_TRUE(quitAndWait()); +} + +} // namespace Envoy diff --git a/test/exe/main_common_test.cc b/test/exe/main_common_test.cc index 68d142e5b3b8..942a53e3e3b1 100644 --- a/test/exe/main_common_test.cc +++ b/test/exe/main_common_test.cc @@ -1,6 +1,5 @@ #include "envoy/common/platform.h" -#include "source/common/common/lock_guard.h" #include "source/common/common/mutex_tracer_impl.h" #include "source/common/common/random_generator.h" #include "source/common/common/thread.h" @@ -9,6 +8,7 @@ #include "source/exe/platform_impl.h" #include "source/server/options_impl.h" +#include "test/exe/main_common_test_base.h" #include "test/mocks/common.h" #include "test/test_common/contention.h" #include "test/test_common/environment.h" @@ -52,34 +52,10 @@ const std::string& outOfMemoryPattern() { * an argv array that is terminated with nullptr. Identifies the config * file relative to runfiles directory. */ -class MainCommonTest : public testing::TestWithParam { +class MainCommonTest : public MainCommonTestBase, + public testing::TestWithParam { protected: - MainCommonTest() - : config_file_(TestEnvironment::temporaryFileSubstitute( - "test/config/integration/google_com_proxy_port_0.yaml", TestEnvironment::ParamMap(), - TestEnvironment::PortMap(), GetParam())), - argv_({"envoy-static", "--use-dynamic-base-id", "-c", config_file_.c_str(), nullptr}) {} - - const char* const* argv() { return &argv_[0]; } - int argc() { return argv_.size() - 1; } - - // Adds an argument, assuring that argv remains null-terminated. - void addArg(const char* arg) { - ASSERT(!argv_.empty()); - const size_t last = argv_.size() - 1; - ASSERT(argv_[last] == nullptr); // invariant established in ctor, maintained below. - argv_[last] = arg; // guaranteed non-empty - argv_.push_back(nullptr); - } - - // Adds options to make Envoy exit immediately after initialization. - void initOnly() { - addArg("--mode"); - addArg("init_only"); - } - - std::string config_file_; - std::vector argv_; + MainCommonTest() : MainCommonTestBase(GetParam()) {} }; INSTANTIATE_TEST_SUITE_P(IpVersions, MainCommonTest, testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), @@ -223,6 +199,16 @@ TEST_P(MainCommonTest, RetryDynamicBaseIdFails) { #endif } +// Verifies that the Logger::Registry is usable after constructing and +// destructing MainCommon. +TEST_P(MainCommonTest, ConstructDestructLogger) { + VERBOSE_EXPECT_NO_THROW(MainCommon main_common(argc(), argv())); + + const std::string logger_name = "logger"; + spdlog::details::log_msg log_msg(logger_name, spdlog::level::level_enum::err, "error"); + Logger::Registry::getSink()->log(log_msg); +} + // Test that std::set_new_handler() was called and the callback functions as expected. // This test fails under TSAN and ASAN, so don't run it in that build: // [ DEATH ] ==845==ERROR: ThreadSanitizer: requested allocation size 0x3e800000000 @@ -268,96 +254,10 @@ TEST_P(MainCommonDeathTest, OutOfMemoryHandler) { #endif } -class AdminRequestTest : public MainCommonTest { +class AdminRequestTest : public AdminRequestTestBase, + public testing::TestWithParam { protected: - AdminRequestTest() { addArg("--disable-hot-restart"); } - - // Runs an admin request specified in path, blocking until completion, and - // returning the response body. - std::string adminRequest(absl::string_view path, absl::string_view method) { - absl::Notification done; - std::string out; - main_common_->adminRequest( - path, method, - [&done, &out](const Http::HeaderMap& /*response_headers*/, absl::string_view body) { - out = std::string(body); - done.Notify(); - }); - done.WaitForNotification(); - return out; - } - - // Initiates Envoy running in its own thread. - void startEnvoy() { - envoy_thread_ = Thread::threadFactoryForTest().createThread([this]() { - // Note: main_common_ is accessed in the testing thread, but - // is race-free, as MainCommon::run() does not return until - // triggered with an adminRequest POST to /quitquitquit, which - // is done in the testing thread. - main_common_ = std::make_unique(argc(), argv()); - envoy_started_ = true; - started_.Notify(); - pauseResumeInterlock(pause_before_run_); - bool status = main_common_->run(); - pauseResumeInterlock(pause_after_run_); - main_common_.reset(); - envoy_finished_ = true; - envoy_return_ = status; - finished_.Notify(); - }); - } - - // Conditionally pauses at a critical point in the Envoy thread, waiting for - // the test thread to trigger something at that exact line. The test thread - // can then call resume_.Notify() to allow the Envoy thread to resume. - void pauseResumeInterlock(bool enable) { - if (enable) { - pause_point_.Notify(); - resume_.WaitForNotification(); - } - } - - // Wait until Envoy is inside the main server run loop proper. Before entering, Envoy runs any - // pending post callbacks, so it's not reliable to use adminRequest() or post() to do this. - // Generally, tests should not depend on this for correctness, but as a result of - // https://github.com/libevent/libevent/issues/779 we need to for TSAN. This is because the entry - // to event_base_loop() is where the signal base race occurs, but once we're in that loop in - // blocking mode, we're safe to take signals. - // TODO(htuch): Remove when https://github.com/libevent/libevent/issues/779 is fixed. - void waitForEnvoyRun() { - absl::Notification done; - main_common_->dispatcherForTest().post([this, &done] { - struct Sacrifice : Event::DeferredDeletable { - Sacrifice(absl::Notification& notify) : notify_(notify) {} - ~Sacrifice() override { notify_.Notify(); } - absl::Notification& notify_; - }; - auto sacrifice = std::make_unique(done); - // Wait for a deferred delete cleanup, this only happens in the main server run loop. - main_common_->dispatcherForTest().deferredDelete(std::move(sacrifice)); - }); - done.WaitForNotification(); - } - - // Having triggered Envoy to quit (via signal or /quitquitquit), this blocks until Envoy exits. - bool waitForEnvoyToExit() { - finished_.WaitForNotification(); - envoy_thread_->join(); - return envoy_return_; - } - - Stats::IsolatedStoreImpl stats_store_; - std::unique_ptr envoy_thread_; - std::unique_ptr main_common_; - absl::Notification started_; - absl::Notification finished_; - absl::Notification resume_; - absl::Notification pause_point_; - bool envoy_return_{false}; - bool envoy_started_{false}; - bool envoy_finished_{false}; - bool pause_before_run_{false}; - bool pause_after_run_{false}; + AdminRequestTest() : AdminRequestTestBase(GetParam()) {} }; INSTANTIATE_TEST_SUITE_P(IpVersions, AdminRequestTest, testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), @@ -367,8 +267,7 @@ TEST_P(AdminRequestTest, AdminRequestGetStatsAndQuit) { startEnvoy(); started_.WaitForNotification(); EXPECT_THAT(adminRequest("/stats", "GET"), HasSubstr("filesystem.reopen_failed")); - adminRequest("/quitquitquit", "POST"); - EXPECT_TRUE(waitForEnvoyToExit()); + quitAndWait(); } // no signals on Windows -- could probably make this work with GenerateConsoleCtrlEvent @@ -459,8 +358,7 @@ TEST_P(AdminRequestTest, AdminRequestBeforeRun) { // We don't get a notification when run(), so it's not safe to check whether the // admin handler is called until after we quit. - adminRequest("/quitquitquit", "POST"); - EXPECT_TRUE(waitForEnvoyToExit()); + quitAndWait(); EXPECT_TRUE(admin_handler_was_called); // This just checks that some stat output was reported. We could pick any stat. @@ -515,14 +413,4 @@ TEST_P(AdminRequestTest, AdminRequestAfterRun) { EXPECT_EQ(1, lambda_destroy_count); } -// Verifies that the Logger::Registry is usable after constructing and -// destructing MainCommon. -TEST_P(MainCommonTest, ConstructDestructLogger) { - VERBOSE_EXPECT_NO_THROW(MainCommon main_common(argc(), argv())); - - const std::string logger_name = "logger"; - spdlog::details::log_msg log_msg(logger_name, spdlog::level::level_enum::err, "error"); - Logger::Registry::getSink()->log(log_msg); -} - } // namespace Envoy diff --git a/test/exe/main_common_test_base.cc b/test/exe/main_common_test_base.cc new file mode 100644 index 000000000000..d0f3960b4148 --- /dev/null +++ b/test/exe/main_common_test_base.cc @@ -0,0 +1,117 @@ +#include "test/exe/main_common_test_base.h" + +#include "source/common/common/thread.h" + +#include "test/test_common/thread_factory_for_test.h" + +namespace Envoy { + +MainCommonTestBase::MainCommonTestBase(Network::Address::IpVersion version) + : config_file_(TestEnvironment::temporaryFileSubstitute( + "test/config/integration/google_com_proxy_port_0.yaml", TestEnvironment::ParamMap(), + TestEnvironment::PortMap(), version)), + argv_({"envoy-static", "--use-dynamic-base-id", "-c", config_file_.c_str(), nullptr}) {} + +const char* const* MainCommonTestBase::argv() { return &argv_[0]; } +int MainCommonTestBase::argc() { return argv_.size() - 1; } + +// Adds an argument, assuring that argv remains null-terminated. +void MainCommonTestBase::addArg(const char* arg) { + ASSERT(!argv_.empty()); + const size_t last = argv_.size() - 1; + ASSERT(argv_[last] == nullptr); // invariant established in ctor, maintained below. + argv_[last] = arg; // guaranteed non-empty + argv_.push_back(nullptr); +} + +// Adds options to make Envoy exit immediately after initialization. +void MainCommonTestBase::initOnly() { + addArg("--mode"); + addArg("init_only"); +} + +AdminRequestTestBase::AdminRequestTestBase(Network::Address::IpVersion version) + : MainCommonTestBase(version) { + addArg("--disable-hot-restart"); +} + +// Runs an admin request specified in path, blocking until completion, and +// returning the response body. +std::string AdminRequestTestBase::adminRequest(absl::string_view path, absl::string_view method) { + absl::Notification done; + std::string out; + main_common_->adminRequest( + path, method, + [&done, &out](const Http::HeaderMap& /*response_headers*/, absl::string_view body) { + out = std::string(body); + done.Notify(); + }); + done.WaitForNotification(); + return out; +} + +// Initiates Envoy running in its own thread. +void AdminRequestTestBase::startEnvoy() { + envoy_thread_ = Thread::threadFactoryForTest().createThread([this]() { + // Note: main_common_ is accessed in the testing thread, but + // is race-free, as MainCommon::run() does not return until + // triggered with an adminRequest POST to /quitquitquit, which + // is done in the testing thread. + main_common_ = std::make_unique(argc(), argv()); + envoy_started_ = true; + started_.Notify(); + pauseResumeInterlock(pause_before_run_); + bool status = main_common_->run(); + pauseResumeInterlock(pause_after_run_); + main_common_.reset(); + envoy_finished_ = true; + envoy_return_ = status; + finished_.Notify(); + }); +} + +// Conditionally pauses at a critical point in the Envoy thread, waiting for +// the test thread to trigger something at that exact line. The test thread +// can then call resume_.Notify() to allow the Envoy thread to resume. +void AdminRequestTestBase::pauseResumeInterlock(bool enable) { + if (enable) { + pause_point_.Notify(); + resume_.WaitForNotification(); + } +} + +// Wait until Envoy is inside the main server run loop proper. Before entering, Envoy runs any +// pending post callbacks, so it's not reliable to use adminRequest() or post() to do this. +// Generally, tests should not depend on this for correctness, but as a result of +// https://github.com/libevent/libevent/issues/779 we need to for TSAN. This is because the entry +// to event_base_loop() is where the signal base race occurs, but once we're in that loop in +// blocking mode, we're safe to take signals. +// TODO(htuch): Remove when https://github.com/libevent/libevent/issues/779 is fixed. +void AdminRequestTestBase::waitForEnvoyRun() { + absl::Notification done; + main_common_->dispatcherForTest().post([this, &done] { + struct Sacrifice : Event::DeferredDeletable { + Sacrifice(absl::Notification& notify) : notify_(notify) {} + ~Sacrifice() override { notify_.Notify(); } + absl::Notification& notify_; + }; + auto sacrifice = std::make_unique(done); + // Wait for a deferred delete cleanup, this only happens in the main server run loop. + main_common_->dispatcherForTest().deferredDelete(std::move(sacrifice)); + }); + done.WaitForNotification(); +} + +// Having triggered Envoy to quit (via signal or /quitquitquit), this blocks until Envoy exits. +bool AdminRequestTestBase::waitForEnvoyToExit() { + finished_.WaitForNotification(); + envoy_thread_->join(); + return envoy_return_; +} + +bool AdminRequestTestBase::quitAndWait() { + adminRequest("/quitquitquit", "POST"); + return waitForEnvoyToExit(); +} + +} // namespace Envoy diff --git a/test/exe/main_common_test_base.h b/test/exe/main_common_test_base.h new file mode 100644 index 000000000000..91ae895dd69f --- /dev/null +++ b/test/exe/main_common_test_base.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include + +#include "source/common/stats/isolated_store_impl.h" +#include "source/exe/main_common.h" + +#include "test/test_common/environment.h" + +#include "absl/synchronization/notification.h" + +namespace Envoy { + +class MainCommonTestBase { +protected: + MainCommonTestBase(Network::Address::IpVersion version); + const char* const* argv(); + int argc(); + + // Adds an argument, assuring that argv remains null-terminated. + void addArg(const char* arg); + + // Adds options to make Envoy exit immediately after initialization. + void initOnly(); + + std::string config_file_; + std::vector argv_; +}; + +class AdminRequestTestBase : public MainCommonTestBase { +protected: + AdminRequestTestBase(Network::Address::IpVersion version); + + // Runs an admin request specified in path, blocking until completion, and + // returning the response body. + std::string adminRequest(absl::string_view path, absl::string_view method); + + // Initiates Envoy running in its own thread. + void startEnvoy(); + + // Conditionally pauses at a critical point in the Envoy thread, waiting for + // the test thread to trigger something at that exact line. The test thread + // can then call resume_.Notify() to allow the Envoy thread to resume. + void pauseResumeInterlock(bool enable); + + // Wait until Envoy is inside the main server run loop proper. Before entering, Envoy runs any + // pending post callbacks, so it's not reliable to use adminRequest() or post() to do this. + // Generally, tests should not depend on this for correctness, but as a result of + // https://github.com/libevent/libevent/issues/779 we need to for TSAN. This is because the entry + // to event_base_loop() is where the signal base race occurs, but once we're in that loop in + // blocking mode, we're safe to take signals. + // TODO(htuch): Remove when https://github.com/libevent/libevent/issues/779 is fixed. + void waitForEnvoyRun(); + + // Having triggered Envoy to quit (via signal or /quitquitquit), this blocks until Envoy exits. + bool waitForEnvoyToExit(); + + // Sends a quit request to the server, and waits for Envoy to exit. Returns + // true if successful. + bool quitAndWait(); + + Stats::IsolatedStoreImpl stats_store_; + std::unique_ptr envoy_thread_; + std::unique_ptr main_common_; + absl::Notification started_; + absl::Notification finished_; + absl::Notification resume_; + absl::Notification pause_point_; + bool envoy_return_{false}; + bool envoy_started_{false}; + bool envoy_finished_{false}; + bool pause_before_run_{false}; + bool pause_after_run_{false}; +}; + +} // namespace Envoy diff --git a/test/extensions/access_loggers/fluentd/fluentd_access_log_impl_test.cc b/test/extensions/access_loggers/fluentd/fluentd_access_log_impl_test.cc index a058c22dcf07..14c7bd089fca 100644 --- a/test/extensions/access_loggers/fluentd/fluentd_access_log_impl_test.cc +++ b/test/extensions/access_loggers/fluentd/fluentd_access_log_impl_test.cc @@ -31,16 +31,25 @@ class FluentdAccessLoggerImplTest : public testing::Test { public: FluentdAccessLoggerImplTest() : async_client_(new Tcp::AsyncClient::MockAsyncTcpClient()), - timer_(new Event::MockTimer(&dispatcher_)) {} + backoff_strategy_(new MockBackOffStrategy()), + flush_timer_(new Event::MockTimer(&dispatcher_)), + retry_timer_(new Event::MockTimer(&dispatcher_)) {} - void init(int buffer_size_bytes = 0) { + void init(int buffer_size_bytes = 1, absl::optional max_connect_attempts = absl::nullopt) { EXPECT_CALL(*async_client_, setAsyncTcpClientCallbacks(_)); - EXPECT_CALL(*timer_, enableTimer(_, _)); + EXPECT_CALL(*flush_timer_, enableTimer(_, _)); config_.set_tag(tag_); + + if (max_connect_attempts.has_value()) { + config_.mutable_retry_options()->mutable_max_connect_attempts()->set_value( + max_connect_attempts.value()); + } + config_.mutable_buffer_size_bytes()->set_value(buffer_size_bytes); logger_ = std::make_unique( - Tcp::AsyncTcpClientPtr{async_client_}, dispatcher_, config_, *stats_store_.rootScope()); + cluster_, Tcp::AsyncTcpClientPtr{async_client_}, dispatcher_, config_, + BackOffStrategyPtr{backoff_strategy_}, *stats_store_.rootScope()); } std::string getExpectedMsgpackPayload(int entries_count) { @@ -62,10 +71,13 @@ class FluentdAccessLoggerImplTest : public testing::Test { std::string tag_ = "test.tag"; uint64_t time_ = 123; std::vector data_ = {10, 20}; + NiceMock cluster_; Tcp::AsyncClient::MockAsyncTcpClient* async_client_; + MockBackOffStrategy* backoff_strategy_; Stats::IsolatedStoreImpl stats_store_; Event::MockDispatcher dispatcher_; - Event::MockTimer* timer_; + Event::MockTimer* flush_timer_; + Event::MockTimer* retry_timer_; std::unique_ptr logger_; envoy::extensions::access_loggers::fluentd::v3::FluentdAccessLogConfig config_; }; @@ -87,8 +99,8 @@ TEST_F(FluentdAccessLoggerImplTest, NoWriteOnLogIfBufferLimitNotPassed) { } TEST_F(FluentdAccessLoggerImplTest, NoWriteOnLogIfDisconnectedByRemote) { - init(); - EXPECT_CALL(*timer_, disableTimer()); + init(1, 1); + EXPECT_CALL(*flush_timer_, disableTimer()); EXPECT_CALL(*async_client_, write(_, _)).Times(0); EXPECT_CALL(*async_client_, connected()).WillOnce(Return(false)); EXPECT_CALL(*async_client_, connect()).WillOnce(Invoke([this]() -> bool { @@ -100,8 +112,8 @@ TEST_F(FluentdAccessLoggerImplTest, NoWriteOnLogIfDisconnectedByRemote) { } TEST_F(FluentdAccessLoggerImplTest, NoWriteOnLogIfDisconnectedByLocal) { - init(); - EXPECT_CALL(*timer_, disableTimer()); + init(1, 1); + EXPECT_CALL(*flush_timer_, disableTimer()); EXPECT_CALL(*async_client_, write(_, _)).Times(0); EXPECT_CALL(*async_client_, connected()).WillOnce(Return(false)); EXPECT_CALL(*async_client_, connect()).WillOnce(Invoke([this]() -> bool { @@ -114,6 +126,8 @@ TEST_F(FluentdAccessLoggerImplTest, NoWriteOnLogIfDisconnectedByLocal) { TEST_F(FluentdAccessLoggerImplTest, LogSingleEntry) { init(); // Default buffer limit is 0 so single entry should be flushed immediately. + EXPECT_CALL(*backoff_strategy_, reset()); + EXPECT_CALL(*retry_timer_, disableTimer()); EXPECT_CALL(*async_client_, connected()).WillOnce(Return(false)).WillOnce(Return(true)); EXPECT_CALL(*async_client_, connect()).WillOnce(Invoke([this]() -> bool { logger_->onEvent(Network::ConnectionEvent::Connected); @@ -133,6 +147,8 @@ TEST_F(FluentdAccessLoggerImplTest, LogTwoEntries) { init(12); // First entry is 10 bytes, so first entry should not cause the logger to flush. // First log should not be flushed. + EXPECT_CALL(*backoff_strategy_, reset()); + EXPECT_CALL(*retry_timer_, disableTimer()); EXPECT_CALL(*async_client_, connected()).Times(0); EXPECT_CALL(*async_client_, write(_, _)).Times(0); logger_->log(std::make_unique(time_, std::move(data_))); @@ -164,11 +180,123 @@ TEST_F(FluentdAccessLoggerImplTest, CallbacksTest) { EXPECT_NO_THROW(logger_->onData(buffer, false)); } +TEST_F(FluentdAccessLoggerImplTest, SuccessfulReconnect) { + init(1, 2); + EXPECT_CALL(*backoff_strategy_, nextBackOffMs()).WillOnce(Return(1)); + EXPECT_CALL(*async_client_, write(_, _)).Times(0); + EXPECT_CALL(*async_client_, connected()).WillOnce(Return(false)).WillOnce(Return(true)); + EXPECT_CALL(*async_client_, connect()) + .WillOnce(Invoke([this]() -> bool { + EXPECT_CALL(*backoff_strategy_, reset()).Times(0); + EXPECT_CALL(*retry_timer_, enableTimer(std::chrono::milliseconds(1), _)); + EXPECT_CALL(*retry_timer_, disableTimer()).Times(0); + logger_->onEvent(Network::ConnectionEvent::LocalClose); + return true; + })) + .WillOnce(Invoke([this]() -> bool { + EXPECT_CALL(*backoff_strategy_, reset()); + EXPECT_CALL(*retry_timer_, enableTimer(_, _)).Times(0); + EXPECT_CALL(*retry_timer_, disableTimer()); + logger_->onEvent(Network::ConnectionEvent::Connected); + return true; + })); + EXPECT_CALL(*async_client_, write(_, _)) + .WillOnce(Invoke([&](Buffer::Instance& buffer, bool end_stream) { + EXPECT_FALSE(end_stream); + std::string expected_payload = getExpectedMsgpackPayload(1); + EXPECT_EQ(expected_payload, buffer.toString()); + })); + + logger_->log(std::make_unique(time_, std::move(data_))); + retry_timer_->invokeCallback(); +} + +TEST_F(FluentdAccessLoggerImplTest, ReconnectFailure) { + init(1, 2); + + EXPECT_CALL(*backoff_strategy_, nextBackOffMs()).WillOnce(Return(1)); + EXPECT_CALL(*backoff_strategy_, reset()).Times(0); + EXPECT_CALL(*retry_timer_, enableTimer(std::chrono::milliseconds(1), _)); + EXPECT_CALL(*retry_timer_, disableTimer()).Times(0); + + EXPECT_CALL(*flush_timer_, disableTimer()); + EXPECT_CALL(*async_client_, write(_, _)).Times(0); + EXPECT_CALL(*async_client_, connected()).WillOnce(Return(false)); + EXPECT_CALL(*async_client_, connect()) + .WillOnce(Invoke([this]() -> bool { + logger_->onEvent(Network::ConnectionEvent::LocalClose); + return true; + })) + .WillOnce(Invoke([this]() -> bool { + logger_->onEvent(Network::ConnectionEvent::LocalClose); + return true; + })); + + logger_->log(std::make_unique(time_, std::move(data_))); + retry_timer_->invokeCallback(); +} + +TEST_F(FluentdAccessLoggerImplTest, TwoReconnects) { + init(1, 3); + + EXPECT_CALL(*backoff_strategy_, nextBackOffMs()).WillOnce(Return(1)).WillOnce(Return(1)); + EXPECT_CALL(*backoff_strategy_, reset()).Times(0); + EXPECT_CALL(*retry_timer_, enableTimer(std::chrono::milliseconds(1), _)).Times(2); + EXPECT_CALL(*retry_timer_, disableTimer()).Times(0); + + EXPECT_CALL(*flush_timer_, disableTimer()); + EXPECT_CALL(*async_client_, write(_, _)).Times(0); + EXPECT_CALL(*async_client_, connected()).WillOnce(Return(false)); + EXPECT_CALL(*async_client_, connect()) + .WillOnce(Invoke([this]() -> bool { + logger_->onEvent(Network::ConnectionEvent::LocalClose); + return true; + })) + .WillOnce(Invoke([this]() -> bool { + logger_->onEvent(Network::ConnectionEvent::LocalClose); + return true; + })) + .WillOnce(Invoke([this]() -> bool { + logger_->onEvent(Network::ConnectionEvent::LocalClose); + return true; + })); + + logger_->log(std::make_unique(time_, std::move(data_))); + retry_timer_->invokeCallback(); + retry_timer_->invokeCallback(); +} + +TEST_F(FluentdAccessLoggerImplTest, RetryOnNoHealthyUpstream) { + init(); + + EXPECT_CALL(*backoff_strategy_, nextBackOffMs()).WillOnce(Return(1)); + EXPECT_CALL(*backoff_strategy_, reset()).Times(0); + EXPECT_CALL(*retry_timer_, enableTimer(std::chrono::milliseconds(1), _)); + EXPECT_CALL(*retry_timer_, disableTimer()).Times(0); + + EXPECT_CALL(*async_client_, write(_, _)).Times(0); + EXPECT_CALL(*async_client_, connected()).WillOnce(Return(false)); + EXPECT_CALL(*async_client_, connect()).WillOnce(Return(false)); + logger_->log(std::make_unique(time_, std::move(data_))); +} + +TEST_F(FluentdAccessLoggerImplTest, NoWriteOnBufferFull) { + // Setting the buffer to 0 so new log will be thrown. + init(0); + EXPECT_CALL(*async_client_, write(_, _)).Times(0); + EXPECT_CALL(*async_client_, connect()).Times(0); + EXPECT_CALL(*async_client_, connected()).Times(0); + logger_->log(std::make_unique(time_, std::move(data_))); +} + class FluentdAccessLoggerCacheImplTest : public testing::Test { public: - FluentdAccessLoggerCacheImplTest() : logger_cache_(cluster_manager_, scope_, tls_) {} - void init(bool second_logger = false) { + tls_.setDispatcher(&dispatcher_); + flush_timer_ = new Event::MockTimer(&dispatcher_); + retry_timer_ = new Event::MockTimer(&dispatcher_); + EXPECT_CALL(*flush_timer_, enableTimer(_, _)); + async_client1_ = new Tcp::AsyncClient::MockAsyncTcpClient(); EXPECT_CALL(*async_client1_, setAsyncTcpClientCallbacks(_)); @@ -176,9 +304,16 @@ class FluentdAccessLoggerCacheImplTest : public testing::Test { async_client2_ = new Tcp::AsyncClient::MockAsyncTcpClient(); EXPECT_CALL(*async_client2_, setAsyncTcpClientCallbacks(_)); } + + logger_cache_ = std::make_unique(cluster_manager_, scope_, tls_); } std::string cluster_name_ = "test_cluster"; + uint64_t time_ = 123; + std::vector data_ = {10, 20}; + Event::MockTimer* flush_timer_; + Event::MockTimer* retry_timer_; + Event::MockDispatcher dispatcher_; NiceMock cluster_; NiceMock cluster_manager_; Tcp::AsyncClient::MockAsyncTcpClient* async_client1_; @@ -186,7 +321,8 @@ class FluentdAccessLoggerCacheImplTest : public testing::Test { NiceMock store_; Stats::Scope& scope_{*store_.rootScope()}; NiceMock tls_; - FluentdAccessLoggerCacheImpl logger_cache_; + NiceMock random_; + std::unique_ptr logger_cache_; }; TEST_F(FluentdAccessLoggerCacheImplTest, CreateNonExistingLogger) { @@ -200,7 +336,8 @@ TEST_F(FluentdAccessLoggerCacheImplTest, CreateNonExistingLogger) { config.set_cluster(cluster_name_); config.set_tag("test.tag"); config.mutable_buffer_size_bytes()->set_value(123); - auto logger = logger_cache_.getOrCreateLogger(std::make_shared(config)); + auto logger = + logger_cache_->getOrCreateLogger(std::make_shared(config), random_); EXPECT_TRUE(logger != nullptr); } @@ -215,14 +352,16 @@ TEST_F(FluentdAccessLoggerCacheImplTest, CreateTwoLoggersSameHash) { config1.set_cluster(cluster_name_); config1.set_tag("test.tag"); config1.mutable_buffer_size_bytes()->set_value(123); - auto logger1 = logger_cache_.getOrCreateLogger(std::make_shared(config1)); + auto logger1 = + logger_cache_->getOrCreateLogger(std::make_shared(config1), random_); EXPECT_TRUE(logger1 != nullptr); envoy::extensions::access_loggers::fluentd::v3::FluentdAccessLogConfig config2; config2.set_cluster(cluster_name_); // config hash will be different than config1 config2.set_tag("test.tag"); config2.mutable_buffer_size_bytes()->set_value(123); - auto logger2 = logger_cache_.getOrCreateLogger(std::make_shared(config2)); + auto logger2 = + logger_cache_->getOrCreateLogger(std::make_shared(config2), random_); EXPECT_TRUE(logger2 != nullptr); // Make sure we got the same logger @@ -243,20 +382,60 @@ TEST_F(FluentdAccessLoggerCacheImplTest, CreateTwoLoggersDifferentHash) { config1.set_cluster(cluster_name_); config1.set_tag("test.tag"); config1.mutable_buffer_size_bytes()->set_value(123); - auto logger1 = logger_cache_.getOrCreateLogger(std::make_shared(config1)); + auto logger1 = + logger_cache_->getOrCreateLogger(std::make_shared(config1), random_); EXPECT_TRUE(logger1 != nullptr); + Event::MockTimer* flush_timer2 = new Event::MockTimer(&dispatcher_); + Event::MockTimer* retry_timer2 = new Event::MockTimer(&dispatcher_); + UNREFERENCED_PARAMETER(retry_timer2); + EXPECT_CALL(*flush_timer2, enableTimer(_, _)); + envoy::extensions::access_loggers::fluentd::v3::FluentdAccessLogConfig config2; config2.set_cluster("different_cluster"); // config hash will be different than config1 config2.set_tag("test.tag"); config2.mutable_buffer_size_bytes()->set_value(123); - auto logger2 = logger_cache_.getOrCreateLogger(std::make_shared(config2)); + auto logger2 = + logger_cache_->getOrCreateLogger(std::make_shared(config2), random_); EXPECT_TRUE(logger2 != nullptr); // Make sure we got two different loggers EXPECT_NE(logger1, logger2); } +TEST_F(FluentdAccessLoggerCacheImplTest, JitteredExponentialBackOffStrategyConfig) { + init(); + + EXPECT_CALL(cluster_manager_, getThreadLocalCluster(cluster_name_)).WillOnce(Return(&cluster_)); + EXPECT_CALL(*async_client1_, connected()).WillOnce(Return(false)); + EXPECT_CALL(*async_client1_, connect()).WillRepeatedly(Return(false)); + EXPECT_CALL(cluster_, tcpAsyncClient(_, _)).WillOnce(Invoke([&] { + return Tcp::AsyncTcpClientPtr{async_client1_}; + })); + + envoy::extensions::access_loggers::fluentd::v3::FluentdAccessLogConfig config; + config.set_cluster(cluster_name_); + config.set_tag("test.tag"); + config.mutable_buffer_size_bytes()->set_value(1); + config.mutable_retry_options()->mutable_backoff_options()->mutable_base_interval()->set_nanos( + 7000000); + config.mutable_retry_options()->mutable_backoff_options()->mutable_max_interval()->set_nanos( + 20000000); + + auto logger = + logger_cache_->getOrCreateLogger(std::make_shared(config), random_); + ASSERT_TRUE(logger != nullptr); + + // Setting random so it doesn't add jitter + EXPECT_CALL(random_, random()).WillOnce(Return(6)).WillOnce(Return(13)).WillOnce(Return(19)); + EXPECT_CALL(*retry_timer_, enableTimer(std::chrono::milliseconds(6), _)); + EXPECT_CALL(*retry_timer_, enableTimer(std::chrono::milliseconds(13), _)); + EXPECT_CALL(*retry_timer_, enableTimer(std::chrono::milliseconds(19), _)); + logger->log(std::make_unique(time_, std::move(data_))); + retry_timer_->invokeCallback(); + retry_timer_->invokeCallback(); +} + class MockFluentdAccessLogger : public FluentdAccessLogger { public: MOCK_METHOD(void, log, (EntryPtr &&)); @@ -265,7 +444,7 @@ class MockFluentdAccessLogger : public FluentdAccessLogger { class MockFluentdAccessLoggerCache : public FluentdAccessLoggerCache { public: MOCK_METHOD(FluentdAccessLoggerSharedPtr, getOrCreateLogger, - (const FluentdAccessLogConfigSharedPtr)); + (const FluentdAccessLogConfigSharedPtr, Random::RandomGenerator&)); }; class MockFluentdFormatter : public FluentdFormatter { @@ -280,31 +459,32 @@ using FilterPtr = Envoy::AccessLog::FilterPtr; class FluentdAccessLogTest : public testing::Test { public: - FluentdAccessLogTest() { - ON_CALL(*filter_, evaluate(_, _)).WillByDefault(Return(true)); - EXPECT_CALL(*logger_cache_, getOrCreateLogger(_)).WillOnce(Return(logger_)); - } + FluentdAccessLogTest() { ON_CALL(*filter_, evaluate(_, _)).WillByDefault(Return(true)); } AccessLog::MockFilter* filter_{new NiceMock()}; NiceMock tls_; + NiceMock random_; + NiceMock context_; envoy::extensions::access_loggers::fluentd::v3::FluentdAccessLogConfig config_; - MockFluentdFormatter* formatter_{new NiceMock()}; - std::shared_ptr logger_{new MockFluentdAccessLogger()}; - std::shared_ptr logger_cache_{new MockFluentdAccessLoggerCache()}; }; TEST_F(FluentdAccessLogTest, CreateAndLog) { - auto access_log = - FluentdAccessLog(AccessLog::FilterPtr{filter_}, FluentdFormatterPtr{formatter_}, - std::make_shared(config_), tls_, logger_cache_); + auto* formatter = new NiceMock(); + auto logger = std::make_shared(); + auto logger_cache = std::make_shared(); + + EXPECT_CALL(*logger_cache, getOrCreateLogger(_, _)).WillOnce(Return(logger)); + auto access_log = FluentdAccessLog(AccessLog::FilterPtr{filter_}, FluentdFormatterPtr{formatter}, + std::make_shared(config_), tls_, + random_, logger_cache); MockTimeSystem time_system; EXPECT_CALL(time_system, systemTime).WillOnce(Return(SystemTime(std::chrono::seconds(200)))); NiceMock stream_info; EXPECT_CALL(stream_info, timeSource()).WillOnce(ReturnRef(time_system)); - EXPECT_CALL(*formatter_, format(_, _)).WillOnce(Return(std::vector{10, 20})); - EXPECT_CALL(*logger_, log(_)).WillOnce(Invoke([](EntryPtr&& entry) { + EXPECT_CALL(*formatter, format(_, _)).WillOnce(Return(std::vector{10, 20})); + EXPECT_CALL(*logger, log(_)).WillOnce(Invoke([](EntryPtr&& entry) { EXPECT_EQ(200, entry->time_); ASSERT_EQ(2, entry->record_.size()); EXPECT_EQ(uint8_t(10), entry->record_[0]); @@ -314,6 +494,44 @@ TEST_F(FluentdAccessLogTest, CreateAndLog) { access_log.log({}, stream_info); } +TEST_F(FluentdAccessLogTest, UnknownCluster) { + FluentdAccessLogFactory factory; + + config_.set_cluster("unknown"); + config_.set_tag("tag"); + config_.set_stat_prefix("prefix"); + auto* record = config_.mutable_record(); + (*record->mutable_fields())["Message"].set_string_value("SomeValue"); + + EXPECT_CALL(context_.server_factory_context_.cluster_manager_, + checkActiveStaticCluster("unknown")) + .WillOnce(Return(absl::InvalidArgumentError("no cluster"))); + + EXPECT_THROW_WITH_MESSAGE( + factory.createAccessLogInstance(config_, AccessLog::FilterPtr{filter_}, context_), + EnvoyException, "cluster 'unknown' was not found"); +} + +TEST_F(FluentdAccessLogTest, InvalidBackoffConfig) { + FluentdAccessLogFactory factory; + + config_.set_cluster("unknown"); + config_.set_tag("tag"); + config_.set_stat_prefix("prefix"); + auto* record = config_.mutable_record(); + (*record->mutable_fields())["Message"].set_string_value("SomeValue"); + auto* retry_options = config_.mutable_retry_options(); + retry_options->mutable_backoff_options()->mutable_base_interval()->set_seconds(3); + retry_options->mutable_backoff_options()->mutable_max_interval()->set_seconds(2); + + EXPECT_CALL(context_.server_factory_context_.cluster_manager_, checkActiveStaticCluster(_)) + .WillOnce(Return(absl::OkStatus())); + + EXPECT_THROW_WITH_MESSAGE( + factory.createAccessLogInstance(config_, AccessLog::FilterPtr{filter_}, context_), + EnvoyException, "max_backoff_interval must be greater or equal to base_backoff_interval"); +} + } // namespace } // namespace Fluentd } // namespace AccessLoggers diff --git a/test/extensions/access_loggers/fluentd/fluentd_access_log_integration_test.cc b/test/extensions/access_loggers/fluentd/fluentd_access_log_integration_test.cc index 933ac3fe1369..6c386c9b979b 100644 --- a/test/extensions/access_loggers/fluentd/fluentd_access_log_integration_test.cc +++ b/test/extensions/access_loggers/fluentd/fluentd_access_log_integration_test.cc @@ -32,7 +32,10 @@ class FluentdAccessLogIntegrationTest : public testing::Test, public BaseIntegra void init(const std::string cluster_name = default_cluster_name, bool flush_access_log_on_connected = false, - absl::optional buffer_size_bytes = absl::nullopt) { + absl::optional buffer_size_bytes = absl::nullopt, + absl::optional max_connect_attempts = 1, + absl::optional base_backoff_interval = absl::nullopt, + absl::optional max_backoff_interval = absl::nullopt) { setUpstreamCount(2); config_helper_.renameListener("tcp_proxy"); config_helper_.addConfigModifier( @@ -64,6 +67,25 @@ class FluentdAccessLogIntegrationTest : public testing::Test, public BaseIntegra access_log_config.mutable_buffer_size_bytes()->set_value(buffer_size_bytes.value()); } + if (max_connect_attempts.has_value()) { + access_log_config.mutable_retry_options()->mutable_max_connect_attempts()->set_value( + max_connect_attempts.value()); + } + + if (base_backoff_interval.has_value()) { + access_log_config.mutable_retry_options() + ->mutable_backoff_options() + ->mutable_base_interval() + ->set_nanos(base_backoff_interval.value() * 1000000); + } + + if (max_backoff_interval.has_value()) { + access_log_config.mutable_retry_options() + ->mutable_backoff_options() + ->mutable_max_interval() + ->set_nanos(max_backoff_interval.value() * 1000000); + } + auto* record = access_log_config.mutable_record(); (*record->mutable_fields())["Message"].set_string_value("SomeValue"); (*record->mutable_fields())["LogType"].set_string_value("%ACCESS_LOG_TYPE%"); @@ -137,6 +159,18 @@ TEST_F(FluentdAccessLogIntegrationTest, UnknownCluster) { EXPECT_DEATH(init("unknown_cluster"), ""); } +TEST_F(FluentdAccessLogIntegrationTest, InvalidBackoffConfig) { + // Invalid config: min interval set to 30, max interval is set to 20. + EXPECT_DEATH(init(default_cluster_name, false, 1, 1, 30, 20), ""); +} + +TEST_F(FluentdAccessLogIntegrationTest, LogLostOnBufferFull) { + init(default_cluster_name, false, /* max_buffer_size = */ 0); + sendBidirectionalData(); + + test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.entries_lost", 1); +} + TEST_F(FluentdAccessLogIntegrationTest, SingleEntrySingleRecord) { init(); sendBidirectionalData(); @@ -145,6 +179,9 @@ TEST_F(FluentdAccessLogIntegrationTest, SingleEntrySingleRecord) { test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.events_sent", 1); ASSERT_TRUE(fake_upstreams_[1]->waitForRawConnection(fake_access_log_connection_)); + test_server_->waitForCounterEq("cluster.fluentd_cluster.upstream_cx_total", 1); + test_server_->waitForGaugeEq("cluster.fluentd_cluster.upstream_cx_active", 1); + EXPECT_TRUE(fake_access_log_connection_->waitForData([&](const std::string& tcp_data) -> bool { bool validated = false; validateFluentdPayload(tcp_data, &validated, @@ -161,6 +198,9 @@ TEST_F(FluentdAccessLogIntegrationTest, SingleEntryTwoRecords) { test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.events_sent", 1); ASSERT_TRUE(fake_upstreams_[1]->waitForRawConnection(fake_access_log_connection_)); + test_server_->waitForCounterEq("cluster.fluentd_cluster.upstream_cx_total", 1); + test_server_->waitForGaugeEq("cluster.fluentd_cluster.upstream_cx_active", 1); + EXPECT_TRUE(fake_access_log_connection_->waitForData([&](const std::string& tcp_data) -> bool { bool validated = false; validateFluentdPayload(tcp_data, &validated, @@ -171,13 +211,16 @@ TEST_F(FluentdAccessLogIntegrationTest, SingleEntryTwoRecords) { } TEST_F(FluentdAccessLogIntegrationTest, TwoEntries) { - init(default_cluster_name, /*flush_access_log_on_connected = */ true, /*buffer_size_bytes = */ 0); + init(default_cluster_name, /*flush_access_log_on_connected = */ true, /*buffer_size_bytes = */ 1); sendBidirectionalData(); test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.entries_buffered", 2); test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.events_sent", 2); ASSERT_TRUE(fake_upstreams_[1]->waitForRawConnection(fake_access_log_connection_)); + test_server_->waitForCounterEq("cluster.fluentd_cluster.upstream_cx_total", 1); + test_server_->waitForGaugeEq("cluster.fluentd_cluster.upstream_cx_active", 1); + EXPECT_TRUE(fake_access_log_connection_->waitForData([&](const std::string& tcp_data) -> bool { bool validated = false; validateFluentdPayload(tcp_data, &validated, @@ -195,6 +238,9 @@ TEST_F(FluentdAccessLogIntegrationTest, UpstreamConnectionClosed) { test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.events_sent", 1); ASSERT_TRUE(fake_upstreams_[1]->waitForRawConnection(fake_access_log_connection_)); + test_server_->waitForCounterEq("cluster.fluentd_cluster.upstream_cx_total", 1); + test_server_->waitForGaugeEq("cluster.fluentd_cluster.upstream_cx_active", 1); + EXPECT_TRUE(fake_access_log_connection_->waitForData([&](const std::string& tcp_data) -> bool { bool validated = false; validateFluentdPayload(tcp_data, &validated, @@ -204,11 +250,42 @@ TEST_F(FluentdAccessLogIntegrationTest, UpstreamConnectionClosed) { ASSERT_TRUE(fake_access_log_connection_->close()); test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.connections_closed", 1); + test_server_->waitForGaugeEq("cluster.fluentd_cluster.upstream_cx_active", 0); // New access log would be discarded because the connection is closed. sendBidirectionalData(); test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.entries_lost", 1); } +TEST_F(FluentdAccessLogIntegrationTest, UpstreamConnectionClosedWithMultipleReconnects) { + init(default_cluster_name, false, {}, /* max_reconnect_attempts = */ 3); + sendBidirectionalData(); + + test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.entries_buffered", 1); + test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.events_sent", 1); + + ASSERT_TRUE(fake_upstreams_[1]->waitForRawConnection(fake_access_log_connection_)); + test_server_->waitForCounterEq("cluster.fluentd_cluster.upstream_cx_total", 1); + ASSERT_TRUE(fake_access_log_connection_->close()); + + test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.connections_closed", 1); + test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.reconnect_attempts", 1); + FakeRawConnectionPtr fake_access_log_connection_2; + ASSERT_TRUE(fake_upstreams_[1]->waitForRawConnection(fake_access_log_connection_2)); + test_server_->waitForCounterEq("cluster.fluentd_cluster.upstream_cx_total", 2); + ASSERT_TRUE(fake_access_log_connection_2->close()); + + test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.connections_closed", 2); + test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.reconnect_attempts", 2); + FakeRawConnectionPtr fake_access_log_connection_3; + ASSERT_TRUE(fake_upstreams_[1]->waitForRawConnection(fake_access_log_connection_3)); + test_server_->waitForCounterEq("cluster.fluentd_cluster.upstream_cx_total", 3); + ASSERT_TRUE(fake_access_log_connection_3->close()); + + test_server_->waitForCounterEq("access_logs.fluentd.fluentd_1.connections_closed", 3); + test_server_->waitForCounterEq("cluster.fluentd_cluster.upstream_cx_connect_attempts_exceeded", + 1); +} + } // namespace } // namespace Envoy diff --git a/test/extensions/access_loggers/grpc/tcp_grpc_access_log_integration_test.cc b/test/extensions/access_loggers/grpc/tcp_grpc_access_log_integration_test.cc index 90d2e7acde2a..1b7e15d0ffe0 100644 --- a/test/extensions/access_loggers/grpc/tcp_grpc_access_log_integration_test.cc +++ b/test/extensions/access_loggers/grpc/tcp_grpc_access_log_integration_test.cc @@ -143,8 +143,8 @@ class TcpGrpcAccessLogIntegrationTest : public Grpc::GrpcClientIntegrationParamT const Ssl::ClientSslTransportOptions& ssl_options = {}, const std::string& curves_list = "") { // Set up the SSL client. - context_manager_ = - std::make_unique(timeSystem()); + context_manager_ = std::make_unique( + server_factory_context_); Network::Address::InstanceConstSharedPtr address = Ssl::getSslAddress(version_, lookupPort("tcp_proxy")); context_ = Ssl::createClientSslTransportSocketFactory(ssl_options, *context_manager_, *api_); diff --git a/test/extensions/bootstrap/wasm/wasm_speed_test.cc b/test/extensions/bootstrap/wasm/wasm_speed_test.cc index aaadd74ba6e2..2c1333fbb8a3 100644 --- a/test/extensions/bootstrap/wasm/wasm_speed_test.cc +++ b/test/extensions/bootstrap/wasm/wasm_speed_test.cc @@ -91,12 +91,6 @@ static void bmWasmSimpleCallSpeedTest(benchmark::State& state, std::string test, std::string("null")); \ BENCHMARK_CAPTURE(bmWasmSimpleCallSpeedTest, WasmSpeedTest_##_t, std::string(#_t), \ std::string("wamr")); -#elif defined(PROXY_WASM_HAS_RUNTIME_WAVM) -#define B(_t) \ - BENCHMARK_CAPTURE(bmWasmSimpleCallSpeedTest, NullSpeedTest_##_t, std::string(#_t), \ - std::string("null")); \ - BENCHMARK_CAPTURE(bmWasmSimpleCallSpeedTest, WasmSpeedTest_##_t, std::string(#_t), \ - std::string("wavm")); #elif defined(PROXY_WASM_HAS_RUNTIME_WASMTIME) #define B(_t) \ BENCHMARK_CAPTURE(bmWasmSimpleCallSpeedTest, NullSpeedTest_##_t, std::string(#_t), \ diff --git a/test/extensions/clusters/aggregate/cluster_update_test.cc b/test/extensions/clusters/aggregate/cluster_update_test.cc index 281cd49b87e5..a432877c001b 100644 --- a/test/extensions/clusters/aggregate/cluster_update_test.cc +++ b/test/extensions/clusters/aggregate/cluster_update_test.cc @@ -43,9 +43,10 @@ class AggregateClusterUpdateTest : public Event::TestUsingSimulatedTime, const bool use_deferred_cluster = GetParam(); bootstrap.mutable_cluster_manager()->set_enable_deferred_cluster_creation(use_deferred_cluster); cluster_manager_ = Upstream::TestClusterManagerImpl::createAndInit( - bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, - factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, validation_context_, - *factory_.api_, http_context_, grpc_context_, router_context_, server_); + bootstrap, factory_, factory_.server_context_, factory_.stats_, factory_.tls_, + factory_.runtime_, factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, + validation_context_, *factory_.api_, http_context_, grpc_context_, router_context_, + server_); ASSERT_TRUE(cluster_manager_->initializeSecondaryClusters(bootstrap).ok()); EXPECT_EQ(cluster_manager_->activeClusters().size(), 1); cluster_ = cluster_manager_->getThreadLocalCluster("aggregate_cluster"); @@ -278,9 +279,9 @@ TEST_P(AggregateClusterUpdateTest, InitializeAggregateClusterAfterOtherClusters) auto bootstrap = parseBootstrapFromV2Yaml(config); cluster_manager_ = Upstream::TestClusterManagerImpl::createAndInit( - bootstrap, factory_, factory_.stats_, factory_.tls_, factory_.runtime_, factory_.local_info_, - log_manager_, factory_.dispatcher_, admin_, validation_context_, *factory_.api_, - http_context_, grpc_context_, router_context_, server_); + bootstrap, factory_, factory_.server_context_, factory_.stats_, factory_.tls_, + factory_.runtime_, factory_.local_info_, log_manager_, factory_.dispatcher_, admin_, + validation_context_, *factory_.api_, http_context_, grpc_context_, router_context_, server_); ASSERT_TRUE(cluster_manager_->initializeSecondaryClusters(bootstrap).ok()); EXPECT_EQ(cluster_manager_->activeClusters().size(), 2); cluster_ = cluster_manager_->getThreadLocalCluster("aggregate_cluster"); diff --git a/test/extensions/clusters/common/BUILD b/test/extensions/clusters/common/BUILD index 0b6e67ac8126..f3f49cbaba0b 100644 --- a/test/extensions/clusters/common/BUILD +++ b/test/extensions/clusters/common/BUILD @@ -16,3 +16,17 @@ envoy_cc_test( "//test/mocks/upstream:host_mocks", ], ) + +envoy_cc_test( + name = "logical_host_integration_test", + srcs = ["logical_host_integration_test.cc"], + deps = [ + "//source/common/config:utility_lib", + "//source/extensions/clusters/common:logical_host_lib", + "//source/extensions/clusters/logical_dns:logical_dns_cluster_lib", + "//test/integration:http_integration_lib", + "//test/mocks/network:network_mocks", + "//test/test_common:registry_lib", + "//test/test_common:threadsafe_singleton_injector_lib", + ], +) diff --git a/test/extensions/clusters/common/logical_host_integration_test.cc b/test/extensions/clusters/common/logical_host_integration_test.cc new file mode 100644 index 000000000000..bf46d6225c3b --- /dev/null +++ b/test/extensions/clusters/common/logical_host_integration_test.cc @@ -0,0 +1,105 @@ +#include "source/common/config/utility.h" + +#include "test/integration/http_integration.h" +#include "test/mocks/network/mocks.h" +#include "test/test_common/registry.h" +#include "test/test_common/threadsafe_singleton_injector.h" + +namespace Envoy { +namespace Extensions { +namespace Clusters { + +// Logical Host integration test. +class LogicalHostIntegrationTest : public testing::TestWithParam, + public HttpIntegrationTest { +public: + LogicalHostIntegrationTest() + : HttpIntegrationTest(Http::CodecType::HTTP1, GetParam()), + registered_dns_factory_(dns_resolver_factory_) {} + + void createUpstreams() override { HttpIntegrationTest::createUpstreams(); } + + NiceMock dns_resolver_factory_; + Registry::InjectFactory registered_dns_factory_; + uint32_t address_ = 0; + std::string first_address_string_; +}; + +INSTANTIATE_TEST_SUITE_P(IpVersions, LogicalHostIntegrationTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + TestUtility::ipTestParamsToString); + +// Reproduces a race from https://github.com/envoyproxy/envoy/issues/32850. +// The test is by mocking the DNS resolver to return multiple different +// addresses, also config dns_refresh_rate to be extremely fast. +TEST_P(LogicalHostIntegrationTest, DISABLED_LogicalDNSRaceCrashTest) { + // first_address_string_ is used to make connections. It needs + // to match with the IpVersion of the test. + if (version_ == Network::Address::IpVersion::v4) { + first_address_string_ = "127.0.0.1"; + } else { + first_address_string_ = "::1"; + } + + auto dns_resolver = std::make_shared(); + EXPECT_CALL(dns_resolver_factory_, createDnsResolver(_, _, _)) + .WillRepeatedly(testing::Return(dns_resolver)); + EXPECT_CALL(*dns_resolver, resolve(_, _, _)) + .WillRepeatedly( + Invoke([&](const std::string&, Network::DnsLookupFamily, + Network::DnsResolver::ResolveCb dns_callback) -> Network::ActiveDnsQuery* { + // Keep changing the returned addresses to force address update. + dns_callback(Network::DnsResolver::ResolutionStatus::Success, + TestUtility::makeDnsResponse({ + // The only significant address is the first one; the other ones are + // just used to populate a list + // whose maintenance is race-prone. + first_address_string_, + absl::StrCat("127.0.0.", address_), + absl::StrCat("127.0.0.", address_ + 1), + absl::StrCat("127.0.0.", address_ + 2), + absl::StrCat("127.0.0.", address_ + 3), + absl::StrCat("127.0.0.", address_ + 4), + absl::StrCat("127.0.0.", address_ + 5), + absl::StrCat("127.0.0.", address_ + 6), + absl::StrCat("127.0.0.", address_ + 7), + absl::StrCat("127.0.0.", address_ + 8), + absl::StrCat("127.0.0.", address_ + 9), + "::2", + "::3", + "::4", + "::5", + "::6", + "::7", + "::8", + "::9", + })); + address_ = (address_ + 1) % 128; + return nullptr; + })); + config_helper_.addConfigModifier([&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { + RELEASE_ASSERT(bootstrap.mutable_static_resources()->clusters_size() == 1, ""); + auto& cluster = *bootstrap.mutable_static_resources()->mutable_clusters(0); + cluster.set_type(envoy::config::cluster::v3::Cluster::LOGICAL_DNS); + cluster.set_dns_lookup_family(envoy::config::cluster::v3::Cluster::ALL); + // Make the refresh rate fast to hit the R/W race. + cluster.mutable_dns_refresh_rate()->set_nanos(1000001); + }); + config_helper_.addConfigModifier( + [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) { + auto* route = hcm.mutable_route_config()->mutable_virtual_hosts(0)->mutable_routes(0); + route->mutable_route()->mutable_auto_host_rewrite()->set_value(true); + }); + initialize(); + codec_client_ = makeHttpConnection(lookupPort("http")); + auto response = + sendRequestAndWaitForResponse(default_request_headers_, 0, default_response_headers_, 0); + + ASSERT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().getStatusValue()); +} + +} // namespace Clusters +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/common/aws/BUILD b/test/extensions/common/aws/BUILD index 69b7469b8ae1..0923c214b656 100644 --- a/test/extensions/common/aws/BUILD +++ b/test/extensions/common/aws/BUILD @@ -31,6 +31,7 @@ envoy_cc_test( "//source/common/http:message_lib", "//source/extensions/common/aws:sigv4_signer_impl_lib", "//test/extensions/common/aws:aws_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/test_common:simulated_time_system_lib", "//test/test_common:utility_lib", ], @@ -45,6 +46,7 @@ envoy_cc_test( "//source/extensions/common/aws:sigv4a_key_derivation_lib", "//source/extensions/common/aws:sigv4a_signer_impl_lib", "//test/extensions/common/aws:aws_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/test_common:simulated_time_system_lib", "//test/test_common:utility_lib", ], diff --git a/test/extensions/common/aws/sigv4_signer_impl_test.cc b/test/extensions/common/aws/sigv4_signer_impl_test.cc index cab15cb91833..eb555a153ef3 100644 --- a/test/extensions/common/aws/sigv4_signer_impl_test.cc +++ b/test/extensions/common/aws/sigv4_signer_impl_test.cc @@ -4,11 +4,13 @@ #include "source/extensions/common/aws/utility.h" #include "test/extensions/common/aws/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/simulated_time_system.h" #include "test/test_common/utility.h" using testing::NiceMock; using testing::Return; +using testing::ReturnRef; namespace Envoy { namespace Extensions { @@ -21,11 +23,12 @@ class SigV4SignerImplTest : public testing::Test { SigV4SignerImplTest() : credentials_provider_(new NiceMock()), message_(new Http::RequestMessageImpl()), - signer_("service", "region", CredentialsProviderSharedPtr{credentials_provider_}, - time_system_, Extensions::Common::Aws::AwsSigningHeaderExclusionVector{}), + signer_("service", "region", CredentialsProviderSharedPtr{credentials_provider_}, context_, + Extensions::Common::Aws::AwsSigningHeaderExclusionVector{}), credentials_("akid", "secret"), token_credentials_("akid", "secret", "token") { // 20180102T030405Z time_system_.setSystemTime(std::chrono::milliseconds(1514862245000)); + ON_CALL(context_, timeSystem()).WillByDefault(ReturnRef(time_system_)); } void addMethod(const std::string& method) { message_->headers().setMethod(method); } @@ -49,7 +52,7 @@ class SigV4SignerImplTest : public testing::Test { headers.addCopy(Http::LowerCaseString("host"), "www.example.com"); SigV4SignerImpl signer(service_name, "region", - CredentialsProviderSharedPtr{credentials_provider}, time_system_, + CredentialsProviderSharedPtr{credentials_provider}, context_, Extensions::Common::Aws::AwsSigningHeaderExclusionVector{}, false, 5); if (use_unsigned_payload) { signer.signUnsignedPayload(headers, override_region); @@ -79,7 +82,7 @@ class SigV4SignerImplTest : public testing::Test { } SigV4SignerImpl signer(service_name, "region", - CredentialsProviderSharedPtr{credentials_provider}, time_system_, + CredentialsProviderSharedPtr{credentials_provider}, context_, Extensions::Common::Aws::AwsSigningHeaderExclusionVector{}, true, 5); signer.signUnsignedPayload(extra_headers, override_region); @@ -90,6 +93,7 @@ class SigV4SignerImplTest : public testing::Test { NiceMock* credentials_provider_; Event::SimulatedTimeSystem time_system_; + NiceMock context_; Http::RequestMessagePtr message_; SigV4SignerImpl signer_; Credentials credentials_; @@ -268,7 +272,7 @@ TEST_F(SigV4SignerImplTest, QueryStringDefault5s) { headers.addCopy(Http::LowerCaseString("host"), "example.service.zz"); headers.addCopy("testheader", "value1"); SigV4SignerImpl querysigner("service", "region", - CredentialsProviderSharedPtr{credentials_provider}, time_system_, + CredentialsProviderSharedPtr{credentials_provider}, context_, Extensions::Common::Aws::AwsSigningHeaderExclusionVector{}, true); querysigner.signUnsignedPayload(headers); diff --git a/test/extensions/common/aws/sigv4a_signer_impl_test.cc b/test/extensions/common/aws/sigv4a_signer_impl_test.cc index 5f92d3e969f8..b6e9e2f2984c 100644 --- a/test/extensions/common/aws/sigv4a_signer_impl_test.cc +++ b/test/extensions/common/aws/sigv4a_signer_impl_test.cc @@ -8,11 +8,13 @@ #include "source/extensions/common/aws/utility.h" #include "test/extensions/common/aws/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/simulated_time_system.h" #include "test/test_common/utility.h" using testing::NiceMock; using testing::Return; +using testing::ReturnRef; namespace Envoy { namespace Extensions { @@ -28,6 +30,7 @@ class SigV4ASignerImplTest : public testing::Test { token_credentials_("akid", "secret", "token") { // 20180102T030405Z time_system_.setSystemTime(std::chrono::milliseconds(1514862245000)); + ON_CALL(context_, timeSystem()).WillByDefault(ReturnRef(time_system_)); } void addMethod(const std::string& method) { message_->headers().setMethod(method); } @@ -54,7 +57,7 @@ class SigV4ASignerImplTest : public testing::Test { return SigV4ASignerImpl{"service", "region", getTestCredentialsProvider(), - time_system_, + context_, Extensions::Common::Aws::AwsSigningHeaderExclusionVector{}, query_string, expiration_time}; @@ -133,6 +136,7 @@ class SigV4ASignerImplTest : public testing::Test { } NiceMock* credentials_provider_; Event::SimulatedTimeSystem time_system_; + NiceMock context_; Http::RequestMessagePtr message_; Credentials credentials_; Credentials token_credentials_; @@ -478,7 +482,7 @@ TEST_F(SigV4ASignerImplTest, QueryStringDefault5s) { headers.setPath("/example/path"); headers.addCopy(Http::LowerCaseString("host"), "example.service.zz"); headers.addCopy("testheader", "value1"); - SigV4ASignerImpl querysigner("service", "region", getTestCredentialsProvider(), time_system_, + SigV4ASignerImpl querysigner("service", "region", getTestCredentialsProvider(), context_, Extensions::Common::Aws::AwsSigningHeaderExclusionVector{}, true); querysigner.signUnsignedPayload(headers); diff --git a/test/extensions/common/dubbo/BUILD b/test/extensions/common/dubbo/BUILD index 67e35e09e460..bd3c0a8acc67 100644 --- a/test/extensions/common/dubbo/BUILD +++ b/test/extensions/common/dubbo/BUILD @@ -13,7 +13,6 @@ envoy_cc_test( name = "message_test", srcs = ["message_test.cc"], deps = [ - "//source/extensions/common/dubbo:codec_lib", "//source/extensions/common/dubbo:message_lib", "//test/test_common:printers_lib", "//test/test_common:utility_lib", diff --git a/test/extensions/common/dubbo/codec_test.cc b/test/extensions/common/dubbo/codec_test.cc index 0ec32de8d4e0..89eb86df8c6f 100644 --- a/test/extensions/common/dubbo/codec_test.cc +++ b/test/extensions/common/dubbo/codec_test.cc @@ -1,7 +1,7 @@ #include #include "source/extensions/common/dubbo/codec.h" -#include "source/extensions/common/dubbo/message_impl.h" +#include "source/extensions/common/dubbo/message.h" #include "test/extensions/common/dubbo/mocks.h" #include "test/test_common/printers.h" @@ -227,7 +227,6 @@ TEST(DubboCodecTest, DecodeDataTest) { context->setMessageType(MessageType::Request); context->setRequestId(1); context->setBodySize(buffer.length() + 1); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -245,12 +244,12 @@ TEST(DubboCodecTest, DecodeDataTest) { context->setMessageType(MessageType::Request); context->setRequestId(1); context->setBodySize(buffer.length()); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); EXPECT_CALL(*raw_serializer, deserializeRpcRequest(_, _)) - .WillOnce(testing::Return(testing::ByMove(std::make_unique()))); + .WillOnce( + testing::Return(testing::ByMove(std::make_unique("a", "b", "c", "d")))); EXPECT_EQ(DecodeStatus::Success, codec.decodeData(buffer, metadata)); EXPECT_EQ(true, metadata.hasRequest()); @@ -267,7 +266,6 @@ TEST(DubboCodecTest, DecodeDataTest) { context->setMessageType(MessageType::HeartbeatRequest); context->setRequestId(1); context->setBodySize(buffer.length()); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -290,12 +288,11 @@ TEST(DubboCodecTest, DecodeDataTest) { context->setRequestId(1); context->setBodySize(buffer.length()); context->setResponseStatus(ResponseStatus::Ok); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); EXPECT_CALL(*raw_serializer, deserializeRpcResponse(_, _)) - .WillOnce(testing::Return(testing::ByMove(std::make_unique()))); + .WillOnce(testing::Return(testing::ByMove(std::make_unique()))); EXPECT_EQ(DecodeStatus::Success, codec.decodeData(buffer, metadata)); EXPECT_EQ(true, metadata.hasResponse()); @@ -313,7 +310,6 @@ TEST(DubboCodecTest, DecodeDataTest) { context->setRequestId(1); context->setBodySize(buffer.length()); context->setResponseStatus(ResponseStatus::Ok); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -336,7 +332,6 @@ TEST(DubboCodecTest, DecodeDataTest) { context->setRequestId(1); context->setBodySize(buffer.length()); context->setResponseStatus(ResponseStatus::Ok); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -360,7 +355,6 @@ TEST(DubboCodecTest, EncodeTest) { auto context = std::make_unique(); context->setMessageType(MessageType::Request); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -397,7 +391,6 @@ TEST(DubboCodecTest, EncodeTest) { auto context = std::make_unique(); context->setMessageType(MessageType::Oneway); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -433,7 +426,6 @@ TEST(DubboCodecTest, EncodeTest) { auto context = std::make_unique(); context->setMessageType(MessageType::HeartbeatRequest); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -470,7 +462,6 @@ TEST(DubboCodecTest, EncodeTest) { context->setMessageType(MessageType::Response); context->setResponseStatus(ResponseStatus::Ok); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -507,7 +498,6 @@ TEST(DubboCodecTest, EncodeTest) { context->setMessageType(MessageType::Exception); context->setResponseStatus(ResponseStatus::BadRequest); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -544,7 +534,6 @@ TEST(DubboCodecTest, EncodeTest) { context->setMessageType(MessageType::Exception); context->setResponseStatus(ResponseStatus::ServerError); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -581,7 +570,6 @@ TEST(DubboCodecTest, EncodeTest) { context->setMessageType(MessageType::HeartbeatResponse); context->setResponseStatus(ResponseStatus::Ok); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -618,7 +606,6 @@ TEST(DubboCodecTest, EncodeTest) { context->setMessageType(static_cast(6)); context->setResponseStatus(ResponseStatus::Ok); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -637,7 +624,6 @@ TEST(DubboCodecTest, EncodeHeaderForTestTest) { context->setMessageType(static_cast(6)); context->setResponseStatus(ResponseStatus::Ok); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); EXPECT_DEATH(codec.encodeHeaderForTest(buffer, *context), ".*panic: corrupted enum.*"); } @@ -650,7 +636,6 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { auto context = std::make_unique(); context->setMessageType(MessageType::HeartbeatRequest); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -658,7 +643,6 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { EXPECT_EQ(MessageType::HeartbeatResponse, response->messageType()); EXPECT_EQ(12345, response->requestId()); - EXPECT_EQ(SerializeType::Hessian2, response->context().serializeType()); EXPECT_EQ(ResponseStatus::Ok, response->responseStatus()); EXPECT_EQ(true, response->context().heartbeat()); } @@ -669,7 +653,6 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { auto context = std::make_unique(); context->setMessageType(MessageType::Request); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -678,14 +661,12 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { EXPECT_EQ(MessageType::Response, response->messageType()); EXPECT_EQ(12345, response->requestId()); - EXPECT_EQ(SerializeType::Hessian2, response->context().serializeType()); EXPECT_EQ(ResponseStatus::Ok, response->responseStatus()); EXPECT_EQ(false, response->context().heartbeat()); EXPECT_EQ(RpcResponseType::ResponseWithValue, response->response().responseType().value()); - auto typed_response = dynamic_cast(&response->mutableResponse()); - EXPECT_NE(nullptr, typed_response); - EXPECT_EQ("anything", typed_response->localRawMessage().value()); + auto& typed_response = response->mutableResponse(); + EXPECT_EQ("anything", typed_response.content().result()->toString().value().get()); } // Local normal response without response type. @@ -694,7 +675,6 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { auto context = std::make_unique(); context->setMessageType(MessageType::Request); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -703,14 +683,13 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { EXPECT_EQ(MessageType::Response, response->messageType()); EXPECT_EQ(12345, response->requestId()); - EXPECT_EQ(SerializeType::Hessian2, response->context().serializeType()); EXPECT_EQ(ResponseStatus::Ok, response->responseStatus()); EXPECT_EQ(false, response->context().heartbeat()); - EXPECT_EQ(false, response->response().responseType().has_value()); + EXPECT_EQ(true, response->response().responseType().has_value()); + EXPECT_EQ(RpcResponseType::ResponseWithValue, response->response().responseType().value()); - auto typed_response = dynamic_cast(&response->mutableResponse()); - EXPECT_NE(nullptr, typed_response); - EXPECT_EQ("anything", typed_response->localRawMessage().value()); + auto& typed_response = response->mutableResponse(); + EXPECT_EQ("anything", typed_response.content().result()->toString().value().get()); } // Local normal response with exception type. @@ -719,7 +698,6 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { auto context = std::make_unique(); context->setMessageType(MessageType::Request); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -728,14 +706,12 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { EXPECT_EQ(MessageType::Exception, response->messageType()); EXPECT_EQ(12345, response->requestId()); - EXPECT_EQ(SerializeType::Hessian2, response->context().serializeType()); EXPECT_EQ(ResponseStatus::Ok, response->responseStatus()); EXPECT_EQ(false, response->context().heartbeat()); EXPECT_EQ(RpcResponseType::ResponseWithException, response->response().responseType().value()); - auto typed_response = dynamic_cast(&response->mutableResponse()); - EXPECT_NE(nullptr, typed_response); - EXPECT_EQ("anything", typed_response->localRawMessage().value()); + auto& typed_response = response->mutableResponse(); + EXPECT_EQ("anything", typed_response.content().result()->toString().value().get()); } // Local normal response with exception type. @@ -744,7 +720,6 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { auto context = std::make_unique(); context->setMessageType(MessageType::Request); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -754,15 +729,13 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { EXPECT_EQ(MessageType::Exception, response->messageType()); EXPECT_EQ(12345, response->requestId()); - EXPECT_EQ(SerializeType::Hessian2, response->context().serializeType()); EXPECT_EQ(ResponseStatus::Ok, response->responseStatus()); EXPECT_EQ(false, response->context().heartbeat()); EXPECT_EQ(RpcResponseType::ResponseWithExceptionWithAttachments, response->response().responseType().value()); - auto typed_response = dynamic_cast(&response->mutableResponse()); - EXPECT_NE(nullptr, typed_response); - EXPECT_EQ("anything", typed_response->localRawMessage().value()); + auto& typed_response = response->mutableResponse(); + EXPECT_EQ("anything", typed_response.content().result()->toString().value().get()); } // Local exception response. @@ -771,7 +744,6 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { auto context = std::make_unique(); context->setMessageType(MessageType::Request); context->setRequestId(12345); - context->setSerializeType(SerializeType::Hessian2); metadata.setContext(std::move(context)); @@ -780,15 +752,13 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { EXPECT_EQ(MessageType::Exception, response->messageType()); EXPECT_EQ(12345, response->requestId()); - EXPECT_EQ(SerializeType::Hessian2, response->context().serializeType()); EXPECT_EQ(ResponseStatus::BadRequest, response->responseStatus()); EXPECT_EQ(false, response->context().heartbeat()); // Response type will be ignored for non-Ok response. EXPECT_EQ(false, response->response().responseType().has_value()); - auto typed_response = dynamic_cast(&response->mutableResponse()); - EXPECT_NE(nullptr, typed_response); - EXPECT_EQ("anything", typed_response->localRawMessage().value()); + auto& typed_response = response->mutableResponse(); + EXPECT_EQ("anything", typed_response.content().result()->toString().value().get()); } // Local response without request context. @@ -800,15 +770,13 @@ TEST(DirectResponseUtilTest, DirectResponseUtilTest) { EXPECT_EQ(MessageType::Exception, response->messageType()); EXPECT_EQ(0, response->requestId()); - EXPECT_EQ(SerializeType::Hessian2, response->context().serializeType()); EXPECT_EQ(ResponseStatus::BadRequest, response->responseStatus()); EXPECT_EQ(false, response->context().heartbeat()); // Response type will be ignored for non-Ok response. EXPECT_EQ(false, response->response().responseType().has_value()); - auto typed_response = dynamic_cast(&response->mutableResponse()); - EXPECT_NE(nullptr, typed_response); - EXPECT_EQ("anything", typed_response->localRawMessage().value()); + auto& typed_response = response->mutableResponse(); + EXPECT_EQ("anything", typed_response.content().result()->toString().value().get()); } } diff --git a/test/extensions/common/dubbo/hessian2_serializer_impl_test.cc b/test/extensions/common/dubbo/hessian2_serializer_impl_test.cc index ef78accf9773..067782a2bf37 100644 --- a/test/extensions/common/dubbo/hessian2_serializer_impl_test.cc +++ b/test/extensions/common/dubbo/hessian2_serializer_impl_test.cc @@ -35,8 +35,8 @@ TEST(Hessian2ProtocolTest, deserializeRpcRequest) { auto result = serializer.deserializeRpcRequest(buffer, *context); ASSERT(result != nullptr); - EXPECT_EQ("test", result->methodName()); - EXPECT_EQ("test", result->serviceName()); + EXPECT_EQ("test", result->method()); + EXPECT_EQ("test", result->service()); EXPECT_EQ("0.0.0", result->serviceVersion()); } @@ -75,12 +75,17 @@ TEST(Hessian2ProtocolTest, deserializeRpcRequest) { TEST(Hessian2ProtocolTest, deserializeRpcRequestWithParametersOrAttachment) { Hessian2SerializerImpl serializer; - RpcRequestImpl::Attachment attach(std::make_unique(), 0); - attach.insert("test1", "test_value1"); - attach.insert("test2", "test_value2"); - attach.insert("test3", "test_value3"); + Hessian2::Object::UntypedMap untyped_map; + untyped_map.emplace(Hessian2::ObjectPtr{new Hessian2::StringObject("test1")}, + Hessian2::ObjectPtr{new Hessian2::StringObject("test_value1")}); + untyped_map.emplace(Hessian2::ObjectPtr{new Hessian2::StringObject("test2")}, + Hessian2::ObjectPtr{new Hessian2::StringObject("test_value2")}); + untyped_map.emplace(Hessian2::ObjectPtr{new Hessian2::StringObject("test3")}, + Hessian2::ObjectPtr{new Hessian2::StringObject("test_value3")}); - RpcRequestImpl::Parameters params; + auto map_object = std::make_unique(std::move(untyped_map)); + + ArgumentVec params; params.push_back(std::make_unique("test_string")); @@ -124,12 +129,10 @@ TEST(Hessian2ProtocolTest, deserializeRpcRequestWithParametersOrAttachment) { encoder.encode(*param); } // Encode an untyped map object as fourth parameter. - encoder.encode(attach.attachment()); - - size_t expected_attachment_offset = buffer.length(); + encoder.encode(*map_object); // Encode attachment - encoder.encode(attach.attachment()); + encoder.encode(*map_object); auto context = std::make_unique(); context->setBodySize(buffer.length()); @@ -137,28 +140,18 @@ TEST(Hessian2ProtocolTest, deserializeRpcRequestWithParametersOrAttachment) { auto result = serializer.deserializeRpcRequest(buffer, *context); EXPECT_NE(nullptr, result); - auto invo = dynamic_cast(result.get()); - // All data be moved to buffer in the request. EXPECT_EQ(0, buffer.length()); - EXPECT_EQ(context->bodySize(), invo->messageBuffer().length()); - - EXPECT_EQ(false, invo->hasAttachment()); - EXPECT_EQ(false, invo->hasParameters()); - - auto& result_params = invo->mutableParameters(); - // When parsing parameters, attachment will not be parsed. - EXPECT_EQ(false, invo->hasAttachment()); - EXPECT_EQ(true, invo->hasParameters()); + auto& result_params = result->content().arguments(); - EXPECT_EQ(4, result_params->size()); + EXPECT_EQ(4, result_params.size()); - EXPECT_EQ("test_string", result_params->at(0)->toString().value().get()); - EXPECT_EQ(4, result_params->at(1)->toBinary().value().get().at(4)); - EXPECT_EQ(233333, *result_params->at(2)->toLong()); - EXPECT_EQ(3, result_params->at(3)->toUntypedMap().value().get().size()); - EXPECT_EQ("test_value2", result_params->at(3) + EXPECT_EQ("test_string", result_params.at(0)->toString().value().get()); + EXPECT_EQ(4, result_params.at(1)->toBinary().value().get().at(4)); + EXPECT_EQ(233333, *result_params.at(2)->toLong()); + EXPECT_EQ(3, result_params.at(3)->toUntypedMap().value().get().size()); + EXPECT_EQ("test_value2", result_params.at(3) ->toUntypedMap() .value() .get() @@ -167,76 +160,9 @@ TEST(Hessian2ProtocolTest, deserializeRpcRequestWithParametersOrAttachment) { .value() .get()); - auto& result_attach = invo->mutableAttachment(); - EXPECT_EQ("test_value2", result_attach->attachment() - .toUntypedMap() - .value() - .get() - .find("test2") - ->second->toString() - .value() - .get()); - - EXPECT_EQ(expected_attachment_offset, result_attach->attachmentOffset()); + EXPECT_EQ("test_value2", result->content().attachments().at("test2")); } - { - Buffer::OwnedImpl buffer; - buffer.add(std::string({ - 0x05, '2', '.', '0', '.', '2', // Dubbo version - 0x04, 't', 'e', 's', 't', // Service name - 0x05, '0', '.', '0', '.', '0', // Service version - 0x04, 't', 'e', 's', 't', // method name - })); - - Hessian2::Encoder encoder(std::make_unique(buffer)); - - encoder.encode(parameters_type); - - for (const auto& param : params) { - encoder.encode(*param); - } - // Encode an untyped map object as fourth parameter. - encoder.encode(attach.attachment()); - - // Encode attachment - encoder.encode(attach.attachment()); - - auto context = std::make_unique(); - context->setBodySize(buffer.length()); - - auto result = serializer.deserializeRpcRequest(buffer, *context); - EXPECT_NE(nullptr, result); - auto invo = dynamic_cast(result.get()); - - EXPECT_EQ(false, invo->hasAttachment()); - EXPECT_EQ(false, invo->hasParameters()); - - auto& result_attach = invo->mutableAttachment(); - - // When parsing attachment, parameters will also be parsed. - EXPECT_EQ(true, invo->hasAttachment()); - EXPECT_EQ(true, invo->hasParameters()); - - EXPECT_EQ("test_value2", result_attach->attachment() - .toUntypedMap() - .value() - .get() - .find("test2") - ->second->toString() - .value() - .get()); - - auto& result_params = invo->parameters(); - EXPECT_EQ("test_value2", result_params.at(3) - ->toUntypedMap() - .value() - .get() - .find("test2") - ->second->toString() - .value() - .get()); - } // Test case that request only have parameters. { Buffer::OwnedImpl buffer; @@ -255,7 +181,7 @@ TEST(Hessian2ProtocolTest, deserializeRpcRequestWithParametersOrAttachment) { encoder.encode(*param); } // Encode an untyped map object as fourth parameter. - encoder.encode(attach.attachment()); + encoder.encode(*map_object); auto context = std::make_unique(); context->setBodySize(buffer.length()); @@ -263,28 +189,8 @@ TEST(Hessian2ProtocolTest, deserializeRpcRequestWithParametersOrAttachment) { auto result = serializer.deserializeRpcRequest(buffer, *context); EXPECT_NE(nullptr, result); - auto invo = dynamic_cast(result.get()); - - EXPECT_EQ(false, invo->hasAttachment()); - EXPECT_EQ(false, invo->hasParameters()); - - auto& result_attach = invo->mutableAttachment(); - - // When parsing attachment, parameters will also be parsed. - EXPECT_EQ(true, invo->hasAttachment()); - EXPECT_EQ(true, invo->hasParameters()); - - auto& result_params = invo->parameters(); - EXPECT_EQ("test_value2", result_params.at(3) - ->toUntypedMap() - .value() - .get() - .find("test2") - ->second->toString() - .value() - .get()); - - EXPECT_EQ(true, result_attach->attachment().toUntypedMap().value().get().empty()); + EXPECT_EQ(4, result->content().arguments().size()); + EXPECT_EQ(true, result->content().attachments().empty()); } // Test the case where there are not enough parameters in the request buffer. { @@ -311,45 +217,9 @@ TEST(Hessian2ProtocolTest, deserializeRpcRequestWithParametersOrAttachment) { auto result = serializer.deserializeRpcRequest(buffer, *context); EXPECT_NE(nullptr, result); - auto invo = dynamic_cast(result.get()); - - // There are not enough parameters and throws an exception. - EXPECT_THROW_WITH_MESSAGE(invo->mutableParameters(), EnvoyException, - "Cannot parse RpcRequest parameter from buffer"); - } - // Test for incorrect attachment types. - { - Buffer::OwnedImpl buffer; - buffer.add(std::string({ - 0x05, '2', '.', '0', '.', '2', // Dubbo version - 0x04, 't', 'e', 's', 't', // Service name - 0x05, '0', '.', '0', '.', '0', // Service version - 0x04, 't', 'e', 's', 't', // method name - })); - - Hessian2::Encoder encoder(std::make_unique(buffer)); - - encoder.encode(parameters_type); - - for (const auto& param : params) { - encoder.encode(*param); - } - // Encode an untyped map object as fourth parameter. - encoder.encode(attach.attachment()); - - // Encode a string object as attachment. - encoder.encode(*params[0]); - - auto context = std::make_unique(); - context->setBodySize(buffer.length()); - - auto result = serializer.deserializeRpcRequest(buffer, *context); - EXPECT_NE(nullptr, result); - - auto invo = dynamic_cast(result.get()); - - auto& result_attach = invo->mutableAttachment(); - EXPECT_EQ(true, result_attach->attachment().toUntypedMap().value().get().empty()); + // The request will be reset to an empty state. + EXPECT_EQ(true, result->content().arguments().empty()); + EXPECT_EQ(true, result->content().attachments().empty()); } } @@ -560,26 +430,6 @@ TEST(Hessian2ProtocolTest, deserializeRpcResponse) { EXPECT_THROW_WITH_MESSAGE(serializer.deserializeRpcResponse(buffer, *context), EnvoyException, "not supported return type 6"); } - - // incorrect value size - { - Buffer::OwnedImpl buffer; - buffer.add(std::string({ - '\x92', // without the value of the return type - 0x04, 't', 'e', 's', 't', // return body - })); - std::string exception_string = - fmt::format("RpcResponse is no value, but the rest of the body size({}) not equal 0", - buffer.length() - 1); - - auto context = std::make_unique(); - context->setMessageType(MessageType::Response); - context->setResponseStatus(ResponseStatus::Ok); - context->setBodySize(buffer.length()); - - EXPECT_THROW_WITH_MESSAGE(serializer.deserializeRpcResponse(buffer, *context), EnvoyException, - exception_string); - } } TEST(Hessian2ProtocolTest, serializeRpcRequest) { @@ -604,11 +454,12 @@ TEST(Hessian2ProtocolTest, serializeRpcRequest) { auto context = std::make_unique(); context->setMessageType(MessageType::Request); - auto request = std::make_unique(); - request->setServiceName("test.service"); - request->setMethodName("test.method"); - request->setServiceVersion("test.version"); - request->messageBuffer().add("anything_for_no_attachment_update"); + auto request = std::make_unique("v", "v", "v", "v"); + + ArgumentVec args; + args.push_back(std::make_unique(true)); + + request->content().initialize("Z", std::move(args), {}); auto metadata = std::make_shared(); metadata->setContext(std::move(context)); @@ -617,9 +468,9 @@ TEST(Hessian2ProtocolTest, serializeRpcRequest) { Buffer::OwnedImpl buffer; serializer.serializeRpcRequest(buffer, *metadata); - // No attachment update and the message buffer will be used directly to - // accelerate the encoding. - EXPECT_EQ("anything_for_no_attachment_update", buffer.toString()); + EXPECT_EQ( + std::string({'\x1', 'v', '\x1', 'v', '\x1', 'v', '\x1', 'v', '\x1', 'Z', 'T', 'H', 'Z'}), + buffer.toString()); } // Normal request with attachment update. @@ -628,23 +479,13 @@ TEST(Hessian2ProtocolTest, serializeRpcRequest) { auto context = std::make_unique(); context->setMessageType(MessageType::Request); - auto request = std::make_unique(); - request->setServiceName("test.service"); - request->setMethodName("test.method"); - request->setServiceVersion("test.version"); - request->messageBuffer().add("anything_for_attachment_update"); - - size_t fake_attachment_offset = 8; + auto request = std::make_unique("v", "v", "v", "v"); - request->setParametersLazyCallback([]() -> RpcRequestImpl::ParametersPtr { - return std::make_unique(); - }); + ArgumentVec args; + args.push_back(std::make_unique(true)); - request->setAttachmentLazyCallback([fake_attachment_offset]() -> RpcRequestImpl::AttachmentPtr { - return std::make_unique( - std::make_unique(), fake_attachment_offset); - }); - request->mutableAttachment()->insert("key", "value"); + request->content().initialize("Z", std::move(args), {}); + request->content().setAttachment("key", "value"); auto metadata = std::make_shared(); metadata->setContext(std::move(context)); @@ -653,9 +494,6 @@ TEST(Hessian2ProtocolTest, serializeRpcRequest) { Buffer::OwnedImpl buffer; serializer.serializeRpcRequest(buffer, *metadata); - // 8 (fake_attachment_offset) bytes for original parameters and 12 bytes for updated attachment. - EXPECT_EQ(20, buffer.length()); - EXPECT_EQ(true, absl::StrContains(buffer.toString(), "value")); } } @@ -684,28 +522,13 @@ TEST(Hessian2ProtocolTest, serializeRpcResponse) { context->setMessageType(MessageType::Response); context->setResponseStatus(ResponseStatus::Ok); - auto response = std::make_unique(); - response->messageBuffer().add("anything"); - - auto metadata = std::make_shared(); - metadata->setContext(std::move(context)); - metadata->setResponse(std::move(response)); - - Buffer::OwnedImpl buffer; - serializer.serializeRpcResponse(buffer, *metadata); - - // The data in message buffer will be used directly for normal response. - EXPECT_EQ("anything", buffer.toString()); - } - - // Local response without response type. - { - auto context = std::make_unique(); - context->setMessageType(MessageType::Response); - context->setResponseStatus(ResponseStatus::Ok); + auto response = std::make_unique(); + response->setResponseType(RpcResponseType::ResponseWithValue); - auto response = std::make_unique(); - response->setLocalRawMessage("anything"); + Buffer::OwnedImpl response_content; + response_content.writeByte('\x08'); + response_content.add("anything"); + response->content().initialize(response_content, 9); auto metadata = std::make_shared(); metadata->setContext(std::move(context)); @@ -714,34 +537,8 @@ TEST(Hessian2ProtocolTest, serializeRpcResponse) { Buffer::OwnedImpl buffer; serializer.serializeRpcResponse(buffer, *metadata); - auto buffer_string = buffer.toString(); - - EXPECT_EQ(0x08, static_cast(buffer_string[0])); // Check length. - EXPECT_EQ("anything", buffer_string.substr(1)); - } - - // Local response with response type. - { - auto context = std::make_unique(); - context->setMessageType(MessageType::Response); - context->setResponseStatus(ResponseStatus::Ok); - - auto response = std::make_unique(); - response->setResponseType(RpcResponseType::ResponseWithException); - response->setLocalRawMessage("anything"); - - auto metadata = std::make_shared(); - metadata->setContext(std::move(context)); - metadata->setResponse(std::move(response)); - - Buffer::OwnedImpl buffer; - serializer.serializeRpcResponse(buffer, *metadata); - - auto buffer_string = buffer.toString(); - - EXPECT_EQ(0x90, static_cast(buffer_string[0])); // Check response type. - EXPECT_EQ(0x08, static_cast(buffer_string[1])); // Check length. - EXPECT_EQ("anything", buffer_string.substr(2)); + // The data in message buffer will be used directly for normal response. + EXPECT_EQ("anything", buffer.toString().substr(2)); } } diff --git a/test/extensions/common/dubbo/message_test.cc b/test/extensions/common/dubbo/message_test.cc index faa933638736..ebe04176d896 100644 --- a/test/extensions/common/dubbo/message_test.cc +++ b/test/extensions/common/dubbo/message_test.cc @@ -1,4 +1,4 @@ -#include "source/extensions/common/dubbo/message_impl.h" +#include "source/extensions/common/dubbo/message.h" #include "test/test_common/printers.h" #include "test/test_common/utility.h" @@ -12,136 +12,552 @@ namespace Common { namespace Dubbo { namespace { -TEST(RpcRequestImplTest, RpcRequestAttachmentTest) { - auto map = std::make_unique(); +TEST(RpcRequestTest, SimpleSetAndGetTest) { + RpcRequest request("a", "b", "c", "d"); - map->emplace(std::make_unique("group"), - std::make_unique("fake_group")); - map->emplace(std::make_unique("fake_key"), - std::make_unique("fake_value")); + EXPECT_EQ("a", request.version()); - map->emplace(std::make_unique(), std::make_unique(0)); + EXPECT_EQ("b", request.service()); - map->emplace(std::make_unique("map_key"), - std::make_unique()); + EXPECT_EQ("c", request.serviceVersion()); - RpcRequestImpl::Attachment attachment(std::move(map), 23333); - - EXPECT_EQ(4, attachment.attachment().toUntypedMap().value().get().size()); - - // Test lookup. - EXPECT_EQ(absl::nullopt, attachment.lookup("map_key")); - EXPECT_EQ("fake_group", *attachment.lookup("group")); - - EXPECT_FALSE(attachment.attachmentUpdated()); - - // Test remove. Remove a normal string type key/value pair. - EXPECT_EQ("fake_value", *attachment.lookup("fake_key")); - attachment.remove("fake_key"); - EXPECT_EQ(absl::nullopt, attachment.lookup("fake_key")); - - EXPECT_EQ(3, attachment.attachment().toUntypedMap().value().get().size()); - - // Test remove. Delete a key/value pair whose value type is map. - attachment.remove("map_key"); - EXPECT_EQ(2, attachment.attachment().toUntypedMap().value().get().size()); - - // Test insert. - attachment.insert("test", "test_value"); - EXPECT_EQ(3, attachment.attachment().toUntypedMap().value().get().size()); - - EXPECT_EQ("test_value", *attachment.lookup("test")); - - EXPECT_TRUE(attachment.attachmentUpdated()); - EXPECT_EQ(23333, attachment.attachmentOffset()); + EXPECT_EQ("d", request.method()); } -TEST(RpcRequestImplTest, RpcRequestImplTest) { - RpcRequestImpl request; - - request.setServiceName("fake_service"); - EXPECT_EQ("fake_service", request.serviceName()); +TEST(RpcRequestTest, InitializeWithBufferTest) { + // Empty buffer. + { + RpcRequest request("a", "b", "c", "d"); + + // Initialize the request with an empty buffer. + Buffer::OwnedImpl buffer; + request.content().initialize(buffer, buffer.length()); + + // Try get the argument vector. + EXPECT_EQ(request.content().arguments().size(), 0); + + // Try get the attachment group. + EXPECT_FALSE(request.content().attachments().contains("group")); + + // attachments() call will make the content to be decoded. + // And the content is empty, so the content will be treated as broken. + // So the content will be reset to an empty state: + // empty types, empty arguments, empty attachments. + EXPECT_EQ(request.content().buffer().toString(), std::string({0x0, 'H', 'Z'})); + + // Set the group. + request.content().setAttachment("group", "group"); + EXPECT_EQ("group", request.content().attachments().at("group")); + + // buffer() call will re-encode the attachments into the content buffer. + EXPECT_EQ(request.content().buffer().toString(), + std::string({ + 0x0, // Empty string types + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); + } + + // Buffer no argument but has attachment. + { + RpcRequest request("a", "b", "c", "d"); + + Buffer::OwnedImpl buffer(std::string({ + 0x0, // Empty string types + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z', // Attachments end + 'x' // Anything that make no sense + })); + + // Initialize the request with an empty buffer. + request.content().initialize(buffer, buffer.length() - 1); + + EXPECT_EQ(1, buffer.length()); // Other data should be consumed. + EXPECT_EQ(buffer.toString(), "x"); + + // Try get the argument vector. + EXPECT_EQ(request.content().arguments().size(), 0); + + // Try get the attachment group. + EXPECT_EQ("group", request.content().attachments().at("group")); + + EXPECT_EQ(request.content().buffer().toString(), + std::string({ + 0x0, // Empty string types + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); + + // Overwrite the group. + request.content().setAttachment("group", "groupx"); + + // buffer() call will re-encode the attachments into the content buffer. + EXPECT_EQ(request.content().buffer().toString(), + std::string({ + 0x0, // Empty string types + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x6, 'g', 'r', 'o', 'u', 'p', 'x', // Value + 'Z' // Attachments end + })); + + request.content().delAttachment("group"); + + // buffer() call will re-encode the attachments into the content buffer. + EXPECT_EQ(request.content().buffer().toString(), + std::string({ + 0x0, // Empty string types + 'H', // Attachments start + 'Z' // Attachments end + })); + } + + // Broken buffer where -1 is used to tell the arguments number but + // no following types string. + { + RpcRequest request("a", "b", "c", "d"); + Buffer::OwnedImpl buffer(std::string({ + '\x8f', // -1 means the following bytes is types string + 'H', + 'Z', + })); + + // Initialize the request with the broken buffer. + request.content().initialize(buffer, buffer.length()); + + EXPECT_EQ(0, request.content().arguments().size()); + + // The content is broken, so the content will be reset to an empty state: + // empty types, empty arguments, empty attachments. + EXPECT_EQ(request.content().buffer().toString(), std::string({0x0, 'H', 'Z'})); + } + + // Broken buffer where -2 is used to tell the arguments number. This is + // unexpected number. + { + RpcRequest request("a", "b", "c", "d"); + Buffer::OwnedImpl buffer(std::string({ + '\x8e', // -2 is unexpected + 'H', + 'Z', + })); + + // Initialize the request with the broken buffer. + request.content().initialize(buffer, buffer.length()); + + EXPECT_EQ(0, request.content().arguments().size()); + + // The content is broken, so the content will be reset to an empty state: + // empty types, empty arguments, empty attachments. + EXPECT_EQ(request.content().buffer().toString(), std::string({0x0, 'H', 'Z'})); + } + + // Correct buffer where -1 is used to tell the arguments number. + { + RpcRequest request("a", "b", "c", "d"); + Buffer::OwnedImpl buffer(std::string({ + '\x8f', // -1 means the following bytes is types string + 0x2, 'Z', 'Z', // The types string + 'T', 'F', // true and false + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); + + // Initialize the request with the correct buffer. + request.content().initialize(buffer, buffer.length()); + + // Get the argument vector. + const auto& args = request.content().arguments(); + + // There are 2 arguments. + EXPECT_EQ(2, args.size()); + + // The first argument is true. + EXPECT_EQ(true, args[0]->toBoolean().value().get()); + // The second argument is false. + EXPECT_EQ(false, args[1]->toBoolean().value().get()); + + // Get the attachment group. + EXPECT_EQ("group", request.content().attachments().at("group")); + + EXPECT_EQ(request.content().buffer().toString(), + std::string({ + '\x8f', // -1 means the following bytes is types string + 0x2, 'Z', 'Z', // The types string + 'T', 'F', // true and false + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); + + // Move the buffer. + Buffer::OwnedImpl buffer2; + request.content().bufferMoveTo(buffer2); + + // The buffer is moved, and if we call buffer() again, everything will be + // re-encoded. + // Note: -1 is removed because the single types string is enough. + EXPECT_EQ(request.content().buffer().toString(), std::string({ + 0x2, 'Z', 'Z', // The types string + 'T', 'F', // true and false + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); + } + + // Correct buffer with non-negative integer for the arguments number. + { + RpcRequest request("a", "b", "c", "d"); + Buffer::OwnedImpl buffer(std::string({ + '\x92', // 2 means the following are two arguments + 'T', 'F', // true and false + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); + + // Initialize the request with the correct buffer. + request.content().initialize(buffer, buffer.length()); + + // Get the argument vector. + const auto& args = request.content().arguments(); + + // There are 2 arguments. + EXPECT_EQ(2, args.size()); + + // The first argument is true. + EXPECT_EQ(true, args[0]->toBoolean().value().get()); + // The second argument is false. + EXPECT_EQ(false, args[1]->toBoolean().value().get()); + + // Get the attachment group. + EXPECT_EQ("group", request.content().attachments().at("group")); + + // Move the buffer. + Buffer::OwnedImpl buffer2; + request.content().bufferMoveTo(buffer2); + + // No types string is provided and the arguments are not empty. + // So the number of arguments will be used directly. + EXPECT_EQ(request.content().buffer().toString(), + std::string({ + '\x92', // 2 means the following are two arguments + 'T', 'F', // true and false + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); + } + + // Normal correct buffer with normal types string. + { + RpcRequest request("a", "b", "c", "d"); + Buffer::OwnedImpl buffer(std::string({ + 0x2, 'Z', 'Z', // The types string + 'T', 'F', // true and false + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'T', // Key + 'F', // Value + 'Z' // Attachments end + })); + + // Initialize the request with the correct buffer. + request.content().initialize(buffer, buffer.length()); + + // Get the argument vector. + const auto& args = request.content().arguments(); + + // There are 2 arguments. + EXPECT_EQ(2, args.size()); + + // The first argument is true. + EXPECT_EQ(true, args[0]->toBoolean().value().get()); + // The second argument is false. + EXPECT_EQ(false, args[1]->toBoolean().value().get()); + + // Only string key and value are used. + EXPECT_EQ(1, request.content().attachments().size()); + + // Get the attachment group. + EXPECT_EQ("group", request.content().attachments().at("group")); + + // Move the buffer. + Buffer::OwnedImpl buffer2; + request.content().bufferMoveTo(buffer2); + + EXPECT_EQ(request.content().buffer().toString(), std::string({ + 0x2, 'Z', 'Z', // The types string + 'T', 'F', // true and false + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z', // Attachments end + })); + } + + // Buffer without attachments. + { + RpcRequest request("a", "b", "c", "d"); + Buffer::OwnedImpl buffer(std::string({ + 0x2, 'Z', 'Z', // The types string + 'T', 'F', // true and false + })); + + // Initialize the request with the correct buffer. + request.content().initialize(buffer, buffer.length()); + + // Get the argument vector. + const auto& args = request.content().arguments(); + + // There are 2 arguments. + EXPECT_EQ(2, args.size()); + + // The first argument is true. + EXPECT_EQ(true, args[0]->toBoolean().value().get()); + // The second argument is false. + EXPECT_EQ(false, args[1]->toBoolean().value().get()); + + EXPECT_TRUE(request.content().attachments().empty()); + + // Move the buffer. + Buffer::OwnedImpl buffer2; + request.content().bufferMoveTo(buffer2); + + // Empty attachments will be encoded to the buffer when buffer() is called + // and the underlying buffer is empty. + EXPECT_EQ(request.content().buffer().toString(), std::string({ + 0x2, 'Z', 'Z', // The types string + 'T', 'F', // true and false + 'H', // Attachments start + 'Z', // Attachments end + })); + + // Set the attachment. + request.content().setAttachment("group", "group"); + + // buffer() call will re-encode the attachments into the content buffer. + EXPECT_EQ(request.content().buffer().toString(), std::string({ + 0x2, 'Z', 'Z', // The types string + 'T', 'F', // true and false + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); + } +} - request.setMethodName("fake_method"); - EXPECT_EQ("fake_method", request.methodName()); +TEST(RpcRequestTest, InitializeWithDecodedValuesTest) { + RpcRequest request("a", "b", "c", "d"); - request.setServiceVersion("fake_version"); - EXPECT_EQ("fake_version", request.serviceVersion()); + ArgumentVec args; + args.push_back(std::make_unique(true)); + args.push_back(std::make_unique(false)); - bool set_parameters{false}; - bool set_attachment{false}; + Attachments attachs; + attachs.emplace("group", "group"); - request.setParametersLazyCallback([&set_parameters]() -> RpcRequestImpl::ParametersPtr { - set_parameters = true; - return std::make_unique(); - }); + // Initialize the request with the given types and arguments and attachments. + request.content().initialize(std::string("ZZ"), std::move(args), std::move(attachs)); - request.setAttachmentLazyCallback([&set_attachment]() -> RpcRequestImpl::AttachmentPtr { - auto map = std::make_unique(); + // Get the argument vector. + const auto& args2 = request.content().arguments(); - map->emplace(std::make_unique("group"), - std::make_unique("fake_group")); + // There are 2 arguments. + EXPECT_EQ(2, args2.size()); - auto attach = std::make_unique(std::move(map), 0); + // The first argument is true. + EXPECT_EQ(true, args2[0]->toBoolean().value().get()); + // The second argument is false. + EXPECT_EQ(false, args2[1]->toBoolean().value().get()); - set_attachment = true; + // Get the attachment group. + EXPECT_EQ("group", request.content().attachments().at("group")); - return attach; - }); + // Get the buffer. + EXPECT_EQ(request.content().buffer().toString(), std::string({ + 0x2, 'Z', 'Z', // The types string + 'T', 'F', // true and false + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); +} - EXPECT_EQ(false, request.hasParameters()); - EXPECT_EQ(false, request.hasAttachment()); +TEST(RpcResponseTest, SimpleSetAndGetTest) { + RpcResponse response; - // When parsing attachment, parameters will also be parsed. - EXPECT_NE(nullptr, request.mutableAttachment()); - request.attachment(); - EXPECT_EQ(true, set_parameters); - EXPECT_EQ(true, set_attachment); - EXPECT_EQ(true, request.hasParameters()); - EXPECT_EQ(true, request.hasAttachment()); - EXPECT_EQ("fake_group", request.serviceGroup().value()); + EXPECT_EQ(absl::nullopt, response.responseType()); - request.setServiceGroup("new_fake_group"); - EXPECT_EQ("new_fake_group", request.serviceGroup().value()); + // Set the response type and validate we can get it. + response.setResponseType(RpcResponseType::ResponseNullValueWithAttachments); + EXPECT_EQ(RpcResponseType::ResponseNullValueWithAttachments, response.responseType().value()); +} - // If parameters and attachment have values, the callback function will not be executed. - set_parameters = false; - set_attachment = false; - EXPECT_NE(nullptr, request.mutableParameters()); - EXPECT_NE(nullptr, request.mutableAttachment()); - EXPECT_EQ(false, set_parameters); - EXPECT_EQ(false, set_attachment); +TEST(RpcResponseTest, InitializeWithBufferTest) { + // Empty buffer. + { + RpcResponse response; + + // Initialize the response with an empty buffer. + Buffer::OwnedImpl buffer; + response.content().initialize(buffer, buffer.length()); + + // Try get the result. + EXPECT_EQ(response.content().result(), nullptr); + + // Try get the attachments. + EXPECT_FALSE(response.content().attachments().contains("group")); + + // attachments() call will make the content to be decoded. + // And the content is empty, so the content will be treated as broken. + // So the content will be reset to an empty state: + // empty result, empty attachments. + EXPECT_EQ(response.content().buffer().toString(), std::string({'N', 'H', 'Z'})); + + // Set the group. + response.content().setAttachment("group", "group"); + EXPECT_EQ("group", response.content().attachments().at("group")); + + // buffer() call will re-encode the attachments into the content buffer. + EXPECT_EQ(response.content().buffer().toString(), std::string({ + 'N', + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); + } + + // Buffer with result but no attachments. + { + RpcResponse response; + + Buffer::OwnedImpl buffer(std::string({ + 'T', // true + })); + + // Initialize the response with the buffer. + response.content().initialize(buffer, buffer.length()); + + // Get the result. + const auto* result = response.content().result(); + + // The result is a boolean. + EXPECT_EQ(true, result->toBoolean().value().get()); + + // Try get the attachments. + EXPECT_FALSE(response.content().attachments().contains("group")); + + // Move the buffer. + Buffer::OwnedImpl buffer2; + response.content().bufferMoveTo(buffer2); + + // Empty attachments will be encoded to the buffer when buffer() is called + // and the underlying buffer is empty. + EXPECT_EQ(response.content().buffer().toString(), std::string({ + 'T', // true + 'H', // Attachments start + 'Z', // Attachments end + })); + + // Set the attachment. + response.content().setAttachment("group", "group"); + + // buffer() call will re-encode the attachments into the content buffer. + EXPECT_EQ(response.content().buffer().toString(), std::string({ + 'T', // true + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); + + // Remove the attachment. + response.content().delAttachment("group"); + + // buffer() call will re-encode the attachments into the content buffer. + EXPECT_EQ(response.content().buffer().toString(), std::string({ + 'T', // true + 'H', // Attachments start + 'Z' // Attachments end + })); + } + + // Buffer with result and attachments. + { + RpcResponse response; + + Buffer::OwnedImpl buffer(std::string({ + 'T', // true + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'T', // Key + 'F', // Value + 'Z' // Attachments end + })); + + // Initialize the response with the buffer. + response.content().initialize(buffer, buffer.length()); + + // Get the result. + const auto* result = response.content().result(); + + // The result is a boolean. + EXPECT_EQ(true, result->toBoolean().value().get()); + + // Only string key and value are used. + EXPECT_EQ(1, response.content().attachments().size()); + + // Get the attachment group. + EXPECT_EQ("group", response.content().attachments().at("group")); + } +} - // Reset attachment and parameters. - request.mutableParameters() = nullptr; - request.mutableAttachment() = nullptr; +TEST(RpcResponseTest, InitializeWithDecodedValuesTest) { + RpcResponse response; - // When parsing parameters, attachment will not be parsed. - request.mutableParameters(); - EXPECT_EQ(true, set_parameters); - EXPECT_EQ(false, set_attachment); - EXPECT_EQ(true, request.hasParameters()); - EXPECT_EQ(false, request.hasAttachment()); + Hessian2::ObjectPtr result = std::make_unique(true); + Attachments attachs; + attachs.emplace("group", "group"); - request.messageBuffer().add("abcdefg"); - EXPECT_EQ("abcdefg", request.messageBuffer().toString()); -} + // Initialize the response with the given result and attachments. + response.content().initialize(std::move(result), std::move(attachs)); -TEST(RpcResponseImplTest, RpcResponseImplTest) { - RpcResponseImpl result; + // Get the result. + const auto* result2 = response.content().result(); - EXPECT_EQ(false, result.responseType().has_value()); - result.setResponseType(RpcResponseType::ResponseWithValue); - EXPECT_EQ(true, result.responseType().has_value()); - EXPECT_EQ(RpcResponseType::ResponseWithValue, result.responseType().value()); + // The result is a boolean. + EXPECT_EQ(true, result2->toBoolean().value().get()); - EXPECT_EQ(false, result.localRawMessage().has_value()); - result.setLocalRawMessage("abcdefg"); - EXPECT_EQ(true, result.localRawMessage().has_value()); - EXPECT_EQ("abcdefg", result.localRawMessage().value()); + // Get the attachment group. + EXPECT_EQ("group", response.content().attachments().at("group")); - result.messageBuffer().add("abcdefg"); - EXPECT_EQ("abcdefg", result.messageBuffer().toString()); + // Get the buffer. + EXPECT_EQ(response.content().buffer().toString(), std::string({ + 'T', // true + 'H', // Attachments start + 0x5, 'g', 'r', 'o', 'u', 'p', // Key + 0x5, 'g', 'r', 'o', 'u', 'p', // Value + 'Z' // Attachments end + })); } } // namespace diff --git a/test/extensions/common/dubbo/metadata_test.cc b/test/extensions/common/dubbo/metadata_test.cc index f4cf7b15869d..4f45a8e7fdd0 100644 --- a/test/extensions/common/dubbo/metadata_test.cc +++ b/test/extensions/common/dubbo/metadata_test.cc @@ -1,6 +1,6 @@ #include -#include "source/extensions/common/dubbo/message_impl.h" +#include "source/extensions/common/dubbo/message.h" #include "source/extensions/common/dubbo/metadata.h" #include "gtest/gtest.h" @@ -14,10 +14,6 @@ namespace { TEST(ContextTest, ContextTest) { Context context; - // Simple set and get of serialize type. - context.setSerializeType(SerializeType::Hessian2); - EXPECT_EQ(SerializeType::Hessian2, context.serializeType()); - // Simple set and get of message type. context.setMessageType(MessageType::HeartbeatResponse); EXPECT_EQ(MessageType::HeartbeatResponse, context.messageType()); @@ -63,10 +59,10 @@ TEST(MessageMetadataTest, MessageMetadataTest) { auto context = std::make_unique(); auto raw_context = context.get(); - auto request = std::make_unique(); + auto request = std::make_unique("a", "b", "c", "d"); auto raw_request = request.get(); - auto response = std::make_unique(); + auto response = std::make_unique(); auto raw_response = response.get(); // Simple set and get of context. diff --git a/test/extensions/common/wasm/BUILD b/test/extensions/common/wasm/BUILD index 62daee5c4c16..cbd4bb7ade1e 100644 --- a/test/extensions/common/wasm/BUILD +++ b/test/extensions/common/wasm/BUILD @@ -100,7 +100,6 @@ envoy_cc_test_library( "//source/extensions/wasm_runtime/v8:config", "//source/extensions/wasm_runtime/wamr:config", "//source/extensions/wasm_runtime/wasmtime:config", - "//source/extensions/wasm_runtime/wavm:config", ], ) diff --git a/test/extensions/common/wasm/wasm_runtime.cc b/test/extensions/common/wasm/wasm_runtime.cc index db5ecc6e400b..e21f9835cea0 100644 --- a/test/extensions/common/wasm/wasm_runtime.cc +++ b/test/extensions/common/wasm/wasm_runtime.cc @@ -10,9 +10,6 @@ std::vector sandboxRuntimes() { #if defined(PROXY_WASM_HAS_RUNTIME_V8) runtimes.push_back("v8"); #endif -#if defined(PROXY_WASM_HAS_RUNTIME_WAVM) - runtimes.push_back("wavm"); -#endif #if defined(PROXY_WASM_HAS_RUNTIME_WAMR) runtimes.push_back("wamr"); #endif diff --git a/test/extensions/compression/zstd/zstd_compression_test.cc b/test/extensions/compression/zstd/zstd_compression_test.cc index 25a5b3efcaac..f6ece8fbf7f7 100644 --- a/test/extensions/compression/zstd/zstd_compression_test.cc +++ b/test/extensions/compression/zstd/zstd_compression_test.cc @@ -49,6 +49,7 @@ class ZstdCompressionTest { .WillRepeatedly( Invoke([this](absl::string_view, uint32_t, Filesystem::Watcher::OnChangedCb cb) { watch_cbs_.push_back(cb); + return absl::OkStatus(); })); return mock_watcher; })); diff --git a/test/extensions/config_subscription/filesystem/filesystem_subscription_impl_test.cc b/test/extensions/config_subscription/filesystem/filesystem_subscription_impl_test.cc index e358f635a989..ae959a94a79d 100644 --- a/test/extensions/config_subscription/filesystem/filesystem_subscription_impl_test.cc +++ b/test/extensions/config_subscription/filesystem/filesystem_subscription_impl_test.cc @@ -107,8 +107,11 @@ class FilesystemCollectionSubscriptionImplTest : public testing::Test, EXPECT_CALL(*dispatcher, createFilesystemWatcher_()).WillOnce(InvokeWithoutArgs([this] { Filesystem::MockWatcher* mock_watcher = new Filesystem::MockWatcher(); EXPECT_CALL(*mock_watcher, addWatch(path_.path(), Filesystem::Watcher::Events::MovedTo, _)) - .WillOnce(Invoke([this](absl::string_view, uint32_t, - Filesystem::Watcher::OnChangedCb cb) { on_changed_cb_ = cb; })); + .WillOnce( + Invoke([this](absl::string_view, uint32_t, Filesystem::Watcher::OnChangedCb cb) { + on_changed_cb_ = cb; + return absl::OkStatus(); + })); return mock_watcher; })); return dispatcher; diff --git a/test/extensions/config_subscription/filesystem/filesystem_subscription_test_harness.h b/test/extensions/config_subscription/filesystem/filesystem_subscription_test_harness.h index a91fbc5e4a1b..14a4d6d08743 100644 --- a/test/extensions/config_subscription/filesystem/filesystem_subscription_test_harness.h +++ b/test/extensions/config_subscription/filesystem/filesystem_subscription_test_harness.h @@ -46,8 +46,11 @@ class FilesystemSubscriptionTestHarness : public SubscriptionTestHarness { EXPECT_CALL(*dispatcher, createFilesystemWatcher_()).WillOnce(InvokeWithoutArgs([this] { Filesystem::MockWatcher* mock_watcher = new Filesystem::MockWatcher(); EXPECT_CALL(*mock_watcher, addWatch(path_.path(), Filesystem::Watcher::Events::MovedTo, _)) - .WillOnce(Invoke([this](absl::string_view, uint32_t, - Filesystem::Watcher::OnChangedCb cb) { on_changed_cb_ = cb; })); + .WillOnce( + Invoke([this](absl::string_view, uint32_t, Filesystem::Watcher::OnChangedCb cb) { + on_changed_cb_ = cb; + return absl::OkStatus(); + })); return mock_watcher; })); return dispatcher; diff --git a/test/extensions/config_subscription/grpc/eds_resources_cache_impl_test.cc b/test/extensions/config_subscription/grpc/eds_resources_cache_impl_test.cc index 4a769c8b25f0..ae65e05b390b 100644 --- a/test/extensions/config_subscription/grpc/eds_resources_cache_impl_test.cc +++ b/test/extensions/config_subscription/grpc/eds_resources_cache_impl_test.cc @@ -224,6 +224,49 @@ TEST_F(EdsResourcesCacheImplTest, ExplicitRemoveCallback) { EXPECT_EQ(0, callback.calls_counter_map_["foo_cla"]); } +// Validate explicit callback removal of multiple callbacks with the same name, +// and a call to setResource in between is executed properly. +TEST_F(EdsResourcesCacheImplTest, ExplicitSameNameRemoveCallbacks) { + ResourceRemovalCallbackCounter callback1; + ResourceRemovalCallbackCounter callback2; + + // Emulate receiving a resource. + ClusterLoadAssignment resource; + resource.set_cluster_name("foo"); + // Set the CLA resource to some resource_name. + resources_cache_.setResource("foo_cla", resource); + EXPECT_EQ(1, resources_cache_.cacheSizeForTest()); + + // Emulate resource fetched from cache with 2 different callbacks. + // Fetch the resource by the first name and register a callback. + const auto& fetched_resource1 = resources_cache_.getResource("foo_cla", &callback1); + EXPECT_TRUE(fetched_resource1.has_value()); + EXPECT_EQ("foo", fetched_resource1->cluster_name()); + EXPECT_EQ(0, callback1.calls_counter_map_["foo_cla"]); + // Fetch the resource by the second name and register the same callback. + const auto& fetched_resource2 = resources_cache_.getResource("foo_cla", &callback2); + EXPECT_TRUE(fetched_resource2.has_value()); + EXPECT_EQ("foo", fetched_resource2->cluster_name()); + EXPECT_EQ(0, callback2.calls_counter_map_["foo_cla"]); + + // Emulate receiving the resource again, and invoking the callbacks removal. + resources_cache_.removeCallback("foo_cla", &callback1); + + // Set the resource again (an ongoing update). + ClusterLoadAssignment resource2; + resource2.set_cluster_name("foo"); + resources_cache_.setResource("foo_cla", resource2); + + // Remove the callback using the second name. + resources_cache_.removeCallback("foo_cla", &callback2); + + // Remove the resource using the first name, callback not invoked. + resources_cache_.removeResource("foo_cla"); + EXPECT_EQ(0, resources_cache_.cacheSizeForTest()); + EXPECT_EQ(0, callback1.calls_counter_map_["foo_cla"]); + EXPECT_EQ(0, callback2.calls_counter_map_["foo_cla"]); +} + // Validate correct removal callback gets notified when multiple resources are used. TEST_F(EdsResourcesCacheImplTest, MultipleResourcesRemoveCallbackCalled) { ResourceRemovalCallbackCounter callback; diff --git a/test/extensions/filters/common/ext_authz/BUILD b/test/extensions/filters/common/ext_authz/BUILD index 5c87d0b63ad4..2802d9397a4f 100644 --- a/test/extensions/filters/common/ext_authz/BUILD +++ b/test/extensions/filters/common/ext_authz/BUILD @@ -19,6 +19,7 @@ envoy_cc_test( "//source/extensions/filters/common/ext_authz:ext_authz_interface", "//test/mocks/http:http_mocks", "//test/mocks/network:network_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/ssl:ssl_mocks", "//test/mocks/stream_info:stream_info_mocks", "//test/test_common:utility_lib", @@ -45,6 +46,7 @@ envoy_cc_test( deps = [ "//source/extensions/filters/common/ext_authz:ext_authz_http_lib", "//test/extensions/filters/common/ext_authz:ext_authz_test_common", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/stream_info:stream_info_mocks", "//test/mocks/upstream:cluster_manager_mocks", "@envoy_api//envoy/extensions/filters/http/ext_authz/v3:pkg_cc_proto", diff --git a/test/extensions/filters/common/ext_authz/check_request_utils_test.cc b/test/extensions/filters/common/ext_authz/check_request_utils_test.cc index 9486088ba523..e87ede32c7ae 100644 --- a/test/extensions/filters/common/ext_authz/check_request_utils_test.cc +++ b/test/extensions/filters/common/ext_authz/check_request_utils_test.cc @@ -10,6 +10,7 @@ #include "test/mocks/http/mocks.h" #include "test/mocks/network/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/ssl/mocks.h" #include "test/mocks/stream_info/mocks.h" @@ -110,11 +111,13 @@ class CheckRequestUtilsTest : public testing::Test { } MatcherSharedPtr createRequestHeaderMatchers() { + NiceMock factory_context; envoy::extensions::filters::http::ext_authz::v3::ExtAuthz ext_autz_proto_; ext_autz_proto_.mutable_allowed_headers()->add_patterns()->set_exact("foo"); ext_autz_proto_.mutable_allowed_headers()->add_patterns()->set_exact("hello"); ext_autz_proto_.mutable_allowed_headers()->add_patterns()->set_exact("duplicate"); - return CheckRequestUtils::toRequestMatchers(ext_autz_proto_.allowed_headers(), false); + return CheckRequestUtils::toRequestMatchers(ext_autz_proto_.allowed_headers(), false, + factory_context); } Network::Address::InstanceConstSharedPtr addr_; diff --git a/test/extensions/filters/common/ext_authz/ext_authz_http_impl_test.cc b/test/extensions/filters/common/ext_authz/ext_authz_http_impl_test.cc index e7ca63a9a97b..6322721b6729 100644 --- a/test/extensions/filters/common/ext_authz/ext_authz_http_impl_test.cc +++ b/test/extensions/filters/common/ext_authz/ext_authz_http_impl_test.cc @@ -10,6 +10,7 @@ #include "test/extensions/filters/common/ext_authz/mocks.h" #include "test/extensions/filters/common/ext_authz/test_common.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/stream_info/mocks.h" #include "test/mocks/upstream/cluster_manager.h" @@ -90,7 +91,7 @@ class ExtAuthzHttpClientTest : public testing::Test { } cm_.initializeThreadLocalClusters({"ext_authz"}); - return std::make_shared(proto_config, timeout, path_prefix); + return std::make_shared(proto_config, timeout, path_prefix, factory_context_); } void dynamicMetadataTest(CheckStatus status, const std::string& http_status) { @@ -178,6 +179,7 @@ class ExtAuthzHttpClientTest : public testing::Test { return message_ptr; } + NiceMock factory_context_; NiceMock cm_; NiceMock async_client_; NiceMock async_request_; diff --git a/test/extensions/filters/common/rbac/BUILD b/test/extensions/filters/common/rbac/BUILD index 3cdc7f4eb9ff..ca59ee1f59d8 100644 --- a/test/extensions/filters/common/rbac/BUILD +++ b/test/extensions/filters/common/rbac/BUILD @@ -22,6 +22,7 @@ envoy_extension_cc_test( "//source/extensions/filters/common/expr:evaluator_lib", "//source/extensions/filters/common/rbac:matchers_lib", "//test/mocks/network:network_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/ssl:ssl_mocks", "//test/test_common:utility_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", diff --git a/test/extensions/filters/common/rbac/engine_impl_test.cc b/test/extensions/filters/common/rbac/engine_impl_test.cc index 872336d6744f..957229877617 100644 --- a/test/extensions/filters/common/rbac/engine_impl_test.cc +++ b/test/extensions/filters/common/rbac/engine_impl_test.cc @@ -100,15 +100,16 @@ void onMetadata(NiceMock& info) { } TEST(RoleBasedAccessControlEngineImpl, Disabled) { + Server::Configuration::MockServerFactoryContext factory_context; envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::ALLOW); RBAC::RoleBasedAccessControlEngineImpl engine_allow( - rbac, ProtobufMessage::getStrictValidationVisitor()); + rbac, ProtobufMessage::getStrictValidationVisitor(), factory_context); checkEngine(engine_allow, false, LogResult::Undecided); rbac.set_action(envoy::config::rbac::v3::RBAC::DENY); - RBAC::RoleBasedAccessControlEngineImpl engine_deny(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine_deny( + rbac, ProtobufMessage::getStrictValidationVisitor(), factory_context); checkEngine(engine_deny, true, LogResult::Undecided); } @@ -199,6 +200,7 @@ TEST(RoleBasedAccessControlEngineImpl, InvalidConfig) { } TEST(RoleBasedAccessControlEngineImpl, AllowedAllowlist) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_destination_port(123); policy.add_principals()->set_any(true); @@ -206,8 +208,8 @@ TEST(RoleBasedAccessControlEngineImpl, AllowedAllowlist) { envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::ALLOW); (*rbac.mutable_policies())["foo"] = policy; - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); Envoy::Network::MockConnection conn; Envoy::Http::TestRequestHeaderMapImpl headers; @@ -223,6 +225,7 @@ TEST(RoleBasedAccessControlEngineImpl, AllowedAllowlist) { } TEST(RoleBasedAccessControlEngineImpl, DeniedDenylist) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_destination_port(123); policy.add_principals()->set_any(true); @@ -230,8 +233,8 @@ TEST(RoleBasedAccessControlEngineImpl, DeniedDenylist) { envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::DENY); (*rbac.mutable_policies())["foo"] = policy; - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); Envoy::Network::MockConnection conn; Envoy::Http::TestRequestHeaderMapImpl headers; @@ -247,6 +250,7 @@ TEST(RoleBasedAccessControlEngineImpl, DeniedDenylist) { } TEST(RoleBasedAccessControlEngineImpl, BasicCondition) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_any(true); policy.add_principals()->set_any(true); @@ -259,12 +263,13 @@ TEST(RoleBasedAccessControlEngineImpl, BasicCondition) { envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::ALLOW); (*rbac.mutable_policies())["foo"] = policy; - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); checkEngine(engine, false, LogResult::Undecided); } TEST(RoleBasedAccessControlEngineImpl, MalformedCondition) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_any(true); policy.add_principals()->set_any(true); @@ -282,16 +287,17 @@ TEST(RoleBasedAccessControlEngineImpl, MalformedCondition) { (*rbac.mutable_policies())["foo"] = policy; EXPECT_THROW_WITH_REGEX(RBAC::RoleBasedAccessControlEngineImpl engine( - rbac, ProtobufMessage::getStrictValidationVisitor()), + rbac, ProtobufMessage::getStrictValidationVisitor(), factory_context), EnvoyException, "failed to create an expression: .*"); rbac.set_action(envoy::config::rbac::v3::RBAC::LOG); EXPECT_THROW_WITH_REGEX(RBAC::RoleBasedAccessControlEngineImpl engine_log( - rbac, ProtobufMessage::getStrictValidationVisitor()), + rbac, ProtobufMessage::getStrictValidationVisitor(), factory_context), EnvoyException, "failed to create an expression: .*"); } TEST(RoleBasedAccessControlEngineImpl, MistypedCondition) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_any(true); policy.add_principals()->set_any(true); @@ -304,12 +310,13 @@ TEST(RoleBasedAccessControlEngineImpl, MistypedCondition) { envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::ALLOW); (*rbac.mutable_policies())["foo"] = policy; - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); checkEngine(engine, false, LogResult::Undecided); } TEST(RoleBasedAccessControlEngineImpl, EvaluationFailure) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_any(true); policy.add_principals()->set_any(true); @@ -325,12 +332,13 @@ TEST(RoleBasedAccessControlEngineImpl, EvaluationFailure) { envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::ALLOW); (*rbac.mutable_policies())["foo"] = policy; - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); checkEngine(engine, false, LogResult::Undecided); } TEST(RoleBasedAccessControlEngineImpl, ErrorCondition) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_any(true); policy.add_principals()->set_any(true); @@ -351,12 +359,13 @@ TEST(RoleBasedAccessControlEngineImpl, ErrorCondition) { envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::ALLOW); (*rbac.mutable_policies())["foo"] = policy; - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); checkEngine(engine, false, LogResult::Undecided, Envoy::Network::MockConnection()); } TEST(RoleBasedAccessControlEngineImpl, HeaderCondition) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_any(true); policy.add_principals()->set_any(true); @@ -382,8 +391,8 @@ TEST(RoleBasedAccessControlEngineImpl, HeaderCondition) { envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::ALLOW); (*rbac.mutable_policies())["foo"] = policy; - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); Envoy::Http::TestRequestHeaderMapImpl headers; Envoy::Http::LowerCaseString key("foo"); @@ -394,6 +403,7 @@ TEST(RoleBasedAccessControlEngineImpl, HeaderCondition) { } TEST(RoleBasedAccessControlEngineImpl, MetadataCondition) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_any(true); policy.add_principals()->set_any(true); @@ -424,8 +434,8 @@ TEST(RoleBasedAccessControlEngineImpl, MetadataCondition) { envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::ALLOW); (*rbac.mutable_policies())["foo"] = policy; - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); Envoy::Http::TestRequestHeaderMapImpl headers; NiceMock info; @@ -440,6 +450,7 @@ TEST(RoleBasedAccessControlEngineImpl, MetadataCondition) { } TEST(RoleBasedAccessControlEngineImpl, ConjunctiveCondition) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_destination_port(123); policy.add_principals()->set_any(true); @@ -452,8 +463,8 @@ TEST(RoleBasedAccessControlEngineImpl, ConjunctiveCondition) { envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::ALLOW); (*rbac.mutable_policies())["foo"] = policy; - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); Envoy::Network::MockConnection conn; Envoy::Http::TestRequestHeaderMapImpl headers; @@ -572,17 +583,19 @@ TEST(RoleBasedAccessControlMatcherEngineImpl, DeniedDenylist) { // Log tests TEST(RoleBasedAccessControlEngineImpl, DisabledLog) { + NiceMock factory_context; NiceMock info; onMetadata(info); envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::LOG); - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); checkEngine(engine, true, RBAC::LogResult::No, info); } TEST(RoleBasedAccessControlEngineImpl, LogIfMatched) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_destination_port(123); policy.add_principals()->set_any(true); @@ -590,8 +603,8 @@ TEST(RoleBasedAccessControlEngineImpl, LogIfMatched) { envoy::config::rbac::v3::RBAC rbac; rbac.set_action(envoy::config::rbac::v3::RBAC::LOG); (*rbac.mutable_policies())["foo"] = policy; - RBAC::RoleBasedAccessControlEngineImpl engine(rbac, - ProtobufMessage::getStrictValidationVisitor()); + RBAC::RoleBasedAccessControlEngineImpl engine(rbac, ProtobufMessage::getStrictValidationVisitor(), + factory_context); Envoy::Network::MockConnection conn; Envoy::Http::TestRequestHeaderMapImpl headers; diff --git a/test/extensions/filters/common/rbac/matchers_test.cc b/test/extensions/filters/common/rbac/matchers_test.cc index 2ef2b577fcea..c64d6ec9818f 100644 --- a/test/extensions/filters/common/rbac/matchers_test.cc +++ b/test/extensions/filters/common/rbac/matchers_test.cc @@ -11,6 +11,7 @@ #include "source/extensions/filters/common/rbac/matchers.h" #include "test/mocks/network/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/ssl/mocks.h" #include "gmock/gmock.h" @@ -40,11 +41,13 @@ PortRangeMatcher createPortRangeMatcher(envoy::type::v3::Int32Range range) { ret TEST(AlwaysMatcher, AlwaysMatches) { checkMatcher(RBAC::AlwaysMatcher(), true); } TEST(AndMatcher, Permission_Set) { + NiceMock factory_context; envoy::config::rbac::v3::Permission::Set set; envoy::config::rbac::v3::Permission* perm = set.add_rules(); perm->set_any(true); - checkMatcher(RBAC::AndMatcher(set, ProtobufMessage::getStrictValidationVisitor()), true); + checkMatcher( + RBAC::AndMatcher(set, ProtobufMessage::getStrictValidationVisitor(), factory_context), true); perm = set.add_rules(); perm->set_destination_port(123); @@ -56,22 +59,25 @@ TEST(AndMatcher, Permission_Set) { Envoy::Network::Utility::parseInternetAddress("1.2.3.4", 123, false); info.downstream_connection_info_provider_->setLocalAddress(addr); - checkMatcher(RBAC::AndMatcher(set, ProtobufMessage::getStrictValidationVisitor()), true, conn, - headers, info); + checkMatcher( + RBAC::AndMatcher(set, ProtobufMessage::getStrictValidationVisitor(), factory_context), true, + conn, headers, info); addr = Envoy::Network::Utility::parseInternetAddress("1.2.3.4", 8080, false); info.downstream_connection_info_provider_->setLocalAddress(addr); - checkMatcher(RBAC::AndMatcher(set, ProtobufMessage::getStrictValidationVisitor()), false, conn, - headers, info); + checkMatcher( + RBAC::AndMatcher(set, ProtobufMessage::getStrictValidationVisitor(), factory_context), false, + conn, headers, info); } TEST(AndMatcher, Principal_Set) { + NiceMock factory_context; envoy::config::rbac::v3::Principal::Set set; envoy::config::rbac::v3::Principal* principal = set.add_ids(); principal->set_any(true); - checkMatcher(RBAC::AndMatcher(set), true); + checkMatcher(RBAC::AndMatcher(set, factory_context), true); principal = set.add_ids(); auto* cidr = principal->mutable_direct_remote_ip(); @@ -85,15 +91,16 @@ TEST(AndMatcher, Principal_Set) { Envoy::Network::Utility::parseInternetAddress("1.2.3.4", 123, false); info.downstream_connection_info_provider_->setDirectRemoteAddressForTest(addr); - checkMatcher(RBAC::AndMatcher(set), true, conn, headers, info); + checkMatcher(RBAC::AndMatcher(set, factory_context), true, conn, headers, info); addr = Envoy::Network::Utility::parseInternetAddress("1.2.4.6", 123, false); info.downstream_connection_info_provider_->setDirectRemoteAddressForTest(addr); - checkMatcher(RBAC::AndMatcher(set), false, conn, headers, info); + checkMatcher(RBAC::AndMatcher(set, factory_context), false, conn, headers, info); } TEST(OrMatcher, Permission_Set) { + NiceMock factory_context; envoy::config::rbac::v3::Permission::Set set; envoy::config::rbac::v3::Permission* perm = set.add_rules(); perm->set_destination_port(123); @@ -105,21 +112,21 @@ TEST(OrMatcher, Permission_Set) { Envoy::Network::Utility::parseInternetAddress("1.2.3.4", 456, false); info.downstream_connection_info_provider_->setLocalAddress(addr); - checkMatcher(RBAC::OrMatcher(set, ProtobufMessage::getStrictValidationVisitor()), false, conn, - headers, info); + checkMatcher(RBAC::OrMatcher(set, ProtobufMessage::getStrictValidationVisitor(), factory_context), + false, conn, headers, info); perm = set.add_rules(); perm->mutable_destination_port_range()->set_start(123); perm->mutable_destination_port_range()->set_end(456); - checkMatcher(RBAC::OrMatcher(set, ProtobufMessage::getStrictValidationVisitor()), false, conn, - headers, info); + checkMatcher(RBAC::OrMatcher(set, ProtobufMessage::getStrictValidationVisitor(), factory_context), + false, conn, headers, info); perm = set.add_rules(); perm->set_any(true); - checkMatcher(RBAC::OrMatcher(set, ProtobufMessage::getStrictValidationVisitor()), true, conn, - headers, info); + checkMatcher(RBAC::OrMatcher(set, ProtobufMessage::getStrictValidationVisitor(), factory_context), + true, conn, headers, info); } TEST(OrMatcher, Principal_Set) { @@ -129,6 +136,7 @@ TEST(OrMatcher, Principal_Set) { cidr->set_address_prefix("1.2.3.0"); cidr->mutable_prefix_len()->set_value(24); + NiceMock factory_context; Envoy::Network::MockConnection conn; Envoy::Http::TestRequestHeaderMapImpl headers; NiceMock info; @@ -136,27 +144,31 @@ TEST(OrMatcher, Principal_Set) { Envoy::Network::Utility::parseInternetAddress("1.2.4.6", 456, false); info.downstream_connection_info_provider_->setDirectRemoteAddressForTest(addr); - checkMatcher(RBAC::OrMatcher(set), false, conn, headers, info); + checkMatcher(RBAC::OrMatcher(set, factory_context), false, conn, headers, info); id = set.add_ids(); id->set_any(true); - checkMatcher(RBAC::OrMatcher(set), true, conn, headers, info); + checkMatcher(RBAC::OrMatcher(set, factory_context), true, conn, headers, info); } TEST(NotMatcher, Permission) { + NiceMock factory_context; envoy::config::rbac::v3::Permission perm; perm.set_any(true); - checkMatcher(RBAC::NotMatcher(perm, ProtobufMessage::getStrictValidationVisitor()), false, - Envoy::Network::MockConnection()); + checkMatcher( + RBAC::NotMatcher(perm, ProtobufMessage::getStrictValidationVisitor(), factory_context), false, + Envoy::Network::MockConnection()); } TEST(NotMatcher, Principal) { + NiceMock factory_context; envoy::config::rbac::v3::Principal principal; principal.set_any(true); - checkMatcher(RBAC::NotMatcher(principal), false, Envoy::Network::MockConnection()); + checkMatcher(RBAC::NotMatcher(principal, factory_context), false, + Envoy::Network::MockConnection()); } TEST(HeaderMatcher, HeaderMatcher) { @@ -315,15 +327,16 @@ TEST(AuthenticatedMatcher, uriSanPeerCertificate) { EXPECT_CALL(Const(conn), ssl()).WillRepeatedly(Return(ssl)); // We should check if any URI SAN matches. + NiceMock factory_context; envoy::config::rbac::v3::Principal::Authenticated auth; auth.mutable_principal_name()->set_exact("foo"); - checkMatcher(AuthenticatedMatcher(auth), true, conn); + checkMatcher(AuthenticatedMatcher(auth, factory_context), true, conn); auth.mutable_principal_name()->set_exact("baz"); - checkMatcher(AuthenticatedMatcher(auth), true, conn); + checkMatcher(AuthenticatedMatcher(auth, factory_context), true, conn); auth.mutable_principal_name()->set_exact("bar"); - checkMatcher(AuthenticatedMatcher(auth), false, conn); + checkMatcher(AuthenticatedMatcher(auth, factory_context), false, conn); } TEST(AuthenticatedMatcher, dnsSanPeerCertificate) { @@ -344,14 +357,15 @@ TEST(AuthenticatedMatcher, dnsSanPeerCertificate) { // We should get check if any DNS SAN matches as URI SAN is not available. envoy::config::rbac::v3::Principal::Authenticated auth; + NiceMock factory_context; auth.mutable_principal_name()->set_exact("foo"); - checkMatcher(AuthenticatedMatcher(auth), true, conn); + checkMatcher(AuthenticatedMatcher(auth, factory_context), true, conn); auth.mutable_principal_name()->set_exact("baz"); - checkMatcher(AuthenticatedMatcher(auth), true, conn); + checkMatcher(AuthenticatedMatcher(auth, factory_context), true, conn); auth.mutable_principal_name()->set_exact("bar"); - checkMatcher(AuthenticatedMatcher(auth), false, conn); + checkMatcher(AuthenticatedMatcher(auth, factory_context), false, conn); } TEST(AuthenticatedMatcher, subjectPeerCertificate) { @@ -366,11 +380,12 @@ TEST(AuthenticatedMatcher, subjectPeerCertificate) { EXPECT_CALL(Const(conn), ssl()).WillRepeatedly(Return(ssl)); envoy::config::rbac::v3::Principal::Authenticated auth; + NiceMock factory_context; auth.mutable_principal_name()->set_exact("bar"); - checkMatcher(AuthenticatedMatcher(auth), true, conn); + checkMatcher(AuthenticatedMatcher(auth, factory_context), true, conn); auth.mutable_principal_name()->set_exact("foo"); - checkMatcher(AuthenticatedMatcher(auth), false, conn); + checkMatcher(AuthenticatedMatcher(auth, factory_context), false, conn); } TEST(AuthenticatedMatcher, AnySSLSubject) { @@ -381,16 +396,18 @@ TEST(AuthenticatedMatcher, AnySSLSubject) { EXPECT_CALL(Const(conn), ssl()).WillRepeatedly(Return(ssl)); envoy::config::rbac::v3::Principal::Authenticated auth; - checkMatcher(AuthenticatedMatcher(auth), true, conn); + NiceMock factory_context; + checkMatcher(AuthenticatedMatcher(auth, factory_context), true, conn); auth.mutable_principal_name()->MergeFrom(TestUtility::createRegexMatcher(".*")); - checkMatcher(AuthenticatedMatcher(auth), true, conn); + checkMatcher(AuthenticatedMatcher(auth, factory_context), true, conn); } TEST(AuthenticatedMatcher, NoSSL) { + NiceMock factory_context; Envoy::Network::MockConnection conn; EXPECT_CALL(Const(conn), ssl()).WillOnce(Return(nullptr)); - checkMatcher(AuthenticatedMatcher({}), false, conn); + checkMatcher(AuthenticatedMatcher({}, factory_context), false, conn); } TEST(MetadataMatcher, MetadataMatcher) { @@ -417,6 +434,7 @@ TEST(MetadataMatcher, MetadataMatcher) { } TEST(PolicyMatcher, PolicyMatcher) { + NiceMock factory_context; envoy::config::rbac::v3::Policy policy; policy.add_permissions()->set_destination_port(123); policy.add_permissions()->set_destination_port(456); @@ -424,7 +442,8 @@ TEST(PolicyMatcher, PolicyMatcher) { policy.add_principals()->mutable_authenticated()->mutable_principal_name()->set_exact("bar"); Expr::BuilderPtr builder = Expr::createBuilder(nullptr); - RBAC::PolicyMatcher matcher(policy, builder.get(), ProtobufMessage::getStrictValidationVisitor()); + RBAC::PolicyMatcher matcher(policy, builder.get(), ProtobufMessage::getStrictValidationVisitor(), + factory_context); Envoy::Network::MockConnection conn; Envoy::Http::TestRequestHeaderMapImpl headers; @@ -457,36 +476,52 @@ TEST(PolicyMatcher, PolicyMatcher) { } TEST(RequestedServerNameMatcher, ValidRequestedServerName) { + NiceMock factory_context; Envoy::Network::MockConnection conn; EXPECT_CALL(conn, requestedServerName()) .Times(9) .WillRepeatedly(Return(absl::string_view("www.cncf.io"))); - checkMatcher(RequestedServerNameMatcher(TestUtility::createRegexMatcher(".*cncf.io")), true, - conn); - checkMatcher(RequestedServerNameMatcher(TestUtility::createRegexMatcher(".*cncf.*")), true, conn); - checkMatcher(RequestedServerNameMatcher(TestUtility::createRegexMatcher("www.*")), true, conn); - checkMatcher(RequestedServerNameMatcher(TestUtility::createRegexMatcher(".*io")), true, conn); - checkMatcher(RequestedServerNameMatcher(TestUtility::createRegexMatcher(".*")), true, conn); - - checkMatcher(RequestedServerNameMatcher(TestUtility::createExactMatcher("")), false, conn); - checkMatcher(RequestedServerNameMatcher(TestUtility::createExactMatcher("www.cncf.io")), true, - conn); - checkMatcher(RequestedServerNameMatcher(TestUtility::createExactMatcher("xyz.cncf.io")), false, - conn); - checkMatcher(RequestedServerNameMatcher(TestUtility::createExactMatcher("example.com")), false, - conn); + checkMatcher( + RequestedServerNameMatcher(TestUtility::createRegexMatcher(".*cncf.io"), factory_context), + true, conn); + checkMatcher( + RequestedServerNameMatcher(TestUtility::createRegexMatcher(".*cncf.*"), factory_context), + true, conn); + checkMatcher( + RequestedServerNameMatcher(TestUtility::createRegexMatcher("www.*"), factory_context), true, + conn); + checkMatcher(RequestedServerNameMatcher(TestUtility::createRegexMatcher(".*io"), factory_context), + true, conn); + checkMatcher(RequestedServerNameMatcher(TestUtility::createRegexMatcher(".*"), factory_context), + true, conn); + + checkMatcher(RequestedServerNameMatcher(TestUtility::createExactMatcher(""), factory_context), + false, conn); + checkMatcher( + RequestedServerNameMatcher(TestUtility::createExactMatcher("www.cncf.io"), factory_context), + true, conn); + checkMatcher( + RequestedServerNameMatcher(TestUtility::createExactMatcher("xyz.cncf.io"), factory_context), + false, conn); + checkMatcher( + RequestedServerNameMatcher(TestUtility::createExactMatcher("example.com"), factory_context), + false, conn); } TEST(RequestedServerNameMatcher, EmptyRequestedServerName) { + NiceMock factory_context; Envoy::Network::MockConnection conn; EXPECT_CALL(conn, requestedServerName()).Times(3).WillRepeatedly(Return(absl::string_view(""))); - checkMatcher(RequestedServerNameMatcher(TestUtility::createRegexMatcher(".*")), true, conn); + checkMatcher(RequestedServerNameMatcher(TestUtility::createRegexMatcher(".*"), factory_context), + true, conn); - checkMatcher(RequestedServerNameMatcher(TestUtility::createExactMatcher("")), true, conn); - checkMatcher(RequestedServerNameMatcher(TestUtility::createExactMatcher("example.com")), false, - conn); + checkMatcher(RequestedServerNameMatcher(TestUtility::createExactMatcher(""), factory_context), + true, conn); + checkMatcher( + RequestedServerNameMatcher(TestUtility::createExactMatcher("example.com"), factory_context), + false, conn); } TEST(PathMatcher, NoPathInHeader) { diff --git a/test/extensions/filters/common/rbac/mocks.h b/test/extensions/filters/common/rbac/mocks.h index 83088d8ab6ef..c1f9ac0916e9 100644 --- a/test/extensions/filters/common/rbac/mocks.h +++ b/test/extensions/filters/common/rbac/mocks.h @@ -17,9 +17,10 @@ namespace RBAC { class MockEngine : public RoleBasedAccessControlEngineImpl { public: MockEngine(const envoy::config::rbac::v3::RBAC& rules, + Server::Configuration::CommonFactoryContext& context, const EnforcementMode mode = EnforcementMode::Enforced) : RoleBasedAccessControlEngineImpl(rules, ProtobufMessage::getStrictValidationVisitor(), - mode){}; + context, mode){}; MOCK_METHOD(bool, handleAction, (const Envoy::Network::Connection&, const Envoy::Http::RequestHeaderMap&, diff --git a/test/extensions/filters/http/cache/BUILD b/test/extensions/filters/http/cache/BUILD index f45131e56f5d..1d1f5f0ab270 100644 --- a/test/extensions/filters/http/cache/BUILD +++ b/test/extensions/filters/http/cache/BUILD @@ -25,6 +25,7 @@ envoy_extension_cc_test( "//envoy/http:header_map_interface", "//source/common/http:header_map_lib", "//source/extensions/filters/http/cache:cache_headers_utils_lib", + "//test/mocks/server:server_factory_context_mocks", "//test/test_common:simulated_time_system_lib", "//test/test_common:utility_lib", ], @@ -56,6 +57,7 @@ envoy_extension_cc_test( "//source/extensions/filters/http/cache:http_cache_lib", "//source/extensions/http/cache/simple_http_cache:config", "//test/mocks/http:http_mocks", + "//test/mocks/server:factory_context_mocks", "//test/test_common:simulated_time_system_lib", "//test/test_common:test_runtime_lib", "//test/test_common:utility_lib", @@ -96,6 +98,7 @@ envoy_extension_cc_test( extension_names = ["envoy.filters.http.cache"], deps = [ "//source/extensions/filters/http/cache:cacheability_utils_lib", + "//test/mocks/server:server_factory_context_mocks", "//test/test_common:utility_lib", ], ) diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc index 9c4e1afb273d..9ddf067b4d58 100644 --- a/test/extensions/filters/http/cache/cache_filter_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -30,15 +30,15 @@ class CacheFilterTest : public ::testing::Test { // The filter has to be created as a shared_ptr to enable shared_from_this() which is used in the // cache callbacks. CacheFilterSharedPtr makeFilter(std::shared_ptr cache, bool auto_destroy = true) { - std::shared_ptr filter( - new CacheFilter(config_, /*stats_prefix=*/"", context_.scope(), - context_.server_factory_context_.timeSource(), cache), - [auto_destroy](CacheFilter* f) { - if (auto_destroy) { - f->onDestroy(); - } - delete f; - }); + std::shared_ptr filter(new CacheFilter(config_, /*stats_prefix=*/"", + context_.scope(), + context_.server_factory_context_, cache), + [auto_destroy](CacheFilter* f) { + if (auto_destroy) { + f->onDestroy(); + } + delete f; + }); filter_state_ = std::make_shared( StreamInfo::FilterState::LifeSpan::FilterChain); filter->setDecoderFilterCallbacks(decoder_callbacks_); diff --git a/test/extensions/filters/http/cache/cache_headers_utils_test.cc b/test/extensions/filters/http/cache/cache_headers_utils_test.cc index 2b5a0de3304e..453ab301513a 100644 --- a/test/extensions/filters/http/cache/cache_headers_utils_test.cc +++ b/test/extensions/filters/http/cache/cache_headers_utils_test.cc @@ -10,6 +10,7 @@ #include "source/common/http/header_utility.h" #include "source/extensions/filters/http/cache/cache_headers_utils.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/simulated_time_system.h" #include "test/test_common/utility.h" @@ -652,8 +653,11 @@ TEST_P(ParseCommaDelimitedHeaderTest, ParseCommaDelimitedHeader) { } TEST(CreateVaryIdentifier, IsStableForAllowListOrder) { - VaryAllowList vary_allow_list1(toStringMatchers({"width", "accept", "accept-language"})); - VaryAllowList vary_allow_list2(toStringMatchers({"accept", "width", "accept-language"})); + NiceMock factory_context; + VaryAllowList vary_allow_list1(toStringMatchers({"width", "accept", "accept-language"}), + factory_context); + VaryAllowList vary_allow_list2(toStringMatchers({"accept", "width", "accept-language"}), + factory_context); Http::TestRequestHeaderMapImpl request_headers{ {"accept", "image/*"}, {"accept-language", "en-us"}, {"width", "640"}}; @@ -710,16 +714,20 @@ TEST(HasVary, NotEmpty) { } TEST(CreateVaryIdentifier, EmptyVaryEntry) { + NiceMock factory_context; Http::TestRequestHeaderMapImpl request_headers{{"accept", "image/*"}}; - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); EXPECT_EQ(VaryHeaderUtils::createVaryIdentifier(vary_allow_list, {}, request_headers), "vary-id\n"); } TEST(CreateVaryIdentifier, SingleHeaderExists) { + NiceMock factory_context; Http::TestRequestHeaderMapImpl request_headers{{"accept", "image/*"}}; - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); EXPECT_EQ(VaryHeaderUtils::createVaryIdentifier(vary_allow_list, {"accept"}, request_headers), "vary-id\naccept\r" @@ -727,17 +735,21 @@ TEST(CreateVaryIdentifier, SingleHeaderExists) { } TEST(CreateVaryIdentifier, SingleHeaderMissing) { + NiceMock factory_context; Http::TestRequestHeaderMapImpl request_headers; - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); EXPECT_EQ(VaryHeaderUtils::createVaryIdentifier(vary_allow_list, {"accept"}, request_headers), "vary-id\naccept\r\n"); } TEST(CreateVaryIdentifier, MultipleHeadersAllExist) { + NiceMock factory_context; Http::TestRequestHeaderMapImpl request_headers{ {"accept", "image/*"}, {"accept-language", "en-us"}, {"width", "640"}}; - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); EXPECT_EQ(VaryHeaderUtils::createVaryIdentifier( vary_allow_list, {"accept", "accept-language", "width"}, request_headers), @@ -747,9 +759,11 @@ TEST(CreateVaryIdentifier, MultipleHeadersAllExist) { } TEST(CreateVaryIdentifier, MultipleHeadersSomeExist) { + NiceMock factory_context; Http::TestResponseHeaderMapImpl response_headers{{"vary", "accept, accept-language, width"}}; Http::TestRequestHeaderMapImpl request_headers{{"accept", "image/*"}, {"width", "640"}}; - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); EXPECT_EQ(VaryHeaderUtils::createVaryIdentifier( vary_allow_list, {"accept", "accept-language", "width"}, request_headers), @@ -758,9 +772,11 @@ TEST(CreateVaryIdentifier, MultipleHeadersSomeExist) { } TEST(CreateVaryIdentifier, ExtraRequestHeaders) { + NiceMock factory_context; Http::TestRequestHeaderMapImpl request_headers{ {"accept", "image/*"}, {"heigth", "1280"}, {"width", "640"}}; - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); EXPECT_EQ( VaryHeaderUtils::createVaryIdentifier(vary_allow_list, {"accept", "width"}, request_headers), @@ -769,8 +785,10 @@ TEST(CreateVaryIdentifier, ExtraRequestHeaders) { } TEST(CreateVaryIdentifier, MultipleHeadersNoneExist) { + NiceMock factory_context; Http::TestRequestHeaderMapImpl request_headers; - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); EXPECT_EQ(VaryHeaderUtils::createVaryIdentifier( vary_allow_list, {"accept", "accept-language", "width"}, request_headers), @@ -778,9 +796,12 @@ TEST(CreateVaryIdentifier, MultipleHeadersNoneExist) { } TEST(CreateVaryIdentifier, DifferentHeadersSameValue) { + NiceMock factory_context; + // Two requests with the same value for different headers must have different // vary-ids. - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); Http::TestRequestHeaderMapImpl request_headers1{{"accept", "foo"}}; absl::optional vary_identifier1 = VaryHeaderUtils::createVaryIdentifier( @@ -796,8 +817,10 @@ TEST(CreateVaryIdentifier, DifferentHeadersSameValue) { } TEST(CreateVaryIdentifier, MultiValueSameHeader) { + NiceMock factory_context; Http::TestRequestHeaderMapImpl request_headers{{"width", "foo"}, {"width", "bar"}}; - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); EXPECT_EQ(VaryHeaderUtils::createVaryIdentifier(vary_allow_list, {"width"}, request_headers), "vary-id\nwidth\r" @@ -806,16 +829,20 @@ TEST(CreateVaryIdentifier, MultiValueSameHeader) { } TEST(CreateVaryIdentifier, DisallowedHeader) { + NiceMock factory_context; Http::TestRequestHeaderMapImpl request_headers{{"width", "foo"}}; - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); EXPECT_EQ(VaryHeaderUtils::createVaryIdentifier(vary_allow_list, {"disallowed"}, request_headers), absl::nullopt); } TEST(CreateVaryIdentifier, DisallowedHeaderWithAllowedHeader) { + NiceMock factory_context; Http::TestRequestHeaderMapImpl request_headers{{"width", "foo"}}; - VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"})); + VaryAllowList vary_allow_list(toStringMatchers({"accept", "accept-language", "width"}), + factory_context); EXPECT_EQ( VaryHeaderUtils::createVaryIdentifier(vary_allow_list, {"disallowed,width"}, request_headers), @@ -840,8 +867,9 @@ envoy::extensions::filters::http::cache::v3::CacheConfig getConfig() { class VaryAllowListTest : public testing::Test { protected: - VaryAllowListTest() : vary_allow_list_(getConfig().allowed_vary_headers()) {} + VaryAllowListTest() : vary_allow_list_(getConfig().allowed_vary_headers(), factory_context_) {} + NiceMock factory_context_; VaryAllowList vary_allow_list_; Http::TestRequestHeaderMapImpl request_headers_; Http::TestResponseHeaderMapImpl response_headers_; diff --git a/test/extensions/filters/http/cache/cacheability_utils_test.cc b/test/extensions/filters/http/cache/cacheability_utils_test.cc index e5bbf9061b8d..d43223f4075c 100644 --- a/test/extensions/filters/http/cache/cacheability_utils_test.cc +++ b/test/extensions/filters/http/cache/cacheability_utils_test.cc @@ -2,6 +2,7 @@ #include "source/extensions/filters/http/cache/cacheability_utils.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/utility.h" #include "gtest/gtest.h" @@ -35,13 +36,16 @@ envoy::extensions::filters::http::cache::v3::CacheConfig getConfig() { class IsCacheableResponseTest : public testing::Test { public: - IsCacheableResponseTest() : vary_allow_list_(getConfig().allowed_vary_headers()) {} + IsCacheableResponseTest() + : vary_allow_list_(getConfig().allowed_vary_headers(), factory_context_) {} protected: std::string cache_control_ = "max-age=3600"; Http::TestResponseHeaderMapImpl response_headers_ = {{":status", "200"}, {"date", "Sun, 06 Nov 1994 08:49:37 GMT"}, {"cache-control", cache_control_}}; + + NiceMock factory_context_; VaryAllowList vary_allow_list_; }; diff --git a/test/extensions/filters/http/cache/http_cache_implementation_test_common.cc b/test/extensions/filters/http/cache/http_cache_implementation_test_common.cc index 1101e451cabe..fdb4c3f240cf 100644 --- a/test/extensions/filters/http/cache/http_cache_implementation_test_common.cc +++ b/test/extensions/filters/http/cache/http_cache_implementation_test_common.cc @@ -41,7 +41,8 @@ MATCHER(IsOk, "") { return arg.ok(); } } // namespace HttpCacheImplementationTest::HttpCacheImplementationTest() - : delegate_(GetParam()()), vary_allow_list_(getConfig().allowed_vary_headers()) { + : delegate_(GetParam()()), + vary_allow_list_(getConfig().allowed_vary_headers(), factory_context_) { request_headers_.setMethod("GET"); request_headers_.setHost("example.com"); request_headers_.setScheme("https"); @@ -490,7 +491,8 @@ TEST_P(HttpCacheImplementationTest, VaryResponses) { Protobuf::RepeatedPtrField<::envoy::type::matcher::v3::StringMatcher> proto_allow_list; ::envoy::type::matcher::v3::StringMatcher* matcher = proto_allow_list.Add(); matcher->set_exact("width"); - vary_allow_list_ = VaryAllowList(proto_allow_list); + NiceMock factory_context; + vary_allow_list_ = VaryAllowList(proto_allow_list, factory_context); lookup(request_path); EXPECT_EQ(lookup_result_.cache_entry_status_, CacheEntryStatus::Unusable); } diff --git a/test/extensions/filters/http/cache/http_cache_implementation_test_common.h b/test/extensions/filters/http/cache/http_cache_implementation_test_common.h index 85c4ba8981fd..53553010fbaf 100644 --- a/test/extensions/filters/http/cache/http_cache_implementation_test_common.h +++ b/test/extensions/filters/http/cache/http_cache_implementation_test_common.h @@ -9,6 +9,7 @@ #include "test/mocks/event/mocks.h" #include "test/mocks/http/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/simulated_time_system.h" #include "test/test_common/utility.h" @@ -89,6 +90,7 @@ class HttpCacheImplementationTest expectLookupSuccessWithBodyAndTrailers(LookupContext* lookup, absl::string_view body, Http::TestResponseTrailerMapImpl trailers = {}); + NiceMock factory_context_; std::unique_ptr delegate_; VaryAllowList vary_allow_list_; LookupResult lookup_result_; diff --git a/test/extensions/filters/http/cache/http_cache_test.cc b/test/extensions/filters/http/cache/http_cache_test.cc index 54c53fb5f2cf..fa22525396ca 100644 --- a/test/extensions/filters/http/cache/http_cache_test.cc +++ b/test/extensions/filters/http/cache/http_cache_test.cc @@ -5,6 +5,7 @@ #include "source/extensions/filters/http/cache/http_cache.h" #include "test/mocks/http/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/simulated_time_system.h" #include "test/test_common/test_runtime.h" #include "test/test_common/utility.h" @@ -39,12 +40,13 @@ envoy::extensions::filters::http::cache::v3::CacheConfig getConfig() { class LookupRequestTest : public testing::TestWithParam { public: - LookupRequestTest() : vary_allow_list_(getConfig().allowed_vary_headers()) {} + LookupRequestTest() : vary_allow_list_(getConfig().allowed_vary_headers(), factory_context_) {} DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; Http::TestRequestHeaderMapImpl request_headers_{ {":path", "/"}, {":method", "GET"}, {":scheme", "https"}, {":authority", "example.com"}}; + NiceMock factory_context_; VaryAllowList vary_allow_list_; static const SystemTime& currentTime() { diff --git a/test/extensions/filters/http/csrf/BUILD b/test/extensions/filters/http/csrf/BUILD index 913a61c7c447..5a958bce60e8 100644 --- a/test/extensions/filters/http/csrf/BUILD +++ b/test/extensions/filters/http/csrf/BUILD @@ -21,6 +21,7 @@ envoy_extension_cc_test( "//source/extensions/filters/http/csrf:csrf_filter_lib", "//test/mocks/buffer:buffer_mocks", "//test/mocks/http:http_mocks", + "//test/mocks/server:factory_context_mocks", "//test/mocks/upstream:upstream_mocks", "@envoy_api//envoy/extensions/filters/http/csrf/v3:pkg_cc_proto", "@envoy_api//envoy/type/v3:pkg_cc_proto", diff --git a/test/extensions/filters/http/csrf/csrf_filter_test.cc b/test/extensions/filters/http/csrf/csrf_filter_test.cc index 85e2bb18a3bf..ba3b3bbe27ba 100644 --- a/test/extensions/filters/http/csrf/csrf_filter_test.cc +++ b/test/extensions/filters/http/csrf/csrf_filter_test.cc @@ -6,6 +6,7 @@ #include "test/mocks/buffer/mocks.h" #include "test/mocks/http/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/stats/mocks.h" #include "test/test_common/printers.h" @@ -43,7 +44,8 @@ class CsrfFilterTest : public testing::Test { const auto& add_regex_origin = policy.mutable_additional_origins()->Add(); add_regex_origin->MergeFrom(TestUtility::createRegexMatcher(R"(www\-[0-9]\.allow\.com)")); - return std::make_shared(policy, "test", *stats_.rootScope(), runtime_); + return std::make_shared(policy, "test", *stats_.rootScope(), + factory_context_); } CsrfFilterTest() : config_(setupConfig()), filter_(config_) {} @@ -69,14 +71,14 @@ class CsrfFilterTest : public testing::Test { } void setFilterEnabled(bool enabled) { - ON_CALL(runtime_.snapshot_, + ON_CALL(factory_context_.runtime_loader_.snapshot_, featureEnabled("csrf.enabled", testing::Matcher(_))) .WillByDefault(Return(enabled)); } void setShadowEnabled(bool enabled) { - ON_CALL(runtime_.snapshot_, + ON_CALL(factory_context_.runtime_loader_.snapshot_, featureEnabled("csrf.shadow_enabled", testing::Matcher(_))) .WillByDefault(Return(enabled)); @@ -87,7 +89,7 @@ class CsrfFilterTest : public testing::Test { Buffer::OwnedImpl data_; Router::MockDirectResponseEntry direct_response_entry_; Stats::IsolatedStoreImpl stats_; - NiceMock runtime_; + NiceMock factory_context_; CsrfFilterConfigSharedPtr config_; CsrfFilter filter_; diff --git a/test/extensions/filters/http/ext_authz/BUILD b/test/extensions/filters/http/ext_authz/BUILD index 6aaad4beb600..dc83e80e281d 100644 --- a/test/extensions/filters/http/ext_authz/BUILD +++ b/test/extensions/filters/http/ext_authz/BUILD @@ -32,6 +32,7 @@ envoy_extension_cc_test( "//test/mocks/http:http_mocks", "//test/mocks/network:network_mocks", "//test/mocks/runtime:runtime_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/tracing:tracing_mocks", "//test/mocks/upstream:cluster_manager_mocks", "//test/proto:helloworld_proto_cc_proto", @@ -107,9 +108,11 @@ envoy_cc_fuzz_test( "//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/grpc:grpc_mocks", "//test/mocks/http:http_mocks", "//test/mocks/network:network_mocks", "//test/mocks/runtime:runtime_mocks", + "//test/mocks/server:server_factory_context_mocks", "@envoy_api//envoy/extensions/filters/http/ext_authz/v3:pkg_cc_proto", ], ) diff --git a/test/extensions/filters/http/ext_authz/config_test.cc b/test/extensions/filters/http/ext_authz/config_test.cc index 62909863a5ec..dcdfc7f81ad5 100644 --- a/test/extensions/filters/http/ext_authz/config_test.cc +++ b/test/extensions/filters/http/ext_authz/config_test.cc @@ -31,10 +31,10 @@ namespace ExtAuthz { class TestAsyncClientManagerImpl : public Grpc::AsyncClientManagerImpl { public: TestAsyncClientManagerImpl(Upstream::ClusterManager& cm, ThreadLocal::Instance& tls, - TimeSource& time_source, Api::Api& api, + Server::Configuration::CommonFactoryContext& context, const Grpc::StatNames& stat_names, const Bootstrap::GrpcAsyncClientManagerConfig& config) - : Grpc::AsyncClientManagerImpl(cm, tls, time_source, api, stat_names, config) {} + : Grpc::AsyncClientManagerImpl(cm, tls, context, stat_names, config) {} absl::StatusOr factoryForGrpcService(const envoy::config::core::v3::GrpcService&, Stats::Scope&, bool) override { return std::make_unique>(); @@ -46,10 +46,13 @@ class ExtAuthzFilterTest : public Event::TestUsingSimulatedTime, public testing::Test { public: ExtAuthzFilterTest() : RealThreadsTestHelper(5), stat_names_(symbol_table_) { + ON_CALL(context_.server_factory_context_, threadLocal()) + .WillByDefault(testing::ReturnRef(tls())); + ON_CALL(context_.server_factory_context_, api()).WillByDefault(testing::ReturnRef(api())); runOnMainBlocking([&]() { async_client_manager_ = std::make_unique( - context_.server_factory_context_.cluster_manager_, tls(), api().timeSource(), api(), - stat_names_, Bootstrap::GrpcAsyncClientManagerConfig()); + context_.server_factory_context_.cluster_manager_, tls(), + context_.server_factory_context_, stat_names_, Bootstrap::GrpcAsyncClientManagerConfig()); }); } 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 index 0467323519e3..c4cf684b3521 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_corpus/bad_config +++ b/test/extensions/filters/http/ext_authz/ext_authz_corpus/bad_config @@ -30,4 +30,5 @@ filter_metadata { value { } } -} \ No newline at end of file +} + 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 index 90eb4a71ef41..82d08abfa9cb 100644 --- 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 @@ -26,6 +26,4 @@ request_data { } } } -result { - error {} -} +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 index f51532b69fc4..48b7bf01c193 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_corpus/example +++ b/test/extensions/filters/http/ext_authz/ext_authz_corpus/example @@ -4,6 +4,4 @@ request_data { } -result { - error {} -} +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 index e8ee26efe390..403ad1070239 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_corpus/metadata_context +++ b/test/extensions/filters/http/ext_authz/ext_authz_corpus/metadata_context @@ -61,9 +61,7 @@ filter_metadata { } } } -result { - ok {} -} +result: OK request_data { headers { headers { 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 index d8723088ef81..9b121b4e6626 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_fuzz_test.cc +++ b/test/extensions/filters/http/ext_authz/ext_authz_fuzz_test.cc @@ -8,9 +8,11 @@ #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/grpc/mocks.h" #include "test/mocks/http/mocks.h" #include "test/mocks/network/mocks.h" #include "test/mocks/runtime/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "gmock/gmock.h" @@ -22,30 +24,29 @@ 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; +std::unique_ptr +makeGrpcCheckResponse(const Grpc::Status::WellKnownGrpcStatus status) { + auto response = std::make_unique(); + response->mutable_status()->set_code(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( +Grpc::Status::WellKnownGrpcStatus resultCaseToGrpcStatus( const envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase::AuthResult result) { - Filters::Common::ExtAuthz::CheckStatus check_status; + Grpc::Status::WellKnownGrpcStatus check_status; switch (result) { case envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase::OK: { - check_status = Filters::Common::ExtAuthz::CheckStatus::OK; + check_status = Grpc::Status::WellKnownGrpcStatus::Ok; break; } case envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase::ERROR: { - check_status = Filters::Common::ExtAuthz::CheckStatus::Error; + check_status = Grpc::Status::WellKnownGrpcStatus::Internal; break; } case envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase::DENIED: { - check_status = Filters::Common::ExtAuthz::CheckStatus::Denied; + check_status = Grpc::Status::WellKnownGrpcStatus::PermissionDenied; break; } default: { @@ -56,32 +57,22 @@ Filters::Common::ExtAuthz::CheckStatus resultCaseToCheckStatus( return check_status; } -class FuzzerMocks { +class StatelessFuzzerMocks { public: - FuzzerMocks() : addr_(std::make_shared("/test/test.sock")) { - - ON_CALL(decoder_callbacks_, connection()) - .WillByDefault(Return(OptRef{connection_})); + StatelessFuzzerMocks() + : addr_(std::make_shared("/test/test.sock")) { connection_.stream_info_.downstream_connection_info_provider_->setRemoteAddress(addr_); connection_.stream_info_.downstream_connection_info_provider_->setLocalAddress(addr_); - - ON_CALL(decoder_callbacks_.stream_info_, dynamicMetadata()) - .WillByDefault( - Invoke([&]() -> envoy::config::core::v3::Metadata& { return filter_metadata_; })); - } - - void setFilterMetadata(const envoy::config::core::v3::Metadata& metadata) { - filter_metadata_.CopyFrom(metadata); } - NiceMock runtime_; - NiceMock decoder_callbacks_; + // Only add mocks here that are stateless. I.e. if you need to call ON_CALL on a mock each fuzzer + // run, do not add the mock here, because it will leak memory. + NiceMock factory_context_; NiceMock encoder_callbacks_; Network::Address::InstanceConstSharedPtr addr_; NiceMock connection_; - - // Returned by mock decoder_callbacks_.stream_info_.dynamicMetadata() - envoy::config::core::v3::Metadata filter_metadata_; + NiceMock async_request_; + Envoy::Tracing::MockSpan mock_span_; }; DEFINE_PROTO_FUZZER(const envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase& input) { @@ -92,50 +83,68 @@ DEFINE_PROTO_FUZZER(const envoy::extensions::filters::http::ext_authz::ExtAuthzT return; } - static FuzzerMocks mocks; + static StatelessFuzzerMocks mocks; NiceMock stats_store; static ScopedInjectableLoader engine(std::make_unique()); - 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.rootScope(), mocks.runtime_, - http_context, "ext_authz_prefix", bootstrap); + config = std::make_shared(proto_config, *stats_store.rootScope(), + "ext_authz_prefix", mocks.factory_context_); } 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_); + auto internal_mock_client = std::make_shared>(); + auto grpc_client = new Filters::Common::ExtAuthz::GrpcClientImpl(internal_mock_client, + std::chrono::milliseconds(1000)); + auto filter = std::make_unique(config, Filters::Common::ExtAuthz::ClientPtr{grpc_client}); // Set metadata context. - mocks.setFilterMetadata(input.filter_metadata()); + NiceMock decoder_callbacks; + ON_CALL(decoder_callbacks, connection()) + .WillByDefault(Return(OptRef{mocks.connection_})); + envoy::config::core::v3::Metadata metadata = input.filter_metadata(); + ON_CALL(decoder_callbacks.stream_info_, dynamicMetadata()) + .WillByDefault(testing::ReturnRef(metadata)); + + filter->setDecoderFilterCallbacks(decoder_callbacks); + filter->setEncoderFilterCallbacks(mocks.encoder_callbacks_); // 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()))); - })); + ON_CALL(*internal_mock_client, sendRaw(_, _, _, _, _, _)) + .WillByDefault( + Invoke([&](absl::string_view, absl::string_view, Buffer::InstancePtr&& serialized_req, + Grpc::RawAsyncRequestCallbacks&, Tracing::Span&, + const Http::AsyncClient::RequestOptions&) -> Grpc::AsyncRequest* { + envoy::service::auth::v3::CheckRequest check_request; + EXPECT_TRUE(check_request.ParseFromString(serialized_req->toString())) + << "Could not parse serialized check request"; + + // 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()); + + const Grpc::Status::WellKnownGrpcStatus status = resultCaseToGrpcStatus(input.result()); + if (status == Grpc::Status::WellKnownGrpcStatus::Ok) { + grpc_client->onSuccess(makeGrpcCheckResponse(status), mocks.mock_span_); + } else { + grpc_client->onFailure(status, "Fuzz input status was not ok!", mocks.mock_span_); + } + return &mocks.async_request_; + })); // 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 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 b7164fa12778..6b66f26bda40 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_test.cc +++ b/test/extensions/filters/http/ext_authz/ext_authz_test.cc @@ -24,6 +24,7 @@ #include "test/mocks/network/mocks.h" #include "test/mocks/router/mocks.h" #include "test/mocks/runtime/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/tracing/mocks.h" #include "test/mocks/upstream/cluster_manager.h" #include "test/proto/helloworld.pb.h" @@ -54,15 +55,15 @@ namespace { template class HttpFilterTestBase : public T { public: - HttpFilterTestBase() : http_context_(stats_store_.symbolTable()) {} + HttpFilterTestBase() {} void initialize(std::string&& yaml) { envoy::extensions::filters::http::ext_authz::v3::ExtAuthz proto_config{}; if (!yaml.empty()) { TestUtility::loadFromYaml(yaml, proto_config); } - config_ = std::make_shared(proto_config, *stats_store_.rootScope(), runtime_, - http_context_, "ext_authz_prefix", bootstrap_); + config_ = std::make_shared(proto_config, *stats_store_.rootScope(), + "ext_authz_prefix", factory_context_); client_ = new Filters::Common::ExtAuthz::MockClient(); filter_ = std::make_unique(config_, Filters::Common::ExtAuthz::ClientPtr{client_}); filter_->setDecoderFilterCallbacks(decoder_filter_callbacks_); @@ -122,7 +123,6 @@ template class HttpFilterTestBase : public T { NiceMock stats_store_; Stats::Scope& stats_scope_{*stats_store_.rootScope()}; - envoy::config::bootstrap::v3::Bootstrap bootstrap_; FilterConfigSharedPtr config_; Filters::Common::ExtAuthz::MockClient* client_; std::unique_ptr filter_; @@ -132,11 +132,10 @@ template class HttpFilterTestBase : public T { Http::TestRequestHeaderMapImpl request_headers_; Http::TestRequestTrailerMapImpl request_trailers_; Buffer::OwnedImpl data_; - NiceMock runtime_; + NiceMock factory_context_; NiceMock cm_; Network::Address::InstanceConstSharedPtr addr_; NiceMock connection_; - Http::ContextImpl http_context_; }; class HttpFilterTest : public HttpFilterTestBase { @@ -1693,7 +1692,7 @@ TEST_F(HttpFilterTest, FilterDisabled) { denominator: HUNDRED )EOF"); - ON_CALL(runtime_.snapshot_, + ON_CALL(factory_context_.runtime_loader_.snapshot_, featureEnabled("http.ext_authz.enabled", testing::Matcher(Percent(0)))) .WillByDefault(Return(false)); @@ -1720,7 +1719,7 @@ TEST_F(HttpFilterTest, FilterEnabled) { prepareCheck(); - ON_CALL(runtime_.snapshot_, + ON_CALL(factory_context_.runtime_loader_.snapshot_, featureEnabled("http.ext_authz.enabled", testing::Matcher(Percent(100)))) .WillByDefault(Return(true)); @@ -1824,7 +1823,7 @@ TEST_F(HttpFilterTest, FilterEnabledButMetadataDisabled) { )EOF"); // Enable in filter_enabled. - ON_CALL(runtime_.snapshot_, + ON_CALL(factory_context_.runtime_loader_.snapshot_, featureEnabled("http.ext_authz.enabled", testing::Matcher(Percent(100)))) .WillByDefault(Return(true)); @@ -1869,7 +1868,7 @@ TEST_F(HttpFilterTest, FilterDisabledButMetadataEnabled) { )EOF"); // Disable in filter_enabled. - ON_CALL(runtime_.snapshot_, + ON_CALL(factory_context_.runtime_loader_.snapshot_, featureEnabled("http.ext_authz.enabled", testing::Matcher(Percent(0)))) .WillByDefault(Return(false)); @@ -1914,7 +1913,7 @@ TEST_F(HttpFilterTest, FilterEnabledAndMetadataEnabled) { )EOF"); // Enable in filter_enabled. - ON_CALL(runtime_.snapshot_, + ON_CALL(factory_context_.runtime_loader_.snapshot_, featureEnabled("http.ext_authz.enabled", testing::Matcher(Percent(100)))) .WillByDefault(Return(true)); @@ -1957,12 +1956,13 @@ TEST_F(HttpFilterTest, FilterDenyAtDisable) { value: true )EOF"); - ON_CALL(runtime_.snapshot_, + ON_CALL(factory_context_.runtime_loader_.snapshot_, featureEnabled("http.ext_authz.enabled", testing::Matcher(Percent(0)))) .WillByDefault(Return(false)); - ON_CALL(runtime_.snapshot_, featureEnabled("http.ext_authz.enabled", false)) + ON_CALL(factory_context_.runtime_loader_.snapshot_, + featureEnabled("http.ext_authz.enabled", false)) .WillByDefault(Return(true)); // Make sure check is not called. @@ -1990,12 +1990,13 @@ TEST_F(HttpFilterTest, FilterAllowAtDisable) { value: false )EOF"); - ON_CALL(runtime_.snapshot_, + ON_CALL(factory_context_.runtime_loader_.snapshot_, featureEnabled("http.ext_authz.enabled", testing::Matcher(Percent(0)))) .WillByDefault(Return(false)); - ON_CALL(runtime_.snapshot_, featureEnabled("http.ext_authz.enabled", false)) + ON_CALL(factory_context_.runtime_loader_.snapshot_, + featureEnabled("http.ext_authz.enabled", false)) .WillByDefault(Return(false)); // Make sure check is not called. diff --git a/test/extensions/filters/http/ext_proc/BUILD b/test/extensions/filters/http/ext_proc/BUILD index 13abe5f9969d..46ec34379f44 100644 --- a/test/extensions/filters/http/ext_proc/BUILD +++ b/test/extensions/filters/http/ext_proc/BUILD @@ -54,10 +54,9 @@ envoy_extension_cc_test( "//test/mocks/event:event_mocks", "//test/mocks/http:stream_encoder_mock", "//test/mocks/http:stream_mock", - "//test/mocks/local_info:local_info_mocks", "//test/mocks/runtime:runtime_mocks", - "//test/mocks/server:factory_context_mocks", "//test/mocks/server:overload_manager_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/proto:helloworld_proto_cc_proto", "//test/test_common:test_runtime_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", @@ -88,7 +87,7 @@ envoy_extension_cc_test( "//test/common/http:common_lib", "//test/mocks/event:event_mocks", "//test/mocks/local_info:local_info_mocks", - "//test/mocks/server:factory_context_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/test_common:test_runtime_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], diff --git a/test/extensions/filters/http/ext_proc/filter_test.cc b/test/extensions/filters/http/ext_proc/filter_test.cc index 85c742435fe7..b10e28c20ceb 100644 --- a/test/extensions/filters/http/ext_proc/filter_test.cc +++ b/test/extensions/filters/http/ext_proc/filter_test.cc @@ -25,7 +25,7 @@ #include "test/mocks/network/mocks.h" #include "test/mocks/router/mocks.h" #include "test/mocks/runtime/mocks.h" -#include "test/mocks/server/factory_context.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/stream_info/mocks.h" #include "test/mocks/tracing/mocks.h" #include "test/mocks/upstream/cluster_manager.h" @@ -136,7 +136,7 @@ class HttpFilterTest : public testing::Test { proto_config, 200ms, 10000, *stats_store_.rootScope(), "", std::make_shared( Envoy::Extensions::Filters::Common::Expr::createBuilder(nullptr)), - local_info_); + factory_context_); filter_ = std::make_unique(config_, std::move(client_), proto_config.grpc_service()); filter_->setEncoderFilterCallbacks(encoder_callbacks_); EXPECT_CALL(encoder_callbacks_, encoderBufferLimit()).WillRepeatedly(Return(BufferSize)); @@ -579,7 +579,7 @@ class HttpFilterTest : public testing::Test { Envoy::Event::SimulatedTimeSystem* test_time_; envoy::config::core::v3::Metadata dynamic_metadata_; testing::NiceMock connection_; - testing::NiceMock local_info_; + NiceMock factory_context_; }; // Using the default configuration, test the filter with a processor that diff --git a/test/extensions/filters/http/ext_proc/ordering_test.cc b/test/extensions/filters/http/ext_proc/ordering_test.cc index 06d1e89e4aba..c6ecc82ea31a 100644 --- a/test/extensions/filters/http/ext_proc/ordering_test.cc +++ b/test/extensions/filters/http/ext_proc/ordering_test.cc @@ -7,9 +7,9 @@ #include "test/extensions/filters/http/ext_proc/mock_server.h" #include "test/mocks/event/mocks.h" #include "test/mocks/http/mocks.h" -#include "test/mocks/local_info/mocks.h" #include "test/mocks/network/mocks.h" #include "test/mocks/router/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/stream_info/mocks.h" #include "test/mocks/tracing/mocks.h" #include "test/mocks/upstream/cluster_manager.h" @@ -75,7 +75,7 @@ class OrderingTest : public testing::Test { proto_config, kMessageTimeout, kMaxMessageTimeoutMs, *stats_store_.rootScope(), "", std::make_shared( Envoy::Extensions::Filters::Common::Expr::createBuilder(nullptr)), - local_info_); + factory_context_); filter_ = std::make_unique(config_, std::move(client_), proto_config.grpc_service()); filter_->setEncoderFilterCallbacks(encoder_callbacks_); filter_->setDecoderFilterCallbacks(decoder_callbacks_); @@ -216,7 +216,7 @@ class OrderingTest : public testing::Test { Http::TestResponseHeaderMapImpl response_headers_; Http::TestRequestTrailerMapImpl request_trailers_; Http::TestResponseTrailerMapImpl response_trailers_; - NiceMock local_info_; + NiceMock factory_context_; }; // A base class for tests that will check that gRPC streams fail while being created diff --git a/test/extensions/filters/http/ext_proc/unit_test_fuzz/BUILD b/test/extensions/filters/http/ext_proc/unit_test_fuzz/BUILD index 630a47ea12df..13649e730933 100644 --- a/test/extensions/filters/http/ext_proc/unit_test_fuzz/BUILD +++ b/test/extensions/filters/http/ext_proc/unit_test_fuzz/BUILD @@ -41,7 +41,7 @@ envoy_cc_fuzz_test( "//source/extensions/filters/http/ext_proc:config", "//test/extensions/filters/http/common/fuzz:http_filter_fuzzer_lib", "//test/mocks/http:http_mocks", - "//test/mocks/local_info:local_info_mocks", "//test/mocks/network:network_mocks", + "//test/mocks/server:server_factory_context_mocks", ], ) diff --git a/test/extensions/filters/http/ext_proc/unit_test_fuzz/ext_proc_unit_test_fuzz.cc b/test/extensions/filters/http/ext_proc/unit_test_fuzz/ext_proc_unit_test_fuzz.cc index 184c4be32365..2675fda20eea 100644 --- a/test/extensions/filters/http/ext_proc/unit_test_fuzz/ext_proc_unit_test_fuzz.cc +++ b/test/extensions/filters/http/ext_proc/unit_test_fuzz/ext_proc_unit_test_fuzz.cc @@ -5,8 +5,8 @@ #include "test/extensions/filters/http/ext_proc/unit_test_fuzz/mocks.h" #include "test/fuzz/fuzz_runner.h" #include "test/mocks/http/mocks.h" -#include "test/mocks/local_info/mocks.h" #include "test/mocks/network/mocks.h" +#include "test/mocks/server/server_factory_context.h" using testing::Return; using testing::ReturnRef; @@ -47,7 +47,7 @@ class FuzzerMocks { NiceMock response_trailers_; NiceMock buffer_; NiceMock async_client_stream_info_; - NiceMock local_info_; + NiceMock factory_context_; }; DEFINE_PROTO_FUZZER( @@ -87,7 +87,7 @@ DEFINE_PROTO_FUZZER( proto_config, std::chrono::milliseconds(200), 200, *stats_store.rootScope(), "", std::make_shared( Envoy::Extensions::Filters::Common::Expr::createBuilder(nullptr)), - mocks.local_info_); + mocks.factory_context_); } catch (const EnvoyException& e) { ENVOY_LOG_MISC(debug, "EnvoyException during ext_proc filter config validation: {}", e.what()); return; diff --git a/test/extensions/filters/http/file_system_buffer/BUILD b/test/extensions/filters/http/file_system_buffer/BUILD index fb28ba6f0b58..661945a57506 100644 --- a/test/extensions/filters/http/file_system_buffer/BUILD +++ b/test/extensions/filters/http/file_system_buffer/BUILD @@ -47,6 +47,7 @@ envoy_extension_cc_test( "filter_integration_test.cc", ], extension_names = ["envoy.filters.http.file_system_buffer"], + shard_count = 4, tags = [ "cpu:3", "skip_on_windows", diff --git a/test/extensions/filters/http/json_to_metadata/filter_test.cc b/test/extensions/filters/http/json_to_metadata/filter_test.cc index e2bd9365cfe7..87a90f95c24a 100644 --- a/test/extensions/filters/http/json_to_metadata/filter_test.cc +++ b/test/extensions/filters/http/json_to_metadata/filter_test.cc @@ -1592,6 +1592,171 @@ TEST_F(FilterTest, ResponseBodyWithRequestRule) { EXPECT_EQ(getCounterValue("json_to_metadata.resp.invalid_json_body"), 0); } +TEST_F(FilterTest, RequestAllowContentTypeRegex) { + initializeFilter(R"EOF( +request_rules: + rules: + - selectors: + - key: version + on_present: + metadata_namespace: envoy.lb + key: version + allow_content_types_regex: + google_re2: {} + regex: "application/.*" +)EOF"); + const std::string request_body = R"delimiter({"version":"good version"})delimiter"; + const std::map expected = {{"version", "good version"}}; + + Http::TestRequestHeaderMapImpl matched_incoming_headers{ + {":path", "/ping"}, {":method", "GET"}, {"Content-Type", "application/better-json"}}; + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, + filter_->decodeHeaders(matched_incoming_headers, false)); + + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(stream_info_)); + EXPECT_CALL(stream_info_, setDynamicMetadata("envoy.lb", MapEq(expected))); + testRequestWithBody(request_body); + + EXPECT_EQ(getCounterValue("json_to_metadata.rq.success"), 1); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.mismatched_content_type"), 0); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.no_body"), 0); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.invalid_json_body"), 0); +} + +TEST_F(FilterTest, RequestAllowContentTypemultipleRegex) { + initializeFilter(R"EOF( +request_rules: + rules: + - selectors: + - key: version + on_present: + metadata_namespace: envoy.lb + key: version + allow_content_types_regex: + google_re2: {} + regex: "(?:text|application)/.*" +)EOF"); + const std::string request_body = R"delimiter({"version":"good version"})delimiter"; + const std::map expected = {{"version", "good version"}}; + + Http::TestRequestHeaderMapImpl matched_incoming_headers{ + {":path", "/ping"}, {":method", "GET"}, {"Content-Type", "application/better-json"}}; + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, + filter_->decodeHeaders(matched_incoming_headers, false)); + + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(stream_info_)); + EXPECT_CALL(stream_info_, setDynamicMetadata("envoy.lb", MapEq(expected))); + testRequestWithBody(request_body); + + EXPECT_EQ(getCounterValue("json_to_metadata.rq.success"), 1); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.mismatched_content_type"), 0); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.no_body"), 0); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.invalid_json_body"), 0); +} + +TEST_F(FilterTest, RequestAllowContentTypeandRegex) { + initializeFilter(R"EOF( +request_rules: + rules: + - selectors: + - key: version + on_present: + metadata_namespace: envoy.lb + key: version + on_missing: + metadata_namespace: envoy.lb + key: version + value: 'unknown' + allow_content_types: + - application/better-json + allow_content_types_regex: + google_re2: {} + regex: "application/.*" +)EOF"); + const std::string request_body = R"delimiter({"version":"good version"})delimiter"; + const std::map expected = {{"version", "good version"}}; + + Http::TestRequestHeaderMapImpl matched_incoming_headers{ + {":path", "/ping"}, {":method", "GET"}, {"Content-Type", "application/better-json"}}; + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, + filter_->decodeHeaders(matched_incoming_headers, false)); + + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(stream_info_)); + EXPECT_CALL(stream_info_, setDynamicMetadata("envoy.lb", MapEq(expected))); + testRequestWithBody(request_body); + + EXPECT_EQ(getCounterValue("json_to_metadata.rq.success"), 1); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.mismatched_content_type"), 0); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.no_body"), 0); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.invalid_json_body"), 0); +} + +TEST_F(FilterTest, RequestAllowContentTypeRegexMatch) { + initializeFilter(R"EOF( +request_rules: + rules: + - selectors: + - key: version + on_present: + metadata_namespace: envoy.lb + key: version + on_missing: + metadata_namespace: envoy.lb + key: version + value: 'unknown' + allow_content_types: + - application/better-json + allow_content_types_regex: + google_re2: {} + regex: "application/.*" +)EOF"); + const std::string request_body_json = R"delimiter({"version":"good version"})delimiter"; + const std::map expected_json = {{"version", "good version"}}; + + Http::TestRequestHeaderMapImpl matched_incoming_headers_json{ + {":path", "/ping"}, {":method", "GET"}, {"Content-Type", "application/json"}}; + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, + filter_->decodeHeaders(matched_incoming_headers_json, false)); + + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(stream_info_)); + EXPECT_CALL(stream_info_, setDynamicMetadata("envoy.lb", MapEq(expected_json))); + testRequestWithBody(request_body_json); + + EXPECT_EQ(getCounterValue("json_to_metadata.rq.success"), 1); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.mismatched_content_type"), 0); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.no_body"), 0); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.invalid_json_body"), 0); +} + +TEST_F(FilterTest, CustomRequestAllowContentTypeNoMatch) { + initializeFilter(R"EOF( +request_rules: + rules: + - selectors: + - key: version + on_present: + metadata_namespace: envoy.lb + key: version + allow_content_types: + - "text/plain" + allow_content_types_regex: + google_re2: {} + regex: "application/.*" +)EOF"); + + Http::TestRequestHeaderMapImpl nomatch_incoming_headers{ + {":path", "/ping"}, {":method", "POST"}, {"Content-Type", "image/png"}}; + EXPECT_EQ(Http::FilterHeadersStatus::Continue, + filter_->decodeHeaders(nomatch_incoming_headers, false)); + + testRequestWithBody("{}"); + + EXPECT_EQ(getCounterValue("json_to_metadata.rq.success"), 0); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.mismatched_content_type"), 1); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.no_body"), 0); + EXPECT_EQ(getCounterValue("json_to_metadata.rq.invalid_json_body"), 0); +} + } // namespace JsonToMetadata } // namespace HttpFilters } // namespace Extensions diff --git a/test/extensions/filters/http/jwt_authn/authenticator_test.cc b/test/extensions/filters/http/jwt_authn/authenticator_test.cc index 9a58370bb3a4..7adbf27588e0 100644 --- a/test/extensions/filters/http/jwt_authn/authenticator_test.cc +++ b/test/extensions/filters/http/jwt_authn/authenticator_test.cc @@ -61,7 +61,8 @@ class AuthenticatorTest : public testing::Test { void expectVerifyStatus(Status expected_status, Http::RequestHeaderMap& headers, bool expect_clear_route = false) { std::function on_complete_cb = [&expected_status](const Status& status) { - ASSERT_EQ(status, expected_status); + ASSERT_STREQ(google::jwt_verify::getStatusString(status).c_str(), + google::jwt_verify::getStatusString(expected_status).c_str()); }; auto set_extracted_jwt_data_cb = [this](const std::string& name, const ProtobufWkt::Struct& extracted_data) { @@ -101,6 +102,60 @@ class AuthenticatorTest : public testing::Test { NiceMock parent_span_; }; +// Test authenticator constraints for subjects +TEST_F(AuthenticatorTest, TestSubject) { + ScopedInjectableLoader engine(std::make_unique()); + TestUtility::loadFromYaml(SubjectConfig, proto_config_); + + { + // Test that GoodToken works. sub is test@example.com and the example_provider + // is constrained to *@example.com + createAuthenticator(); + EXPECT_CALL(*raw_fetcher_, fetch(_, _)) + .WillOnce(Invoke([this](Tracing::Span&, JwksFetcher::JwksReceiver& receiver) { + receiver.onJwksSuccess(std::move(jwks_)); + })); + Http::TestRequestHeaderMapImpl headers{{"Authorization", "Bearer " + std::string(GoodToken)}}; + expectVerifyStatus(Status::Ok, headers); + } + + { + // spiffe_provider is constrained to prefixes spiffe://spiffe.example.com/ so test@eample.com + // should fail. + createAuthenticator(nullptr, "spiffe_provider"); + Http::TestRequestHeaderMapImpl headers{{"Authorization", "Bearer " + std::string(GoodToken)}}; + expectVerifyStatus(Status::JwtVerificationFail, headers); + } +} + +// Test authenticator constraints for subjects +TEST_F(AuthenticatorTest, TestLifetimeConstraint) { + TestUtility::loadFromYaml(ExpirationConfig, proto_config_); + + { + // Test that GoodToken fails because the expiration time is more than 24h in the future. + createAuthenticator(); + Http::TestRequestHeaderMapImpl headers{{"Authorization", "Bearer " + std::string(GoodToken)}}; + expectVerifyStatus(Status::JwtVerificationFail, headers); + } + + { + // spiffe_provider has an inf expiration time, so any expiration works. + createAuthenticator(nullptr, "spiffe_provider"); + EXPECT_CALL(*raw_fetcher_, fetch(_, _)) + .WillOnce(Invoke([this](Tracing::Span&, JwksFetcher::JwksReceiver& receiver) { + receiver.onJwksSuccess(std::move(jwks_)); + })); + Http::TestRequestHeaderMapImpl headers{{"Authorization", "Bearer " + std::string(GoodToken)}}; + expectVerifyStatus(Status::Ok, headers); + + // Tokens without expiration should fail for infinite constraints + Http::TestRequestHeaderMapImpl non_expiring_headers{ + {"Authorization", "Bearer " + std::string(NonExpiringToken)}}; + expectVerifyStatus(Status::JwtVerificationFail, non_expiring_headers); + } +} + // This test validates a good JWT authentication with a remote Jwks. // It also verifies Jwks cache with 10 JWT authentications, but only one Jwks fetch. TEST_F(AuthenticatorTest, TestOkJWTandCache) { diff --git a/test/extensions/filters/http/jwt_authn/jwks_cache_test.cc b/test/extensions/filters/http/jwt_authn/jwks_cache_test.cc index bd27f7417f70..b1da3485a5f6 100644 --- a/test/extensions/filters/http/jwt_authn/jwks_cache_test.cc +++ b/test/extensions/filters/http/jwt_authn/jwks_cache_test.cc @@ -11,6 +11,8 @@ #include "test/mocks/server/factory_context.h" #include "test/test_common/utility.h" +#include "absl/time/time.h" + using envoy::extensions::filters::http::jwt_authn::v3::JwtAuthentication; using envoy::extensions::filters::http::jwt_authn::v3::RemoteJwks; using ::google::jwt_verify::Status; @@ -181,6 +183,85 @@ TEST_F(JwksCacheTest, TestAudiences) { EXPECT_FALSE(jwks->areAudiencesAllowed({"wrong-audience1", "wrong-audience2"})); } +// Test subject constraints for JwtProvider +TEST_F(JwksCacheTest, TestSubjects) { + ScopedInjectableLoader engine(std::make_unique()); + setupCache(SubjectConfig); + + { + auto jwks = cache_->findByIssuer("https://example.com"); + + // example.com has a suffix constraint of "@example.com" + EXPECT_TRUE(jwks->isSubjectAllowed("test@example.com")); + // Negative test for other subjects + EXPECT_FALSE(jwks->isSubjectAllowed("othersubject")); + } + + { + auto jwks = cache_->findByIssuer("https://spiffe.example.com"); + + // Provider has a prefix constraint of spiffe://spiffe.example.com + EXPECT_TRUE(jwks->isSubjectAllowed("spiffe://spiffe.example.com/service")); + // Negative test for other subjects + EXPECT_FALSE(jwks->isSubjectAllowed("spiffe://spiffe.baz.com/service")); + } + + { + auto jwks = cache_->findByIssuer("https://nosub.com"); + + // Provider no constraints, so test any subject should be allowed + EXPECT_TRUE(jwks->isSubjectAllowed("any_subject")); + } + + { + auto jwks = cache_->findByIssuer("https://regexsub.com"); + + // Provider allows spiffe://*.example.com/ + EXPECT_TRUE(jwks->isSubjectAllowed("spiffe://test1.example.com/service")); + EXPECT_TRUE(jwks->isSubjectAllowed("spiffe://test2.example.com/service")); + EXPECT_FALSE(jwks->isSubjectAllowed("spiffe://test1.baz.com/service")); + } +} + +// Test lifetime constraints for JwtProvider +TEST_F(JwksCacheTest, TestLifetime) { + ScopedInjectableLoader engine(std::make_unique()); + setupCache(ExpirationConfig); + + { + auto jwks = cache_->findByIssuer("https://example.com"); + + absl::Time created; + absl::Time good_exp = created + absl::Minutes(30); + absl::Time bad_exp = created + absl::Hours(25); + // Issuer has a lifetime constraint of 24 hours, so 30 minutes is good. + EXPECT_TRUE(jwks->isLifetimeAllowed(created, &good_exp)); + // 25 hours should fail based on lifetime + EXPECT_FALSE(jwks->isLifetimeAllowed(created, &bad_exp)); + // Tokens without expiration should also fail + EXPECT_FALSE(jwks->isLifetimeAllowed(created, nullptr)); + } + + { + auto jwks = cache_->findByIssuer("https://spiffe.example.com"); + + absl::Time created; + absl::Time long_exp = created + absl::Hours(2500); + // Spiffe provider has a infinite constraint, so any time should work. + EXPECT_TRUE(jwks->isLifetimeAllowed(created, &long_exp)); + // Infinite constraints require an expiration, so null should fail. + EXPECT_FALSE(jwks->isLifetimeAllowed(created, nullptr)); + } + + { + auto jwks = cache_->findByIssuer("https://noexp.example.com"); + + absl::Time created; + // Require_expiration set to false, so this should pass. + EXPECT_TRUE(jwks->isLifetimeAllowed(created, nullptr)); + } +} + } // namespace } // namespace JwtAuthn } // namespace HttpFilters diff --git a/test/extensions/filters/http/jwt_authn/mock.h b/test/extensions/filters/http/jwt_authn/mock.h index e3a792c765ba..3167fcd67a08 100644 --- a/test/extensions/filters/http/jwt_authn/mock.h +++ b/test/extensions/filters/http/jwt_authn/mock.h @@ -8,6 +8,7 @@ #include "test/mocks/upstream/cluster_manager.h" +#include "absl/strings/string_view.h" #include "gmock/gmock.h" using ::google::jwt_verify::Status; @@ -76,9 +77,13 @@ class MockJwksData : public JwksCache::JwksData { ON_CALL(*this, getJwtProvider()).WillByDefault(::testing::ReturnRef(jwt_provider_)); ON_CALL(*this, isExpired()).WillByDefault(::testing::Return(false)); ON_CALL(*this, getJwtCache()).WillByDefault(::testing::ReturnRef(jwt_cache_)); + ON_CALL(*this, isSubjectAllowed(_)).WillByDefault(::testing::Return(true)); + ON_CALL(*this, isLifetimeAllowed(_, _)).WillByDefault(::testing::Return(true)); } MOCK_METHOD(bool, areAudiencesAllowed, (const std::vector&), (const)); + MOCK_METHOD(bool, isSubjectAllowed, (const absl::string_view), (const)); + MOCK_METHOD(bool, isLifetimeAllowed, (const absl::Time&, const absl::Time*), (const)); MOCK_METHOD(const envoy::extensions::filters::http::jwt_authn::v3::JwtProvider&, getJwtProvider, (), (const)); MOCK_METHOD(const ::google::jwt_verify::Jwks*, getJwksObj, (), (const)); diff --git a/test/extensions/filters/http/jwt_authn/test_common.h b/test/extensions/filters/http/jwt_authn/test_common.h index 2c583604d20b..813565e26bc6 100644 --- a/test/extensions/filters/http/jwt_authn/test_common.h +++ b/test/extensions/filters/http/jwt_authn/test_common.h @@ -66,6 +66,85 @@ const char PublicKey[] = R"( } )"; +// Provider config with various subject constraints +const char SubjectConfig[] = R"( +providers: + example_provider: + issuer: https://example.com + subjects: + suffix: "@example.com" + remote_jwks: + http_uri: + uri: https://pubkey_server/pubkey_path + cluster: pubkey_cluster + timeout: + seconds: 5 + spiffe_provider: + issuer: https://spiffe.example.com + subjects: + prefix: spiffe://spiffe.example.com/ + remote_jwks: + http_uri: + uri: https://pubkey_server/pubkey_path + cluster: pubkey_cluster + timeout: + seconds: 5 + no_subj_provider: + issuer: https://nosub.com + remote_jwks: + http_uri: + uri: https://pubkey_server/pubkey_path + cluster: pubkey_cluster + timeout: + seconds: 5 + regex_provider: + issuer: https://regexsub.com + subjects: + safe_regex: + safe_regex: + regex: "spiffe://.*\\.example\\.com/.*" + remote_jwks: + http_uri: + uri: https://pubkey_server/pubkey_path + cluster: pubkey_cluster + timeout: + seconds: 5 + +)"; + +// Provider config with various subject constraints +const char ExpirationConfig[] = R"( +providers: + example_provider: + issuer: https://example.com + max_lifetime: + seconds: 86400 + remote_jwks: + http_uri: + uri: https://pubkey_server/pubkey_path + cluster: pubkey_cluster + timeout: + seconds: 5 + spiffe_provider: + issuer: https://spiffe.example.com + require_expiration: true + remote_jwks: + http_uri: + uri: https://pubkey_server/pubkey_path + cluster: pubkey_cluster + timeout: + seconds: 5 + noexp_provider: + issuer: https://noexp.example.com + require_expiration: false + remote_jwks: + http_uri: + uri: https://pubkey_server/pubkey_path + cluster: pubkey_cluster + timeout: + seconds: 5 +)"; + // A good config. const char ExampleConfig[] = R"( providers: diff --git a/test/extensions/filters/http/oauth2/filter_test.cc b/test/extensions/filters/http/oauth2/filter_test.cc index d216c156cb08..66cf1d976c13 100644 --- a/test/extensions/filters/http/oauth2/filter_test.cc +++ b/test/extensions/filters/http/oauth2/filter_test.cc @@ -111,7 +111,8 @@ class OAuth2Test : public testing::TestWithParam { getConfig(bool forward_bearer_token = true, bool use_refresh_token = false, ::envoy::extensions::filters::http::oauth2::v3::OAuth2Config_AuthType auth_type = ::envoy::extensions::filters::http::oauth2::v3::OAuth2Config_AuthType:: - OAuth2Config_AuthType_URL_ENCODED_BODY) { + OAuth2Config_AuthType_URL_ENCODED_BODY, + int default_refresh_token_expires_in = 0) { envoy::extensions::filters::http::oauth2::v3::OAuth2Config p; auto* endpoint = p.mutable_token_endpoint(); endpoint->set_cluster("auth.example.com"); @@ -125,6 +126,12 @@ class OAuth2Test : public testing::TestWithParam { auto* useRefreshToken = p.mutable_use_refresh_token(); useRefreshToken->set_value(use_refresh_token); + + if (default_refresh_token_expires_in != 0) { + auto* refresh_token_expires_in = p.mutable_default_refresh_token_expires_in(); + refresh_token_expires_in->set_seconds(default_refresh_token_expires_in); + } + p.set_auth_type(auth_type); p.add_auth_scopes("user"); p.add_auth_scopes("openid"); @@ -1751,6 +1758,298 @@ TEST_P(OAuth2Test, OAuthAccessTokenSucessWithTokens) { std::chrono::seconds(600)); } +TEST_P(OAuth2Test, OAuthAccessTokenSucessWithTokensUseRefreshToken) { + init(getConfig(true /* forward_bearer_token */, true /* use_refresh_token */)); + TestScopedRuntime scoped_runtime; + if (GetParam() == 1) { + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "false"}, + }); + oauthHMAC = + "ZTEzMmIyYzRmNTdmMTdiY2IyYmViZDE3ODA5ZDliOTE2MTRlNzNjYjc4MjBlMTVlOWY1OTM2ZjViZjM4MzAwNA==;"; + } else { + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "true"}, + }); + oauthHMAC = "4TKyxPV/F7yyvr0XgJ2bkWFOc8t4IOFen1k29b84MAQ=;"; + } + // Set SystemTime to a fixed point so we get consistent HMAC encodings between test runs. + test_time_.setSystemTime(SystemTime(std::chrono::seconds(1000))); + + // host_ must be set, which is guaranteed (ASAN). + Http::TestRequestHeaderMapImpl request_headers{ + {Http::Headers::get().Host.get(), "traffic.example.com"}, + {Http::Headers::get().Path.get(), "/_signout"}, + {Http::Headers::get().Method.get(), Http::Headers::get().MethodValues.Get}, + }; + filter_->decodeHeaders(request_headers, false); + + // Expected response after the callback is complete. + Http::TestRequestHeaderMapImpl expected_headers{ + {Http::Headers::get().Status.get(), "302"}, + {Http::Headers::get().SetCookie.get(), + "OauthHMAC=" + oauthHMAC + "path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "OauthExpires=1600;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "BearerToken=access_code;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "IdToken=some-id-token;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "RefreshToken=some-refresh-token;path=/;Max-Age=604800;secure;HttpOnly"}, + {Http::Headers::get().Location.get(), ""}, + }; + + EXPECT_CALL(decoder_callbacks_, encodeHeaders_(HeaderMapEqualRef(&expected_headers), true)); + + filter_->onGetAccessTokenSuccess("access_code", "some-id-token", "some-refresh-token", + std::chrono::seconds(600)); +} + +TEST_P(OAuth2Test, OAuthAccessTokenSucessWithTokensUseRefreshTokenAndDefaultRefreshTokenExpiresIn) { + + init(getConfig(true /* forward_bearer_token */, true /* use_refresh_token */, + ::envoy::extensions::filters::http::oauth2::v3::OAuth2Config_AuthType:: + OAuth2Config_AuthType_URL_ENCODED_BODY /* encoded_body_type */, + 1200 /* default_refresh_token_expires_in */)); + TestScopedRuntime scoped_runtime; + if (GetParam() == 1) { + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "false"}, + }); + oauthHMAC = + "ZTEzMmIyYzRmNTdmMTdiY2IyYmViZDE3ODA5ZDliOTE2MTRlNzNjYjc4MjBlMTVlOWY1OTM2ZjViZjM4MzAwNA==;"; + } else { + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "true"}, + }); + oauthHMAC = "4TKyxPV/F7yyvr0XgJ2bkWFOc8t4IOFen1k29b84MAQ=;"; + } + // Set SystemTime to a fixed point so we get consistent HMAC encodings between test runs. + test_time_.setSystemTime(SystemTime(std::chrono::seconds(1000))); + + // host_ must be set, which is guaranteed (ASAN). + Http::TestRequestHeaderMapImpl request_headers{ + {Http::Headers::get().Host.get(), "traffic.example.com"}, + {Http::Headers::get().Path.get(), "/_signout"}, + {Http::Headers::get().Method.get(), Http::Headers::get().MethodValues.Get}, + }; + filter_->decodeHeaders(request_headers, false); + + // Expected response after the callback is complete. + Http::TestRequestHeaderMapImpl expected_headers{ + {Http::Headers::get().Status.get(), "302"}, + {Http::Headers::get().SetCookie.get(), + "OauthHMAC=" + oauthHMAC + "path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "OauthExpires=1600;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "BearerToken=access_code;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "IdToken=some-id-token;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "RefreshToken=some-refresh-token;path=/;Max-Age=1200;secure;HttpOnly"}, + {Http::Headers::get().Location.get(), ""}, + }; + + EXPECT_CALL(decoder_callbacks_, encodeHeaders_(HeaderMapEqualRef(&expected_headers), true)); + + filter_->onGetAccessTokenSuccess("access_code", "some-id-token", "some-refresh-token", + std::chrono::seconds(600)); +} + +/** + * Scenario: The Oauth filter saves cookies with tokens after successful receipt of the tokens. + * + * Expected behavior: The lifetime of the refresh token cookie is taken from the exp claim of the + * refresh token. + */ + +TEST_P(OAuth2Test, OAuthAccessTokenSucessWithTokensUseRefreshTokenAndRefreshTokenExpiresInFromJwt) { + + init(getConfig(true /* forward_bearer_token */, true /* use_refresh_token */, + ::envoy::extensions::filters::http::oauth2::v3::OAuth2Config_AuthType:: + OAuth2Config_AuthType_URL_ENCODED_BODY /* encoded_body_type */, + 1200 /* default_refresh_token_expires_in */)); + TestScopedRuntime scoped_runtime; + if (GetParam() == 1) { + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "false"}, + }); + oauthHMAC = + "MGE2YWQyNjU0YjBmMTA1ZDQzZTE0ODA0OWVlY2Y2Nzc2YjNjZWZjNjI3MDI4M2E5YzUwMGFkMTNkMmM5ZjNkMw==;"; + } else { + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "true"}, + }); + oauthHMAC = "CmrSZUsPEF1D4UgEnuz2d2s878YnAoOpxQCtE9LJ89M=;"; + } + // Set SystemTime to a fixed point so we get consistent HMAC encodings between test runs. + test_time_.setSystemTime(SystemTime(std::chrono::seconds(1000))); + + // host_ must be set, which is guaranteed (ASAN). + Http::TestRequestHeaderMapImpl request_headers{ + {Http::Headers::get().Host.get(), "traffic.example.com"}, + {Http::Headers::get().Path.get(), "/_signout"}, + {Http::Headers::get().Method.get(), Http::Headers::get().MethodValues.Get}, + }; + filter_->decodeHeaders(request_headers, false); + + const std::string refreshToken = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJ1bmlxdWVfbmFtZSI6ImFsZXhjZWk4OCIsInN1YiI6ImFsZXhjZWk4OCIsImp0aSI6IjQ5ZTFjMzc1IiwiYXVkIjoi" + "dGVzdCIsIm5iZiI6MTcwNzQxNDYzNSwiZXhwIjoyNTU0NDE2MDAwLCJpYXQiOjE3MDc0MTQ2MzYsImlzcyI6ImRvdG5l" + "dC11c2VyLWp3dHMifQ.LaGOw6x0-m7r-WzxgCIdPnAfp0O1hy6mW4klq9Vs2XM"; + + // Expected response after the callback is complete. + Http::TestRequestHeaderMapImpl expected_headers{ + {Http::Headers::get().Status.get(), "302"}, + {Http::Headers::get().SetCookie.get(), + "OauthHMAC=" + oauthHMAC + "path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "OauthExpires=1600;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "BearerToken=access_code;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "IdToken=some-id-token;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "RefreshToken=" + refreshToken + ";path=/;Max-Age=2554415000;secure;HttpOnly"}, + {Http::Headers::get().Location.get(), ""}, + }; + + EXPECT_CALL(decoder_callbacks_, encodeHeaders_(HeaderMapEqualRef(&expected_headers), true)); + + filter_->onGetAccessTokenSuccess("access_code", "some-id-token", refreshToken, + std::chrono::seconds(600)); +} + +/** + * Scenario: The Oauth filter doesn't save cookie with refresh token because the token is expired. + * + * Expected behavior: The age of the cookie with refresh token is equal to zero. + */ + +TEST_P(OAuth2Test, OAuthAccessTokenSucessWithTokensUseRefreshTokenAndExpiredRefreshToken) { + + init(getConfig(true /* forward_bearer_token */, true /* use_refresh_token */, + ::envoy::extensions::filters::http::oauth2::v3::OAuth2Config_AuthType:: + OAuth2Config_AuthType_URL_ENCODED_BODY /* encoded_body_type */, + 1200 /* default_refresh_token_expires_in */)); + TestScopedRuntime scoped_runtime; + if (GetParam() == 1) { + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "false"}, + }); + oauthHMAC = + "ZWY3NDZlMDcwNTM3MmIxZmZiNDRmZTBkZDcyY2JlZjEwOWUxMDExOGMwZDc5NDBlYTMxNzRhMGZiY2U0ZDY5Mg==;"; + } else { + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "true"}, + }); + oauthHMAC = "73RuBwU3Kx/7RP4N1yy+8QnhARjA15QOoxdKD7zk1pI=;"; + } + // Set SystemTime to a fixed point so we get consistent HMAC encodings between test runs. + test_time_.setSystemTime(SystemTime(std::chrono::seconds(2554515000))); + + // host_ must be set, which is guaranteed (ASAN). + Http::TestRequestHeaderMapImpl request_headers{ + {Http::Headers::get().Host.get(), "traffic.example.com"}, + {Http::Headers::get().Path.get(), "/_signout"}, + {Http::Headers::get().Method.get(), Http::Headers::get().MethodValues.Get}, + }; + filter_->decodeHeaders(request_headers, false); + + const std::string refreshToken = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJ1bmlxdWVfbmFtZSI6ImFsZXhjZWk4OCIsInN1YiI6ImFsZXhjZWk4OCIsImp0aSI6IjQ5ZTFjMzc1IiwiYXVkIjoi" + "dGVzdCIsIm5iZiI6MTcwNzQxNDYzNSwiZXhwIjoyNTU0NDE2MDAwLCJpYXQiOjE3MDc0MTQ2MzYsImlzcyI6ImRvdG5l" + "dC11c2VyLWp3dHMifQ.LaGOw6x0-m7r-WzxgCIdPnAfp0O1hy6mW4klq9Vs2XM"; + + // Expected response after the callback is complete. + Http::TestRequestHeaderMapImpl expected_headers{ + {Http::Headers::get().Status.get(), "302"}, + {Http::Headers::get().SetCookie.get(), + "OauthHMAC=" + oauthHMAC + "path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "OauthExpires=2554515600;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "BearerToken=access_code;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "IdToken=some-id-token;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "RefreshToken=" + refreshToken + ";path=/;Max-Age=0;secure;HttpOnly"}, + {Http::Headers::get().Location.get(), ""}, + }; + + EXPECT_CALL(decoder_callbacks_, encodeHeaders_(HeaderMapEqualRef(&expected_headers), true)); + + filter_->onGetAccessTokenSuccess("access_code", "some-id-token", refreshToken, + std::chrono::seconds(600)); +} + +/** + * Scenario: The Oauth filter receipts the refresh token without exp claim. + * + * Expected behavior: The age of the cookie with refresh token is equal to default value. + */ + +TEST_P(OAuth2Test, OAuthAccessTokenSucessWithTokensUseRefreshTokenAndNoExpClaimInRefreshToken) { + + init(getConfig(true /* forward_bearer_token */, true /* use_refresh_token */, + ::envoy::extensions::filters::http::oauth2::v3::OAuth2Config_AuthType:: + OAuth2Config_AuthType_URL_ENCODED_BODY /* encoded_body_type */, + 1200 /* default_refresh_token_expires_in */)); + TestScopedRuntime scoped_runtime; + if (GetParam() == 1) { + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "false"}, + }); + oauthHMAC = + "N2FlNDRlNzQwZjgyNmI4YTdmZjQ5YTBjOWQ3ZTc0N2UyYTg3YTJiOTg4NThmZmQyZjg1YjFlZmIwMGZlNTdjMg==;"; + } else { + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "true"}, + }); + oauthHMAC = "euROdA+Ca4p/9JoMnX50fiqHormIWP/S+Fse+wD+V8I=;"; + } + // Set SystemTime to a fixed point so we get consistent HMAC encodings between test runs. + test_time_.setSystemTime(SystemTime(std::chrono::seconds(1000))); + + // host_ must be set, which is guaranteed (ASAN). + Http::TestRequestHeaderMapImpl request_headers{ + {Http::Headers::get().Host.get(), "traffic.example.com"}, + {Http::Headers::get().Path.get(), "/_signout"}, + {Http::Headers::get().Method.get(), Http::Headers::get().MethodValues.Get}, + }; + filter_->decodeHeaders(request_headers, false); + + const std::string refreshToken = + "eyJhbGciOiJIUzI1NiJ9." + "eyJSb2xlIjoiQWRtaW4iLCJJc3N1ZXIiOiJJc3N1ZXIiLCJVc2VybmFtZSI6IkphdmFJblVzZSIsImlhdCI6MTcwODA2" + "NDcyOH0.92H-X2Oa4ECNmFLZBWBHP0BJyEHDprLkEIc2JBJYwkI"; + + // Expected response after the callback is complete. + Http::TestRequestHeaderMapImpl expected_headers{ + {Http::Headers::get().Status.get(), "302"}, + {Http::Headers::get().SetCookie.get(), + "OauthHMAC=" + oauthHMAC + "path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "OauthExpires=1600;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "BearerToken=access_code;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "IdToken=some-id-token;path=/;Max-Age=600;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "RefreshToken=" + refreshToken + ";path=/;Max-Age=1200;secure;HttpOnly"}, + {Http::Headers::get().Location.get(), ""}, + }; + + EXPECT_CALL(decoder_callbacks_, encodeHeaders_(HeaderMapEqualRef(&expected_headers), true)); + + filter_->onGetAccessTokenSuccess("access_code", "some-id-token", refreshToken, + std::chrono::seconds(600)); +} + INSTANTIATE_TEST_SUITE_P(EndcodingParams, OAuth2Test, testing::Values(1, 0)); TEST_P(OAuth2Test, OAuthAccessTokenSucessWithTokens_oauth_use_standard_max_age_value) { @@ -1843,7 +2142,7 @@ TEST_P(OAuth2Test, OAuthAccessTokenSucessWithTokens_oauth_make_token_cookie_http {Http::Headers::get().SetCookie.get(), "BearerToken=access_code;path=/;Max-Age=600;secure"}, {Http::Headers::get().SetCookie.get(), "IdToken=some-id-token;path=/;Max-Age=600;secure"}, {Http::Headers::get().SetCookie.get(), - "RefreshToken=some-refresh-token;path=/;Max-Age=600;secure"}, + "RefreshToken=some-refresh-token;path=/;Max-Age=600;secure;HttpOnly"}, {Http::Headers::get().Location.get(), ""}, }; @@ -2217,12 +2516,17 @@ TEST_F(OAuth2Test, OAuthTestSetCookiesAfterRefreshAccessToken) { init(getConfig(true /* forward_bearer_token */, true /* use_refresh_token */)); + const auto expires_at_s = DateUtil::nowToSeconds(test_time_.timeSystem()) - 10; + // the third request to the oauth filter with URI parameters. Http::TestRequestHeaderMapImpl request_headers{ {Http::Headers::get().Path.get(), "/test?name=admin&level=trace"}, {Http::Headers::get().Host.get(), "traffic.example.com"}, {Http::Headers::get().Method.get(), Http::Headers::get().MethodValues.Post}, {Http::Headers::get().Scheme.get(), "https"}, + {Http::Headers::get().Cookie.get(), fmt::format("OauthExpires={}", expires_at_s)}, + {Http::Headers::get().Cookie.get(), "BearerToken=xyztoken"}, + {Http::Headers::get().Cookie.get(), "OauthHMAC=dCu0otMcLoaGF73jrT+R8rGA0pnWyMgNf4+GivGrHEI="}, }; std::string legit_refresh_token{"legit_refresh_token"}; @@ -2241,6 +2545,11 @@ TEST_F(OAuth2Test, OAuthTestSetCookiesAfterRefreshAccessToken) { EXPECT_CALL(decoder_callbacks_, continueDecoding()); + // Set SystemTime to a fixed point so we get consistent HMAC encodings between test runs. + test_time_.setSystemTime(SystemTime(std::chrono::seconds(0))); + const std::chrono::seconds expiredTime(10); + filter_->updateTokens("accessToken", "idToken", "refreshToken", expiredTime); + filter_->finishRefreshAccessTokenFlow(); Http::TestResponseHeaderMapImpl response_headers{}; @@ -2249,13 +2558,24 @@ TEST_F(OAuth2Test, OAuthTestSetCookiesAfterRefreshAccessToken) { Http::TestResponseHeaderMapImpl expected_response_headers{ {Http::Headers::get().SetCookie.get(), "OauthHMAC=" - "fV62OgLipChTQQC3UFgDp+l5sCiSb3zt7nCoJiVivWw=;" - "path=/;Max-Age=;secure;HttpOnly"}, - {Http::Headers::get().SetCookie.get(), "OauthExpires=;path=/;Max-Age=;secure;HttpOnly"}, - {Http::Headers::get().SetCookie.get(), "BearerToken=;path=/;Max-Age=;secure;HttpOnly"}, + "OYnODPsSGabEpZ2LAiPxyjAFgN/7/5Xg24G7jUoUbyI=;" + "path=/;Max-Age=10;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), "OauthExpires=10;path=/;Max-Age=10;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "BearerToken=accessToken;path=/;Max-Age=10;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), "IdToken=idToken;path=/;Max-Age=10;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "RefreshToken=refreshToken;path=/;Max-Age=604800;secure;HttpOnly"}, }; EXPECT_THAT(response_headers, HeaderMapEqualRef(&expected_response_headers)); + + auto cookies = Http::Utility::parseCookies(request_headers); + EXPECT_EQ(cookies.at("OauthHMAC"), "OYnODPsSGabEpZ2LAiPxyjAFgN/7/5Xg24G7jUoUbyI="); + EXPECT_EQ(cookies.at("OauthExpires"), "10"); + EXPECT_EQ(cookies.at("BearerToken"), "accessToken"); + EXPECT_EQ(cookies.at("IdToken"), "idToken"); + EXPECT_EQ(cookies.at("RefreshToken"), "refreshToken"); } TEST_F(OAuth2Test, OAuthTestSetCookiesAfterRefreshAccessTokenWithBasicAuth) { @@ -2265,11 +2585,17 @@ TEST_F(OAuth2Test, OAuthTestSetCookiesAfterRefreshAccessTokenWithBasicAuth) { OAuth2Config_AuthType_BASIC_AUTH /* authType */)); + const auto expires_at_s = DateUtil::nowToSeconds(test_time_.timeSystem()) - 10; + Http::TestRequestHeaderMapImpl request_headers{ {Http::Headers::get().Path.get(), "/test?name=admin&level=trace"}, {Http::Headers::get().Host.get(), "traffic.example.com"}, {Http::Headers::get().Method.get(), Http::Headers::get().MethodValues.Post}, {Http::Headers::get().Scheme.get(), "https"}, + {Http::Headers::get().Cookie.get(), fmt::format("OauthExpires={}", expires_at_s)}, + {Http::Headers::get().Cookie.get(), "BearerToken=xyztoken"}, + {Http::Headers::get().Cookie.get(), "OauthHMAC=dCu0otMcLoaGF73jrT+R8rGA0pnWyMgNf4+GivGrHEI="}, + {Http::Headers::get().Cookie.get(), "RefreshToken=legit_refresh_token"}, }; std::string legit_refresh_token{"legit_refresh_token"}; @@ -2288,6 +2614,11 @@ TEST_F(OAuth2Test, OAuthTestSetCookiesAfterRefreshAccessTokenWithBasicAuth) { EXPECT_CALL(decoder_callbacks_, continueDecoding()); + // Set SystemTime to a fixed point so we get consistent HMAC encodings between test runs. + test_time_.setSystemTime(SystemTime(std::chrono::seconds(0))); + const std::chrono::seconds expiredTime(10); + filter_->updateTokens("accessToken", "idToken", "refreshToken", expiredTime); + filter_->finishRefreshAccessTokenFlow(); Http::TestResponseHeaderMapImpl response_headers{}; @@ -2296,13 +2627,24 @@ TEST_F(OAuth2Test, OAuthTestSetCookiesAfterRefreshAccessTokenWithBasicAuth) { Http::TestResponseHeaderMapImpl expected_response_headers{ {Http::Headers::get().SetCookie.get(), "OauthHMAC=" - "fV62OgLipChTQQC3UFgDp+l5sCiSb3zt7nCoJiVivWw=;" - "path=/;Max-Age=;secure;HttpOnly"}, - {Http::Headers::get().SetCookie.get(), "OauthExpires=;path=/;Max-Age=;secure;HttpOnly"}, - {Http::Headers::get().SetCookie.get(), "BearerToken=;path=/;Max-Age=;secure;HttpOnly"}, + "OYnODPsSGabEpZ2LAiPxyjAFgN/7/5Xg24G7jUoUbyI=;" + "path=/;Max-Age=10;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), "OauthExpires=10;path=/;Max-Age=10;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "BearerToken=accessToken;path=/;Max-Age=10;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), "IdToken=idToken;path=/;Max-Age=10;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "RefreshToken=refreshToken;path=/;Max-Age=604800;secure;HttpOnly"}, }; EXPECT_THAT(response_headers, HeaderMapEqualRef(&expected_response_headers)); + + auto cookies = Http::Utility::parseCookies(request_headers); + EXPECT_EQ(cookies.at("OauthHMAC"), "OYnODPsSGabEpZ2LAiPxyjAFgN/7/5Xg24G7jUoUbyI="); + EXPECT_EQ(cookies.at("OauthExpires"), "10"); + EXPECT_EQ(cookies.at("BearerToken"), "accessToken"); + EXPECT_EQ(cookies.at("IdToken"), "idToken"); + EXPECT_EQ(cookies.at("RefreshToken"), "refreshToken"); } } // namespace Oauth2 diff --git a/test/extensions/filters/http/rbac/BUILD b/test/extensions/filters/http/rbac/BUILD index a2fb02c8fe24..4c397d1e0389 100644 --- a/test/extensions/filters/http/rbac/BUILD +++ b/test/extensions/filters/http/rbac/BUILD @@ -58,7 +58,7 @@ envoy_extension_cc_test( size = "large", srcs = ["rbac_filter_integration_test.cc"], extension_names = ["envoy.filters.http.rbac"], - shard_count = 2, + shard_count = 3, tags = ["skip_on_windows"], deps = [ "//source/extensions/clusters/dynamic_forward_proxy:cluster", diff --git a/test/extensions/filters/http/rbac/rbac_filter_test.cc b/test/extensions/filters/http/rbac/rbac_filter_test.cc index 29e638179176..93cacd425885 100644 --- a/test/extensions/filters/http/rbac/rbac_filter_test.cc +++ b/test/extensions/filters/http/rbac/rbac_filter_test.cc @@ -35,7 +35,8 @@ enum class LogResult { Yes, No, Undecided }; class RoleBasedAccessControlFilterTest : public testing::Test { public: - void setupPolicy(envoy::config::rbac::v3::RBAC::Action action) { + void setupPolicy(envoy::config::rbac::v3::RBAC::Action action, + std::string rules_stat_prefix = "") { envoy::extensions::filters::http::rbac::v3::RBAC config; envoy::config::rbac::v3::Policy policy; @@ -47,6 +48,7 @@ class RoleBasedAccessControlFilterTest : public testing::Test { policy.add_principals()->set_any(true); config.mutable_rules()->set_action(action); (*config.mutable_rules()->mutable_policies())["foo"] = policy; + config.set_rules_stat_prefix(rules_stat_prefix); envoy::config::rbac::v3::Policy shadow_policy; auto shadow_policy_rules = shadow_policy.add_permissions()->mutable_or_rules(); @@ -55,7 +57,7 @@ class RoleBasedAccessControlFilterTest : public testing::Test { shadow_policy.add_principals()->set_any(true); config.mutable_shadow_rules()->set_action(action); (*config.mutable_shadow_rules()->mutable_policies())["bar"] = shadow_policy; - config.set_shadow_rules_stat_prefix("prefix_"); + config.set_shadow_rules_stat_prefix("shadow_rules_prefix_"); setupConfig(std::make_shared( config, "test", *store_.rootScope(), context_, @@ -156,7 +158,7 @@ class RoleBasedAccessControlFilterTest : public testing::Test { TestUtility::loadFromYaml(fmt::format(shadow_matcher_yaml, action, on_no_match_action), shadow_matcher); *config.mutable_shadow_matcher() = shadow_matcher; - config.set_shadow_rules_stat_prefix("prefix_"); + config.set_shadow_rules_stat_prefix("shadow_rules_prefix_"); setupConfig(std::make_shared( config, "test", *store_.rootScope(), context_, @@ -238,7 +240,7 @@ class RoleBasedAccessControlFilterTest : public testing::Test { *config.mutable_matcher() = matcher; *config.mutable_shadow_matcher() = matcher; - config.set_shadow_rules_stat_prefix("prefix_"); + config.set_shadow_rules_stat_prefix("shadow_rules_prefix_"); setupConfig(std::make_shared( config, "test", *store_.rootScope(), context_, @@ -335,8 +337,9 @@ TEST_F(RoleBasedAccessControlFilterTest, Allowed) { EXPECT_EQ(1U, config_->stats().shadow_denied_.value()); EXPECT_EQ("test.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("test.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); Buffer::OwnedImpl data(""); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data, false)); @@ -359,8 +362,9 @@ TEST_F(RoleBasedAccessControlFilterTest, RequestedServerName) { EXPECT_EQ(1U, config_->stats().shadow_denied_.value()); EXPECT_EQ("test.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("test.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); Buffer::OwnedImpl data(""); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data, false)); @@ -404,12 +408,16 @@ TEST_F(RoleBasedAccessControlFilterTest, Denied) { EXPECT_EQ(1U, config_->stats().shadow_allowed_.value()); EXPECT_EQ("test.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("test.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); auto filter_meta = req_info_.dynamicMetadata().filter_metadata().at("envoy.filters.http.rbac"); - EXPECT_EQ("allowed", filter_meta.fields().at("prefix_shadow_engine_result").string_value()); - EXPECT_EQ("bar", filter_meta.fields().at("prefix_shadow_effective_policy_id").string_value()); + EXPECT_EQ("allowed", + filter_meta.fields().at("shadow_rules_prefix_shadow_engine_result").string_value()); + EXPECT_EQ( + "bar", + filter_meta.fields().at("shadow_rules_prefix_shadow_effective_policy_id").string_value()); EXPECT_EQ("rbac_access_denied_matched_policy[none]", callbacks_.details()); checkAccessLogMetadata(LogResult::Undecided); } @@ -422,7 +430,8 @@ TEST_F(RoleBasedAccessControlFilterTest, RouteLocalOverride) { envoy::extensions::filters::http::rbac::v3::RBACPerRoute route_config; route_config.mutable_rbac()->mutable_rules()->set_action(envoy::config::rbac::v3::RBAC::DENY); - NiceMock engine{route_config.rbac().rules()}; + NiceMock factory_context; + NiceMock engine{route_config.rbac().rules(), factory_context}; NiceMock per_route_config_{route_config, context_}; @@ -495,8 +504,9 @@ TEST_F(RoleBasedAccessControlFilterTest, MatcherAllowed) { EXPECT_EQ(1U, config_->stats().shadow_denied_.value()); EXPECT_EQ("test.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("test.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); Buffer::OwnedImpl data(""); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data, false)); @@ -519,8 +529,9 @@ TEST_F(RoleBasedAccessControlFilterTest, RequestedServerNameMatcher) { EXPECT_EQ(1U, config_->stats().shadow_denied_.value()); EXPECT_EQ("test.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("test.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); Buffer::OwnedImpl data(""); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data, false)); @@ -564,12 +575,16 @@ TEST_F(RoleBasedAccessControlFilterTest, MatcherDenied) { EXPECT_EQ(1U, config_->stats().shadow_allowed_.value()); EXPECT_EQ("test.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("test.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); auto filter_meta = req_info_.dynamicMetadata().filter_metadata().at("envoy.filters.http.rbac"); - EXPECT_EQ("allowed", filter_meta.fields().at("prefix_shadow_engine_result").string_value()); - EXPECT_EQ("bar", filter_meta.fields().at("prefix_shadow_effective_policy_id").string_value()); + EXPECT_EQ("allowed", + filter_meta.fields().at("shadow_rules_prefix_shadow_engine_result").string_value()); + EXPECT_EQ( + "bar", + filter_meta.fields().at("shadow_rules_prefix_shadow_effective_policy_id").string_value()); EXPECT_EQ("rbac_access_denied_matched_policy[none]", callbacks_.details()); checkAccessLogMetadata(LogResult::Undecided); } @@ -617,8 +632,9 @@ TEST_F(RoleBasedAccessControlFilterTest, ShouldLog) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("test.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("test.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); Buffer::OwnedImpl data(""); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data, false)); @@ -638,8 +654,9 @@ TEST_F(RoleBasedAccessControlFilterTest, ShouldNotLog) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("test.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("test.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); Buffer::OwnedImpl data(""); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data, false)); @@ -659,8 +676,9 @@ TEST_F(RoleBasedAccessControlFilterTest, MatcherShouldLog) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("test.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("test.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); Buffer::OwnedImpl data(""); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data, false)); @@ -680,8 +698,9 @@ TEST_F(RoleBasedAccessControlFilterTest, MatcherShouldNotLog) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("test.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("test.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("test.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("test.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); Buffer::OwnedImpl data(""); EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(data, false)); @@ -690,6 +709,13 @@ TEST_F(RoleBasedAccessControlFilterTest, MatcherShouldNotLog) { checkAccessLogMetadata(LogResult::No); } +TEST_F(RoleBasedAccessControlFilterTest, RulesStatPrefix) { + setupPolicy(envoy::config::rbac::v3::RBAC::ALLOW, "rules_prefix_"); + + EXPECT_EQ("test.rbac.rules_prefix_.allowed", config_->stats().allowed_.name()); + EXPECT_EQ("test.rbac.rules_prefix_.denied", config_->stats().denied_.name()); +} + // Upstream Ip and Port matcher tests. class UpstreamIpPortMatcherTests : public RoleBasedAccessControlFilterTest { public: diff --git a/test/extensions/filters/http/wasm/BUILD b/test/extensions/filters/http/wasm/BUILD index 2b4f39eb4631..004163e9c65f 100644 --- a/test/extensions/filters/http/wasm/BUILD +++ b/test/extensions/filters/http/wasm/BUILD @@ -18,7 +18,6 @@ envoy_package() envoy_extension_cc_test( name = "wasm_filter_test", - size = "enormous", # For WAVM without precompilation. TODO: add precompilation. srcs = ["wasm_filter_test.cc"], data = envoy_select_wasm_cpp_tests([ "//test/extensions/filters/http/wasm/test_data:test_cpp.wasm", @@ -55,7 +54,6 @@ envoy_extension_cc_test( envoy_extension_cc_test( name = "config_test", - size = "enormous", # For WAVM without precompilation. TODO: add precompilation. srcs = ["config_test.cc"], data = envoy_select_wasm_cpp_tests([ "//test/extensions/filters/http/wasm/test_data:test_cpp.wasm", diff --git a/test/extensions/filters/http/wasm/test_data/BUILD b/test/extensions/filters/http/wasm/test_data/BUILD index fe2816d95412..c9e28cd8de9d 100644 --- a/test/extensions/filters/http/wasm/test_data/BUILD +++ b/test/extensions/filters/http/wasm/test_data/BUILD @@ -178,22 +178,3 @@ cc_proto_library( name = "test_cc_proto", deps = [":test_proto"], ) - -# TODO: FIXME -# -#filegroup( -# name = "wavm_binary", -# srcs = ["//bazel/foreign_cc:wavm"], -# output_group = "wavm", -#) -# -#genrule( -# name = "test_cpp_wavm_compile", -# srcs = [":test_cpp.wasm"], -# outs = ["test_cpp.wavm_compiled.wasm"], -# cmd = "./$(location wavm_binary) compile $(location test_cpp.wasm) $(location test_cpp.wavm_compiled.wasm)", -# tools = [ -# ":test_cpp.wasm", -# ":wavm_binary", -# ], -#) diff --git a/test/extensions/filters/listener/tls_inspector/tls_inspector_integration_test.cc b/test/extensions/filters/listener/tls_inspector/tls_inspector_integration_test.cc index 78b57390d80d..aa2279c70839 100644 --- a/test/extensions/filters/listener/tls_inspector/tls_inspector_integration_test.cc +++ b/test/extensions/filters/listener/tls_inspector/tls_inspector_integration_test.cc @@ -90,8 +90,8 @@ class TlsInspectorIntegrationTest : public testing::TestWithParam(timeSystem()); + context_manager_ = std::make_unique( + server_factory_context_); } void setupConnections(bool listener_filter_disabled, bool expect_connection_open, bool ssl_client, diff --git a/test/extensions/filters/network/http_connection_manager/config_filter_chain_test.cc b/test/extensions/filters/network/http_connection_manager/config_filter_chain_test.cc index 372a971e0a18..1dac87eb74d5 100644 --- a/test/extensions/filters/network/http_connection_manager/config_filter_chain_test.cc +++ b/test/extensions/filters/network/http_connection_manager/config_filter_chain_test.cc @@ -47,7 +47,8 @@ TEST_F(FilterChainTest, CreateFilterChain) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(basic_config_), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); NiceMock manager; EXPECT_CALL(manager.callbacks_, addStreamFilter(_)); // Buffer @@ -81,7 +82,8 @@ stat_prefix: router HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(basic_config_), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); NiceMock manager; EXPECT_CALL(manager.callbacks_, addStreamDecoderFilter(_)); // Router @@ -89,7 +91,7 @@ stat_prefix: router } TEST_F(FilterChainTest, CreateFilterChainWithDisabledTerminalFilter) { - const std::string config_yaml = R"EOF( + const std::string yaml_string = R"EOF( codec_type: http1 server_name: foo stat_prefix: router @@ -112,11 +114,7 @@ stat_prefix: router )EOF"; EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig(parseHttpConnectionManagerFromYaml(config_yaml), context_, - date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_), - EnvoyException, + createHttpConnectionManagerConfig(yaml_string), EnvoyException, "Error: the last (terminal) filter (envoy.filters.http.router) in the chain cannot be " "disabled by default."); } @@ -155,7 +153,8 @@ stat_prefix: router HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); NiceMock manager; Http::StreamDecoderFilterSharedPtr missing_config_filter; @@ -180,9 +179,11 @@ TEST_F(FilterChainTest, CreateUpgradeFilterChain) { auto hcm_config = parseHttpConnectionManagerFromYaml(basic_config_); hcm_config.add_upgrade_configs()->set_upgrade_type("websocket"); - HttpConnectionManagerConfig config( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_); + HttpConnectionManagerConfig config(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); NiceMock manager; ; @@ -228,9 +229,11 @@ TEST_F(FilterChainTest, CreateUpgradeFilterChainHCMDisabled) { hcm_config.add_upgrade_configs()->set_upgrade_type("websocket"); hcm_config.mutable_upgrade_configs(0)->mutable_enabled()->set_value(false); - HttpConnectionManagerConfig config( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_); + HttpConnectionManagerConfig config(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); NiceMock manager; ; @@ -283,9 +286,11 @@ TEST_F(FilterChainTest, CreateCustomUpgradeFilterChain) { "\x19" "envoy.filters.http.router"); - HttpConnectionManagerConfig config( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_); + HttpConnectionManagerConfig config(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); { NiceMock manager; @@ -327,9 +332,10 @@ TEST_F(FilterChainTest, CreateCustomUpgradeFilterChainWithRouterNotLast) { "encoder-decoder-buffer-filter"); EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_), + HttpConnectionManagerConfig(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_), EnvoyException, "Error: terminal filter named envoy.filters.http.router of type envoy.filters.http.router " "must be the last filter in a http upgrade filter chain."); @@ -340,11 +346,12 @@ TEST_F(FilterChainTest, InvalidConfig) { hcm_config.add_upgrade_configs()->set_upgrade_type("WEBSOCKET"); hcm_config.add_upgrade_configs()->set_upgrade_type("websocket"); - EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_), - EnvoyException, "Error: multiple upgrade configs with the same name: 'websocket'"); + HttpConnectionManagerConfig config(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + EXPECT_EQ(creation_status_.message(), + "Error: multiple upgrade configs with the same name: 'websocket'"); } } // namespace diff --git a/test/extensions/filters/network/http_connection_manager/config_filter_dependencies_test.cc b/test/extensions/filters/network/http_connection_manager/config_filter_dependencies_test.cc index 7d03a24e9fe4..75154ec224a4 100644 --- a/test/extensions/filters/network/http_connection_manager/config_filter_dependencies_test.cc +++ b/test/extensions/filters/network/http_connection_manager/config_filter_dependencies_test.cc @@ -82,9 +82,10 @@ TEST_F(HttpConnectionManagerConfigTest, UnregisteredFilterException) { hcm_config.add_http_filters()->set_name("envoy.filters.http.router"); EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_), + HttpConnectionManagerConfig(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_), EnvoyException, "Didn't find a registered implementation for name: 'test.pantry'"); } @@ -102,7 +103,8 @@ TEST_F(HttpConnectionManagerConfigTest, AllDependenciesSatisfiedOk) { HttpConnectionManagerConfig(hcm_config, context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); } // PantryFilter provides a potato, which is not required by any other filter. @@ -116,7 +118,8 @@ TEST_F(HttpConnectionManagerConfigTest, UnusedProvidencyOk) { HttpConnectionManagerConfig(hcm_config, context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); } // ChefFilter requires a potato, but no filter provides it. @@ -128,12 +131,12 @@ TEST_F(HttpConnectionManagerConfigTest, UnmetDependencyError) { ChefFilterFactory cf; Registry::InjectFactory rc(cf); - EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_), - EnvoyException, - "Dependency violation: filter 'test.chef' requires a FILTER_STATE_KEY named 'potato'"); + HttpConnectionManagerConfig config(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + EXPECT_EQ(creation_status_.message(), + "Dependency violation: filter 'test.chef' requires a FILTER_STATE_KEY named 'potato'"); } // ChefFilter requires a potato, but no preceding filter provides it. @@ -149,12 +152,12 @@ TEST_F(HttpConnectionManagerConfigTest, MisorderedDependenciesError) { ChefFilterFactory cf; Registry::InjectFactory rc(cf); - EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_), - EnvoyException, - "Dependency violation: filter 'test.chef' requires a FILTER_STATE_KEY named 'potato'"); + HttpConnectionManagerConfig config(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + EXPECT_EQ(creation_status_.message(), + "Dependency violation: filter 'test.chef' requires a FILTER_STATE_KEY named 'potato'"); } TEST_F(HttpConnectionManagerConfigTest, UpgradeUnmetDependencyError) { @@ -168,12 +171,12 @@ TEST_F(HttpConnectionManagerConfigTest, UpgradeUnmetDependencyError) { ChefFilterFactory cf; Registry::InjectFactory rc(cf); - EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_), - EnvoyException, - "Dependency violation: filter 'test.chef' requires a FILTER_STATE_KEY named 'potato'"); + HttpConnectionManagerConfig config(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + EXPECT_EQ(creation_status_.message(), + "Dependency violation: filter 'test.chef' requires a FILTER_STATE_KEY named 'potato'"); } TEST_F(HttpConnectionManagerConfigTest, UpgradeDependencyOK) { @@ -189,9 +192,11 @@ TEST_F(HttpConnectionManagerConfigTest, UpgradeDependencyOK) { ChefFilterFactory cf; Registry::InjectFactory rc(cf); - HttpConnectionManagerConfig(hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + HttpConnectionManagerConfig config(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); } // Dependencies provided in the HCM config filter chain do not satisfy @@ -211,12 +216,12 @@ TEST_F(HttpConnectionManagerConfigTest, UpgradeFilterChainDependenciesIsolatedFr ChefFilterFactory cf; Registry::InjectFactory rc(cf); - EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_), - EnvoyException, - "Dependency violation: filter 'test.chef' requires a FILTER_STATE_KEY named 'potato'"); + HttpConnectionManagerConfig config(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + EXPECT_EQ(creation_status_.message(), + "Dependency violation: filter 'test.chef' requires a FILTER_STATE_KEY named 'potato'"); } // Dependencies provided in one upgrade filter chain do not satisfy @@ -238,12 +243,12 @@ TEST_F(HttpConnectionManagerConfigTest, UpgradeFilterChainDependenciesIsolatedFr ChefFilterFactory cf; Registry::InjectFactory rc(cf); - EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig( - hcm_config, context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, filter_config_provider_manager_), - EnvoyException, - "Dependency violation: filter 'test.chef' requires a FILTER_STATE_KEY named 'potato'"); + HttpConnectionManagerConfig config(hcm_config, context_, date_provider_, + route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + EXPECT_EQ(creation_status_.message(), + "Dependency violation: filter 'test.chef' requires a FILTER_STATE_KEY named 'potato'"); } } // namespace diff --git a/test/extensions/filters/network/http_connection_manager/config_test.cc b/test/extensions/filters/network/http_connection_manager/config_test.cc index 1b13b76a00bc..d1a2a7a738f0 100644 --- a/test/extensions/filters/network/http_connection_manager/config_test.cc +++ b/test/extensions/filters/network/http_connection_manager/config_test.cc @@ -178,7 +178,8 @@ stat_prefix: router HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(128, config.tracingConfig()->max_path_tag_length_); EXPECT_EQ(*context_.server_factory_context_.local_info_.address_, config.localAddress()); @@ -214,27 +215,21 @@ stat_prefix: router { EXPECT_CALL(listener_info_, isQuic()).WillOnce(Return(false)); - EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), - context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_), - EnvoyException, "HTTP/3 codec configured on non-QUIC listener."); + EXPECT_THROW_WITH_MESSAGE(createHttpConnectionManagerConfig(yaml_string), EnvoyException, + "HTTP/3 codec configured on non-QUIC listener."); } { + creation_status_ = absl::OkStatus(); EXPECT_CALL(listener_info_, isQuic()).WillOnce(Return(true)); HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); } #else - EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig(parseHttpConnectionManagerFromYaml(yaml_string), context_, - date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_), - EnvoyException, "HTTP3 configured but not enabled in the build."); + EXPECT_THROW_WITH_MESSAGE(createHttpConnectionManagerConfig(yaml_string), EnvoyException, + "HTTP3 configured but not enabled in the build."); #endif } @@ -261,12 +256,8 @@ stat_prefix: router EXPECT_CALL(listener_info_, isQuic()).WillOnce(Return(true)); - EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, - date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_), - EnvoyException, "Non-HTTP/3 codec configured on QUIC listener."); + EXPECT_THROW_WITH_MESSAGE(createHttpConnectionManagerConfig(yaml_string), EnvoyException, + "Non-HTTP/3 codec configured on QUIC listener."); } TEST_F(HttpConnectionManagerConfigTest, TracingNotEnabledAndNoTracingConfigInBootstrap) { @@ -297,7 +288,8 @@ stat_prefix: router HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); // By default, tracer must be a null object (Tracing::NullTracer) rather than nullptr. EXPECT_THAT(config.tracer().get(), WhenDynamicCastTo(NotNull())); @@ -338,7 +330,8 @@ stat_prefix: router HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); // Even though tracer provider is configured in the bootstrap config, a given filter instance // should not have a tracer associated with it. @@ -376,7 +369,8 @@ tracing: {} # notice that tracing is enabled HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); // Actual Tracer must be obtained from the HttpTracerManager. EXPECT_THAT(config.tracer(), Eq(tracer_)); @@ -419,7 +413,8 @@ tracing: {} # notice that tracing is enabled HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); // Actual Tracer must be obtained from the HttpTracerManager. EXPECT_THAT(config.tracer(), Eq(tracer_)); @@ -480,7 +475,8 @@ stat_prefix: router HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); // Actual Tracer must be obtained from the HttpTracerManager. EXPECT_THAT(config.tracer(), Eq(tracer_)); @@ -512,7 +508,8 @@ stat_prefix: router HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); std::vector custom_tags{"ltag", "etag", "rtag", "mtag"}; const Tracing::CustomTagMap& custom_tag_map = config.tracingConfig()->custom_tags_; @@ -538,7 +535,8 @@ TEST_F(HttpConnectionManagerConfigTest, SamplingDefault) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(100, config.tracingConfig()->client_sampling_.numerator()); EXPECT_EQ(Tracing::DefaultMaxPathTagLength, config.tracingConfig()->max_path_tag_length_); @@ -575,7 +573,8 @@ TEST_F(HttpConnectionManagerConfigTest, SamplingConfigured) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(1, config.tracingConfig()->client_sampling_.numerator()); EXPECT_EQ(envoy::type::v3::FractionalPercent::HUNDRED, @@ -611,7 +610,8 @@ TEST_F(HttpConnectionManagerConfigTest, FractionalSamplingConfigured) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(0, config.tracingConfig()->client_sampling_.numerator()); EXPECT_EQ(envoy::type::v3::FractionalPercent::HUNDRED, @@ -647,7 +647,8 @@ TEST_F(HttpConnectionManagerConfigTest, OverallSampling) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); Stats::TestUtil::TestStore store; Api::ApiPtr api = Api::createApiForTest(store); @@ -658,8 +659,10 @@ TEST_F(HttpConnectionManagerConfigTest, OverallSampling) { envoy::config::bootstrap::v3::LayeredRuntime runtime_config; NiceMock local_info; NiceMock validation_visitor; + absl::Status creation_status; Runtime::LoaderImpl runtime(dispatcher, tls, runtime_config, local_info, store, generator, - validation_visitor, *api); + validation_visitor, *api, creation_status); + ASSERT_TRUE(creation_status.ok()); int sampled_count = 0; NiceMock route; @@ -694,7 +697,8 @@ TEST_F(HttpConnectionManagerConfigTest, UnixSocketInternalAddress) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); Network::Address::PipeInstance unixAddress{"/foo"}; Network::Address::Ipv4Instance internalIpAddress{"127.0.0.1", 0, nullptr}; Network::Address::Ipv4Instance externalIpAddress{"12.0.0.1", 0, nullptr}; @@ -722,7 +726,8 @@ TEST_F(HttpConnectionManagerConfigTest, CidrRangeBasedInternalAddress) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); Network::Address::Ipv4Instance first_internal_ip_address{"100.64.0.10", 0, nullptr}; Network::Address::Ipv4Instance second_internal_ip_address{"50.20.0.5", 0, nullptr}; // This address is in the list of acceptable addresses (based on RFC1918) when the new config is @@ -754,7 +759,8 @@ TEST_F(HttpConnectionManagerConfigTest, MaxRequestHeadersKbDefault) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(60, config.maxRequestHeadersKb()); } @@ -773,7 +779,8 @@ TEST_F(HttpConnectionManagerConfigTest, MaxRequestHeadersKbConfigured) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(16, config.maxRequestHeadersKb()); } @@ -792,7 +799,8 @@ TEST_F(HttpConnectionManagerConfigTest, MaxRequestHeadersKbMaxConfigurable) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(8192, config.maxRequestHeadersKb()); } @@ -814,7 +822,8 @@ TEST_F(HttpConnectionManagerConfigTest, MaxRequestHeadersKbMaxConfiguredViaRunti HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(9000, config.maxRequestHeadersKb()); } @@ -834,7 +843,8 @@ TEST_F(HttpConnectionManagerConfigTest, DisabledStreamIdleTimeout) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(0, config.streamIdleTimeout().count()); } @@ -855,7 +865,8 @@ TEST_F(HttpConnectionManagerConfigTest, CommonHttpProtocolIdleTimeout) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(1000, config.idleTimeout().value().count()); } @@ -874,7 +885,8 @@ TEST_F(HttpConnectionManagerConfigTest, CommonHttpProtocolIdleTimeoutDefault) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(std::chrono::hours(1), config.idleTimeout().value()); } @@ -895,7 +907,8 @@ TEST_F(HttpConnectionManagerConfigTest, CommonHttpProtocolIdleTimeoutOff) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_FALSE(config.idleTimeout().has_value()); } @@ -914,7 +927,8 @@ TEST_F(HttpConnectionManagerConfigTest, DefaultMaxRequestHeaderCount) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(100, config.maxRequestHeadersCount()); } @@ -935,7 +949,8 @@ TEST_F(HttpConnectionManagerConfigTest, MaxRequestHeaderCountConfigurable) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(200, config.maxRequestHeadersCount()); } @@ -954,7 +969,8 @@ TEST_F(HttpConnectionManagerConfigTest, DefaultMaxRequestPerConnection) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(0, config.maxRequestsPerConnection()); } @@ -975,7 +991,8 @@ TEST_F(HttpConnectionManagerConfigTest, MaxRequestPerConnectionConfigurable) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(5, config.maxRequestsPerConnection()); } @@ -998,7 +1015,8 @@ TEST_F(HttpConnectionManagerConfigTest, ServerOverwrite) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(HttpConnectionManagerConfig::HttpConnectionManagerProto::OVERWRITE, config.serverHeaderTransformation()); } @@ -1022,7 +1040,8 @@ TEST_F(HttpConnectionManagerConfigTest, ServerAppendIfAbsent) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(HttpConnectionManagerConfig::HttpConnectionManagerProto::APPEND_IF_ABSENT, config.serverHeaderTransformation()); } @@ -1046,7 +1065,8 @@ TEST_F(HttpConnectionManagerConfigTest, ServerPassThrough) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(HttpConnectionManagerConfig::HttpConnectionManagerProto::PASS_THROUGH, config.serverHeaderTransformation()); } @@ -1071,7 +1091,8 @@ TEST_F(HttpConnectionManagerConfigTest, SchemeOverwrite) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(config.schemeToSet(), "http"); } @@ -1095,7 +1116,8 @@ TEST_F(HttpConnectionManagerConfigTest, NormalizePathDefault) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); #ifdef ENVOY_NORMALIZE_PATH_BY_DEFAULT EXPECT_TRUE(config.shouldNormalizePath()); #else @@ -1125,7 +1147,8 @@ TEST_F(HttpConnectionManagerConfigTest, NormalizePathRuntime) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_TRUE(config.shouldNormalizePath()); } @@ -1152,7 +1175,8 @@ TEST_F(HttpConnectionManagerConfigTest, NormalizePathTrue) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_TRUE(config.shouldNormalizePath()); } @@ -1179,7 +1203,8 @@ TEST_F(HttpConnectionManagerConfigTest, NormalizePathFalse) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_FALSE(config.shouldNormalizePath()); } @@ -1198,7 +1223,8 @@ TEST_F(HttpConnectionManagerConfigTest, MergeSlashesDefault) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_FALSE(config.shouldMergeSlashes()); } @@ -1218,7 +1244,8 @@ TEST_F(HttpConnectionManagerConfigTest, MergeSlashesTrue) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_TRUE(config.shouldMergeSlashes()); } @@ -1238,7 +1265,8 @@ TEST_F(HttpConnectionManagerConfigTest, MergeSlashesFalse) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_FALSE(config.shouldMergeSlashes()); } @@ -1257,7 +1285,8 @@ TEST_F(HttpConnectionManagerConfigTest, RemovePortDefault) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(Http::StripPortType::None, config.stripPortType()); } @@ -1277,7 +1306,8 @@ TEST_F(HttpConnectionManagerConfigTest, RemovePortTrue) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(Http::StripPortType::MatchingHost, config.stripPortType()); } @@ -1296,11 +1326,7 @@ TEST_F(HttpConnectionManagerConfigTest, BothStripOptionsAreSet) { )EOF"; EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig(parseHttpConnectionManagerFromYaml(yaml_string), context_, - date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_), - EnvoyException, + createHttpConnectionManagerConfig(yaml_string), EnvoyException, "Error: Only one of `strip_matching_host_port` or `strip_any_host_port` can be set."); } @@ -1320,7 +1346,8 @@ TEST_F(HttpConnectionManagerConfigTest, RemovePortFalse) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(Http::StripPortType::None, config.stripPortType()); } @@ -1340,7 +1367,8 @@ TEST_F(HttpConnectionManagerConfigTest, RemoveAnyPortTrue) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(Http::StripPortType::Any, config.stripPortType()); } @@ -1360,7 +1388,8 @@ TEST_F(HttpConnectionManagerConfigTest, RemoveAnyPortFalse) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(Http::StripPortType::None, config.stripPortType()); } @@ -1379,7 +1408,8 @@ TEST_F(HttpConnectionManagerConfigTest, RemoveTrailingDotDefault) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(false, config.shouldStripTrailingHostDot()); } @@ -1399,7 +1429,8 @@ TEST_F(HttpConnectionManagerConfigTest, RemoveTrailingDotTrue) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(true, config.shouldStripTrailingHostDot()); } @@ -1419,7 +1450,8 @@ TEST_F(HttpConnectionManagerConfigTest, RemoveTrailingDotFalse) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(false, config.shouldStripTrailingHostDot()); } @@ -1438,7 +1470,8 @@ TEST_F(HttpConnectionManagerConfigTest, HeadersWithUnderscoresAllowedByDefault) HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(envoy::config::core::v3::HttpProtocolOptions::ALLOW, config.headersWithUnderscoresAction()); } @@ -1460,7 +1493,8 @@ TEST_F(HttpConnectionManagerConfigTest, HeadersWithUnderscoresDroppedByConfig) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(envoy::config::core::v3::HttpProtocolOptions::DROP_HEADER, config.headersWithUnderscoresAction()); } @@ -1482,7 +1516,8 @@ TEST_F(HttpConnectionManagerConfigTest, HeadersWithUnderscoresRequestRejectedByC HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(envoy::config::core::v3::HttpProtocolOptions::REJECT_REQUEST, config.headersWithUnderscoresAction()); } @@ -1502,7 +1537,8 @@ TEST_F(HttpConnectionManagerConfigTest, ConfiguredRequestTimeout) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(53 * 1000, config.requestTimeout().count()); } @@ -1521,7 +1557,8 @@ TEST_F(HttpConnectionManagerConfigTest, DisabledRequestTimeout) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(0, config.requestTimeout().count()); } @@ -1539,7 +1576,8 @@ TEST_F(HttpConnectionManagerConfigTest, UnconfiguredRequestTimeout) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(0, config.requestTimeout().count()); } @@ -1622,7 +1660,8 @@ TEST_F(HttpConnectionManagerConfigTest, FlushAccessLogOnNewRequest) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(base_yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_FALSE(config.flushAccessLogOnNewRequest()); } @@ -1636,7 +1675,8 @@ TEST_F(HttpConnectionManagerConfigTest, FlushAccessLogOnNewRequest) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_FALSE(config.flushAccessLogOnNewRequest()); } @@ -1650,7 +1690,8 @@ TEST_F(HttpConnectionManagerConfigTest, FlushAccessLogOnNewRequest) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_TRUE(config.flushAccessLogOnNewRequest()); } @@ -1685,7 +1726,8 @@ TEST_F(HttpConnectionManagerConfigTest, HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_TRUE(config.flushAccessLogOnNewRequest()); } @@ -1698,11 +1740,7 @@ TEST_F(HttpConnectionManagerConfigTest, )EOF"; EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), - context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_), - EnvoyException, + createHttpConnectionManagerConfig(yaml_string), EnvoyException, "Only one of flush_access_log_on_new_request or access_log_options can be specified."); } @@ -1714,11 +1752,7 @@ TEST_F(HttpConnectionManagerConfigTest, )EOF"; EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), - context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_), - EnvoyException, + createHttpConnectionManagerConfig(yaml_string), EnvoyException, "Only one of access_log_flush_interval or access_log_options can be specified."); } } @@ -1747,7 +1781,8 @@ TEST_F(HttpConnectionManagerConfigTest, AccessLogFlushInterval) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(base_yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_FALSE(config.accessLogFlushInterval().has_value()); } @@ -1761,7 +1796,8 @@ TEST_F(HttpConnectionManagerConfigTest, AccessLogFlushInterval) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_TRUE(config.accessLogFlushInterval().has_value()); EXPECT_EQ(std::chrono::seconds(1), config.accessLogFlushInterval().value()); @@ -1796,7 +1832,8 @@ TEST_F(HttpConnectionManagerConfigTest, DEPRECATED_FEATURE_TEST(DeprecatedAccess HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_TRUE(config.accessLogFlushInterval().has_value()); EXPECT_EQ(std::chrono::seconds(1), config.accessLogFlushInterval().value()); @@ -1810,11 +1847,7 @@ TEST_F(HttpConnectionManagerConfigTest, DEPRECATED_FEATURE_TEST(DeprecatedAccess )EOF"; EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), - context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_), - EnvoyException, + createHttpConnectionManagerConfig(yaml_string), EnvoyException, "Only one of access_log_flush_interval or access_log_options can be specified."); } @@ -1826,11 +1859,7 @@ TEST_F(HttpConnectionManagerConfigTest, DEPRECATED_FEATURE_TEST(DeprecatedAccess )EOF"; EXPECT_THROW_WITH_MESSAGE( - HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), - context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_), - EnvoyException, + createHttpConnectionManagerConfig(yaml_string), EnvoyException, "Only one of flush_access_log_on_new_request or access_log_options can be specified."); } } @@ -2143,7 +2172,8 @@ TEST_F(HttpConnectionManagerConfigTest, AlwaysSetRequestIdInResponseDefault) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_FALSE(config.alwaysSetRequestIdInResponse()); } @@ -2162,7 +2192,8 @@ TEST_F(HttpConnectionManagerConfigTest, AlwaysSetRequestIdInResponseConfigured) HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_TRUE(config.alwaysSetRequestIdInResponse()); } @@ -2235,7 +2266,8 @@ TEST_F(HttpConnectionManagerConfigTest, CustomRequestIDExtension) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); auto request_id_extension = dynamic_cast(config.requestIDExtension().get()); ASSERT_NE(nullptr, request_id_extension); @@ -2301,7 +2333,8 @@ TEST_F(HttpConnectionManagerConfigTest, DefaultRequestIDExtension) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); auto request_id_extension = dynamic_cast( config.requestIDExtension().get()); ASSERT_NE(nullptr, request_id_extension); @@ -2328,7 +2361,8 @@ TEST_F(HttpConnectionManagerConfigTest, DefaultRequestIDExtensionWithParams) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); auto request_id_extension = dynamic_cast( config.requestIDExtension().get()); ASSERT_NE(nullptr, request_id_extension); @@ -2479,7 +2513,8 @@ TEST_F(HttpConnectionManagerConfigTest, OriginalIPDetectionExtension) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); const auto& original_ip_detection_extensions = config.originalIpDetectionExtensions(); EXPECT_EQ(1, original_ip_detection_extensions.size()); @@ -2505,7 +2540,8 @@ TEST_F(HttpConnectionManagerConfigTest, EarlyHeaderMutationExtension) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); const auto& early_header_mutation_extensions = config.earlyHeaderMutationExtensions(); EXPECT_EQ(1, early_header_mutation_extensions.size()); @@ -2825,7 +2861,8 @@ TEST_F(HttpConnectionManagerConfigTest, PathWithEscapedSlashesActionDefault) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(envoy::extensions::filters::network::http_connection_manager::v3:: HttpConnectionManager::KEEP_UNCHANGED, config.pathWithEscapedSlashesAction()); @@ -2854,7 +2891,8 @@ TEST_F(HttpConnectionManagerConfigTest, PathWithEscapedSlashesActionDefaultOverr HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(envoy::extensions::filters::network::http_connection_manager::v3:: HttpConnectionManager::UNESCAPE_AND_REDIRECT, config.pathWithEscapedSlashesAction()); @@ -2866,7 +2904,8 @@ TEST_F(HttpConnectionManagerConfigTest, PathWithEscapedSlashesActionDefaultOverr HttpConnectionManagerConfig config1(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(envoy::extensions::filters::network::http_connection_manager::v3:: HttpConnectionManager::UNESCAPE_AND_FORWARD, config1.pathWithEscapedSlashesAction()); @@ -2899,7 +2938,8 @@ TEST_F(HttpConnectionManagerConfigTest, HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(envoy::extensions::filters::network::http_connection_manager::v3:: HttpConnectionManager::REJECT_REQUEST, config.pathWithEscapedSlashesAction()); @@ -2934,7 +2974,8 @@ TEST_F(HttpConnectionManagerConfigTest, PathWithEscapedSlashesActionDefaultOverr HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(envoy::extensions::filters::network::http_connection_manager::v3:: HttpConnectionManager::KEEP_UNCHANGED, config.pathWithEscapedSlashesAction()); @@ -2958,7 +2999,8 @@ TEST_F(HttpConnectionManagerConfigTest, SetCurrentClientCertDetailsCertAndChain) HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_EQ(Http::ForwardClientCertType::AppendForward, config.forwardClientCert()); EXPECT_EQ(2, config.setCurrentClientCertDetails().size()); EXPECT_EQ(Http::ClientCertDetailsType::Cert, config.setCurrentClientCertDetails()[0]); @@ -3058,18 +3100,15 @@ TEST_F(HttpConnectionManagerConfigTest, HeaderValidatorConfig) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_NE(nullptr, config.makeHeaderValidator(Http::Protocol::Http2)); #else // If UHV is disabled, providing config should result in rejection - EXPECT_THROW( - { - HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), - context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); - }, - EnvoyException); + HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, + date_provider_, route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); #endif } @@ -3100,20 +3139,18 @@ TEST_F(HttpConnectionManagerConfigTest, HeaderValidatorConfigWithRuntimeDisabled HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); // Without envoy.reloadable_features.enable_universal_header_validator runtime set, UHV is always // disabled EXPECT_EQ(nullptr, config.makeHeaderValidator(Http::Protocol::Http2)); #else // If UHV is disabled, providing config should result in rejection - EXPECT_THROW( - { - HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), - context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); - }, - EnvoyException); + HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, + date_provider_, route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + ASSERT_FALSE(creation_status_.ok()); #endif } @@ -3146,7 +3183,8 @@ TEST_F(HttpConnectionManagerConfigTest, DefaultHeaderValidatorConfigWithRuntimeE HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_NE(nullptr, config.makeHeaderValidator(Http::Protocol::Http2)); EXPECT_FALSE(proto_config.restrict_http_methods()); EXPECT_FALSE(proto_config.strip_fragment_from_path()); @@ -3159,14 +3197,11 @@ TEST_F(HttpConnectionManagerConfigTest, DefaultHeaderValidatorConfigWithRuntimeE #else // If UHV is disabled, enabling envoy.reloadable_features.enable_universal_header_validator should // result in rejection - EXPECT_THROW( - { - HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), - context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); - }, - EnvoyException); + HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, + date_provider_, route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + ASSERT_FALSE(creation_status_.ok()); #endif } @@ -3194,7 +3229,8 @@ TEST_F(HttpConnectionManagerConfigTest, DefaultHeaderValidatorConfig) { HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); // Without envoy.reloadable_features.enable_universal_header_validator runtime set, UHV is always // disabled @@ -3237,7 +3273,8 @@ TEST_F(HttpConnectionManagerConfigTest, TranslateLegacyConfigToDefaultHeaderVali HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + ASSERT_TRUE(creation_status_.ok()); EXPECT_NE(nullptr, config.makeHeaderValidator(Http::Protocol::Http2)); EXPECT_TRUE(proto_config.strip_fragment_from_path()); EXPECT_FALSE(proto_config.restrict_http_methods()); @@ -3250,14 +3287,11 @@ TEST_F(HttpConnectionManagerConfigTest, TranslateLegacyConfigToDefaultHeaderVali #else // If UHV is disabled, enabling envoy.reloadable_features.enable_universal_header_validator should // result in rejection - EXPECT_THROW( - { - HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), - context_, date_provider_, route_config_provider_manager_, - scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); - }, - EnvoyException); + HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, + date_provider_, route_config_provider_manager_, + scoped_routes_config_provider_manager_, tracer_manager_, + filter_config_provider_manager_, creation_status_); + EXPECT_FALSE(creation_status_.ok()); #endif } diff --git a/test/extensions/filters/network/http_connection_manager/config_test_base.h b/test/extensions/filters/network/http_connection_manager/config_test_base.h index 0af8d4902e29..e7bb332ef745 100644 --- a/test/extensions/filters/network/http_connection_manager/config_test_base.h +++ b/test/extensions/filters/network/http_connection_manager/config_test_base.h @@ -51,11 +51,14 @@ class HttpConnectionManagerConfigTest : public testing::Test { std::make_shared>()}; TestScopedRuntime scoped_runtime_; void createHttpConnectionManagerConfig(const std::string& yaml) { + creation_status_ = absl::OkStatus(); HttpConnectionManagerConfig(parseHttpConnectionManagerFromYaml(yaml), context_, date_provider_, route_config_provider_manager_, scoped_routes_config_provider_manager_, tracer_manager_, - filter_config_provider_manager_); + filter_config_provider_manager_, creation_status_); + THROW_IF_NOT_OK(creation_status_); } + absl::Status creation_status_{absl::OkStatus()}; }; class PassThroughFilterFactory : public Extensions::HttpFilters::Common::FactoryBase< diff --git a/test/extensions/filters/network/rbac/filter_test.cc b/test/extensions/filters/network/rbac/filter_test.cc index df60598f379d..78b4d05d32ea 100644 --- a/test/extensions/filters/network/rbac/filter_test.cc +++ b/test/extensions/filters/network/rbac/filter_test.cc @@ -33,7 +33,7 @@ class RoleBasedAccessControlNetworkFilterTest : public testing::Test { envoy::extensions::filters::network::rbac::v3::RBAC config; config.set_stat_prefix("tcp."); - config.set_shadow_rules_stat_prefix("prefix_"); + config.set_shadow_rules_stat_prefix("shadow_rules_prefix_"); if (with_policy) { envoy::config::rbac::v3::Policy policy; @@ -69,7 +69,7 @@ class RoleBasedAccessControlNetworkFilterTest : public testing::Test { std::string on_no_match_action = "DENY") { envoy::extensions::filters::network::rbac::v3::RBAC config; config.set_stat_prefix("tcp."); - config.set_shadow_rules_stat_prefix("prefix_"); + config.set_shadow_rules_stat_prefix("shadow_rules_prefix_"); if (with_matcher) { constexpr absl::string_view matcher_yaml = R"EOF( @@ -250,8 +250,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, AllowedWithOneTimeEnforcement) { EXPECT_EQ(1U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); } TEST_F(RoleBasedAccessControlNetworkFilterTest, AllowedWithContinuousEnforcement) { @@ -270,8 +271,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, AllowedWithContinuousEnforcement EXPECT_EQ(2U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); } TEST_F(RoleBasedAccessControlNetworkFilterTest, RequestedServerName) { @@ -291,8 +293,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, RequestedServerName) { EXPECT_EQ(1U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); } TEST_F(RoleBasedAccessControlNetworkFilterTest, AllowedWithNoPolicy) { @@ -309,8 +312,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, AllowedWithNoPolicy) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); } TEST_F(RoleBasedAccessControlNetworkFilterTest, Denied) { @@ -330,13 +334,17 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, Denied) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); auto filter_meta = stream_info_.dynamicMetadata().filter_metadata().at(NetworkFilterNames::get().Rbac); - EXPECT_EQ("bar", filter_meta.fields().at("prefix_shadow_effective_policy_id").string_value()); - EXPECT_EQ("allowed", filter_meta.fields().at("prefix_shadow_engine_result").string_value()); + EXPECT_EQ( + "bar", + filter_meta.fields().at("shadow_rules_prefix_shadow_effective_policy_id").string_value()); + EXPECT_EQ("allowed", + filter_meta.fields().at("shadow_rules_prefix_shadow_engine_result").string_value()); } TEST_F(RoleBasedAccessControlNetworkFilterTest, MatcherAllowedWithOneTimeEnforcement) { @@ -355,8 +363,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, MatcherAllowedWithOneTimeEnforce EXPECT_EQ(1U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); } TEST_F(RoleBasedAccessControlNetworkFilterTest, MatcherAllowedWithContinuousEnforcement) { @@ -375,8 +384,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, MatcherAllowedWithContinuousEnfo EXPECT_EQ(2U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); } TEST_F(RoleBasedAccessControlNetworkFilterTest, RequestedServerNameMatcher) { @@ -396,8 +406,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, RequestedServerNameMatcher) { EXPECT_EQ(1U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); } TEST_F(RoleBasedAccessControlNetworkFilterTest, AllowedWithNoMatcher) { @@ -414,8 +425,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, AllowedWithNoMatcher) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); } TEST_F(RoleBasedAccessControlNetworkFilterTest, MatcherDenied) { @@ -435,13 +447,17 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, MatcherDenied) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); auto filter_meta = stream_info_.dynamicMetadata().filter_metadata().at(NetworkFilterNames::get().Rbac); - EXPECT_EQ("bar", filter_meta.fields().at("prefix_shadow_effective_policy_id").string_value()); - EXPECT_EQ("allowed", filter_meta.fields().at("prefix_shadow_engine_result").string_value()); + EXPECT_EQ( + "bar", + filter_meta.fields().at("shadow_rules_prefix_shadow_effective_policy_id").string_value()); + EXPECT_EQ("allowed", + filter_meta.fields().at("shadow_rules_prefix_shadow_engine_result").string_value()); } // Log Tests @@ -456,8 +472,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, ShouldLog) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); checkAccessLogMetadata(true); } @@ -473,8 +490,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, ShouldNotLog) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); checkAccessLogMetadata(false); } @@ -504,8 +522,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, MatcherShouldLog) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); checkAccessLogMetadata(true); } @@ -521,8 +540,9 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, MatcherShouldNotLog) { EXPECT_EQ(0U, config_->stats().shadow_denied_.value()); EXPECT_EQ("tcp.rbac.allowed", config_->stats().allowed_.name()); EXPECT_EQ("tcp.rbac.denied", config_->stats().denied_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_allowed", config_->stats().shadow_allowed_.name()); - EXPECT_EQ("tcp.rbac.prefix_.shadow_denied", config_->stats().shadow_denied_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_allowed", + config_->stats().shadow_allowed_.name()); + EXPECT_EQ("tcp.rbac.shadow_rules_prefix_.shadow_denied", config_->stats().shadow_denied_.name()); checkAccessLogMetadata(false); } 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 02f4dfff74b0..5cff9cebd328 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 @@ -148,7 +148,7 @@ class UdpProxyFilterTest : public UdpProxyFilterBase { if (parent_.expect_gro_) { EXPECT_CALL(*socket_->io_handle_, supportsUdpGro()); } - EXPECT_CALL(*socket_->io_handle_, supportsMmsg()).Times(2u); + EXPECT_CALL(*socket_->io_handle_, supportsMmsg()).Times(1u); // Return the datagram. EXPECT_CALL(*socket_->io_handle_, recvmsg(_, 1, _, _)) .WillOnce( @@ -179,7 +179,6 @@ class UdpProxyFilterTest : public UdpProxyFilterBase { } })); // Return an EAGAIN result. - EXPECT_CALL(*socket_->io_handle_, supportsMmsg()); EXPECT_CALL(*socket_->io_handle_, recvmsg(_, 1, _, _)) .WillOnce(Return(ByMove( Api::IoCallUint64Result(0, Network::IoSocketError::getIoSocketEagainError())))); diff --git a/test/extensions/http/cache/file_system_http_cache/file_system_http_cache_test.cc b/test/extensions/http/cache/file_system_http_cache/file_system_http_cache_test.cc index 004eb996d33e..dad95f3f046c 100644 --- a/test/extensions/http/cache/file_system_http_cache/file_system_http_cache_test.cc +++ b/test/extensions/http/cache/file_system_http_cache/file_system_http_cache_test.cc @@ -370,7 +370,8 @@ class FileSystemHttpCacheTestWithMockFiles : public FileSystemHttpCacheTest { NiceMock encoder_callbacks_; Event::SimulatedTimeSystem time_system_; Http::TestRequestHeaderMapImpl request_headers_; - VaryAllowList vary_allow_list_{varyAllowListConfig().allowed_vary_headers()}; + NiceMock factory_context_; + VaryAllowList vary_allow_list_{varyAllowListConfig().allowed_vary_headers(), factory_context_}; DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; Http::TestResponseHeaderMapImpl response_headers_{ {":status", "200"}, diff --git a/test/extensions/http/header_validators/envoy_default/http1_header_validator_test.cc b/test/extensions/http/header_validators/envoy_default/http1_header_validator_test.cc index 8556ec7d1173..e8a6ad73950d 100644 --- a/test/extensions/http/header_validators/envoy_default/http1_header_validator_test.cc +++ b/test/extensions/http/header_validators/envoy_default/http1_header_validator_test.cc @@ -78,12 +78,12 @@ TEST_F(Http1HeaderValidatorTest, ValidateTransferEncodingInRequest) { TEST_F(Http1HeaderValidatorTest, ValidateTransferEncodingInResponse) { auto uhv = createH1(empty_config); - TestResponseHeaderMapImpl request_headers = makeGoodResponseHeaders(); - request_headers.setCopy(LowerCaseString("transfer-encoding"), "ChuNKeD"); - EXPECT_ACCEPT(uhv->validateResponseHeaders(request_headers)); + TestResponseHeaderMapImpl response_headers = makeGoodResponseHeaders(); + response_headers.setCopy(LowerCaseString("transfer-encoding"), "ChuNKeD"); + EXPECT_ACCEPT(uhv->validateResponseHeaders(response_headers)); - request_headers.setCopy(LowerCaseString("transfer-encoding"), "gzip"); - EXPECT_REJECT_WITH_DETAILS(uhv->validateResponseHeaders(request_headers), + response_headers.setCopy(LowerCaseString("transfer-encoding"), "gzip"); + EXPECT_REJECT_WITH_DETAILS(uhv->validateResponseHeaders(response_headers), "http1.invalid_transfer_encoding"); } diff --git a/test/extensions/string_matcher/lua/lua_test.cc b/test/extensions/string_matcher/lua/lua_test.cc index 4e834e0d6d71..3842b89f3469 100644 --- a/test/extensions/string_matcher/lua/lua_test.cc +++ b/test/extensions/string_matcher/lua/lua_test.cc @@ -88,21 +88,20 @@ TEST(LuaStringMatcher, LuaStdLib) { } TEST(LuaStringMatcher, NoCode) { - ScopedInjectableLoader api_inject(std::make_unique()); - ScopedInjectableLoader tls_inject( - std::make_unique()); + Api::MockApi api; + ThreadLocal::MockInstance tls; LuaStringMatcherFactory factory; ::envoy::extensions::string_matcher::lua::v3::Lua empty_config; ProtobufWkt::Any any; any.PackFrom(empty_config); - EXPECT_THROW_WITH_MESSAGE(factory.createStringMatcher(any), EnvoyException, + EXPECT_THROW_WITH_MESSAGE(factory.createStringMatcher(any, tls, api), EnvoyException, "Failed to get lua string matcher code from source: INVALID_ARGUMENT: " "Unexpected DataSource::specifier_case(): 0"); empty_config.mutable_source_code()->set_inline_string(""); any.PackFrom(empty_config); - EXPECT_THROW_WITH_MESSAGE(factory.createStringMatcher(any), EnvoyException, + EXPECT_THROW_WITH_MESSAGE(factory.createStringMatcher(any, tls, api), EnvoyException, "Failed to get lua string matcher code from source: INVALID_ARGUMENT: " "DataSource cannot be empty"); } diff --git a/test/extensions/tracers/opencensus/BUILD b/test/extensions/tracers/opencensus/BUILD index 64e7311cec58..53cf89ab6677 100644 --- a/test/extensions/tracers/opencensus/BUILD +++ b/test/extensions/tracers/opencensus/BUILD @@ -20,7 +20,7 @@ envoy_extension_cc_test( deps = [ "//source/extensions/tracers/opencensus:opencensus_tracer_impl", "//test/mocks/http:http_mocks", - "//test/mocks/local_info:local_info_mocks", + "//test/mocks/server:server_factory_context_mocks", "//test/mocks/stream_info:stream_info_mocks", "//test/mocks/tracing:tracing_mocks", "@envoy_api//envoy/config/trace/v3:pkg_cc_proto", diff --git a/test/extensions/tracers/opencensus/tracer_test.cc b/test/extensions/tracers/opencensus/tracer_test.cc index 0548b99bc569..367420d5bf73 100644 --- a/test/extensions/tracers/opencensus/tracer_test.cc +++ b/test/extensions/tracers/opencensus/tracer_test.cc @@ -11,7 +11,7 @@ #include "source/extensions/tracers/opencensus/opencensus_tracer_impl.h" #include "test/mocks/http/mocks.h" -#include "test/mocks/local_info/mocks.h" +#include "test/mocks/server/server_factory_context.h" #include "test/mocks/stream_info/mocks.h" #include "test/mocks/tracing/mocks.h" @@ -102,9 +102,8 @@ void registerSpanCatcher() { TEST(OpenCensusTracerTest, Span) { registerSpanCatcher(); OpenCensusConfig oc_config; - NiceMock local_info; - std::unique_ptr driver( - new OpenCensus::Driver(oc_config, local_info, *Api::createApiForTest())); + NiceMock context; + std::unique_ptr driver(new OpenCensus::Driver(oc_config, context)); NiceMock config; Tracing::TestTraceContextImpl request_headers{ @@ -189,7 +188,6 @@ void testIncomingHeaders( const std::initializer_list>& headers) { registerSpanCatcher(); OpenCensusConfig oc_config; - NiceMock local_info; oc_config.add_incoming_trace_context(OpenCensusConfig::NONE); oc_config.add_incoming_trace_context(OpenCensusConfig::B3); oc_config.add_incoming_trace_context(OpenCensusConfig::TRACE_CONTEXT); @@ -200,8 +198,8 @@ void testIncomingHeaders( oc_config.add_outgoing_trace_context(OpenCensusConfig::TRACE_CONTEXT); oc_config.add_outgoing_trace_context(OpenCensusConfig::GRPC_TRACE_BIN); oc_config.add_outgoing_trace_context(OpenCensusConfig::CLOUD_TRACE_CONTEXT); - std::unique_ptr driver( - new OpenCensus::Driver(oc_config, local_info, *Api::createApiForTest())); + NiceMock context; + std::unique_ptr driver(new OpenCensus::Driver(oc_config, context)); NiceMock config; Tracing::TestTraceContextImpl request_headers{ {":path", "/"}, @@ -291,9 +289,8 @@ namespace { // the exporter (either zero or one). int samplerTestHelper(const OpenCensusConfig& oc_config) { registerSpanCatcher(); - NiceMock local_info; - std::unique_ptr driver( - new OpenCensus::Driver(oc_config, local_info, *Api::createApiForTest())); + NiceMock context; + std::unique_ptr driver(new OpenCensus::Driver(oc_config, context)); auto span = ::opencensus::trace::Span::StartSpan("test_span"); span.End(); // Retrieve SpanData from the OpenCensus trace exporter. diff --git a/test/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider_test.cc b/test/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider_test.cc index d5a8c68d71b8..bdc0add55955 100644 --- a/test/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider_test.cc +++ b/test/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_provider_test.cc @@ -18,17 +18,190 @@ namespace Envoy { namespace Extensions { namespace Tracers { namespace OpenTelemetry { - using testing::NiceMock; using testing::Return; using testing::ReturnRef; class SamplerConfigProviderTest : public testing::Test { public: + SamplerConfigProviderTest() + : request_(&tracer_factory_context_.server_factory_context_.cluster_manager_ + .thread_local_cluster_.async_client_) { + + const std::string yaml_string = R"EOF( + tenant: "abc12345" + cluster_id: -1743916452 + token: "tokenval" + http_uri: + cluster: "cluster_name" + uri: "https://testhost.com/api/v2/samplingConfiguration" + timeout: 0.250s + root_spans_per_minute: 1000 + )EOF"; + TestUtility::loadFromYaml(yaml_string, proto_config_); + + ON_CALL(tracer_factory_context_.server_factory_context_.cluster_manager_, + getThreadLocalCluster(_)) + .WillByDefault(Return(&tracer_factory_context_.server_factory_context_.cluster_manager_ + .thread_local_cluster_)); + timer_ = new NiceMock( + &tracer_factory_context_.server_factory_context_.dispatcher_); + ON_CALL(tracer_factory_context_.server_factory_context_.dispatcher_, createTimer_(_)) + .WillByDefault(Invoke([this](Event::TimerCb) { return timer_; })); + } + protected: NiceMock tracer_factory_context_; + envoy::extensions::tracers::opentelemetry::samplers::v3::DynatraceSamplerConfig proto_config_; + NiceMock* timer_; + Http::MockAsyncClientRequest request_; }; +MATCHER_P(MessageMatcher, unusedArg, "") { + // prefix 'Api-Token' should be added to 'tokenval' set via SamplerConfigProvider constructor + return (arg->headers() + .get(Http::CustomHeaders::get().Authorization)[0] + ->value() + .getStringView() == "Api-Token tokenval") && + (arg->headers().get(Http::Headers::get().Path)[0]->value().getStringView() == + "/api/v2/samplingConfiguration") && + (arg->headers().get(Http::Headers::get().Host)[0]->value().getStringView() == + "testhost.com") && + (arg->headers().get(Http::Headers::get().Method)[0]->value().getStringView() == "GET"); +} + +// Test that a request is sent if timer fires +TEST_F(SamplerConfigProviderTest, TestRequestIsSent) { + EXPECT_CALL(tracer_factory_context_.server_factory_context_.cluster_manager_.thread_local_cluster_ + .async_client_, + send_(MessageMatcher("unused-arg"), _, _)); + SamplerConfigProviderImpl config_provider(tracer_factory_context_, proto_config_); + timer_->invokeCallback(); +} + +// Test that a pending request is canceled +TEST_F(SamplerConfigProviderTest, TestPendingRequestIsCanceled) { + class TestRequest : public Http::AsyncClient::Request { + public: + MOCK_METHOD(void, cancel, ()); + }; + + NiceMock test_request; + EXPECT_CALL(test_request, cancel()); + ON_CALL(tracer_factory_context_.server_factory_context_.cluster_manager_.thread_local_cluster_ + .async_client_, + send_(_, _, _)) + .WillByDefault(Return(&test_request)); + SamplerConfigProviderImpl config_provider(tracer_factory_context_, proto_config_); + timer_->invokeCallback(); +} + +// Test receiving http response code 200 and valid json +TEST_F(SamplerConfigProviderTest, TestResponseOkValidJson) { + SamplerConfigProviderImpl config_provider(tracer_factory_context_, proto_config_); + timer_->invokeCallback(); + + Http::ResponseMessagePtr message(new Http::ResponseMessageImpl( + Http::ResponseHeaderMapPtr{new Http::TestResponseHeaderMapImpl{{":status", "200"}}})); + message->body().add("{\n \"rootSpansPerMinute\" : 4356 \n }"); + config_provider.onSuccess(request_, std::move(message)); + EXPECT_EQ(config_provider.getSamplerConfig().getRootSpansPerMinute(), 4356); + EXPECT_TRUE(timer_->enabled()); +} + +// Test receiving http response code 200 and invalid json +TEST_F(SamplerConfigProviderTest, TestResponseOkInvalidJson) { + SamplerConfigProviderImpl config_provider(tracer_factory_context_, proto_config_); + timer_->invokeCallback(); + + Http::ResponseMessagePtr message(new Http::ResponseMessageImpl( + Http::ResponseHeaderMapPtr{new Http::TestResponseHeaderMapImpl{{":status", "200"}}})); + message->body().add("{\n "); + config_provider.onSuccess(request_, std::move(message)); + EXPECT_EQ(config_provider.getSamplerConfig().getRootSpansPerMinute(), + SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); + EXPECT_TRUE(timer_->enabled()); +} + +void doTestResponseCode(Http::Code response_code, bool timer_enabled, + SamplerConfigProviderImpl& config_provider, + Http::MockAsyncClientRequest& request, NiceMock* timer, + int line_num) { + SCOPED_TRACE(absl::StrCat(__FUNCTION__, " called from line ", line_num)); + Http::ResponseMessagePtr message(new Http::ResponseMessageImpl(Http::ResponseHeaderMapPtr{ + new Http::TestResponseHeaderMapImpl{{":status", std::to_string(enumToInt(response_code))}}})); + message->body().add("{\n \"rootSpansPerMinute\" : 1000 \n }"); + config_provider.onSuccess(request, std::move(message)); + EXPECT_EQ(timer->enabled(), timer_enabled); +} + +// Test that timer is re-enabled depending on the response code +TEST_F(SamplerConfigProviderTest, TestReenableTimer) { + SamplerConfigProviderImpl config_provider(tracer_factory_context_, proto_config_); + timer_->invokeCallback(); + doTestResponseCode(Http::Code::Forbidden, false, config_provider, request_, timer_, __LINE__); + doTestResponseCode(Http::Code::NotFound, false, config_provider, request_, timer_, __LINE__); + doTestResponseCode(Http::Code::OK, true, config_provider, request_, timer_, __LINE__); + timer_->invokeCallback(); + doTestResponseCode(Http::Code::TooManyRequests, true, config_provider, request_, timer_, + __LINE__); + timer_->invokeCallback(); + doTestResponseCode(Http::Code::InternalServerError, true, config_provider, request_, timer_, + __LINE__); + timer_->invokeCallback(); + doTestResponseCode(Http::Code::BadGateway, true, config_provider, request_, timer_, __LINE__); + timer_->invokeCallback(); + doTestResponseCode(Http::Code::ServiceUnavailable, true, config_provider, request_, timer_, + __LINE__); + timer_->invokeCallback(); + doTestResponseCode(Http::Code::GatewayTimeout, true, config_provider, request_, timer_, __LINE__); + timer_->invokeCallback(); +} + +// Test receiving http response code != 200 +TEST_F(SamplerConfigProviderTest, TestResponseErrorCode) { + SamplerConfigProviderImpl config_provider(tracer_factory_context_, proto_config_); + timer_->invokeCallback(); + + Http::ResponseMessagePtr message(new Http::ResponseMessageImpl( + Http::ResponseHeaderMapPtr{new Http::TestResponseHeaderMapImpl{{":status", "401"}}})); + message->body().add("{\n \"rootSpansPerMinute\" : 4356 \n }"); + config_provider.onSuccess(request_, std::move(message)); + EXPECT_EQ(config_provider.getSamplerConfig().getRootSpansPerMinute(), + SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); + EXPECT_FALSE(timer_->enabled()); +} + +// Test sending failed +TEST_F(SamplerConfigProviderTest, TestOnFailure) { + SamplerConfigProviderImpl config_provider(tracer_factory_context_, proto_config_); + timer_->invokeCallback(); + config_provider.onFailure(request_, Http::AsyncClient::FailureReason::Reset); + EXPECT_EQ(config_provider.getSamplerConfig().getRootSpansPerMinute(), + SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); + EXPECT_TRUE(timer_->enabled()); +} + +// Test calling onBeforeFinalizeUpstreamSpan +TEST_F(SamplerConfigProviderTest, TestOnBeforeFinalizeUpstreamSpan) { + Tracing::MockSpan child_span_; + SamplerConfigProviderImpl config_provider(tracer_factory_context_, proto_config_); + // onBeforeFinalizeUpstreamSpan() is an empty method, nothing to ASSERT, nothing should happen + config_provider.onBeforeFinalizeUpstreamSpan(child_span_, nullptr); +} + +// Test invoking the timer if no cluster can be found +TEST_F(SamplerConfigProviderTest, TestNoCluster) { + // simulate no configured cluster, return nullptr. + ON_CALL(tracer_factory_context_.server_factory_context_.cluster_manager_, + getThreadLocalCluster(_)) + .WillByDefault(Return(nullptr)); + SamplerConfigProviderImpl config_provider(tracer_factory_context_, proto_config_); + timer_->invokeCallback(); + // nothing to assert, should not crash or throw. +} + +// Test that configured value is used TEST_F(SamplerConfigProviderTest, TestValueConfigured) { const std::string yaml_string = R"EOF( tenant: "abc12345" @@ -39,7 +212,6 @@ TEST_F(SamplerConfigProviderTest, TestValueConfigured) { uri: "https://testhost.com/otlp/v1/traces" timeout: 0.250s root_spans_per_minute: 3456 - )EOF"; envoy::extensions::tracers::opentelemetry::samplers::v3::DynatraceSamplerConfig proto_config; @@ -49,6 +221,7 @@ TEST_F(SamplerConfigProviderTest, TestValueConfigured) { EXPECT_EQ(config_provider.getSamplerConfig().getRootSpansPerMinute(), 3456); } +// Test using a config without a setting for configured root spans TEST_F(SamplerConfigProviderTest, TestNoValueConfigured) { const std::string yaml_string = R"EOF( tenant: "abc12345" @@ -57,8 +230,7 @@ TEST_F(SamplerConfigProviderTest, TestNoValueConfigured) { http_uri: cluster: "cluster_name" uri: "https://testhost.com/otlp/v1/traces" - timeout: 0.250s - + timeout: 500s )EOF"; envoy::extensions::tracers::opentelemetry::samplers::v3::DynatraceSamplerConfig proto_config; @@ -69,6 +241,7 @@ TEST_F(SamplerConfigProviderTest, TestNoValueConfigured) { SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); } +// Test using a config with 0 configured root spans TEST_F(SamplerConfigProviderTest, TestValueZeroConfigured) { const std::string yaml_string = R"EOF( tenant: "abc12345" diff --git a/test/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_test.cc b/test/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_test.cc index 8cb45a8ca3e6..f849b582c5f1 100644 --- a/test/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_test.cc +++ b/test/extensions/tracers/opentelemetry/samplers/dynatrace/sampler_config_test.cc @@ -14,25 +14,25 @@ namespace OpenTelemetry { TEST(SamplerConfigTest, TestParsing) { // default_root_spans_per_minute not set, ROOT_SPANS_PER_MINUTE_DEFAULT should be used SamplerConfig config(0); - config.parse("{\n \"rootSpansPerMinute\" : 2000 \n }"); + EXPECT_TRUE(config.parse("{\n \"rootSpansPerMinute\" : 2000 \n }")); EXPECT_EQ(config.getRootSpansPerMinute(), 2000u); - config.parse("{\n \"rootSpansPerMinute\" : 10000 \n }"); + EXPECT_TRUE(config.parse("{\n \"rootSpansPerMinute\" : 10000 \n }")); EXPECT_EQ(config.getRootSpansPerMinute(), 10000u); // unexpected json, default value should be used - config.parse("{}"); + EXPECT_FALSE(config.parse("{}")); EXPECT_EQ(config.getRootSpansPerMinute(), SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); - config.parse(""); + EXPECT_FALSE(config.parse("")); EXPECT_EQ(config.getRootSpansPerMinute(), SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); - config.parse("\\"); + EXPECT_FALSE(config.parse("\\")); EXPECT_EQ(config.getRootSpansPerMinute(), SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); - config.parse(" { "); + EXPECT_FALSE(config.parse(" { ")); EXPECT_EQ(config.getRootSpansPerMinute(), SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); - config.parse("{\n \"rootSpansPerMinute\" : 10000 "); // closing } is missing + EXPECT_FALSE(config.parse("{\n \"rootSpansPerMinute\" : 10000 ")); // closing } is missing EXPECT_EQ(config.getRootSpansPerMinute(), SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); } @@ -41,19 +41,19 @@ TEST(SamplerConfigTest, TestDefaultConfig) { { SamplerConfig config(0); EXPECT_EQ(config.getRootSpansPerMinute(), SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); - config.parse(" { "); // parse invalid json, default value should still be used + EXPECT_FALSE(config.parse(" { ")); // parse invalid json, default value should still be used EXPECT_EQ(config.getRootSpansPerMinute(), SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); } { SamplerConfig config(900); EXPECT_EQ(config.getRootSpansPerMinute(), 900); - config.parse(" { "); + EXPECT_FALSE(config.parse(" { ")); EXPECT_EQ(config.getRootSpansPerMinute(), 900); } { SamplerConfig config(SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); EXPECT_EQ(config.getRootSpansPerMinute(), SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); - config.parse(" { "); + EXPECT_FALSE(config.parse(" { ")); EXPECT_EQ(config.getRootSpansPerMinute(), SamplerConfig::ROOT_SPANS_PER_MINUTE_DEFAULT); } } diff --git a/test/extensions/transport_sockets/starttls/starttls_integration_test.cc b/test/extensions/transport_sockets/starttls/starttls_integration_test.cc index cb2564e38286..01a27199c184 100644 --- a/test/extensions/transport_sockets/starttls/starttls_integration_test.cc +++ b/test/extensions/transport_sockets/starttls/starttls_integration_test.cc @@ -208,8 +208,8 @@ void StartTlsIntegrationTest::initialize() { factory->createTransportSocketFactory(*config, factory_context_)}; // Setup factories and contexts for tls transport socket. - tls_context_manager_ = - std::make_unique(timeSystem()); + tls_context_manager_ = std::make_unique( + server_factory_context_); tls_context_ = Ssl::createClientSslTransportSocketFactory({}, *tls_context_manager_, *api_); payload_reader_ = std::make_shared(*dispatcher_); diff --git a/test/extensions/transport_sockets/starttls/upstream_starttls_integration_test.cc b/test/extensions/transport_sockets/starttls/upstream_starttls_integration_test.cc index f3efba23db8d..b106d0043f30 100644 --- a/test/extensions/transport_sockets/starttls/upstream_starttls_integration_test.cc +++ b/test/extensions/transport_sockets/starttls/upstream_starttls_integration_test.cc @@ -249,8 +249,8 @@ void StartTlsIntegrationTest::initialize() { // Setup factory and context for tls transport socket. // The tls transport socket will be inserted into fake_upstream when // upstream starttls transport socket is converted to secure mode. - tls_context_manager_ = - std::make_unique(timeSystem()); + tls_context_manager_ = std::make_unique( + server_factory_context_); envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext downstream_tls_context; diff --git a/test/extensions/transport_sockets/tap/BUILD b/test/extensions/transport_sockets/tap/BUILD index ca752227472b..6071e12726cd 100644 --- a/test/extensions/transport_sockets/tap/BUILD +++ b/test/extensions/transport_sockets/tap/BUILD @@ -1,6 +1,7 @@ load( "//bazel:envoy_build_system.bzl", "envoy_package", + "envoy_select_admin_functionality", ) load( "//test/extensions:extensions_build_system.bzl", @@ -22,3 +23,23 @@ envoy_extension_cc_test( "//test/test_common:simulated_time_system_lib", ], ) + +envoy_extension_cc_test( + name = "ssl_tap_integration_test", + srcs = envoy_select_admin_functionality(["ssl_tap_integration_test.cc"]), + data = [ + "//test/config/integration/certs", + ], + extension_names = ["envoy.transport_sockets.tap"], + deps = [ + "//source/common/network:connection_lib", + "//source/extensions/transport_sockets/tap:config", + "//source/extensions/transport_sockets/tls:config", + "//test/common/tls/integration:ssl_integration_test_lib", + "//test/extensions/common/tap:common", + "@envoy_api//envoy/config/tap/v3:pkg_cc_proto", + "@envoy_api//envoy/data/tap/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/transport_sockets/tap/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", + ], +) diff --git a/test/extensions/transport_sockets/tap/ssl_tap_integration_test.cc b/test/extensions/transport_sockets/tap/ssl_tap_integration_test.cc new file mode 100644 index 000000000000..3eee173d9a04 --- /dev/null +++ b/test/extensions/transport_sockets/tap/ssl_tap_integration_test.cc @@ -0,0 +1,329 @@ +#include "envoy/config/tap/v3/common.pb.h" +#include "envoy/data/tap/v3/wrapper.pb.h" +#include "envoy/extensions/transport_sockets/tap/v3/tap.pb.h" +#include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h" + +#include "source/common/network/connection_impl.h" + +#include "test/common/tls/integration/ssl_integration_test_base.h" +#include "test/extensions/common/tap/common.h" + +namespace Envoy { +namespace Ssl { + +// TODO(zuercher): write an additional OCSP integration test that validates behavior with an +// expired OCSP response. (Requires OCSP client-side support in upstream TLS.) +class SslTapIntegrationTest : public testing::TestWithParam, + public SslIntegrationTestBase { +public: + SslTapIntegrationTest() : SslIntegrationTestBase(GetParam()) {} + + void TearDown() override { SslIntegrationTestBase::TearDown(); }; + + void initialize() override { + // TODO(mattklein123): Merge/use the code in ConfigHelper::setTapTransportSocket(). + config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + // The test supports tapping either the downstream or upstream connection, but not both. + if (upstream_tap_) { + setupUpstreamTap(bootstrap); + } else { + setupDownstreamTap(bootstrap); + } + }); + SslIntegrationTestBase::initialize(); + // This confuses our socket counting. + debug_with_s_client_ = false; + } + + void setupUpstreamTap(envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + auto* transport_socket = + bootstrap.mutable_static_resources()->mutable_clusters(0)->mutable_transport_socket(); + transport_socket->set_name("envoy.transport_sockets.tap"); + envoy::config::core::v3::TransportSocket raw_transport_socket; + raw_transport_socket.set_name("envoy.transport_sockets.raw_buffer"); + envoy::extensions::transport_sockets::tap::v3::Tap tap_config = + createTapConfig(raw_transport_socket); + tap_config.mutable_transport_socket()->MergeFrom(raw_transport_socket); + transport_socket->mutable_typed_config()->PackFrom(tap_config); + } + + void setupDownstreamTap(envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + auto* filter_chain = + bootstrap.mutable_static_resources()->mutable_listeners(0)->mutable_filter_chains(0); + // Configure inner SSL transport socket based on existing config. + envoy::config::core::v3::TransportSocket ssl_transport_socket; + auto* transport_socket = filter_chain->mutable_transport_socket(); + ssl_transport_socket.Swap(transport_socket); + // Configure outer tap transport socket. + transport_socket->set_name("envoy.transport_sockets.tap"); + envoy::extensions::transport_sockets::tap::v3::Tap tap_config = + createTapConfig(ssl_transport_socket); + tap_config.mutable_transport_socket()->MergeFrom(ssl_transport_socket); + transport_socket->mutable_typed_config()->PackFrom(tap_config); + } + + envoy::extensions::transport_sockets::tap::v3::Tap + createTapConfig(const envoy::config::core::v3::TransportSocket& inner_transport) { + envoy::extensions::transport_sockets::tap::v3::Tap tap_config; + tap_config.mutable_common_config()->mutable_static_config()->mutable_match()->set_any_match( + true); + auto* output_config = + tap_config.mutable_common_config()->mutable_static_config()->mutable_output_config(); + if (max_rx_bytes_.has_value()) { + output_config->mutable_max_buffered_rx_bytes()->set_value(max_rx_bytes_.value()); + } + if (max_tx_bytes_.has_value()) { + output_config->mutable_max_buffered_tx_bytes()->set_value(max_tx_bytes_.value()); + } + output_config->set_streaming(streaming_tap_); + + auto* output_sink = output_config->mutable_sinks()->Add(); + output_sink->set_format(format_); + output_sink->mutable_file_per_tap()->set_path_prefix(path_prefix_); + tap_config.mutable_transport_socket()->MergeFrom(inner_transport); + return tap_config; + } + + std::string path_prefix_ = TestEnvironment::temporaryPath("ssl_trace"); + envoy::config::tap::v3::OutputSink::Format format_{ + envoy::config::tap::v3::OutputSink::PROTO_BINARY}; + absl::optional max_rx_bytes_; + absl::optional max_tx_bytes_; + bool upstream_tap_{}; + bool streaming_tap_{}; +}; + +INSTANTIATE_TEST_SUITE_P(IpVersions, SslTapIntegrationTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + TestUtility::ipTestParamsToString); + +// Validate two back-to-back requests with binary proto output. +TEST_P(SslTapIntegrationTest, TwoRequestsWithBinaryProto) { + initialize(); + ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { + return makeSslClientConnection({}); + }; + + // First request (ID will be +1 since the client will also bump). + const uint64_t first_id = Network::ConnectionImpl::nextGlobalIdForTest() + 1; + codec_client_ = makeHttpConnection(creator()); + Http::TestRequestHeaderMapImpl post_request_headers{ + {":method", "POST"}, {":path", "/test/long/url"}, + {":scheme", "http"}, {":authority", "sni.lyft.com"}, + {"x-lyft-user-id", "123"}, {"x-forwarded-for", "10.0.0.1"}}; + auto response = + sendRequestAndWaitForResponse(post_request_headers, 128, default_response_headers_, 256); + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_EQ(128, upstream_request_->bodyLength()); + ASSERT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().getStatusValue()); + EXPECT_EQ(256, response->body().size()); + checkStats(); + envoy::config::core::v3::Address expected_local_address; + Network::Utility::addressToProtobufAddress( + *codec_client_->connection()->connectionInfoProvider().remoteAddress(), + expected_local_address); + envoy::config::core::v3::Address expected_remote_address; + Network::Utility::addressToProtobufAddress( + *codec_client_->connection()->connectionInfoProvider().localAddress(), + expected_remote_address); + codec_client_->close(); + test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 1); + envoy::data::tap::v3::TraceWrapper trace; + TestUtility::loadFromFile(fmt::format("{}_{}.pb", path_prefix_, first_id), trace, *api_); + // Validate general expected properties in the trace. + EXPECT_EQ(first_id, trace.socket_buffered_trace().trace_id()); + EXPECT_THAT(expected_local_address, + ProtoEq(trace.socket_buffered_trace().connection().local_address())); + EXPECT_THAT(expected_remote_address, + ProtoEq(trace.socket_buffered_trace().connection().remote_address())); + ASSERT_GE(trace.socket_buffered_trace().events().size(), 2); + EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(0).read().data().as_bytes(), + "POST /test/long/url HTTP/1.1")); + EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(1).write().data().as_bytes(), + "HTTP/1.1 200 OK")); + EXPECT_FALSE(trace.socket_buffered_trace().read_truncated()); + EXPECT_FALSE(trace.socket_buffered_trace().write_truncated()); + + // Verify a second request hits a different file. + const uint64_t second_id = Network::ConnectionImpl::nextGlobalIdForTest() + 1; + codec_client_ = makeHttpConnection(creator()); + Http::TestRequestHeaderMapImpl get_request_headers{ + {":method", "GET"}, {":path", "/test/long/url"}, + {":scheme", "http"}, {":authority", "sni.lyft.com"}, + {"x-lyft-user-id", "123"}, {"x-forwarded-for", "10.0.0.1"}}; + response = + sendRequestAndWaitForResponse(get_request_headers, 128, default_response_headers_, 256); + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_EQ(128, upstream_request_->bodyLength()); + ASSERT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().getStatusValue()); + EXPECT_EQ(256, response->body().size()); + checkStats(); + codec_client_->close(); + test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 2); + TestUtility::loadFromFile(fmt::format("{}_{}.pb", path_prefix_, second_id), trace, *api_); + // Validate second connection ID. + EXPECT_EQ(second_id, trace.socket_buffered_trace().trace_id()); + ASSERT_GE(trace.socket_buffered_trace().events().size(), 2); + EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(0).read().data().as_bytes(), + "GET /test/long/url HTTP/1.1")); + EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(1).write().data().as_bytes(), + "HTTP/1.1 200 OK")); + EXPECT_FALSE(trace.socket_buffered_trace().read_truncated()); + EXPECT_FALSE(trace.socket_buffered_trace().write_truncated()); +} + +// Verify that truncation works correctly across multiple transport socket frames. +TEST_P(SslTapIntegrationTest, TruncationWithMultipleDataFrames) { + max_rx_bytes_ = 4; + max_tx_bytes_ = 5; + + initialize(); + ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { + return makeSslClientConnection({}); + }; + + const uint64_t id = Network::ConnectionImpl::nextGlobalIdForTest() + 1; + codec_client_ = makeHttpConnection(creator()); + const Http::TestRequestHeaderMapImpl request_headers{{":method", "GET"}, + {":path", "/test/long/url"}, + {":scheme", "http"}, + {":authority", "sni.lyft.com"}}; + auto result = codec_client_->startRequest(request_headers); + auto response = std::move(result.second); + Buffer::OwnedImpl data1("one"); + result.first.encodeData(data1, false); + Buffer::OwnedImpl data2("two"); + result.first.encodeData(data2, true); + waitForNextUpstreamRequest(); + const Http::TestResponseHeaderMapImpl response_headers{{":status", "200"}}; + upstream_request_->encodeHeaders(response_headers, false); + Buffer::OwnedImpl data3("three"); + upstream_request_->encodeData(data3, false); + response->waitForBodyData(5); + Buffer::OwnedImpl data4("four"); + upstream_request_->encodeData(data4, true); + ASSERT_TRUE(response->waitForEndStream()); + + checkStats(); + codec_client_->close(); + test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 1); + + envoy::data::tap::v3::TraceWrapper trace; + TestUtility::loadFromFile(fmt::format("{}_{}.pb", path_prefix_, id), trace, *api_); + + ASSERT_EQ(trace.socket_buffered_trace().events().size(), 2); + EXPECT_TRUE(trace.socket_buffered_trace().events(0).read().data().truncated()); + EXPECT_TRUE(trace.socket_buffered_trace().events(1).write().data().truncated()); + EXPECT_TRUE(trace.socket_buffered_trace().read_truncated()); + EXPECT_TRUE(trace.socket_buffered_trace().write_truncated()); +} + +// Validate a single request with text proto output. +TEST_P(SslTapIntegrationTest, RequestWithTextProto) { + format_ = envoy::config::tap::v3::OutputSink::PROTO_TEXT; + ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { + return makeSslClientConnection({}); + }; + + // Disable for this test because it uses connection IDs, which disrupts the accounting below + // leading to the wrong path for the `pb_text` being used. + skip_tag_extraction_rule_check_ = true; + + const uint64_t id = Network::ConnectionImpl::nextGlobalIdForTest() + 1; + testRouterRequestAndResponseWithBody(1024, 512, false, false, &creator); + checkStats(); + codec_client_->close(); + test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 1); + envoy::data::tap::v3::TraceWrapper trace; + TestUtility::loadFromFile(fmt::format("{}_{}.pb_text", path_prefix_, id), trace, *api_); + // Test some obvious properties. + EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(0).read().data().as_bytes(), + "GET /test/long/url HTTP/1.1")); + EXPECT_TRUE(absl::StartsWith(trace.socket_buffered_trace().events(1).write().data().as_bytes(), + "HTTP/1.1 200 OK")); + EXPECT_TRUE(trace.socket_buffered_trace().read_truncated()); + EXPECT_FALSE(trace.socket_buffered_trace().write_truncated()); +} + +// Validate a single request with JSON (body as string) output. This test uses an upstream tap. +TEST_P(SslTapIntegrationTest, RequestWithJsonBodyAsStringUpstreamTap) { + upstream_tap_ = true; + max_rx_bytes_ = 5; + max_tx_bytes_ = 4; + + format_ = envoy::config::tap::v3::OutputSink::JSON_BODY_AS_STRING; + ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { + return makeSslClientConnection({}); + }; + + // Disable for this test because it uses connection IDs, which disrupts the accounting below + // leading to the wrong path for the `pb_text` being used. + skip_tag_extraction_rule_check_ = true; + + const uint64_t id = Network::ConnectionImpl::nextGlobalIdForTest() + 2; + testRouterRequestAndResponseWithBody(512, 1024, false, false, &creator); + checkStats(); + codec_client_->close(); + test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 1); + test_server_.reset(); + + // This must be done after server shutdown so that connection pool connections are closed and + // the tap written. + envoy::data::tap::v3::TraceWrapper trace; + TestUtility::loadFromFile(fmt::format("{}_{}.json", path_prefix_, id), trace, *api_); + + // Test some obvious properties. + EXPECT_EQ(trace.socket_buffered_trace().events(0).write().data().as_string(), "GET "); + EXPECT_EQ(trace.socket_buffered_trace().events(1).read().data().as_string(), "HTTP/"); + EXPECT_TRUE(trace.socket_buffered_trace().read_truncated()); + EXPECT_TRUE(trace.socket_buffered_trace().write_truncated()); +} + +// Validate a single request with length delimited binary proto output. This test uses an upstream +// tap. +TEST_P(SslTapIntegrationTest, RequestWithStreamingUpstreamTap) { + upstream_tap_ = true; + streaming_tap_ = true; + max_rx_bytes_ = 5; + max_tx_bytes_ = 4; + + format_ = envoy::config::tap::v3::OutputSink::PROTO_BINARY_LENGTH_DELIMITED; + ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { + return makeSslClientConnection({}); + }; + + // Disable for this test because it uses connection IDs, which disrupts the accounting below + // leading to the wrong path for the `pb_text` being used. + skip_tag_extraction_rule_check_ = true; + + const uint64_t id = Network::ConnectionImpl::nextGlobalIdForTest() + 2; + testRouterRequestAndResponseWithBody(512, 1024, false, false, &creator); + checkStats(); + codec_client_->close(); + test_server_->waitForCounterGe("http.config_test.downstream_cx_destroy", 1); + test_server_.reset(); + + // This must be done after server shutdown so that connection pool connections are closed and + // the tap written. + std::vector traces = + Extensions::Common::Tap::readTracesFromFile( + fmt::format("{}_{}.pb_length_delimited", path_prefix_, id)); + ASSERT_GE(traces.size(), 4); + + // The initial connection message has no local address, but has a remote address (not connected + // yet). + EXPECT_TRUE(traces[0].socket_streamed_trace_segment().has_connection()); + EXPECT_FALSE(traces[0].socket_streamed_trace_segment().connection().has_local_address()); + EXPECT_TRUE(traces[0].socket_streamed_trace_segment().connection().has_remote_address()); + + // Verify truncated request/response data. + EXPECT_EQ(traces[1].socket_streamed_trace_segment().event().write().data().as_bytes(), "GET "); + EXPECT_TRUE(traces[1].socket_streamed_trace_segment().event().write().data().truncated()); + EXPECT_EQ(traces[2].socket_streamed_trace_segment().event().read().data().as_bytes(), "HTTP/"); + EXPECT_TRUE(traces[2].socket_streamed_trace_segment().event().read().data().truncated()); +} + +} // namespace Ssl +} // namespace Envoy diff --git a/test/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD b/test/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD index 9f9644768aee..c95555b9be37 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD +++ b/test/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD @@ -25,6 +25,7 @@ envoy_extension_cc_test( "//source/extensions/transport_sockets/tls/cert_validator/spiffe:config", "//test/common/tls:ssl_test_utils", "//test/common/tls/cert_validator:test_common", + "//test/mocks/server:server_factory_context_mocks", "//test/test_common:environment_lib", "//test/test_common:simulated_time_system_lib", "//test/test_common:test_runtime_lib", 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 1806d0985ed1..db9af217a43b 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 @@ -21,8 +21,8 @@ void SslSPIFFECertValidatorIntegrationTest::initialize() { .setAllowExpiredCertificate(allow_expired_cert_)); HttpIntegrationTest::initialize(); - context_manager_ = - std::make_unique(timeSystem()); + context_manager_ = std::make_unique( + server_factory_context_); registerTestServerPorts({"http"}); } 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 51cc1ad97016..327298a3efe6 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 @@ -13,6 +13,7 @@ #include "test/common/tls/cert_validator/test_common.h" #include "test/common/tls/ssl_test_utility.h" +#include "test/mocks/server/server_factory_context.h" #include "test/test_common/environment.h" #include "test/test_common/simulated_time_system.h" #include "test/test_common/test_runtime.h" @@ -44,7 +45,8 @@ class TestSPIFFEValidator : public testing::Test { TestUtility::loadFromYaml(yaml, typed_conf); config_ = std::make_unique( typed_conf, allow_expired_certificate_, san_matchers_); - validator_ = std::make_unique(config_.get(), stats_, time_source); + ON_CALL(factory_context_, timeSource()).WillByDefault(testing::ReturnRef(time_source)); + validator_ = std::make_unique(config_.get(), stats_, factory_context_); } void initialize(std::string yaml) { @@ -52,8 +54,7 @@ class TestSPIFFEValidator : public testing::Test { TestUtility::loadFromYaml(yaml, typed_conf); config_ = std::make_unique( typed_conf, allow_expired_certificate_, san_matchers_); - validator_ = - std::make_unique(config_.get(), stats_, config_->api().timeSource()); + validator_ = std::make_unique(config_.get(), stats_, factory_context_); }; void initialize() { validator_ = std::make_unique(stats_, time_system_); } @@ -90,6 +91,7 @@ class TestSPIFFEValidator : public testing::Test { }; private: + NiceMock factory_context_; bool allow_expired_certificate_{false}; TestCertificateValidationContextConfigPtr config_; std::vector san_matchers_{}; diff --git a/test/fuzz/utility.h b/test/fuzz/utility.h index a450ac92e837..499f429e1e57 100644 --- a/test/fuzz/utility.h +++ b/test/fuzz/utility.h @@ -177,11 +177,15 @@ inline std::unique_ptr fromStreamInfo(const test::fuzz::StreamIn } else { address = Network::Utility::resolveUrl("tcp://10.0.0.1:443"); } - auto upstream_local_address = - stream_info.has_upstream_local_address() - ? Envoy::Network::Address::resolveProtoAddress(stream_info.upstream_local_address()) - .value() - : Network::Utility::resolveUrl("tcp://10.0.0.1:10000"); + Envoy::Network::Address::InstanceConstSharedPtr upstream_local_address; + if (stream_info.has_upstream_local_address()) { + auto upstream_local_address_or_error = + Envoy::Network::Address::resolveProtoAddress(stream_info.upstream_local_address()); + THROW_IF_STATUS_NOT_OK(upstream_local_address_or_error, throw); + upstream_local_address = upstream_local_address_or_error.value(); + } else { + upstream_local_address = Network::Utility::resolveUrl("tcp://10.0.0.1:10000"); + } test_stream_info->upstreamInfo()->setUpstreamLocalAddress(upstream_local_address); test_stream_info->downstream_connection_info_provider_ = std::make_shared(address, address); diff --git a/test/integration/BUILD b/test/integration/BUILD index fce0e38d657d..9db08519afbb 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -373,6 +373,7 @@ envoy_cc_test_binary( "//source/exe:process_wide_lib", "//source/exe:stripped_main_base_lib", "//source/extensions/listener_managers/validation_listener_manager:validation_listener_manager_lib", + "//source/extensions/transport_sockets/tls:config", ], ) @@ -985,7 +986,7 @@ envoy_cc_test( srcs = ["idle_timeout_integration_test.cc"], # As this test has many pauses for idle timeouts, it takes a while to run. # Shard it enough to bring the run time in line with other integration tests. - shard_count = 4, + shard_count = 8, tags = [ "cpu:3", ], diff --git a/test/integration/admin_html/test_server.cc b/test/integration/admin_html/test_server.cc index 4a53d7661bb9..d371d7b22108 100644 --- a/test/integration/admin_html/test_server.cc +++ b/test/integration/admin_html/test_server.cc @@ -14,7 +14,8 @@ namespace { * a query param but it could not be found. * * This test-server is only for testing; it potentially makes the - * entire file-system avail + * entire file-system available to HTTP clients, so this should not + * be used for production systems. */ Http::Code testCallback(Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, Server::AdminStream& admin_stream) { diff --git a/test/integration/ads_integration_test.cc b/test/integration/ads_integration_test.cc index 024247a0e569..0fb8c4ecb006 100644 --- a/test/integration/ads_integration_test.cc +++ b/test/integration/ads_integration_test.cc @@ -717,6 +717,102 @@ TEST_P(AdsIntegrationTest, CdsKeepEdsAfterWarmingFailure) { makeSingleRequest(); } +// Validate that an update to 2 Clusters that have the same ClusterLoadAssignment, and +// that don't receive updated ClusterLoadAssignment use the previous (cached) cluster +// load assignment. +TEST_P(AdsIntegrationTest, DoubleClustersCachedLoadAssignment) { + // TODO(adisuissa): this test should be kept after the runtime guard is deprecated + // (only the runtime guard should be removed). + config_helper_.addRuntimeOverride("envoy.restart_features.use_eds_cache_for_ads", "true"); + initialize(); + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Cluster, "", {}, {}, {}, true)); + envoy::config::cluster::v3::Cluster cluster0 = buildCluster("cluster_0"); + envoy::config::cluster::v3::Cluster cluster1 = buildCluster("cluster_1"); + // Set a small EDS subscription expiration. + cluster0.mutable_eds_cluster_config() + ->mutable_eds_config() + ->mutable_initial_fetch_timeout() + ->set_nanos(100 * 1000 * 1000); + cluster1.mutable_eds_cluster_config() + ->mutable_eds_config() + ->mutable_initial_fetch_timeout() + ->set_nanos(100 * 1000 * 1000); + // Set the EDS service of cluster0 and cluster1 to be the same. + cluster0.mutable_eds_cluster_config()->set_service_name("same_eds"); + cluster1.mutable_eds_cluster_config()->set_service_name("same_eds"); + sendDiscoveryResponse( + Config::TypeUrl::get().Cluster, {cluster0, cluster1}, {cluster0, cluster1}, {}, "1"); + + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().ClusterLoadAssignment, "", + {"same_eds"}, {"same_eds"}, {})); + auto cla_0 = buildClusterLoadAssignment("same_eds"); + sendDiscoveryResponse( + Config::TypeUrl::get().ClusterLoadAssignment, {cla_0}, {cla_0}, {}, "1"); + + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Cluster, "1", {}, {}, {})); + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Listener, "", {}, {}, {})); + sendDiscoveryResponse( + Config::TypeUrl::get().Listener, {buildListener("listener_0", "route_config_0")}, + {buildListener("listener_0", "route_config_0")}, {}, "1"); + + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().ClusterLoadAssignment, "1", + {"same_eds"}, {}, {})); + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().RouteConfiguration, "", + {"route_config_0"}, {"route_config_0"}, {})); + sendDiscoveryResponse( + Config::TypeUrl::get().RouteConfiguration, {buildRouteConfig("route_config_0", "cluster_0")}, + {buildRouteConfig("route_config_0", "cluster_0")}, {}, "1"); + + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Listener, "1", {}, {}, {})); + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().RouteConfiguration, "1", + {"route_config_0"}, {}, {})); + test_server_->waitForCounterGe("listener_manager.listener_create_success", 1); + makeSingleRequest(); + + // Update a field of the clusters (connect_timeout) so the clusters in Envoy will be explicitly + // updated. + cluster0.mutable_connect_timeout()->set_seconds(7); + cluster1.mutable_connect_timeout()->set_seconds(7); + sendDiscoveryResponse( + Config::TypeUrl::get().Cluster, {cluster0, cluster1}, {cluster0, cluster1}, {}, "2"); + // Inconsistent SotW and delta behaviors for warming, see + // https://github.com/envoyproxy/envoy/issues/11477#issuecomment-657855029. + // TODO (dmitri-d) this should be remove when legacy mux implementations have been removed. + if (sotw_or_delta_ == Grpc::SotwOrDelta::Sotw) { + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().ClusterLoadAssignment, "1", + {"same_eds"}, {}, {})); + } + + // Avoid sending an EDS update, and wait for EDS update timeout (that results in + // a cluster update without resources). + test_server_->waitForCounterGe("cluster.cluster_0.init_fetch_timeout", 1); + + if (sotw_or_delta_ == Grpc::SotwOrDelta::Sotw) { + // Expect another EDS request after the previous one wasn't answered and timed out. + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().ClusterLoadAssignment, "1", + {"same_eds"}, {}, {})); + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().ClusterLoadAssignment, "1", + {"same_eds"}, {}, {})); + } + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Cluster, "2", {}, {}, {})); + + // Envoy uses the cached resource. + EXPECT_EQ(1, test_server_->counter("cluster.cluster_0.assignment_use_cached")->value()); + // A single message should be successfully sent to the upstream. + makeSingleRequest(); + + // Now send an EDS update. + cla_0.mutable_policy()->mutable_overprovisioning_factor()->set_value(141); + sendDiscoveryResponse( + Config::TypeUrl::get().ClusterLoadAssignment, {cla_0}, {cla_0}, {}, "2"); + + // Wait for ingesting the update. + test_server_->waitForCounterEq("cluster.cluster_0.update_success", 2); + + // A single message should be successfully sent to the upstream. + makeSingleRequest(); +} + // Validate that the request with duplicate clusters in the initial request during server init is // rejected. TEST_P(AdsIntegrationTest, DuplicateInitialClusters) { diff --git a/test/integration/base_integration_test.h b/test/integration/base_integration_test.h index 1f1df3f7c765..c5eb1129d41b 100644 --- a/test/integration/base_integration_test.h +++ b/test/integration/base_integration_test.h @@ -525,7 +525,8 @@ class BaseIntegrationTest : protected Logger::Loggable { createUpstreamTlsContext(const FakeUpstreamConfig& upstream_config); testing::NiceMock thread_local_; testing::NiceMock factory_context_; - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{timeSystem()}; + testing::NiceMock server_factory_context_; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{server_factory_context_}; // The fake upstreams_ are created using the context_manager, so make sure // they are destroyed before it is. diff --git a/test/integration/fake_upstream.cc b/test/integration/fake_upstream.cc index 1453b47044e2..e8ae3c677553 100644 --- a/test/integration/fake_upstream.cc +++ b/test/integration/fake_upstream.cc @@ -579,7 +579,11 @@ void FakeConnectionBase::postToConnectionThread(std::function cb) { ++pending_cbs_; dispatcher_.post([this, cb]() { cb(); - --pending_cbs_; + { + // Snag this lock not because it's needed but so waitForNoPost doesn't stall + absl::MutexLock lock(&lock_); + --pending_cbs_; + } }); } diff --git a/test/integration/fake_upstream.h b/test/integration/fake_upstream.h index 24a89f96d7c0..78bc9b1566aa 100644 --- a/test/integration/fake_upstream.h +++ b/test/integration/fake_upstream.h @@ -644,6 +644,7 @@ struct FakeUpstreamConfig { http2_options_.set_allow_connect(true); http2_options_.set_allow_metadata(true); http3_options_.set_allow_extended_connect(true); + http3_options_.set_allow_metadata(true); } Event::TestTimeSystem& time_system_; diff --git a/test/integration/http_integration.cc b/test/integration/http_integration.cc index c25103ffabb0..368054ba95a0 100644 --- a/test/integration/http_integration.cc +++ b/test/integration/http_integration.cc @@ -245,15 +245,13 @@ Network::ClientConnectionPtr HttpIntegrationTest::makeClientConnectionWithOption fmt::format("udp://{}:{}", Network::Test::getLoopbackAddressUrlString(version_), port)); Network::Address::InstanceConstSharedPtr local_addr = Network::Test::getCanonicalLoopbackAddress(version_); - auto& quic_transport_socket_factory_ref = - dynamic_cast(*quic_transport_socket_factory_); return Quic::createQuicNetworkConnection( - *quic_connection_persistent_info_, quic_transport_socket_factory_ref.getCryptoConfig(), + *quic_connection_persistent_info_, quic_transport_socket_factory_->getCryptoConfig(), quic::QuicServerId( - quic_transport_socket_factory_ref.clientContextConfig()->serverNameIndication(), + quic_transport_socket_factory_->clientContextConfig()->serverNameIndication(), static_cast(port)), *dispatcher_, server_addr, local_addr, quic_stat_names_, {}, *stats_store_.rootScope(), - options, nullptr, connection_id_generator_, quic_transport_socket_factory_ref); + options, nullptr, connection_id_generator_, *quic_transport_socket_factory_); #else ASSERT(false, "running a QUIC integration test without compiling QUIC"); return nullptr; @@ -276,13 +274,14 @@ IntegrationCodecClientPtr HttpIntegrationTest::makeRawHttpConnection( .value(); http2_options.value().set_allow_connect(true); http2_options.value().set_allow_metadata(true); + } #ifdef ENVOY_ENABLE_QUIC - } else { - cluster->http3_options_ = ConfigHelper::http2ToHttp3ProtocolOptions( - http2_options.value(), quic::kStreamReceiveWindowLimit); - cluster->http3_options_.set_allow_extended_connect(true); + cluster->http3_options_ = ConfigHelper::http2ToHttp3ProtocolOptions( + http2_options.value(), quic::kStreamReceiveWindowLimit); + cluster->http3_options_.set_allow_extended_connect(true); + cluster->http3_options_.set_allow_metadata(true); #endif - } + cluster->http2_options_ = http2_options.value(); cluster->http1_settings_.enable_trailers_ = true; diff --git a/test/integration/http_protocol_integration.cc b/test/integration/http_protocol_integration.cc index 50f79fdf6366..a6856e1f1dd5 100644 --- a/test/integration/http_protocol_integration.cc +++ b/test/integration/http_protocol_integration.cc @@ -8,6 +8,8 @@ std::vector HttpProtocolIntegrationTest::getProtocolTest const std::vector& upstream_protocols) { std::vector ret; + bool handled_http2_special_cases_downstream = false; + bool handled_http2_special_cases_upstream = false; for (auto ip_version : TestEnvironment::getIpVersionsForTest()) { for (auto downstream_protocol : downstream_protocols) { for (auto upstream_protocol : upstream_protocols) { @@ -27,10 +29,19 @@ std::vector HttpProtocolIntegrationTest::getProtocolTest std::vector http2_implementations = {Http2Impl::Nghttp2}; std::vector http2_bool_values = {false}; - if (downstream_protocol == Http::CodecType::HTTP2 || - upstream_protocol == Http::CodecType::HTTP2) { + if ((!handled_http2_special_cases_downstream && + downstream_protocol == Http::CodecType::HTTP2) || + (!handled_http2_special_cases_upstream && + upstream_protocol == Http::CodecType::HTTP2)) { http2_implementations.push_back(Http2Impl::Oghttp2); http2_bool_values.push_back(true); + + if (downstream_protocol == Http::CodecType::HTTP2) { + handled_http2_special_cases_downstream = true; + } + if (upstream_protocol == Http::CodecType::HTTP2) { + handled_http2_special_cases_upstream = true; + } } std::vector use_header_validator_values; diff --git a/test/integration/multiplexed_integration_test.cc b/test/integration/multiplexed_integration_test.cc index d83e2e395ef2..4f534ecaf40d 100644 --- a/test/integration/multiplexed_integration_test.cc +++ b/test/integration/multiplexed_integration_test.cc @@ -276,7 +276,7 @@ static std::string response_metadata_filter = R"EOF( name: response-metadata-filter )EOF"; -class Http2MetadataIntegrationTest : public HttpProtocolIntegrationTest { +class MetadataIntegrationTest : public HttpProtocolIntegrationTest { public: void SetUp() override { HttpProtocolIntegrationTest::SetUp(); @@ -284,15 +284,25 @@ class Http2MetadataIntegrationTest : public HttpProtocolIntegrationTest { [&](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); + if (GetParam().upstream_protocol == Http::CodecType::HTTP3) { + protocol_options.mutable_explicit_http_config() + ->mutable_http3_protocol_options() + ->set_allow_metadata(true); + protocol_options.mutable_upstream_http_protocol_options()->set_auto_sni(true); + } else { + 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); }); + hcm) -> void { + hcm.mutable_http2_protocol_options()->set_allow_metadata(true); + hcm.mutable_http3_protocol_options()->set_allow_metadata(true); + }); } void testRequestMetadataWithStopAllFilter(); @@ -301,6 +311,8 @@ class Http2MetadataIntegrationTest : public HttpProtocolIntegrationTest { void runHeaderOnlyTest(bool send_request_body, size_t body_size); + Http::CodecClient::Type upstreamProtocol() { return GetParam().upstream_protocol; } + protected: // Utility function to prepend filters. Note that the filters // are added in reverse order. @@ -312,7 +324,7 @@ class Http2MetadataIntegrationTest : public HttpProtocolIntegrationTest { }; // Verifies metadata can be sent at different locations of the responses. -TEST_P(Http2MetadataIntegrationTest, ProxyMetadataInResponse) { +TEST_P(MetadataIntegrationTest, ProxyMetadataInResponse) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -445,7 +457,7 @@ TEST_P(Http2MetadataIntegrationTest, ProxyMetadataInResponse) { test_server_->waitForCounterEq(counter, 1); } -TEST_P(Http2MetadataIntegrationTest, ProxyMultipleMetadata) { +TEST_P(MetadataIntegrationTest, ProxyMultipleMetadata) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -485,7 +497,7 @@ TEST_P(Http2MetadataIntegrationTest, ProxyMultipleMetadata) { // Disabled temporarily see #19040 #if 0 -TEST_P(Http2MetadataIntegrationTest, ProxyInvalidMetadata) { +TEST_P(MetadataIntegrationTest, ProxyInvalidMetadata) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -516,14 +528,16 @@ TEST_P(Http2MetadataIntegrationTest, ProxyInvalidMetadata) { #endif void verifyExpectedMetadata(Http::MetadataMap metadata_map, std::set keys) { + EXPECT_EQ(metadata_map.size(), keys.size()); for (const auto& key : keys) { // keys are the same as their corresponding values. + auto it = metadata_map.find(key); + ASSERT_FALSE(it == metadata_map.end()) << "key: " << key; EXPECT_EQ(metadata_map.find(key)->second, key); } - EXPECT_EQ(metadata_map.size(), keys.size()); } -TEST_P(Http2MetadataIntegrationTest, TestResponseMetadata) { +TEST_P(MetadataIntegrationTest, TestResponseMetadata) { prependFilters({response_metadata_filter}); config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& @@ -540,6 +554,11 @@ TEST_P(Http2MetadataIntegrationTest, TestResponseMetadata) { ASSERT_TRUE(response->waitForEndStream()); ASSERT_TRUE(response->complete()); std::set expected_metadata_keys = {"headers", "duplicate"}; + if (upstreamProtocol() == Http::CodecType::HTTP3) { + // HTTP/3 Sends "end stream" in an empty DATA frame which results in the test filter + // adding the "data" metadata header. + expected_metadata_keys.insert("data"); + } verifyExpectedMetadata(response->metadataMap(), expected_metadata_keys); // Upstream responds with headers and data. @@ -571,13 +590,9 @@ TEST_P(Http2MetadataIntegrationTest, TestResponseMetadata) { EXPECT_EQ(4, response->metadataMapsDecodedCount()); // Upstream responds with headers, 100-continue and data. - response = - codec_client_->makeRequestWithBody(Http::TestRequestHeaderMapImpl{{":method", "GET"}, - {":path", "/dynamo/url"}, - {":scheme", "http"}, - {":authority", "host"}, - {"expect", "100-contINUE"}}, - 10); + Http::TestRequestHeaderMapImpl headers = default_request_headers_; + headers.addCopy("expect", "100-contINUE"); + response = codec_client_->makeRequestWithBody(headers, 10); waitForNextUpstreamRequest(); upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "100"}}); @@ -609,8 +624,17 @@ TEST_P(Http2MetadataIntegrationTest, TestResponseMetadata) { expected_metadata_keys.erase("100-continue"); expected_metadata_keys.insert("aaa"); expected_metadata_keys.insert("keep"); + if (upstreamProtocol() == Http::CodecType::HTTP3) { + // HTTP/3 Sends "end stream" in an empty DATA frame which results in the test filter + // adding the "data" metadata header. + expected_metadata_keys.insert("data"); + } verifyExpectedMetadata(response->metadataMap(), expected_metadata_keys); - EXPECT_EQ(2, response->metadataMapsDecodedCount()); + if (upstreamProtocol() == Http::CodecType::HTTP3) { + EXPECT_EQ(3, response->metadataMapsDecodedCount()); + } else { + EXPECT_EQ(2, response->metadataMapsDecodedCount()); + } // Upstream responds with headers, data and metadata that will be consumed. response = codec_client_->makeRequestWithBody(default_request_headers_, 10); @@ -633,7 +657,7 @@ TEST_P(Http2MetadataIntegrationTest, TestResponseMetadata) { EXPECT_EQ(3, response->metadataMapsDecodedCount()); } -TEST_P(Http2MetadataIntegrationTest, ProxyMultipleMetadataReachSizeLimit) { +TEST_P(MetadataIntegrationTest, ProxyMultipleMetadataReachSizeLimit) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -659,7 +683,7 @@ TEST_P(Http2MetadataIntegrationTest, ProxyMultipleMetadataReachSizeLimit) { } // Verifies small metadata can be sent at different locations of a request. -TEST_P(Http2MetadataIntegrationTest, ProxySmallMetadataInRequest) { +TEST_P(MetadataIntegrationTest, ProxySmallMetadataInRequest) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -688,7 +712,7 @@ TEST_P(Http2MetadataIntegrationTest, ProxySmallMetadataInRequest) { } // Verifies large metadata can be sent at different locations of a request. -TEST_P(Http2MetadataIntegrationTest, ProxyLargeMetadataInRequest) { +TEST_P(MetadataIntegrationTest, ProxyLargeMetadataInRequest) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -717,7 +741,7 @@ TEST_P(Http2MetadataIntegrationTest, ProxyLargeMetadataInRequest) { ASSERT_TRUE(response->complete()); } -TEST_P(Http2MetadataIntegrationTest, RequestMetadataReachSizeLimit) { +TEST_P(MetadataIntegrationTest, RequestMetadataReachSizeLimit) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -742,7 +766,7 @@ TEST_P(Http2MetadataIntegrationTest, RequestMetadataReachSizeLimit) { ASSERT_FALSE(response->complete()); } -TEST_P(Http2MetadataIntegrationTest, RequestMetadataThenTrailers) { +TEST_P(MetadataIntegrationTest, RequestMetadataThenTrailers) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -765,7 +789,7 @@ static std::string request_metadata_filter = R"EOF( name: request-metadata-filter )EOF"; -TEST_P(Http2MetadataIntegrationTest, ConsumeAndInsertRequestMetadata) { +TEST_P(MetadataIntegrationTest, ConsumeAndInsertRequestMetadata) { prependFilters({request_metadata_filter}); config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& @@ -784,6 +808,11 @@ TEST_P(Http2MetadataIntegrationTest, ConsumeAndInsertRequestMetadata) { // Verifies a headers metadata added. std::set expected_metadata_keys = {"headers"}; expected_metadata_keys.insert("metadata"); + if (downstreamProtocol() == Http::CodecType::HTTP3) { + // HTTP/3 Sends "end stream" in an empty DATA frame which results in the test filter + // adding the "data" metadata header. + expected_metadata_keys.insert("data"); + } verifyExpectedMetadata(upstream_request_->metadataMap(), expected_metadata_keys); // Sends a headers only request with metadata. An empty data frame carries end_stream. @@ -869,7 +898,7 @@ TEST_P(Http2MetadataIntegrationTest, ConsumeAndInsertRequestMetadata) { EXPECT_EQ(upstream_request_->duplicatedMetadataKeyCount().find("metadata")->second, 6); } -void Http2MetadataIntegrationTest::runHeaderOnlyTest(bool send_request_body, size_t body_size) { +void MetadataIntegrationTest::runHeaderOnlyTest(bool send_request_body, size_t body_size) { config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& hcm) -> void { hcm.set_proxy_100_continue(true); }); @@ -880,18 +909,9 @@ void Http2MetadataIntegrationTest::runHeaderOnlyTest(bool send_request_body, siz // Sends a request with body. Only headers will pass through filters. IntegrationStreamDecoderPtr response; if (send_request_body) { - response = codec_client_->makeRequestWithBody( - Http::TestRequestHeaderMapImpl{{":method", "POST"}, - {":path", "/test/long/url"}, - {":scheme", "http"}, - {":authority", "host"}}, - body_size); + response = codec_client_->makeRequestWithBody(default_request_headers_, body_size); } else { - response = codec_client_->makeHeaderOnlyRequest( - Http::TestRequestHeaderMapImpl{{":method", "POST"}, - {":path", "/test/long/url"}, - {":scheme", "http"}, - {":authority", "host"}}); + response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); } waitForNextUpstreamRequest(); @@ -900,10 +920,15 @@ void Http2MetadataIntegrationTest::runHeaderOnlyTest(bool send_request_body, siz ASSERT_TRUE(response->complete()); } -void Http2MetadataIntegrationTest::verifyHeadersOnlyTest() { +void MetadataIntegrationTest::verifyHeadersOnlyTest() { // Verifies a headers metadata added. std::set expected_metadata_keys = {"headers"}; expected_metadata_keys.insert("metadata"); + if (downstreamProtocol() == Http::CodecType::HTTP3) { + // HTTP/3 Sends "end stream" in an empty DATA frame which results in the test filter + // adding the "data" metadata header. + expected_metadata_keys.insert("data"); + } verifyExpectedMetadata(upstream_request_->metadataMap(), expected_metadata_keys); // Verifies zero length data received, and end_stream is true. @@ -912,14 +937,14 @@ void Http2MetadataIntegrationTest::verifyHeadersOnlyTest() { EXPECT_EQ(true, upstream_request_->complete()); } -TEST_P(Http2MetadataIntegrationTest, HeadersOnlyRequestWithRequestMetadata) { +TEST_P(MetadataIntegrationTest, HeadersOnlyRequestWithRequestMetadata) { prependFilters({request_metadata_filter}); // Send a headers only request. runHeaderOnlyTest(false, 0); verifyHeadersOnlyTest(); } -void Http2MetadataIntegrationTest::testRequestMetadataWithStopAllFilter() { +void MetadataIntegrationTest::testRequestMetadataWithStopAllFilter() { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -953,17 +978,17 @@ static std::string metadata_stop_all_filter = R"EOF( name: metadata-stop-all-filter )EOF"; -TEST_P(Http2MetadataIntegrationTest, RequestMetadataWithStopAllFilterBeforeMetadataFilter) { +TEST_P(MetadataIntegrationTest, RequestMetadataWithStopAllFilterBeforeMetadataFilter) { prependFilters({request_metadata_filter, metadata_stop_all_filter}); testRequestMetadataWithStopAllFilter(); } -TEST_P(Http2MetadataIntegrationTest, RequestMetadataWithStopAllFilterAfterMetadataFilter) { +TEST_P(MetadataIntegrationTest, RequestMetadataWithStopAllFilterAfterMetadataFilter) { prependFilters({metadata_stop_all_filter, request_metadata_filter}); testRequestMetadataWithStopAllFilter(); } -TEST_P(Http2MetadataIntegrationTest, TestAddEncodedMetadata) { +TEST_P(MetadataIntegrationTest, TestAddEncodedMetadata) { config_helper_.prependFilter(R"EOF( name: encode-headers-return-stop-all-filter )EOF"); @@ -1704,9 +1729,10 @@ INSTANTIATE_TEST_SUITE_P(IpVersions, MultiplexedRingHashIntegrationTest, {Http::CodecType::HTTP1})), HttpProtocolIntegrationTest::protocolTestParamsToString); -INSTANTIATE_TEST_SUITE_P(IpVersions, Http2MetadataIntegrationTest, +INSTANTIATE_TEST_SUITE_P(IpVersions, MetadataIntegrationTest, testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams( - {Http::CodecType::HTTP2}, {Http::CodecType::HTTP2})), + {Http::CodecType::HTTP2, Http::CodecType::HTTP3}, + {Http::CodecType::HTTP2, Http::CodecType::HTTP3})), HttpProtocolIntegrationTest::protocolTestParamsToString); void MultiplexedRingHashIntegrationTest::sendMultipleRequests( @@ -2700,7 +2726,11 @@ TEST_P(Http2FrameIntegrationTest, DownstreamSendingEmptyMetadata) { } // Tests that an empty metadata map from upstream is ignored. -TEST_P(Http2MetadataIntegrationTest, UpstreamSendingEmptyMetadata) { +TEST_P(MetadataIntegrationTest, UpstreamSendingEmptyMetadata) { + if (upstreamProtocol() == Http::CodecType::HTTP3) { + // rawWriteConnection is not available for QUIC. + return; + } initialize(); // Send a request and make sure an upstream connection is established. @@ -2728,7 +2758,7 @@ TEST_P(Http2MetadataIntegrationTest, UpstreamSendingEmptyMetadata) { } // Tests upstream sending a metadata frame after ending a stream. -TEST_P(Http2MetadataIntegrationTest, UpstreamMetadataAfterEndStream) { +TEST_P(MetadataIntegrationTest, UpstreamMetadataAfterEndStream) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -2758,6 +2788,7 @@ TEST_P(Http2MetadataIntegrationTest, UpstreamMetadataAfterEndStream) { ASSERT_TRUE(fake_upstream_connection_->close()); ASSERT_TRUE(response->complete()); EXPECT_EQ("200", response->headers().getStatusValue()); + cleanupUpstreamAndDownstream(); } TEST_P(MultiplexedIntegrationTest, InvalidTrailers) { diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index e13e5a493b72..fec8f47df756 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -3110,17 +3110,23 @@ TEST_P(DownstreamProtocolIntegrationTest, LocalReplyWithMetadata) { } TEST_P(ProtocolIntegrationTest, ContinueAllFromDecodeMetadata) { - if (downstream_protocol_ != Http::CodecType::HTTP2 || - upstreamProtocol() != Http::CodecType::HTTP2) { - GTEST_SKIP() << "Metadata is not enabled for non HTTP2 protocols."; + if (downstream_protocol_ == Http::CodecType::HTTP1 || + upstreamProtocol() == Http::CodecType::HTTP1) { + GTEST_SKIP() << "Metadata is not enabled for HTTP1 protocols."; } 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); + if (upstreamProtocol() == Http::CodecType::HTTP3) { + protocol_options.mutable_explicit_http_config() + ->mutable_http3_protocol_options() + ->set_allow_metadata(true); + } else { + 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); }); @@ -3161,17 +3167,23 @@ TEST_P(ProtocolIntegrationTest, ContinueAllFromDecodeMetadata) { } TEST_P(DownstreamProtocolIntegrationTest, ContinueAllFromDecodeMetadataFollowedByLocalReply) { - if (downstream_protocol_ != Http::CodecType::HTTP2 || - upstreamProtocol() != Http::CodecType::HTTP2) { - GTEST_SKIP() << "Metadata is not enabled for non HTTP2 protocols."; + if (downstream_protocol_ == Http::CodecType::HTTP1 || + upstreamProtocol() == Http::CodecType::HTTP1) { + GTEST_SKIP() << "Metadata is not enabled for HTTP1 protocols."; } 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); + if (upstreamProtocol() == Http::CodecType::HTTP3) { + protocol_options.mutable_explicit_http_config() + ->mutable_http3_protocol_options() + ->set_allow_metadata(true); + } else { + 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); }); @@ -3205,16 +3217,22 @@ TEST_P(DownstreamProtocolIntegrationTest, ContinueAllFromDecodeMetadataFollowedB } TEST_P(ProtocolIntegrationTest, ContinueAllFromEncodeMetadata) { - if (upstreamProtocol() != Http::CodecType::HTTP2 || - downstream_protocol_ != Http::CodecType::HTTP2) { - GTEST_SKIP() << "Metadata is not enabled for non HTTP2 protocols."; + if (downstream_protocol_ == Http::CodecType::HTTP1 || + upstreamProtocol() == Http::CodecType::HTTP1) { + GTEST_SKIP() << "Metadata is not enabled for HTTP1 protocols."; } 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); + if (upstreamProtocol() == Http::CodecType::HTTP3) { + protocol_options.mutable_explicit_http_config() + ->mutable_http3_protocol_options() + ->set_allow_metadata(true); + } else { + 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); }); @@ -4634,6 +4652,7 @@ TEST_P(ProtocolIntegrationTest, InvalidResponseHeaderName) { // } is invalid character in header name upstream_request_->encodeHeaders( Http::TestResponseHeaderMapImpl{{":status", "200"}, {"foo}name", "foo_value"}}, false); + upstream_request_->encodeData(1, true); ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); ASSERT_TRUE(response->waitForEndStream()); diff --git a/test/integration/quic_http_integration_test.cc b/test/integration/quic_http_integration_test.cc index af3420839050..e18042946688 100644 --- a/test/integration/quic_http_integration_test.cc +++ b/test/integration/quic_http_integration_test.cc @@ -73,7 +73,8 @@ class TestEnvoyQuicClientConnection : public EnvoyQuicClientConnection { bool validation_failure_on_path_response, quic::ConnectionIdGeneratorInterface& generator) : EnvoyQuicClientConnection(server_connection_id, initial_peer_address, helper, alarm_factory, - supported_versions, local_addr, dispatcher, options, generator), + supported_versions, local_addr, dispatcher, options, generator, + /*prefer_gro=*/true), dispatcher_(dispatcher), validation_failure_on_path_response_(validation_failure_on_path_response) {} @@ -837,7 +838,7 @@ TEST_P(QuicHttpIntegrationTest, PortMigration) { Network::Address::InstanceConstSharedPtr local_addr = Network::Test::getCanonicalLoopbackAddress(version_); quic_connection_->switchConnectionSocket( - createConnectionSocket(server_addr_, local_addr, nullptr)); + createConnectionSocket(server_addr_, local_addr, nullptr, /*prefer_gro=*/true)); EXPECT_NE(old_port, local_addr->ip()->port()); // Send the rest data. codec_client_->sendData(*request_encoder_, 1024u, true); @@ -866,7 +867,7 @@ TEST_P(QuicHttpIntegrationTest, PortMigration) { auto options = std::make_shared(); options->push_back(option); quic_connection_->switchConnectionSocket( - createConnectionSocket(server_addr_, local_addr, options)); + createConnectionSocket(server_addr_, local_addr, options, /*prefer_gro=*/true)); EXPECT_TRUE(codec_client_->disconnected()); cleanupUpstreamAndDownstream(); } diff --git a/test/integration/sds_static_integration_test.cc b/test/integration/sds_static_integration_test.cc index 49d42c1eb6c2..0e63eb3d4267 100644 --- a/test/integration/sds_static_integration_test.cc +++ b/test/integration/sds_static_integration_test.cc @@ -89,7 +89,7 @@ class SdsStaticDownstreamIntegrationTest } private: - Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{timeSystem()}; + Extensions::TransportSockets::Tls::ContextManagerImpl context_manager_{server_factory_context_}; Network::UpstreamTransportSocketFactoryPtr client_ssl_ctx_; }; @@ -148,7 +148,7 @@ class SdsStaticUpstreamIntegrationTest : public testing::TestWithParam(timeSystem()); + context_manager_ = std::make_unique( + server_factory_context_); payload_reader_ = std::make_shared(*dispatcher_); } diff --git a/test/integration/utility.cc b/test/integration/utility.cc index 3d06c513d7fa..85019c6f45c2 100644 --- a/test/integration/utility.cc +++ b/test/integration/utility.cc @@ -239,7 +239,8 @@ IntegrationUtil::makeSingleRequest(const Network::Address::InstanceConstSharedPt #ifdef ENVOY_ENABLE_QUIC testing::NiceMock threadlocal; - Extensions::TransportSockets::Tls::ContextManagerImpl manager(time_system); + NiceMock server_factory_context; + Extensions::TransportSockets::Tls::ContextManagerImpl manager(server_factory_context); Network::UpstreamTransportSocketFactoryPtr transport_socket_factory = createQuicUpstreamTransportSocketFactory(api, mock_stats_store, manager, threadlocal, "spiffe://lyft.com/backend-team"); diff --git a/test/integration/xds_integration_test.cc b/test/integration/xds_integration_test.cc index 5435bb17d55e..497c0e6522fa 100644 --- a/test/integration/xds_integration_test.cc +++ b/test/integration/xds_integration_test.cc @@ -193,7 +193,7 @@ class LdsInplaceUpdateTcpProxyIntegrationTest BaseIntegrationTest::initialize(); context_manager_ = std::make_unique( - BaseIntegrationTest::timeSystem()); + server_factory_context_); context_ = Ssl::createClientSslTransportSocketFactory({}, *context_manager_, *api_); } @@ -462,8 +462,8 @@ class LdsInplaceUpdateHttpIntegrationTest BaseIntegrationTest::initialize(); - context_manager_ = - std::make_unique(timeSystem()); + context_manager_ = std::make_unique( + server_factory_context_); context_ = Ssl::createClientSslTransportSocketFactory({}, *context_manager_, *api_); address_ = Ssl::getSslAddress(version_, lookupPort("http")); } diff --git a/test/integration/xfcc_integration_test.cc b/test/integration/xfcc_integration_test.cc index eeec6cf876b5..689611d5345a 100644 --- a/test/integration/xfcc_integration_test.cc +++ b/test/integration/xfcc_integration_test.cc @@ -169,8 +169,8 @@ void XfccIntegrationTest::initialize() { config_helper_.addSslConfig(); } - context_manager_ = - std::make_unique(timeSystem()); + context_manager_ = std::make_unique( + server_factory_context_); client_tls_ssl_ctx_ = createClientSslContext(false); client_mtls_ssl_ctx_ = createClientSslContext(true); HttpIntegrationTest::initialize(); diff --git a/test/mocks/common.h b/test/mocks/common.h index 455dc2f59523..246aec0f0068 100644 --- a/test/mocks/common.h +++ b/test/mocks/common.h @@ -2,6 +2,7 @@ #include +#include "envoy/common/backoff_strategy.h" #include "envoy/common/conn_pool.h" #include "envoy/common/key_value_store.h" #include "envoy/common/random_generator.h" @@ -67,6 +68,16 @@ class MockTimeSystem : public Event::TestTimeSystem { Event::TestRealTimeSystem real_time_; // NO_CHECK_FORMAT(real_time) }; +class MockBackOffStrategy : public BackOffStrategy { +public: + ~MockBackOffStrategy() override = default; + + MOCK_METHOD(uint64_t, nextBackOffMs, ()); + MOCK_METHOD(void, reset, ()); + MOCK_METHOD(void, reset, (uint64_t base_interval)); + MOCK_METHOD(bool, isOverTimeLimit, (uint64_t interval_ms), (const)); +}; + // Captures absl::string_view parameters into temp strings, for use // with gmock's SaveArg. Providing an absl::string_view compiles, // but fails because by the time you examine the saved value, its diff --git a/test/mocks/filesystem/mocks.h b/test/mocks/filesystem/mocks.h index 524cefd269f3..7032ce506d4f 100644 --- a/test/mocks/filesystem/mocks.h +++ b/test/mocks/filesystem/mocks.h @@ -75,7 +75,7 @@ class MockWatcher : public Watcher { MockWatcher(); ~MockWatcher() override; - MOCK_METHOD(void, addWatch, (absl::string_view, uint32_t, OnChangedCb)); + MOCK_METHOD(absl::Status, addWatch, (absl::string_view, uint32_t, OnChangedCb)); }; } // namespace Filesystem diff --git a/test/mocks/runtime/mocks.h b/test/mocks/runtime/mocks.h index c1ebf09315aa..0329bf8154ca 100644 --- a/test/mocks/runtime/mocks.h +++ b/test/mocks/runtime/mocks.h @@ -64,7 +64,7 @@ class MockLoader : public Loader { MOCK_METHOD(void, initialize, (Upstream::ClusterManager & cm)); MOCK_METHOD(const Snapshot&, snapshot, ()); MOCK_METHOD(SnapshotConstSharedPtr, threadsafeSnapshot, ()); - MOCK_METHOD(void, mergeValues, ((const absl::node_hash_map&))); + MOCK_METHOD(absl::Status, mergeValues, ((const absl::node_hash_map&))); MOCK_METHOD(void, startRtdsSubscriptions, (ReadyCallback)); MOCK_METHOD(Stats::Scope&, getRootScope, ()); MOCK_METHOD(void, countDeprecatedFeatureUse, (), (const)); diff --git a/test/mocks/server/admin.h b/test/mocks/server/admin.h index c7308d6a044e..a5be3ab379a0 100644 --- a/test/mocks/server/admin.h +++ b/test/mocks/server/admin.h @@ -40,6 +40,7 @@ class MockAdmin : public Admin { MOCK_METHOD(void, addListenerToHandler, (Network::ConnectionHandler * handler)); MOCK_METHOD(uint32_t, concurrency, (), (const)); MOCK_METHOD(void, closeSocket, ()); + MOCK_METHOD(RequestPtr, makeRequest, (AdminStream & admin_stream), (const)); NiceMock config_tracker_; NiceMock socket_; diff --git a/test/mocks/server/instance.cc b/test/mocks/server/instance.cc index e40b1e125331..ebee702e5dbb 100644 --- a/test/mocks/server/instance.cc +++ b/test/mocks/server/instance.cc @@ -13,7 +13,7 @@ using ::testing::ReturnRef; MockInstance::MockInstance() : secret_manager_(std::make_unique(admin_.getConfigTracker())), - cluster_manager_(timeSource()), ssl_context_manager_(timeSource()), + cluster_manager_(timeSource()), singleton_manager_(new Singleton::ManagerImpl(Thread::threadFactoryForTest())), grpc_context_(stats_store_.symbolTable()), http_context_(stats_store_.symbolTable()), router_context_(stats_store_.symbolTable()), quic_stat_names_(stats_store_.symbolTable()), @@ -21,7 +21,8 @@ MockInstance::MockInstance() server_factory_context_( std::make_shared>()), transport_socket_factory_context_( - std::make_shared>()) { + std::make_shared>()), + ssl_context_manager_(*server_factory_context_) { ON_CALL(*this, threadLocal()).WillByDefault(ReturnRef(thread_local_)); ON_CALL(*this, stats()).WillByDefault(ReturnRef(stats_store_)); ON_CALL(*this, grpcContext()).WillByDefault(ReturnRef(grpc_context_)); @@ -47,6 +48,7 @@ MockInstance::MockInstance() ON_CALL(*this, overloadManager()).WillByDefault(ReturnRef(overload_manager_)); ON_CALL(*this, messageValidationContext()).WillByDefault(ReturnRef(validation_context_)); ON_CALL(*this, statsConfig()).WillByDefault(ReturnRef(*stats_config_)); + ON_CALL(*this, regexEngine()).WillByDefault(ReturnRef(regex_engine_)); ON_CALL(*this, serverFactoryContext()).WillByDefault(ReturnRef(*server_factory_context_)); ON_CALL(*this, transportSocketFactoryContext()) .WillByDefault(ReturnRef(*transport_socket_factory_context_)); diff --git a/test/mocks/server/instance.h b/test/mocks/server/instance.h index a8561d534b9c..b5418fd5f6ab 100644 --- a/test/mocks/server/instance.h +++ b/test/mocks/server/instance.h @@ -52,6 +52,7 @@ class MockInstance : public Instance { MOCK_METHOD(ThreadLocal::Instance&, threadLocal, ()); MOCK_METHOD(LocalInfo::LocalInfo&, localInfo, (), (const)); MOCK_METHOD(Configuration::StatsConfig&, statsConfig, (), ()); + MOCK_METHOD(Regex::Engine&, regexEngine, ()); MOCK_METHOD(void, flushStats, ()); MOCK_METHOD(ProtobufMessage::ValidationContext&, messageValidationContext, ()); MOCK_METHOD(Configuration::ServerFactoryContext&, serverFactoryContext, ()); @@ -78,7 +79,6 @@ class MockInstance : public Instance { testing::NiceMock cluster_manager_; Thread::MutexBasicLockable access_log_lock_; testing::NiceMock runtime_loader_; - Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_; testing::NiceMock dispatcher_; testing::NiceMock drain_manager_; testing::NiceMock access_log_manager_; @@ -101,6 +101,8 @@ class MockInstance : public Instance { server_factory_context_; std::shared_ptr> transport_socket_factory_context_; + Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager_; + Regex::GoogleReEngine regex_engine_; }; } // namespace Server diff --git a/test/mocks/server/server_factory_context.cc b/test/mocks/server/server_factory_context.cc index 6de698d5c0a2..fb12fbe4f8b8 100644 --- a/test/mocks/server/server_factory_context.cc +++ b/test/mocks/server/server_factory_context.cc @@ -23,6 +23,7 @@ MockServerFactoryContext::MockServerFactoryContext() ON_CALL(*this, admin()).WillByDefault(Return(OptRef{admin_})); ON_CALL(*this, api()).WillByDefault(ReturnRef(api_)); ON_CALL(*this, timeSource()).WillByDefault(ReturnRef(time_system_)); + ON_CALL(*this, timeSystem()).WillByDefault(ReturnRef(time_system_)); ON_CALL(*this, messageValidationContext()).WillByDefault(ReturnRef(validation_context_)); ON_CALL(*this, messageValidationVisitor()) .WillByDefault(ReturnRef(ProtobufMessage::getStrictValidationVisitor())); diff --git a/test/mocks/server/server_factory_context.h b/test/mocks/server/server_factory_context.h index 7e69dbad447e..d9670dead628 100644 --- a/test/mocks/server/server_factory_context.h +++ b/test/mocks/server/server_factory_context.h @@ -67,7 +67,7 @@ class MockServerFactoryContext : public virtual ServerFactoryContext { MOCK_METHOD(ThreadLocal::Instance&, threadLocal, ()); MOCK_METHOD(OptRef, admin, ()); MOCK_METHOD(TimeSource&, timeSource, ()); - Event::TestTimeSystem& timeSystem() { return time_system_; } + MOCK_METHOD(Event::TestTimeSystem&, timeSystem, ()); MOCK_METHOD(ProtobufMessage::ValidationContext&, messageValidationContext, ()); MOCK_METHOD(ProtobufMessage::ValidationVisitor&, messageValidationVisitor, ()); MOCK_METHOD(Api::Api&, api, ()); @@ -79,6 +79,7 @@ class MockServerFactoryContext : public virtual ServerFactoryContext { MOCK_METHOD(Server::DrainManager&, drainManager, ()); MOCK_METHOD(Init::Manager&, initManager, ()); MOCK_METHOD(ServerLifecycleNotifier&, lifecycleNotifier, ()); + Regex::Engine& regexEngine() override { return regex_engine_; } MOCK_METHOD(StatsConfig&, statsConfig, (), ()); MOCK_METHOD(AccessLog::AccessLogManager&, accessLogManager, (), ()); MOCK_METHOD(OverloadManager&, overloadManager, ()); @@ -107,6 +108,7 @@ class MockServerFactoryContext : public virtual ServerFactoryContext { Router::ContextImpl router_context_; envoy::config::bootstrap::v3::Bootstrap bootstrap_; testing::NiceMock options_; + Regex::GoogleReEngine regex_engine_; }; class MockGenericFactoryContext : public GenericFactoryContext { @@ -155,6 +157,7 @@ class StatelessMockServerFactoryContext : public virtual ServerFactoryContext { MOCK_METHOD(Server::DrainManager&, drainManager, ()); MOCK_METHOD(Init::Manager&, initManager, ()); MOCK_METHOD(ServerLifecycleNotifier&, lifecycleNotifier, ()); + MOCK_METHOD(Regex::Engine&, regexEngine, ()); MOCK_METHOD(StatsConfig&, statsConfig, (), ()); MOCK_METHOD(AccessLog::AccessLogManager&, accessLogManager, (), ()); MOCK_METHOD(OverloadManager&, overloadManager, ()); diff --git a/test/mocks/upstream/health_check_event_logger.h b/test/mocks/upstream/health_check_event_logger.h index 1d6e75819b3b..200512ec9e28 100644 --- a/test/mocks/upstream/health_check_event_logger.h +++ b/test/mocks/upstream/health_check_event_logger.h @@ -22,6 +22,8 @@ class MockHealthCheckEventLogger : public HealthCheckEventLogger { MOCK_METHOD(void, logAddHealthy, (envoy::data::core::v3::HealthCheckerType, const HostDescriptionConstSharedPtr&, bool)); + MOCK_METHOD(void, logSuccessfulHealthCheck, + (envoy::data::core::v3::HealthCheckerType, const HostDescriptionConstSharedPtr&)); MOCK_METHOD(void, logUnhealthy, (envoy::data::core::v3::HealthCheckerType, const HostDescriptionConstSharedPtr&, envoy::data::core::v3::HealthCheckFailureType, bool)); diff --git a/test/per_file_coverage.sh b/test/per_file_coverage.sh index cd8f977a61fb..7f9b5e00b4a6 100755 --- a/test/per_file_coverage.sh +++ b/test/per_file_coverage.sh @@ -3,26 +3,25 @@ # directory:coverage_percent # for existing directories with low coverage. declare -a KNOWN_LOW_COVERAGE=( -"source/common:95.9" # TODO(#32149): increase this once io_uring is tested. +"source/common:96.2" "source/common/api:84.5" # flaky due to posix: be careful adjusting "source/common/api/posix:83.8" # flaky (accept failover non-deterministic): be careful adjusting "source/common/common/posix:88.8" # No easy way to test pthread_create failure. -"source/common/config:95.4" +"source/common/config:95.8" "source/common/crypto:95.5" -"source/common/event:95.0" # Emulated edge events guards don't report LCOV +"source/common/event:95.1" # Emulated edge events guards don't report LCOV "source/common/filesystem/posix:96.2" # FileReadToEndNotReadable fails in some env; createPath can't test all failure branches. -"source/common/http/http2:95.2" -"source/common/io:57.1" # TODO(#32149): CI has stopped executing this code. +"source/common/http/http2:95.6" "source/common/json:94.6" "source/common/matcher:94.6" "source/common/network:94.4" # Flaky, `activateFileEvents`, `startSecureTransport` and `ioctl`, listener_socket do not always report LCOV "source/common/network/dns_resolver:91.4" # A few lines of MacOS code not tested in linux scripts. Tested in MacOS scripts -"source/common/quic:93.3" +"source/common/quic:93.5" "source/common/secret:95.1" "source/common/signal:87.2" # Death tests don't report LCOV "source/common/thread:0.0" # Death tests don't report LCOV "source/common/watchdog:58.6" # Death tests don't report LCOV -"source/exe:90.3" +"source/exe:94.0" # increased by #32346, need coverage for terminate_handler and hot restart failures "source/extensions/clusters/common:91.5" # This can be increased again once `#24903` lands "source/extensions/common:93.0" #flaky: be careful adjusting "source/extensions/common/proxy_protocol:93.8" # Adjusted for security patch @@ -31,43 +30,42 @@ declare -a KNOWN_LOW_COVERAGE=( "source/extensions/common/wasm/ext:92.0" "source/extensions/filters/common/fault:94.5" "source/extensions/filters/common/rbac:90.5" -"source/extensions/filters/http/cache:94.9" +"source/extensions/filters/http/cache:95.0" "source/extensions/filters/http/grpc_json_transcoder:93.8" # TODO(#28232) "source/extensions/filters/http/ip_tagging:88.0" "source/extensions/filters/http/kill_request:91.7" # Death tests don't report LCOV "source/extensions/filters/http/wasm:1.8" "source/extensions/filters/listener/original_src:92.1" "source/extensions/filters/network/common:96.4" -"source/extensions/filters/network/mongo_proxy:96.0" +"source/extensions/filters/network/mongo_proxy:96.1" "source/extensions/filters/network/sni_cluster:88.9" "source/extensions/filters/network/wasm:76.9" "source/extensions/http/cache/simple_http_cache:95.9" "source/extensions/rate_limit_descriptors:95.0" "source/extensions/rate_limit_descriptors/expr:95.0" -"source/extensions/stat_sinks/graphite_statsd:78.6" # Death tests don't report LCOV -"source/extensions/stat_sinks/statsd:80.8" # Death tests don't report LCOV -"source/extensions/tracers:96.1" +"source/extensions/stat_sinks/graphite_statsd:82.8" # Death tests don't report LCOV +"source/extensions/stat_sinks/statsd:85.2" # Death tests don't report LCOV +"source/extensions/tracers:96.4" "source/extensions/tracers/common:74.8" "source/extensions/tracers/common/ot:72.9" -"source/extensions/tracers/opencensus:94.0" +"source/extensions/tracers/opencensus:93.9" "source/extensions/tracers/zipkin:95.8" -"source/extensions/transport_sockets:95.8" +"source/extensions/transport_sockets:97.4" "source/common/tls:94.9" "source/common/tls/cert_validator:94.2" "source/common/tls/private_key:88.9" "source/extensions/wasm_runtime/wamr:0.0" # Not enabled in coverage build "source/extensions/wasm_runtime/wasmtime:0.0" # Not enabled in coverage build -"source/extensions/wasm_runtime/wavm:0.0" # Not enabled in coverage build "source/extensions/watchdog:83.3" # Death tests within extensions "source/extensions/listener_managers:70.5" "source/extensions/listener_managers/validation_listener_manager:70.5" "source/extensions/watchdog/profile_action:83.3" "source/server:91.0" # flaky: be careful adjusting. See https://github.com/envoyproxy/envoy/issues/15239 -"source/server/config_validation:89.2" +"source/server/config_validation:88.9" "source/extensions/health_checkers:96.0" "source/extensions/health_checkers/http:93.9" "source/extensions/health_checkers/grpc:92.0" -"source/extensions/config_subscription/rest:94.3" +"source/extensions/config_subscription/rest:94.7" "source/extensions/matching/input_matchers/cel_matcher:91.3" #Death tests don't report LCOV ) diff --git a/test/server/BUILD b/test/server/BUILD index 9cceee1474dc..eb3641017ec0 100644 --- a/test/server/BUILD +++ b/test/server/BUILD @@ -363,18 +363,6 @@ envoy_cc_test( ], ) -envoy_cc_test( - name = "ssl_context_manager_test", - srcs = ["ssl_context_manager_test.cc"], - deps = [ - "//source/server:ssl_context_manager_lib", - "//test/mocks/ssl:ssl_mocks", - "//test/mocks/stats:stats_mocks", - "//test/test_common:simulated_time_system_lib", - "//test/test_common:utility_lib", - ], -) - envoy_cc_test_library( name = "utility_lib", hdrs = ["utility.h"], diff --git a/test/server/admin/BUILD b/test/server/admin/BUILD index 8c39988f9557..310b5f87b4b8 100644 --- a/test/server/admin/BUILD +++ b/test/server/admin/BUILD @@ -228,6 +228,8 @@ envoy_cc_test( srcs = envoy_select_admin_functionality(["clusters_handler_test.cc"]), deps = [ ":admin_instance_lib", + "//test/common/upstream:utility_lib", + "//test/mocks/event:event_mocks", "@envoy_api//envoy/admin/v3:pkg_cc_proto", ], ) diff --git a/test/server/admin/admin_filter_test.cc b/test/server/admin/admin_filter_test.cc index 9627cfd1ca31..fbf6c93cd72d 100644 --- a/test/server/admin/admin_filter_test.cc +++ b/test/server/admin/admin_filter_test.cc @@ -7,27 +7,28 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +using testing::ByMove; using testing::InSequence; using testing::NiceMock; +using testing::Return; namespace Envoy { namespace Server { class AdminFilterTest : public testing::TestWithParam { public: - AdminFilterTest() : filter_(adminHandlerCallback), request_headers_{{":path", "/"}} { + AdminFilterTest() : filter_(admin_), request_headers_{{":path", "/"}} { + EXPECT_CALL(admin_, makeRequest(_)).WillOnce(Return(ByMove(adminHandlerCallback()))); filter_.setDecoderFilterCallbacks(callbacks_); } - NiceMock server_; + NiceMock admin_; Stats::IsolatedStoreImpl listener_scope_; AdminFilter filter_; NiceMock callbacks_; Http::TestRequestHeaderMapImpl request_headers_; - static Admin::RequestPtr adminHandlerCallback(AdminStream& admin_stream) { - // silence compiler warnings for unused params - UNREFERENCED_PARAMETER(admin_stream); + static Admin::RequestPtr adminHandlerCallback() { return AdminImpl::makeStaticTextRequest("OK\n", Http::Code::OK); } }; diff --git a/test/server/admin/admin_instance.cc b/test/server/admin/admin_instance.cc index 7b18c448e18f..a2b4d2f15d17 100644 --- a/test/server/admin/admin_instance.cc +++ b/test/server/admin/admin_instance.cc @@ -10,7 +10,7 @@ namespace Server { AdminInstanceTest::AdminInstanceTest() : cpu_profile_path_(TestEnvironment::temporaryPath("envoy.prof")), admin_(cpu_profile_path_, server_, false), request_headers_{{":path", "/"}}, - admin_filter_(admin_.createRequestFunction()) { + admin_filter_(admin_) { 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/clusters_handler_test.cc b/test/server/admin/clusters_handler_test.cc index 389e713d1e19..8d5f090ae142 100644 --- a/test/server/admin/clusters_handler_test.cc +++ b/test/server/admin/clusters_handler_test.cc @@ -1,5 +1,7 @@ #include "envoy/admin/v3/clusters.pb.h" +#include "test/common/upstream/utility.h" +#include "test/mocks/event/mocks.h" #include "test/server/admin/admin_instance.h" using testing::Return; @@ -243,5 +245,98 @@ fake_cluster::1.2.3.4:80::local_origin_success_rate::93.2 EXPECT_EQ(expected_text, response2.toString()); } +TEST_P(AdminInstanceTest, TestSetHealthFlag) { + std::shared_ptr cluster{new NiceMock()}; + Event::MockDispatcher dispatcher; + auto host = Upstream::makeTestHost(cluster, "tcp://127.0.0.1:9000", dispatcher.timeSource()); + envoy::admin::v3::HostHealthStatus health_status; + + // FAILED_ACTIVE_HC + host->healthFlagSet(Upstream::Host::HealthFlag::FAILED_ACTIVE_HC); + setHealthFlag(Upstream::Host::HealthFlag::FAILED_ACTIVE_HC, *host, health_status); + EXPECT_TRUE(health_status.failed_active_health_check()); + + host->healthFlagClear(Upstream::Host::HealthFlag::FAILED_ACTIVE_HC); + setHealthFlag(Upstream::Host::HealthFlag::FAILED_ACTIVE_HC, *host, health_status); + EXPECT_FALSE(health_status.failed_active_health_check()); + + // FAILED_OUTLIER_CHECK + host->healthFlagSet(Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK); + setHealthFlag(Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK, *host, health_status); + EXPECT_TRUE(health_status.failed_outlier_check()); + + host->healthFlagClear(Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK); + setHealthFlag(Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK, *host, health_status); + EXPECT_FALSE(health_status.failed_outlier_check()); + + // FAILED_EDS_HEALTH + host->healthFlagSet(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH); + setHealthFlag(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH, *host, health_status); + EXPECT_EQ(health_status.eds_health_status(), envoy::config::core::v3::UNHEALTHY); + + host->healthFlagClear(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH); + + // DEGRADED_EDS_HEALTH + host->healthFlagSet(Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH); + setHealthFlag(Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH, *host, health_status); + EXPECT_EQ(health_status.eds_health_status(), envoy::config::core::v3::DEGRADED); + + // EDS_STATUS_DRAINING + host->healthFlagSet(Upstream::Host::HealthFlag::EDS_STATUS_DRAINING); + setHealthFlag(Upstream::Host::HealthFlag::EDS_STATUS_DRAINING, *host, health_status); + EXPECT_EQ(health_status.eds_health_status(), envoy::config::core::v3::DRAINING); + + health_status.Clear(); + + host->healthFlagClear(Upstream::Host::HealthFlag::EDS_STATUS_DRAINING); + setHealthFlag(Upstream::Host::HealthFlag::EDS_STATUS_DRAINING, *host, health_status); + EXPECT_FALSE(health_status.eds_health_status()); + + // DEGRADED_ACTIVE_HC + host->healthFlagSet(Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC); + setHealthFlag(Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC, *host, health_status); + EXPECT_TRUE(health_status.failed_active_degraded_check()); + + host->healthFlagClear(Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC); + setHealthFlag(Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC, *host, health_status); + EXPECT_FALSE(health_status.failed_active_degraded_check()); + + // PENDING_DYNAMIC_REMOVAL + host->healthFlagSet(Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL); + setHealthFlag(Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL, *host, health_status); + EXPECT_TRUE(health_status.pending_dynamic_removal()); + + host->healthFlagClear(Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL); + setHealthFlag(Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL, *host, health_status); + EXPECT_FALSE(health_status.pending_dynamic_removal()); + + // PENDING_ACTIVE_HC + host->healthFlagSet(Upstream::Host::HealthFlag::PENDING_ACTIVE_HC); + setHealthFlag(Upstream::Host::HealthFlag::PENDING_ACTIVE_HC, *host, health_status); + EXPECT_TRUE(health_status.pending_active_hc()); + + host->healthFlagClear(Upstream::Host::HealthFlag::PENDING_ACTIVE_HC); + setHealthFlag(Upstream::Host::HealthFlag::PENDING_ACTIVE_HC, *host, health_status); + EXPECT_FALSE(health_status.pending_active_hc()); + + // EXCLUDED_VIA_IMMEDIATE_HC_FAIL + host->healthFlagSet(Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL); + setHealthFlag(Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL, *host, health_status); + EXPECT_TRUE(health_status.excluded_via_immediate_hc_fail()); + + host->healthFlagClear(Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL); + setHealthFlag(Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL, *host, health_status); + EXPECT_FALSE(health_status.excluded_via_immediate_hc_fail()); + + // ACTIVE_HC_TIMEOUT + host->healthFlagSet(Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT); + setHealthFlag(Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT, *host, health_status); + EXPECT_TRUE(health_status.active_hc_timeout()); + + host->healthFlagClear(Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT); + setHealthFlag(Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT, *host, health_status); + EXPECT_FALSE(health_status.active_hc_timeout()); +} + } // namespace Server } // namespace Envoy diff --git a/test/server/config_validation/BUILD b/test/server/config_validation/BUILD index 6d838b121c91..7d03991b9d65 100644 --- a/test/server/config_validation/BUILD +++ b/test/server/config_validation/BUILD @@ -17,7 +17,6 @@ envoy_cc_test( "//source/common/stats:stats_lib", "//source/common/tls:context_lib", "//source/server/config_validation:cluster_manager_lib", - "//source/server/config_validation:dns_lib", "//test/mocks/access_log:access_log_mocks", "//test/mocks/event:event_mocks", "//test/mocks/http:http_mocks", @@ -59,6 +58,7 @@ envoy_cc_test( "//source/extensions/filters/network/http_connection_manager:config", "//source/extensions/listener_managers/validation_listener_manager:validation_listener_manager_lib", "//source/extensions/transport_sockets/tls:config", + "//source/server/admin:admin_filter_lib", "//source/server/config_validation:server_lib", "//test/integration:integration_lib", "//test/mocks/network:network_mocks", @@ -77,7 +77,6 @@ envoy_cc_test( "//source/common/event:libevent_lib", "//source/common/stats:isolated_store_lib", "//source/server/config_validation:api_lib", - "//source/server/config_validation:dns_lib", "//test/test_common:environment_lib", "//test/test_common:network_utility_lib", "//test/test_common:test_time_lib", diff --git a/test/server/config_validation/cluster_manager_test.cc b/test/server/config_validation/cluster_manager_test.cc index 02442808b230..12db1d70e553 100644 --- a/test/server/config_validation/cluster_manager_test.cc +++ b/test/server/config_validation/cluster_manager_test.cc @@ -40,7 +40,8 @@ TEST(ValidationClusterManagerTest, MockedMethods) { testing::NiceMock secret_manager; auto dns_resolver = std::make_shared>(); - Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager{api->timeSource()}; + Extensions::TransportSockets::Tls::ContextManagerImpl ssl_context_manager{ + *server.server_factory_context_}; Http::ContextImpl http_context(stats_store.symbolTable()); Quic::QuicStatNames quic_stat_names(stats_store.symbolTable()); diff --git a/test/server/config_validation/server_test.cc b/test/server/config_validation/server_test.cc index 8102b84b01d4..6462503b2cc8 100644 --- a/test/server/config_validation/server_test.cc +++ b/test/server/config_validation/server_test.cc @@ -4,6 +4,7 @@ #include "envoy/server/filter_config.h" #include "source/extensions/listener_managers/validation_listener_manager/validation_listener_manager.h" +#include "source/server/admin/admin_filter.h" #include "source/server/config_validation/server.h" #include "source/server/process_context_impl.h" @@ -207,6 +208,8 @@ TEST_P(ValidationServerTest, DummyMethodsTest) { server.admin()->addListenerToHandler(nullptr); server.admin()->closeSocket(); server.admin()->startHttpListener({}, nullptr, nullptr); + AdminFilter filter(*server.admin()); + EXPECT_TRUE(server.admin()->makeRequest(filter) == nullptr); Network::MockTcpListenerCallbacks listener_callbacks; Network::MockListenerConfig listener_config; diff --git a/test/server/ssl_context_manager_test.cc b/test/server/ssl_context_manager_test.cc deleted file mode 100644 index dbe2bdc8db6e..000000000000 --- a/test/server/ssl_context_manager_test.cc +++ /dev/null @@ -1,39 +0,0 @@ -#include - -#include "source/server/ssl_context_manager.h" - -#include "test/mocks/ssl/mocks.h" -#include "test/mocks/stats/mocks.h" -#include "test/test_common/simulated_time_system.h" -#include "test/test_common/utility.h" - -#include "gtest/gtest.h" - -namespace Envoy { -namespace Server { -namespace { - -TEST(SslContextManager, createStub) { - Event::SimulatedTimeSystem time_system; - Stats::MockStore store; - Stats::Scope& scope(*store.rootScope()); - Ssl::MockClientContextConfig client_config; - Ssl::MockServerContextConfig server_config; - std::vector server_names; - - Ssl::ContextManagerPtr manager = createContextManager("fake_factory_name", time_system); - - // Check we've created a stub, not real manager. - EXPECT_EQ(manager->daysUntilFirstCertExpires().value(), std::numeric_limits::max()); - EXPECT_EQ(manager->secondsUntilFirstOcspResponseExpires(), absl::nullopt); - EXPECT_THROW_WITH_MESSAGE(manager->createSslClientContext(scope, client_config), EnvoyException, - "SSL is not supported in this configuration"); - EXPECT_THROW_WITH_MESSAGE( - manager->createSslServerContext(scope, server_config, server_names, nullptr), EnvoyException, - "SSL is not supported in this configuration"); - EXPECT_NO_THROW(manager->iterateContexts([](const Envoy::Ssl::Context&) -> void {})); -} - -} // namespace -} // namespace Server -} // namespace Envoy diff --git a/test/test_common/network_utility.cc b/test/test_common/network_utility.cc index c873089811fb..40c0abfa0523 100644 --- a/test/test_common/network_utility.cc +++ b/test/test_common/network_utility.cc @@ -226,8 +226,13 @@ Api::IoCallUint64Result readFromSocket(IoHandle& handle, const Address::Instance std::list& data, uint64_t max_rx_datagram_size) { SyncPacketProcessor processor(data, max_rx_datagram_size); + UdpRecvMsgMethod recv_msg_method = UdpRecvMsgMethod::RecvMsg; + if (Api::OsSysCallsSingleton::get().supportsMmsg()) { + recv_msg_method = UdpRecvMsgMethod::RecvMmsg; + } return Network::Utility::readFromSocket(handle, local_address, processor, - MonotonicTime(std::chrono::seconds(0)), false, nullptr); + MonotonicTime(std::chrono::seconds(0)), recv_msg_method, + nullptr); } UdpSyncPeer::UdpSyncPeer(Network::Address::IpVersion version, uint64_t max_rx_datagram_size) diff --git a/test/test_common/test_runtime.h b/test/test_common/test_runtime.h index f270948f3f13..318f7ead190e 100644 --- a/test/test_common/test_runtime.h +++ b/test/test_common/test_runtime.h @@ -43,8 +43,11 @@ class TestScopedRuntime { // The existence of an admin layer is required for mergeValues() to work. config.add_layers()->mutable_admin_layer(); + absl::Status creation_status; Runtime::LoaderPtr runtime_ptr = std::make_unique( - dispatcher_, tls_, config, local_info_, store_, generator_, validation_visitor_, *api_); + dispatcher_, tls_, config, local_info_, store_, generator_, validation_visitor_, *api_, + creation_status); + THROW_IF_NOT_OK(creation_status); // This will ignore values set in test, but just use flag defaults! runtime_ = std::move(runtime_ptr); } @@ -52,7 +55,7 @@ class TestScopedRuntime { Runtime::Loader& loader() { return *runtime_; } void mergeValues(const absl::node_hash_map& values) { - loader().mergeValues(values); + THROW_IF_NOT_OK(loader().mergeValues(values)); } protected: @@ -85,8 +88,11 @@ class TestScopedStaticReloadableFeaturesRuntime { } runtime->mutable_static_layer()->MergeFrom(envoy_layer); + absl::Status creation_status; Runtime::LoaderPtr runtime_ptr = std::make_unique( - dispatcher_, tls_, config, local_info_, store_, generator_, validation_visitor_, *api_); + dispatcher_, tls_, config, local_info_, store_, generator_, validation_visitor_, *api_, + creation_status); + THROW_IF_NOT_OK(creation_status); // This will ignore values set in test, but just use flag defaults! runtime_ = std::move(runtime_ptr); } @@ -107,10 +113,10 @@ class TestDeprecatedV2Api : public TestScopedRuntime { public: TestDeprecatedV2Api() { allowDeprecatedV2(); } void allowDeprecatedV2() { - loader().mergeValues({ + THROW_IF_NOT_OK(loader().mergeValues({ {"envoy.test_only.broken_in_production.enable_deprecated_v2_api", "true"}, {"envoy.features.enable_all_deprecated_features", "true"}, - }); + })); } }; diff --git a/test/test_common/utility.cc b/test/test_common/utility.cc index 7f40c6e3efd3..8b1843373671 100644 --- a/test/test_common/utility.cc +++ b/test/test_common/utility.cc @@ -130,7 +130,7 @@ bool TestUtility::rawSlicesEqual(const Buffer::RawSlice* lhs, const Buffer::RawS } void TestUtility::feedBufferWithRandomCharacters(Buffer::Instance& buffer, uint64_t n_char, - uint64_t seed) { + uint64_t seed, uint64_t n_slice) { const std::string sample = "Neque porro quisquam est qui dolorem ipsum.."; std::mt19937 generate(seed); std::uniform_int_distribution<> distribute(1, sample.length() - 1); @@ -138,7 +138,9 @@ void TestUtility::feedBufferWithRandomCharacters(Buffer::Instance& buffer, uint6 for (uint64_t n = 0; n < n_char; ++n) { str += sample.at(distribute(generate)); } - buffer.add(str); + for (uint64_t n = 0; n < n_slice; ++n) { + buffer.add(str); + } } Stats::CounterSharedPtr TestUtility::findCounter(Stats::Store& store, const std::string& name) { diff --git a/test/test_common/utility.h b/test/test_common/utility.h index c365450e83a4..77e0785ea210 100644 --- a/test/test_common/utility.h +++ b/test/test_common/utility.h @@ -178,9 +178,10 @@ class TestUtility { * @param buffer supplies the buffer to be fed. * @param n_char number of characters that should be added to the supplied buffer. * @param seed seeds pseudo-random number generator (default = 0). + * @param n_slice number of slices (default = 1). */ static void feedBufferWithRandomCharacters(Buffer::Instance& buffer, uint64_t n_char, - uint64_t seed = 0); + uint64_t seed = 0, uint64_t n_slice = 1); /** * Finds a stat in a vector with the given name. diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index 26f39ac48a40..58b2065b1dab 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -173,30 +173,30 @@ aiohttp==3.9.3 \ # envoy-github-abstract # envoy-github-release # google-auth -aioquic==0.9.25 \ - --hash=sha256:0066c0867c7c78aad05cd1f7ebcc1a61b61f3dbc57e65823df26edc0098b6c75 \ - --hash=sha256:18658be4dc06eb1cba9a7bbc80b716b25d3dcbfb89360575de9e2b66c0bee6a7 \ - --hash=sha256:3fad05736e0152e698a3fd18d421bab1a77f379ff085b953e306e53df00d0b9e \ - --hash=sha256:4032a718dea1cc670379dcac15da6ee49440ffaffca565d4505c74f6ac56bb34 \ - --hash=sha256:45241ac5b9b6d4cd976109220dfecddc377d610d4675fffb69869bedcdfa841c \ - --hash=sha256:4ab61fe290e3eed71e2f0ee1dd6916040adc087fc2d4f9dc0dfd037c09a6defc \ - --hash=sha256:4d4641eee9cdd05b9c11088077b376423f8ed148f198d491d72d8189596f1aaf \ - --hash=sha256:4d8b00e2fbf6fee5c9bb5e6d481f1d414f9a3318ae500f673470f6571f2455dd \ - --hash=sha256:70795c78905326d855c2ae524072234aae586c789b81292e272d021e9b0430a3 \ - --hash=sha256:7e4f592f0ad0d57753c7d3851f75041052528b76a7255011294b208c6a9e360b \ - --hash=sha256:7fb11167019d24ec9b5c62e40cef365a4911cd74f5fb23a1283d772e92c8ef7d \ - --hash=sha256:7fd3b0e42e3dab1ca7396fbb6810deb3a0d9324bfc730fb4a7697de08f1b4dc3 \ - --hash=sha256:827652aa7b52ac069fc1fc9b1d8308f6c19adcfb86cd7f563c0ce5be8b416ce9 \ - --hash=sha256:9852358f7bbb52c56e1151fa054505a3880f1d2cffef8a83a1bbb653a2faaab0 \ - --hash=sha256:9a416579f78177ea3590fdb16933f6168f425f9109fcad00e09b3ac3f991d0bb \ - --hash=sha256:9a7a69f4396540e38caf2cf3f69f42844a9130e3dac2590fd8713d5dc77b3a1f \ - --hash=sha256:bac804af55b230acaebefc33eb04356df1844cc77da5f4a7f860cbe41052553d \ - --hash=sha256:bb187080955b026da4d3c9ea5fa1be32c4413e27bd8e458f66d94bf9a2b42e72 \ - --hash=sha256:cbd60cec8cc8e134dc1e2ebb79047827298b84d3b5ff011c36ee101110da63b8 \ - --hash=sha256:d8637030a95f68454cdaa58c0a7d0cbee5eca1e694a5cb8d6c179846f7d4d86c \ - --hash=sha256:da07befc3fa186621a6ff34695d9bf51c803e49f6c02fec53f50c86b74cdd55f \ - --hash=sha256:dd1cda94f7c5e1a4bb75a2f195c0f20839b54b014e3d81eeab47d6a625c7a761 \ - --hash=sha256:f73db85db29e35260f85961840d5089c3da3e404c6b7dfdaadbd9842a53c10a1 +aioquic==1.0.0 \ + --hash=sha256:0697ee1c293296e9673ed03888ce5eed978d0070edbdb7b7ec53ea84162f64ed \ + --hash=sha256:188feedbc4eb7062cfda5095b372261dffa73e08e570fd6d72c65d7760792266 \ + --hash=sha256:195ea3b15d6d2874afea641b2b0ba3eb3b598810ccff1646fa2bb0f1a2f79fde \ + --hash=sha256:27a372943b95b4ecf7dd5091640cf55e685a27fba55f5b2d14c6f0efb0a8da6a \ + --hash=sha256:2848b4bd3d7e59baa4aa164af325fad4aa14fb5071c8082419a9881aa78cfe42 \ + --hash=sha256:45076b6fa4e4e7bf7e812effa68427746626b1d6d8d894d41b60c3ef2c7f79d2 \ + --hash=sha256:47f33359929bf7255afe63ceaeed120fa8ba25cfa78b4a85dff1afb7bb4519d0 \ + --hash=sha256:49693b883ffaba31b47a59b3d3f22486299afbc7162711271e225aae18bc703b \ + --hash=sha256:67d169567d5deb6b6ade67b08520f713b20406724e32102fc257bcb153e4bc2f \ + --hash=sha256:741949d0feaded5ef0ba75b0b11e99f8d9e11ebe2a6516336a483bd4f7e6dd95 \ + --hash=sha256:78a0492d1d122d8aa399a0168b37aa2390500c9719117750b213fd16187475da \ + --hash=sha256:7bcaf62b1727eb4187f8167e0827224b047eda4010d3d6c2f451edf5beb9f8e1 \ + --hash=sha256:8656a70d352bdc580f8433ffd32566d1868e5be8ff4abade6a2fd858a3dcc0c0 \ + --hash=sha256:8ea030d2edf6f8bf37c57b3c25929b405af4d5f0f71ac122ca3f60a5bbebe9b1 \ + --hash=sha256:948d1f524057f1675c1cb5f77aaa29f32073bfb77ede810ecbbb798cd2f5b551 \ + --hash=sha256:9509fefef63abd9956832fb52f2f2ec8bf90a8767126d19c868409dc6ce5f1a5 \ + --hash=sha256:96798b8186f192155246c1d9d2662bbe1768aa11fb707418a6887b19f5ef8621 \ + --hash=sha256:9cc4a5319729ea19c407b5f301cc1692c3c887bbbd5d9fc715445843129fa689 \ + --hash=sha256:abd3c01cf1079431d9b4a1740cb92e5d57d24cd7a5bed8d6b4af0ecf21176782 \ + --hash=sha256:bfc4cbf8a40952b6d3916b21101b2d36f3be96ca670f28ffe6315f1947958325 \ + --hash=sha256:c4cf8069e6ef3065a9888a81a0f8fa2ac21dc4f88833c910567c16fc3846f1f6 \ + --hash=sha256:d3f3a89f92fb23df4999c8216b4fed8a7e08a760dad5fcc9cecae436d8d90343 \ + --hash=sha256:ed31c2b5afa98c5b6cafa4f36149deaf1dff6c5a69701eadd27167415f9f1660 # via -r requirements.in aiosignal==1.3.1 \ --hash=sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc \ @@ -700,9 +700,7 @@ google-apitools==0.5.32 \ google-auth[aiohttp]==2.27.0 \ --hash=sha256:8e4bad367015430ff253fe49d500fdc3396c1a434db5740828c728e45bcce245 \ --hash=sha256:e863a56ccc2d8efa83df7a80272601e43487fa9a728a376205c86c26aaefa821 - # via - # google-auth - # gsutil + # via gsutil google-reauth==0.1.1 \ --hash=sha256:cb39074488d74c8853074dde47368bbf8f739d4a4338b89aab696c895b6d8368 \ --hash=sha256:f9f6852a55c2c5453d581cd01f3d1278e86147c03d008409800390a834235892 @@ -724,9 +722,9 @@ humanfriendly==10.0 \ --hash=sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477 \ --hash=sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc # via coloredlogs -icalendar==5.0.11 \ - --hash=sha256:7a298bb864526589d0de81f4b736eeb6ff9e539fefb405f7977aa5c1e201ca00 \ - --hash=sha256:81864971ac43a1b7d0a555dc1b667836ce59fc719a7f845a96f2f03205fb83b9 +icalendar==5.0.12 \ + --hash=sha256:73f9be68477722c98320621400943705dcfdbbc6c2b565253f72d3f87e514db8 \ + --hash=sha256:d873bb859df9c6d0e597b16d247436e0f83f7ac1b90a06429b8393fe8afeba40 # via -r requirements.in idna==3.6 \ --hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \ @@ -1063,7 +1061,6 @@ pyjwt[crypto]==2.8.0 \ # via # gidgethub # pygithub - # pyjwt pylsqpack==0.3.18 \ --hash=sha256:005ddce84bdcbf5c3cf99f764504208e1aa0a91a8331bf47108f2708f2a315e6 \ --hash=sha256:06e1bbe47514b83cd03158e5558ef8cc44f578169c1820098be9f3cc4137f16a \ @@ -1607,7 +1604,7 @@ zstandard==0.22.0 \ # via envoy-base-utils # The following packages are considered to be unsafe in a requirements file: -setuptools==69.1.1 \ - --hash=sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56 \ - --hash=sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8 +setuptools==69.2.0 \ + --hash=sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e \ + --hash=sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c # via -r requirements.in diff --git a/tools/code_format/config.yaml b/tools/code_format/config.yaml index 7ba8ba23cbd0..9e8952000b44 100644 --- a/tools/code_format/config.yaml +++ b/tools/code_format/config.yaml @@ -139,11 +139,9 @@ paths: - source/common/router/config_impl.cc - source/common/router/scoped_config_impl.cc - source/common/router/header_parser.cc - - source/common/filesystem/inotify/watcher_impl.cc - source/common/filesystem/posix/directory_iterator_impl.cc - source/common/filesystem/kqueue/watcher_impl.cc - source/common/filesystem/win32/directory_iterator_impl.cc - - source/common/filesystem/win32/watcher_impl.cc - source/common/common/utility.cc - source/common/common/regex.cc - source/common/common/matchers.cc @@ -152,6 +150,7 @@ paths: - source/server/overload_manager_impl.cc - source/server/config_validation/server.cc - source/server/admin/html/active_stats.js + - source/server/admin/runtime_handler.cc - source/server/server.cc - source/server/hot_restarting_base.cc - source/server/hot_restart_impl.cc @@ -171,6 +170,7 @@ paths: - source/common/local_reply/local_reply.cc - source/common/tls/context_impl.cc - source/common/tls/context_config_impl.cc + - source/common/config/watched_directory.cc # Only one C++ file should instantiate grpc_init grpc_init: diff --git a/tools/proto_format/format_api.py b/tools/proto_format/format_api.py index 8306a1f51515..d096f0677376 100644 --- a/tools/proto_format/format_api.py +++ b/tools/proto_format/format_api.py @@ -32,6 +32,7 @@ 'envoy.extensions.filters.network.client_ssl_auth.v3', 'envoy.extensions.filters.network.generic_proxy.action.v3', 'envoy.extensions.filters.network.generic_proxy.codecs.dubbo.v3', + 'envoy.extensions.filters.network.generic_proxy.codecs.http1.v3', 'envoy.extensions.filters.network.generic_proxy.codecs.kafka.v3', 'envoy.extensions.filters.network.generic_proxy.matcher.v3', 'envoy.extensions.filters.network.generic_proxy.router.v3', diff --git a/tools/repo/notify.py b/tools/repo/notify.py index 689e8dde6e7c..ec6495494446 100644 --- a/tools/repo/notify.py +++ b/tools/repo/notify.py @@ -34,24 +34,24 @@ SLACK_EXPORT_URL = "https://api.slack.com/apps/A023NPQQ33K/oauth?" OPSGENIE_TO_SLACK = { - 'Adi': 'Adi Peleg', - 'Alyssa': 'Alyssa Wilk', - 'Greg': 'Greg Greenway', - 'Harvey': 'htuch', - 'Joshua': 'jmarantz', - 'Kevin': 'kbaichoo', - 'Keith': 'Keith Smiley', - 'kuat': 'kuat', - 'Lizan': 'Lizan Zhou', - 'Matt': 'mklein', - 'Kateryna': 'nexdolik', - 'phlax': 'phlax', - 'Raven': 'ravenblackx', - 'Ryan': 'Ryan Hamilton', - 'Hejie': 'soulxu', - 'Baiping': 'wbpcode', - 'Yan': 'Yan Avlasov', - 'Stephan': 'stephan', + 'Adi': 'UT17EMMTP', + 'Alyssa': 'U78RP48V9', + 'Greg': 'U78MBV869', + 'Harvey': 'U78E7055Z', + 'Joshua': 'U80HPLBPG', + 'Kevin': 'U016ZPU8KBK', + 'Keith': 'UGS5P90CF', + 'kuat': 'U7KTRAA8M', + 'Lizan': 'U79E51EQ6', + 'Matt': 'U5CALEVSL', + 'Kateryna': 'UDYUWRL13', + 'phlax': 'U017PLM0GNQ', + 'Raven': 'U02MJHFEX35', + 'Ryan': 'U01SW3JC8GP', + 'Hejie': 'U01GNQ3B8AY', + 'Baiping': 'U017KF5C0Q6', + 'Yan': 'UJHLR5KFS', + 'Stephan': 'U78J72Q82', } MAINTAINERS = { @@ -287,7 +287,7 @@ async def post_to_oncall(self): await self.send_message(channel='#envoy-maintainer-oncall', text=(f"{oncall}")) await self.send_message(channel='#general', text=(f"{oncall}")) await self.send_message( - channel='#envoy-maintainer-oncall', text=(f"Oncall now @", oncall_handle)) + channel='#envoy-maintainer-oncall', text=(f"Oncall now <@{oncall_handle}>")) await self.send_message( channel='#envoy-maintainer-oncall', text=(f"*'Unassigned' PRs* (PRs with no maintainer assigned)\n{unassigned}")) @@ -300,6 +300,11 @@ async def post_to_oncall(self): text=( f"*{num_issues} Untriaged Issues* (please tag and cc area experts)\n<{ISSUE_LINK}|{ISSUE_LINK}>" )) + await self.send_message( + channel='#envoy-ci', + text=( + f"<@{oncall_handle}> please triage flakes per " + )) except SlackApiError as e: self.log.error(f"Unexpected error {e.response['error']}") diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index 1a1eae3faa56..2b4096b313e5 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -354,6 +354,7 @@ QUIC QoS qat qatzip +qatzstd RAII RANLUX RBAC @@ -470,7 +471,6 @@ VM VPN WAITFORONE WASM -WAVM WIP WKT WRONGPASS