diff --git a/.bazelignore b/.bazelignore index 53d1c72fcc2b..eb18a5457043 100644 --- a/.bazelignore +++ b/.bazelignore @@ -1,6 +1,7 @@ # only directories can be ignored, and no globbing api examples/grpc-bridge/script +mobile tools/clang_tools tools/dev/src .project diff --git a/.bazelrc b/.bazelrc index d59e60868582..5a60d3852599 100644 --- a/.bazelrc +++ b/.bazelrc @@ -295,7 +295,7 @@ build:remote-clang-cl --config=rbe-toolchain-clang-cl # 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:b0ff77ae3f25b0bf595f9b8bba46b489723ab446 +build:docker-sandbox --experimental_docker_image=envoyproxy/envoy-build-ubuntu:7304f974de2724617b7492ccb4c9c58cd420353a build:docker-sandbox --spawn_strategy=docker build:docker-sandbox --strategy=Javac=docker build:docker-sandbox --strategy=Closure=docker diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 26a7dd543c6e..21d2cdd26354 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/envoy-ci/envoy-build:b0ff77ae3f25b0bf595f9b8bba46b489723ab446 +FROM gcr.io/envoy-ci/envoy-build:7304f974de2724617b7492ccb4c9c58cd420353a ARG USERNAME=vscode ARG USER_UID=501 diff --git a/.github/dependabot.yml b/.github/dependabot.yml index cdbb72f769e9..1a2d98c33a1a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -51,11 +51,6 @@ updates: schedule: interval: "daily" -- package-ecosystem: "pip" - directory: "/tools/dependency" - schedule: - interval: "daily" - - package-ecosystem: "docker" directory: "/.devcontainer" schedule: diff --git a/.github/workflows/android_build.yml b/.github/workflows/android_build.yml index 5b381a4df61c..851654f18c20 100644 --- a/.github/workflows/android_build.yml +++ b/.github/workflows/android_build.yml @@ -6,6 +6,10 @@ on: - main pull_request: +concurrency: + group: ${{ github.head_ref-github.workflow || github.run_id }} + cancel-in-progress: true + jobs: androidbuild: name: android_build diff --git a/.github/workflows/android_tests.yml b/.github/workflows/android_tests.yml index 3b2461ac5ab3..2d6e4a2e71e7 100644 --- a/.github/workflows/android_tests.yml +++ b/.github/workflows/android_tests.yml @@ -6,6 +6,10 @@ on: - main pull_request: +concurrency: + group: ${{ github.head_ref-github.workflow || github.run_id }} + cancel-in-progress: true + jobs: kotlintestsmac: # revert to //test/kotlin/... once fixed @@ -80,7 +84,7 @@ jobs: runs-on: ubuntu-20.04 timeout-minutes: 90 container: - image: envoyproxy/envoy-build-ubuntu:b0ff77ae3f25b0bf595f9b8bba46b489723ab446 + image: envoyproxy/envoy-build-ubuntu:7304f974de2724617b7492ccb4c9c58cd420353a env: CC: /opt/llvm/bin/clang CXX: /opt/llvm/bin/clang++ diff --git a/.github/workflows/asan.yml b/.github/workflows/asan.yml index 271e331119cc..8f680e9ea98a 100644 --- a/.github/workflows/asan.yml +++ b/.github/workflows/asan.yml @@ -6,13 +6,17 @@ on: - main pull_request: +concurrency: + group: ${{ github.head_ref-github.workflow || github.run_id }} + cancel-in-progress: true + jobs: asan: name: asan runs-on: ubuntu-20.04 timeout-minutes: 180 container: - image: envoyproxy/envoy-build-ubuntu:b0ff77ae3f25b0bf595f9b8bba46b489723ab446 + image: envoyproxy/envoy-build-ubuntu:7304f974de2724617b7492ccb4c9c58cd420353a env: CC: /opt/llvm/bin/clang CXX: /opt/llvm/bin/clang++ diff --git a/.github/workflows/cc_tests.yml b/.github/workflows/cc_tests.yml index fed0c2485b52..6ef2e9993dbb 100644 --- a/.github/workflows/cc_tests.yml +++ b/.github/workflows/cc_tests.yml @@ -6,13 +6,17 @@ on: - main pull_request: +concurrency: + group: ${{ github.head_ref-github.workflow || github.run_id }} + cancel-in-progress: true + jobs: cctests: name: cc_tests runs-on: ubuntu-20.04 timeout-minutes: 120 container: - image: envoyproxy/envoy-build-ubuntu:b0ff77ae3f25b0bf595f9b8bba46b489723ab446 + image: envoyproxy/envoy-build-ubuntu:7304f974de2724617b7492ccb4c9c58cd420353a steps: - uses: actions/checkout@v1 - name: Add safe directory diff --git a/.github/workflows/codeql-daily.yml b/.github/workflows/codeql-daily.yml index 02b10bcc90ea..ee2ca5a48b21 100644 --- a/.github/workflows/codeql-daily.yml +++ b/.github/workflows/codeql-daily.yml @@ -2,6 +2,10 @@ on: schedule: - cron: '0 12 * * 4' +concurrency: + group: ${{ github.head_ref-github.workflow || github.run_id }} + cancel-in-progress: true + jobs: CodeQL-Build: @@ -28,7 +32,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@515828d97454b8354517688ddc5b48402b723750 + uses: github/codeql-action/init@3ebbd71c74ef574dbc558c82f70e52732c8b44fe # Override language selection by uncommenting this and choosing your languages with: languages: cpp @@ -59,4 +63,4 @@ jobs: git clean -xdf - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@515828d97454b8354517688ddc5b48402b723750 + uses: github/codeql-action/analyze@3ebbd71c74ef574dbc558c82f70e52732c8b44fe diff --git a/.github/workflows/codeql-push.yml b/.github/workflows/codeql-push.yml index 94f56b4e53a8..ea3443996caf 100644 --- a/.github/workflows/codeql-push.yml +++ b/.github/workflows/codeql-push.yml @@ -8,6 +8,10 @@ on: - 'dependabot/**' pull_request: +concurrency: + group: ${{ github.head_ref-github.workflow || github.run_id }} + cancel-in-progress: true + jobs: CodeQL-Build: @@ -41,7 +45,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@515828d97454b8354517688ddc5b48402b723750 + uses: github/codeql-action/init@3ebbd71c74ef574dbc558c82f70e52732c8b44fe # Override language selection by uncommenting this and choosing your languages with: languages: cpp @@ -74,4 +78,4 @@ jobs: - name: Perform CodeQL Analysis if: env.BUILD_TARGETS != '' - uses: github/codeql-action/analyze@515828d97454b8354517688ddc5b48402b723750 + uses: github/codeql-action/analyze@3ebbd71c74ef574dbc558c82f70e52732c8b44fe diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index 425b33fa475a..95ec2b2c7210 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -6,26 +6,29 @@ on: - main pull_request: +concurrency: + group: ${{ github.head_ref-github.workflow || github.run_id }} + cancel-in-progress: true + jobs: unittests: name: unit_tests - runs-on: macos-12 + runs-on: ubuntu-20.04 timeout-minutes: 120 + container: + image: envoyproxy/envoy-build-ubuntu:b0ff77ae3f25b0bf595f9b8bba46b489723ab446 steps: - uses: actions/checkout@v1 - - id: should_run - name: 'Check whether to run' - run: ./mobile/tools/should_run_ci.sh - - name: 'Install dependencies' - if: steps.should_run.outputs.run_ci_job == 'true' - run: cd mobile && ./ci/mac_ci_setup.sh + - name: Add safe directory + run: git config --global --add safe.directory /__w/envoy/envoy - name: 'Run tests' - if: steps.should_run.outputs.run_ci_job == 'true' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | cd mobile && ./bazelw test \ + --action_env=LD_LIBRARY_PATH \ + --test_env=ENVOY_IP_TEST_VERSIONS=v4only \ --test_output=all \ - $([ -z $GITHUB_TOKEN ] || echo "--config=remote-ci-macos") \ + $([ -z $GITHUB_TOKEN ] || echo "--config=remote-ci-linux") \ --remote_header="Authorization=Bearer $GITHUB_TOKEN" \ //test/common/... diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index b8985af4b80a..0a594f70b4a9 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -6,6 +6,10 @@ on: - main pull_request: +concurrency: + group: ${{ github.head_ref-github.workflow || github.run_id }} + cancel-in-progress: true + jobs: coverage: name: coverage @@ -15,7 +19,7 @@ jobs: run: shell: bash container: - image: envoyproxy/envoy-build-ubuntu:b0ff77ae3f25b0bf595f9b8bba46b489723ab446 + image: envoyproxy/envoy-build-ubuntu:7304f974de2724617b7492ccb4c9c58cd420353a steps: - uses: actions/checkout@v1 - name: Add safe directory diff --git a/.github/workflows/depsreview.yml b/.github/workflows/depsreview.yml index 55200a322a05..c06344c40d2a 100644 --- a/.github/workflows/depsreview.yml +++ b/.github/workflows/depsreview.yml @@ -1,6 +1,10 @@ name: 'Dependency Review' on: [pull_request] +concurrency: + group: ${{ github.head_ref-github.workflow || github.run_id }} + cancel-in-progress: true + jobs: dependency-review: runs-on: ubuntu-20.04 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 83bdf57e000c..d615da2c0947 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -6,12 +6,16 @@ on: - main pull_request: +concurrency: + group: ${{ github.head_ref-github.workflow || github.run_id }} + cancel-in-progress: true + jobs: docs: runs-on: ubuntu-20.04 timeout-minutes: 20 container: - image: envoyproxy/envoy-build-ubuntu:b0ff77ae3f25b0bf595f9b8bba46b489723ab446 + image: envoyproxy/envoy-build-ubuntu:7304f974de2724617b7492ccb4c9c58cd420353a steps: - uses: actions/checkout@v3 - name: Add safe directory diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 42eeafc28815..6d7b0055a764 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -6,13 +6,17 @@ on: - main pull_request: +concurrency: + group: ${{ github.head_ref-github.workflow || github.run_id }} + cancel-in-progress: true + jobs: formatall: name: format_all runs-on: ubuntu-20.04 timeout-minutes: 45 container: - image: envoyproxy/envoy-build-ubuntu:b0ff77ae3f25b0bf595f9b8bba46b489723ab446 + image: envoyproxy/envoy-build-ubuntu:7304f974de2724617b7492ccb4c9c58cd420353a env: CLANG_FORMAT: /opt/llvm/bin/clang-format BUILDIFIER_BIN: /usr/local/bin/buildifier diff --git a/.github/workflows/ios_build.yml b/.github/workflows/ios_build.yml index 9ec10dc5d0a1..6b9a39b0e09e 100644 --- a/.github/workflows/ios_build.yml +++ b/.github/workflows/ios_build.yml @@ -6,6 +6,10 @@ on: - main pull_request: +concurrency: + group: ${{ github.head_ref-github.workflow || github.run_id }} + cancel-in-progress: true + jobs: iosbuild: name: ios_build diff --git a/.github/workflows/ios_tests.yml b/.github/workflows/ios_tests.yml index d95e62750457..2a93a2ba6acb 100644 --- a/.github/workflows/ios_tests.yml +++ b/.github/workflows/ios_tests.yml @@ -6,6 +6,10 @@ on: - main pull_request: +concurrency: + group: ${{ github.head_ref-github.workflow || github.run_id }} + cancel-in-progress: true + jobs: swifttests: name: swift_tests diff --git a/.github/workflows/perf.yml b/.github/workflows/perf.yml index 0f95d33f344d..491d5ea642ea 100644 --- a/.github/workflows/perf.yml +++ b/.github/workflows/perf.yml @@ -6,13 +6,17 @@ on: - main pull_request: +concurrency: + group: ${{ github.head_ref-github.workflow || github.run_id }} + cancel-in-progress: true + jobs: sizecurrent: name: size_current runs-on: ubuntu-20.04 timeout-minutes: 120 container: - image: envoyproxy/envoy-build-ubuntu:b0ff77ae3f25b0bf595f9b8bba46b489723ab446 + image: envoyproxy/envoy-build-ubuntu:7304f974de2724617b7492ccb4c9c58cd420353a env: CC: /opt/llvm/bin/clang CXX: /opt/llvm/bin/clang++ @@ -26,7 +30,7 @@ jobs: run: | cd mobile && ./bazelw build \ --config=sizeopt \ - --config=remote-ci-linux-clang \ + --config=release-common \ --remote_header="Authorization=Bearer $GITHUB_TOKEN" \ //test/performance:test_binary_size - uses: actions/upload-artifact@v3 @@ -38,7 +42,7 @@ jobs: runs-on: ubuntu-20.04 timeout-minutes: 90 container: - image: envoyproxy/envoy-build-ubuntu:b0ff77ae3f25b0bf595f9b8bba46b489723ab446 + image: envoyproxy/envoy-build-ubuntu:7304f974de2724617b7492ccb4c9c58cd420353a env: CC: /opt/llvm/bin/clang CXX: /opt/llvm/bin/clang++ @@ -54,7 +58,7 @@ jobs: git checkout main && git pull origin main cd mobile && ./bazelw build \ --config=sizeopt \ - --config=remote-ci-linux-clang \ + --config=release-common \ --remote_header="Authorization=Bearer $GITHUB_TOKEN" \ //test/performance:test_binary_size - uses: actions/upload-artifact@v3 @@ -67,7 +71,7 @@ jobs: runs-on: ubuntu-20.04 timeout-minutes: 30 container: - image: envoyproxy/envoy-build-ubuntu:b0ff77ae3f25b0bf595f9b8bba46b489723ab446 + image: envoyproxy/envoy-build-ubuntu:7304f974de2724617b7492ccb4c9c58cd420353a steps: - uses: actions/checkout@v1 - uses: actions/download-artifact@v3 @@ -86,4 +90,4 @@ jobs: zip -9 dist/main.zip dist/main.stripped zip -9 dist/current.zip dist/current.stripped - name: 'Test size regression' - run: cd mobile && ./ci/test_size_regression.sh dist/main.zip dist/current.zip + run: cd mobile && ./ci/test_size_regression.sh ../dist/main.zip ../dist/current.zip diff --git a/.github/workflows/python_tests.yml b/.github/workflows/python_tests.yml index 64e990a8b50b..9f62ab3434fc 100644 --- a/.github/workflows/python_tests.yml +++ b/.github/workflows/python_tests.yml @@ -6,13 +6,17 @@ on: - main pull_request: +concurrency: + group: ${{ github.head_ref-github.workflow || github.run_id }} + cancel-in-progress: true + jobs: pythontests: name: python_tests runs-on: ubuntu-20.04 timeout-minutes: 90 container: - image: envoyproxy/envoy-build-ubuntu:b0ff77ae3f25b0bf595f9b8bba46b489723ab446 + image: envoyproxy/envoy-build-ubuntu:7304f974de2724617b7492ccb4c9c58cd420353a steps: - uses: actions/checkout@v1 - name: Add safe directory diff --git a/.github/workflows/release_validation.yml b/.github/workflows/release_validation.yml index d9c7847527bf..6be515f81fea 100644 --- a/.github/workflows/release_validation.yml +++ b/.github/workflows/release_validation.yml @@ -6,6 +6,10 @@ on: - main pull_request: +concurrency: + group: ${{ github.head_ref-github.workflow || github.run_id }} + cancel-in-progress: true + jobs: validate_swiftpm_example: name: validate_swiftpm_example diff --git a/.github/workflows/tsan.yml b/.github/workflows/tsan.yml index 7509fb7324bd..a1624d41fffa 100644 --- a/.github/workflows/tsan.yml +++ b/.github/workflows/tsan.yml @@ -6,13 +6,17 @@ on: - main pull_request: +concurrency: + group: ${{ github.head_ref-github.workflow || github.run_id }} + cancel-in-progress: true + jobs: tsan: name: tsan runs-on: ubuntu-20.04 timeout-minutes: 90 container: - image: envoyproxy/envoy-build-ubuntu:b0ff77ae3f25b0bf595f9b8bba46b489723ab446 + image: envoyproxy/envoy-build-ubuntu:7304f974de2724617b7492ccb4c9c58cd420353a env: CC: /opt/llvm/bin/clang CXX: /opt/llvm/bin/clang++ diff --git a/CODEOWNERS b/CODEOWNERS index 215e0ea4e158..c9e3c9151c3e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -319,6 +319,7 @@ extensions/filters/http/oauth2 @derekargueta @snowp /contrib/client_ssl_auth/ @UNOWNED @UNOWNED /contrib/common/sqlutils/ @cpakulski @cpakulski /contrib/dynamo/ @UNOWNED @UNOWNED +/contrib/golang/ @doujiang24 @wangfakang /contrib/squash/ @yuval-k @alyssawilk /contrib/kafka/ @mattklein123 @adamkotwasinski /contrib/rocketmq_proxy/ @aaron-ai @lizhanhui @lizan diff --git a/OWNERS.md b/OWNERS.md index e7c4598d0c04..ccd9b2a9f1c6 100644 --- a/OWNERS.md +++ b/OWNERS.md @@ -35,8 +35,6 @@ routing PRs, questions, etc. to the right place. * Joshua Marantz ([jmarantz](https://github.com/jmarantz)) (jmarantz@google.com) * Stats, abseil, scalability, and performance. -* William A Rowe Jr ([wrowe](https://github.com/wrowe)) (wrowe@vmware.com) - * Windows port and CI build, `bazel/foreign_cc` build and dependencies liaison. * Antonio Vicente ([antoniovicente](https://github.com/antoniovicente)) (avd@google.com) * Event management, security, performance, data plane. * Adi Peleg ([adisuissa](https://github.com/adisuissa)) (adip@google.com) @@ -75,7 +73,6 @@ without further review. * All senior maintainers * Tony Allen ([tonya11en](https://github.com/tonya11en)) (tony@allen.gg) * Yan Avlasov ([yanavlasov](https://github.com/yanavlasov)) (yavlasov@google.com) -* William A Rowe Jr ([wrowe](https://github.com/wrowe)) (wrowe@vmware.com) * Otto van der Schaaf ([oschaaf](https://github.com/oschaaf)) (oschaaf@redhat.com) * Tim Walsh ([twghu](https://github.com/twghu)) (twalsh@redhat.com) * Ryan Northey ([phlax](https://github.com/phlax)) (ryan@synca.io) @@ -113,6 +110,7 @@ contributors to envoy-setec and relevant Slack channels from: * Michael Rebello ([rebello95](https://github.com/rebello95)) (mrebello@lyft.com) * Alan Chiu ([buildbreaker](https://github.com/buildbreaker)) (achiu@lyft.com) * Charles Le Borgne ([carloseltuerto](https://github.com/carloseltuerto)) (cleborgne@google.com) +* William A Rowe Jr ([wrowe](https://github.com/wrowe)) (wrowe@rowe-clan.net) # Friends of Envoy diff --git a/api/contrib/envoy/extensions/filters/http/golang/v3alpha/golang.proto b/api/contrib/envoy/extensions/filters/http/golang/v3alpha/golang.proto index db53f0b7c296..01aa4cecbd1e 100644 --- a/api/contrib/envoy/extensions/filters/http/golang/v3alpha/golang.proto +++ b/api/contrib/envoy/extensions/filters/http/golang/v3alpha/golang.proto @@ -16,170 +16,67 @@ option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/fil option (udpa.annotations.file_status).package_version_status = ACTIVE; option (xds.annotations.v3.file_status).work_in_progress = true; -// [#protodoc-title: golang extension filter] -// Golang :ref:`configuration overview `. -// [#extension: envoy.filters.http.golang] -// -// In the below example, we configured the go plugin 'auth' and 'limit' dynamic libraries into -// Envoy, which can avoid rebuilding Envoy. -// -// * Develop go-plugin -// -// We can implement the interface of ``StreamFilter `` -// API by the GO language to achieve the effects of Envoy native filter. -// -// The filter based on the APIs implementation ``StreamFilter `` -// For details, take a look at the :repo:`/contrib/golang/filters/http/test/test_data/echo`. -// -// Then put the GO plugin source code into the ${OUTPUT}/src/ directory with the name of the plugin -// for GO plugin builds. -// The following examples implement limit and auth GO plugins. -// -// .. code-block:: bash -// -// $ tree /home/admin/envoy/go-plugins/src/ -// |--auth -// | |--config.go -// | |--filter.go -// ---limit -// |--config.go -// |--filter.go -// -// * Build go-plugin -// -// Build the Go plugin so by `go_plugin_generate.sh` script, below example the `liblimit.so` and -// `libauth.so` will be generated in the `/home/admin/envoy/go-plugins/` directory. -// -// .. code-block:: bash -// -// #!/bin/bash -// if [ $# != 2 ]; then -// echo "need input the go plugin name" -// exit 1 -// fi -// -// PLUGINNAME=$1 -// OUTPUT=/home/admin/envoy/go-plugins/ -// PLUGINSRCDIR=${OUTPUT}/src/${PLUGINNAME} -// go build --buildmode=c-shared -v -o $OUTPUT/lib${PLUGINNAME}.so $PLUGINSRCDIR -// -// .. code-block:: bash -// -// $ go_plugin_generate.sh limit -// $ go_plugin_generate.sh auth -// -// * Configure go-plugin -// -// Use the http filter of :ref: `golang ` to specify -// :ref: `library` in ingress and egress to enable the plugin. -// -// Example: +// [#protodoc-title: Golang] // -// .. code-block:: yaml -// -// static_resources: -// listeners: -// - name: ingress -// address: -// socket_address: -// protocol: TCP -// address: 0.0.0.0 -// port_value: 8080 -// filter_chains: -// - filters: -// - name: envoy.filters.network.http_connection_manager -// ...... -// http_filters: -// - name: envoy.filters.http.golang -// typed_config: -// "@type": type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.Config -// library_id: limit-id -// library_path: "/home/admin/envoy/go-plugins/liblimit.so" -// plugine_name: limit -// plugin_config: -// "@type": type.googleapis.com/envoy.extensions.filters.http.golang.plugins.limit.v3.Config -// xxx1: xx1 -// xxx2: xx2 -// - name: envoy.filters.http.header_to_metadata -// - name: envoy.filters.http.golang -// typed_config: -// "@type": type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.Config -// library_id: auth-id -// library_path: "/home/admin/envoy/go-plugins/libauth.so" -// plugine_name: auth -// plugin_config: -// "@type": type.googleapis.com/envoy.extensions.filters.http.golang.plugins.auth.v3.Config -// xxx1: xx1 -// xxx2: xx2 -// - name: envoy.filters.http.router -// - name: egress -// address: -// socket_address: -// protocol: TCP -// address: 0.0.0.0 -// port_value: 8081 -// filter_chains: -// - filters: -// - name: envoy.filters.network.http_connection_manager -// ...... -// http_filters: -// - name: envoy.filters.http.golang -// typed_config: -// "@type": type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.Config -// library_id: auth-id -// library_path: "/home/admin/envoy/go-plugins/libauth.so" -// plugine_name: auth -// plugin_config: -// "@type": type.googleapis.com/envoy.extensions.filters.http.golang.plugins.auth.v3.Config -// xxx1: xx1 -// xxx2: xx2 -// - name: envoy.filters.http.router +// For an overview of the Golang filter please see the :ref:`configuration reference documentation `. +// [#extension: envoy.filters.http.golang] + // [#next-free-field: 6] message Config { + // The meanings are as follows: + // + // :``MERGE_VIRTUALHOST_ROUTER_FILTER``: Pass all configuration into Go plugin. + // :``MERGE_VIRTUALHOST_ROUTER``: Pass merged Virtual host and Router configuration into Go plugin. + // :``OVERRIDE``: Pass merged Virtual host, Router, and plugin configuration into Go plugin. + // + // [#not-implemented-hide:] enum MergePolicy { MERGE_VIRTUALHOST_ROUTER_FILTER = 0; MERGE_VIRTUALHOST_ROUTER = 1; OVERRIDE = 3; } - // library_id is a unique ID for a dynamic library file, must be unique globally. + // Globally unique ID for a dynamic library file. string library_id = 1 [(validate.rules).string = {min_len: 1}]; - // Dynamic library implementing the interface of - // ``StreamFilter ``. + // Path to a dynamic library implementing the + // :repo:`StreamFilter API ` + // interface. // [#comment:TODO(wangfakang): Support for downloading libraries from remote repositories.] string library_path = 2 [(validate.rules).string = {min_len: 1}]; - // plugin_name is the name of the go plugin, which needs to be consistent with the name - // registered in http::RegisterHttpFilterConfigFactory. - string plugin_name = 3 [(validate.rules).string = {min_bytes: 1}]; + // Globally unique name of the Go plugin. + // + // This name **must** be consistent with the name registered in ``http::RegisterHttpFilterConfigFactory``, + // and can be used to associate :ref:`route and virtualHost plugin configuration + // `. + // + string plugin_name = 3 [(validate.rules).string = {min_len: 1}]; - // plugin_config is the configuration of the go plugin, note that this configuration is - // only parsed in the go plugin. + // Configuration for the Go plugin. + // + // .. note:: + // This configuration is only parsed in the go plugin, and is therefore not validated + // by Envoy. + // + // See the :repo:`StreamFilter API ` + // for more information about how the plugin's configuration data can be accessed. + // google.protobuf.Any plugin_config = 4; - // merge_policy is the merge policy configured by the go plugin. - // go plugin configuration supports three dimensions: the virtual host’s typed_per_filter_config, - // the route’s typed_per_filter_config or filter's config. - // The meanings are as follows: - // MERGE_VIRTUALHOST_ROUTER_FILTER: pass all configuration into go plugin. - // MERGE_VIRTUALHOST_ROUTER: pass Virtual-Host and Router configuration into go plugin. - // OVERRIDE: override according to Router > Virtual_host > Filter priority and pass the - // configuration to the go plugin. + // Merge policy for plugin configuration. + // + // The Go plugin configuration supports three dimensions: + // + // * Virtual host’s :ref:`typed_per_filter_config ` + // * Route’s :ref:`typed_per_filter_config ` + // * The filter's :ref:`plugin_config ` + // + // [#not-implemented-hide:] MergePolicy merge_policy = 5 [(validate.rules).enum = {defined_only: true}]; } message RouterPlugin { - // Example - // - // .. code-block:: yaml - // - // typed_per_filter_config: - // envoy.filters.http.golang: - // "@type": type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.ConfigsPerRoute - // plugins_config: - // plugin1: - // disabled: true oneof override { option (validate.required) = true; @@ -188,27 +85,15 @@ message RouterPlugin { // If disabled is specified in multiple per-filter-configs, the most specific one will be used. bool disabled = 1 [(validate.rules).bool = {const: true}]; - // The config field is used to setting per-route plugin config. + // The config field is used for setting per-route and per-virtualhost plugin config. google.protobuf.Any config = 2; } } message ConfigsPerRoute { - // plugins_config is the configuration of the go plugin at the per-router, and - // key is the name of the go plugin. - // Example - // - // .. code-block:: yaml + // Configuration of the Go plugin at the per-router or per-virtualhost level, + // keyed on the :ref:`plugin_name ` + // of the Go plugin. // - // typed_per_filter_config: - // envoy.filters.http.golang: - // "@type": type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.ConfigsPerRoute - // plugins_config: - // plugin1: - // disabled: true - // plugin2: - // config: - // "@type": type.googleapis.com/golang.http.plugin2 - // xxx: xxx map plugins_config = 1; } diff --git a/api/envoy/config/core/v3/proxy_protocol.proto b/api/envoy/config/core/v3/proxy_protocol.proto index c276ce4d58ed..6a1c87fe64b2 100644 --- a/api/envoy/config/core/v3/proxy_protocol.proto +++ b/api/envoy/config/core/v3/proxy_protocol.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package envoy.config.core.v3; import "udpa/annotations/status.proto"; +import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.core.v3"; option java_outer_classname = "ProxyProtocolProto"; @@ -12,6 +13,25 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Proxy protocol] +message ProxyProtocolPassThroughTLVs { + enum PassTLVsMatchType { + // Pass all TLVs. + INCLUDE_ALL = 0; + + // Pass specific TLVs defined in tlv_type. + INCLUDE = 1; + } + + // The strategy to pass through TLVs. Default is INCLUDE_ALL. + // If INCLUDE_ALL is set, all TLVs will be passed through no matter the tlv_type field. + PassTLVsMatchType match_type = 1; + + // The TLV types that are applied based on match_type. + // TLV type is defined as uint8_t in proxy protocol. See `the spec + // `_ for details. + repeated uint32 tlv_type = 2 [(validate.rules).repeated = {items {uint32 {lt: 256}}}]; +} + message ProxyProtocolConfig { enum Version { // PROXY protocol version 1. Human readable format. @@ -23,4 +43,8 @@ message ProxyProtocolConfig { // The PROXY protocol version to use. See https://www.haproxy.org/download/2.1/doc/proxy-protocol.txt for details Version version = 1; + + // This config controls which TLVs can be passed to filter state if it is Proxy Protocol + // V2 header. If there is no setting for this field, no TLVs will be passed through. + ProxyProtocolPassThroughTLVs pass_through_tlvs = 2; } diff --git a/api/envoy/extensions/filters/listener/proxy_protocol/v3/BUILD b/api/envoy/extensions/filters/listener/proxy_protocol/v3/BUILD index ee92fb652582..1c1a6f6b4423 100644 --- a/api/envoy/extensions/filters/listener/proxy_protocol/v3/BUILD +++ b/api/envoy/extensions/filters/listener/proxy_protocol/v3/BUILD @@ -5,5 +5,8 @@ load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") licenses(["notice"]) # Apache 2 api_proto_package( - deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], + deps = [ + "//envoy/config/core/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], ) diff --git a/api/envoy/extensions/filters/listener/proxy_protocol/v3/proxy_protocol.proto b/api/envoy/extensions/filters/listener/proxy_protocol/v3/proxy_protocol.proto index 50472e568830..3fc5306831af 100644 --- a/api/envoy/extensions/filters/listener/proxy_protocol/v3/proxy_protocol.proto +++ b/api/envoy/extensions/filters/listener/proxy_protocol/v3/proxy_protocol.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package envoy.extensions.filters.listener.proxy_protocol.v3; +import "envoy/config/core/v3/proxy_protocol.proto"; + import "udpa/annotations/status.proto"; import "udpa/annotations/versioning.proto"; import "validate/validate.proto"; @@ -58,4 +60,8 @@ message ProxyProtocol { // signature will timeout (Envoy is unable to differentiate these requests // from incomplete proxy protocol requests). bool allow_requests_without_proxy_protocol = 2; + + // This config controls which TLVs can be passed to filter state if it is Proxy Protocol + // V2 header. If there is no setting for this field, no TLVs will be passed through. + config.core.v3.ProxyProtocolPassThroughTLVs pass_through_tlvs = 3; } diff --git a/bazel/dependency_imports.bzl b/bazel/dependency_imports.bzl index 7dbdb0174ec1..10a9add8aefb 100644 --- a/bazel/dependency_imports.bzl +++ b/bazel/dependency_imports.bzl @@ -8,6 +8,7 @@ load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies") load("@rules_rust//rust:repositories.bzl", "rules_rust_dependencies", "rust_register_toolchains") load("@proxy_wasm_rust_sdk//bazel:dependencies.bzl", "proxy_wasm_rust_sdk_dependencies") load("@base_pip3//:requirements.bzl", pip_dependencies = "install_deps") +load("@dev_pip3//:requirements.bzl", pip_dev_dependencies = "install_deps") load("@fuzzing_pip3//:requirements.bzl", pip_fuzzing_dependencies = "install_deps") load("@emsdk//:emscripten_deps.bzl", "emscripten_deps") load("@com_github_aignas_rules_shellcheck//:deps.bzl", "shellcheck_dependencies") @@ -29,6 +30,7 @@ def envoy_dependency_imports(go_version = GO_VERSION, jq_version = JQ_VERSION, y gazelle_dependencies(go_sdk = "go_sdk") apple_rules_dependencies() pip_dependencies() + pip_dev_dependencies() pip_fuzzing_dependencies() rules_pkg_dependencies() rules_rust_dependencies() diff --git a/bazel/external/BUILD b/bazel/external/BUILD index 62a6ca994d26..4558a1cee2cd 100644 --- a/bazel/external/BUILD +++ b/bazel/external/BUILD @@ -13,6 +13,7 @@ cc_library( tags = ["skip_on_windows"], deps = [ "@com_github_datadog_dd_opentracing_cpp//:dd_opentracing_cpp", + "@com_github_datadog_dd_trace_cpp//:dd_trace_cpp", "@com_google_googletest//:gtest", "@io_opentracing_cpp//:opentracing", ], diff --git a/bazel/external/quiche.BUILD b/bazel/external/quiche.BUILD index bfba0a2d60b0..9e20bea7952f 100644 --- a/bazel/external/quiche.BUILD +++ b/bazel/external/quiche.BUILD @@ -2945,6 +2945,34 @@ envoy_cc_library( deps = [":quiche_common_platform_logging"], ) +envoy_cc_library( + name = "quiche_common_status_utils", + hdrs = ["quiche/common/quiche_status_utils.h"], + copts = quiche_copts, + repository = "@envoy", + tags = ["nofips"], + deps = [ + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + ], +) + +envoy_cc_library( + name = "quiche_common_wire_serialization", + hdrs = ["quiche/common/wire_serialization.h"], + copts = quiche_copts, + repository = "@envoy", + tags = ["nofips"], + deps = [ + ":quiche_common_buffer_allocator_lib", + ":quiche_common_lib", + ":quiche_common_platform_logging", + ":quiche_common_status_utils", + "@com_google_absl//absl/status:statusor", + ], +) + envoy_cc_library( name = "quic_core_data_lib", srcs = [ @@ -3124,6 +3152,7 @@ envoy_cc_library( ":quic_core_types_lib", ":quic_platform_base", ":quiche_common_buffer_allocator_lib", + ":quiche_common_wire_serialization", ":quiche_web_transport_web_transport_lib", ], ) @@ -5661,7 +5690,6 @@ envoy_cc_library( envoy_cc_library( name = "quiche_balsa_http_validation_policy_lib", - srcs = ["quiche/balsa/http_validation_policy.cc"], hdrs = ["quiche/balsa/http_validation_policy.h"], copts = quiche_copts, repository = "@envoy", diff --git a/bazel/python_dependencies.bzl b/bazel/python_dependencies.bzl index a5c3283d0a25..37c0183664f5 100644 --- a/bazel/python_dependencies.bzl +++ b/bazel/python_dependencies.bzl @@ -1,4 +1,4 @@ -load("@rules_python//python:pip.bzl", "pip_install", "pip_parse") +load("@rules_python//python:pip.bzl", "pip_parse") load("@python3_10//:defs.bzl", "interpreter") def envoy_python_dependencies(): @@ -9,12 +9,11 @@ def envoy_python_dependencies(): extra_pip_args = ["--require-hashes"], ) - # TODO(phlax): switch to `pip_parse` - pip_install( - # Note: dev requirements do *not* check hashes - python_interpreter_target = interpreter, + pip_parse( name = "dev_pip3", - requirements = "@envoy//tools/dev:requirements.txt", + python_interpreter_target = interpreter, + requirements_lock = "@envoy//tools/dev:requirements.txt", + extra_pip_args = ["--require-hashes"], ) pip_parse( diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index 6a282d131580..c6a79aab01f4 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -164,6 +164,7 @@ def envoy_dependencies(skip_targets = []): _com_github_circonus_labs_libcircllhist() _com_github_cyan4973_xxhash() _com_github_datadog_dd_opentracing_cpp() + _com_github_datadog_dd_trace_cpp() _com_github_mirror_tclap() _com_github_envoyproxy_sqlparser() _com_github_fmtlib_fmt() @@ -574,6 +575,13 @@ def _com_github_datadog_dd_opentracing_cpp(): actual = "@com_github_datadog_dd_opentracing_cpp//:dd_opentracing_cpp", ) +def _com_github_datadog_dd_trace_cpp(): + external_http_archive("com_github_datadog_dd_trace_cpp") + native.bind( + name = "dd_trace_cpp", + actual = "@com_github_datadog_dd_trace_cpp//:dd_trace_cpp", + ) + def _com_github_skyapm_cpp2sky(): external_http_archive( name = "com_github_skyapm_cpp2sky", @@ -901,6 +909,10 @@ def _com_github_google_quiche(): name = "quiche_http2_protocol", actual = "@com_github_google_quiche//:http2_adapter_http2_protocol", ) + native.bind( + name = "quiche_http2_test_tools", + actual = "@com_github_google_quiche//:http2_adapter_mock_http2_visitor", + ) native.bind( name = "quiche_quic_platform", actual = "@com_github_google_quiche//:quic_platform", diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index f611fa85b1d7..ccc88c8e2c89 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -90,11 +90,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 = "0f17a22c7902e21e5e003f5296adb628d2f16b2f", - sha256 = "5e47c368092b66fd0bcd488118e8e990b42e5314c385fa9168d50daa95e2515b", + version = "c9eabcc8dde026715b35f262e30ba628bab5976c", + sha256 = "6b1ef916c5318d54a2ab11c31c5a7fb3a1c0e6fd3dc14d3a46b482c0261b9256", strip_prefix = "envoy-build-tools-{version}", urls = ["https://github.com/envoyproxy/envoy-build-tools/archive/{version}.tar.gz"], - release_date = "2022-08-26", + release_date = "2023-01-21", use_category = ["build"], license = "Apache-2.0", license_url = "https://github.com/envoyproxy/envoy-build-tools/blob/{version}/LICENSE", @@ -206,12 +206,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "c-ares", project_desc = "C library for asynchronous DNS requests", project_url = "https://c-ares.haxx.se/", - version = "1.18.1", - sha256 = "1a7d52a8a84a9fbffb1be9133c0f6e17217d91ea5a6fa61f6b4729cda78ebbcf", + version = "1.19.0", + sha256 = "bfceba37e23fd531293829002cac0401ef49a6dc55923f7f92236585b7ad1dd3", strip_prefix = "c-ares-{version}", urls = ["https://github.com/c-ares/c-ares/releases/download/cares-{underscore_version}/c-ares-{version}.tar.gz"], use_category = ["dataplane_core", "controlplane"], - release_date = "2021-10-27", + release_date = "2023-01-28", cpe = "cpe:2.3:a:c-ares_project:c-ares:*", license = "c-ares", license_url = "https://github.com/c-ares/c-ares/blob/cares-{underscore_version}/LICENSE.md", @@ -364,12 +364,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "gRPC", project_desc = "gRPC C core library", project_url = "https://grpc.io", - version = "1.49.0", - sha256 = "15715e1847cc9e42014f02c727dbcb48e39dbdb90f79ad3d66fe4361709ff935", + version = "1.49.2", + sha256 = "cdeb805385fba23242bf87073e68d590c446751e09089f26e5e0b3f655b0f089", strip_prefix = "grpc-{version}", urls = ["https://github.com/grpc/grpc/archive/v{version}.tar.gz"], use_category = ["dataplane_core", "controlplane"], - release_date = "2022-09-14", + release_date = "2022-12-09", cpe = "cpe:2.3:a:grpc:grpc:*", license = "Apache-2.0", license_url = "https://github.com/grpc/grpc/blob/v{version}/LICENSE", @@ -546,6 +546,21 @@ REPOSITORY_LOCATIONS_SPEC = dict( license = "Apache-2.0", license_url = "https://github.com/DataDog/dd-opentracing-cpp/blob/v{version}/LICENSE", ), + com_github_datadog_dd_trace_cpp = dict( + project_name = "Datadog C++ Tracing Library", + project_desc = "Datadog distributed tracing for C++", + project_url = "https://github.com/DataDog/dd-trace-cpp", + version = "0.1.5", + sha256 = "d76c98109822d6c3e8deb335117766b67c2be636169e60e5c813c9075b721aa1", + strip_prefix = "dd-trace-cpp-{version}", + urls = ["https://github.com/DataDog/dd-trace-cpp/archive/v{version}.tar.gz"], + use_category = ["observability_ext"], + extensions = ["envoy.tracers.datadog"], + release_date = "2022-12-06", + cpe = "N/A", + license = "Apache-2.0", + license_url = "https://github.com/DataDog/dd-trace-cpp/blob/v{version}/LICENSE", + ), com_github_google_benchmark = dict( project_name = "Benchmark", project_desc = "Library to benchmark code snippets", @@ -1080,12 +1095,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 = "2733b11ff02a7ebd66543e69667a8dc503e43d6c", - sha256 = "f57cb78c78d8605897dc743223c7505458b4fac60af33257887ce0ebc78e50ee", + version = "bebdfcd7ed48601337202811dcf61725dd86717b", + sha256 = "3f8f75e02f2dd76fc9449df8588f5faf366d37c6ba26a12bb4939eec2e03788d", urls = ["https://github.com/google/quiche/archive/{version}.tar.gz"], strip_prefix = "quiche-{version}", use_category = ["controlplane", "dataplane_core"], - release_date = "2023-01-23", + release_date = "2023-01-31", cpe = "N/A", license = "BSD-3-Clause", license_url = "https://github.com/google/quiche/blob/{version}/LICENSE", diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 39b68ce64c1c..f612c7ea4ac5 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -5,6 +5,9 @@ behavior_changes: minor_behavior_changes: # *Changes that may cause incompatibilities for some users, but should not for most* +- area: quic + change: | + Access logging is now deferred to the QUIC ack listener, and roundtrip response time is added as a downstream timing metric. New runtime flag ``envoy.reloadable_features.quic_defer_logging_to_ack_listener`` can be used for revert this behavior. - area: healthcheck change: | If active HC is enabled and a host is ejected by outlier detection, a successful active health check unejects the host and consider it healthy. This also clears all the outlier detection counters. This behavior change can be reverted by setting ``envoy.reloadable_features_successful_active_health_check_uneject_host`` to ``false``. @@ -18,9 +21,15 @@ bug_fixes: change: | added ``envoy.reloadable_features.multiplex_eds`` to disable eds multiplexing. Eds multiplexing is enabled by default, so that all subscriptions for the same resource type and management server reuse a single channel/mux. When eds multiplexing is disabled each subscription uses a dedicated channel/mux. +- area: router + change: | + fixed the bug that custom tags of the route metadata type are not set for upstream spans. removed_config_or_runtime: # *Normally occurs at the end of the* :ref:`deprecation period ` +- area: config + change: | + removed ``envoy.reloadable_features.combine_sds_requests`` and legacy code paths. - area: dns change: | removed ``envoy.reloadable_features.dns_multiple_addresses`` runtime flag and legacy code paths. @@ -30,6 +39,9 @@ removed_config_or_runtime: - area: http change: | removed ``envoy.reloadable_features.http2_delay_keepalive_timeout`` and legacy code paths. +- area: http + change: | + removed ``envoy.reloadable_features.local_ratelimit_match_all_descriptors`` and legacy code paths. - area: http change: | @@ -39,6 +51,9 @@ removed_config_or_runtime: removed ``envoy.reloadable_features.allow_concurrency_for_alpn_pool`` and legacy code path. new_features: +- area: access_log + change: | + enhanced observability into local close for :ref:`%RESPONSE_CODE_DETAILS% `. - area: tracing change: | allow :ref:`grpc_service ` to be optional. This enables a means to disable collection of traces. @@ -48,5 +63,11 @@ new_features: - area: upstream change: | added :ref:`maglev extension ` to suppport the :ref:`load balancer policy `. +- area: router + change: | + support route info in upstream access log. +- area: lua + change: | + added an new option to the options of lua ``httpCall``. This allows to skip adding ``x-forwarded-for`` by setting ``{["send_xff"] = false}`` as the ``options``. deprecated: diff --git a/ci/Dockerfile-envoy b/ci/Dockerfile-envoy index abfb0fcafcd9..ed27e513bc2f 100644 --- a/ci/Dockerfile-envoy +++ b/ci/Dockerfile-envoy @@ -42,7 +42,7 @@ CMD ["envoy", "-c", "/etc/envoy/envoy.yaml"] # STAGE: envoy-distroless # gcr.io/distroless/base-nossl-debian11:nonroot -FROM gcr.io/distroless/base-nossl-debian11:nonroot@sha256:3ee458a5858e11666ad3773e974e0e78d3530953745780bdf681dfcd4216c94b AS envoy-distroless +FROM gcr.io/distroless/base-nossl-debian11:nonroot@sha256:bf6159657aa6ac2c0d52e78a616991ac523adafd0718d17c6a013958a6770e1d AS envoy-distroless COPY --from=binary /usr/local/bin/envoy* /usr/local/bin/ COPY --from=binary /etc/envoy/envoy.yaml /etc/envoy/envoy.yaml diff --git a/ci/README.md b/ci/README.md index a27946c4b9ff..f5e7d0f43e42 100644 --- a/ci/README.md +++ b/ci/README.md @@ -70,6 +70,12 @@ Besides `http_proxy` and `https_proxy`, maybe you need to set `go_proxy` to repl IMAGE_NAME=envoyproxy/envoy-build-ubuntu go_proxy=https://goproxy.cn,direct http_proxy=http://proxy.foo.com:8080 https_proxy=http://proxy.bar.com:8080 ./ci/run_envoy_docker.sh ``` +To force the Envoy build image to be refreshed by Docker you can set `ENVOY_DOCKER_PULL=true`. + +```bash +ENVOY_DOCKER_PULL=true ./ci/run_envoy_docker.sh +``` + ## On Linux An example basic invocation to build a developer version of the Envoy static binary (using the Bazel `fastbuild` type) is: diff --git a/ci/do_ci.sh b/ci/do_ci.sh index 4c730d1a38c1..e059587c41a5 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -508,8 +508,7 @@ elif [[ "$CI_TARGET" == "deps" ]]; then bazel run "${BAZEL_BUILD_OPTIONS[@]}" //tools/dependency:check \ --action_env=TODAY_DATE \ -- -v warn \ - -c cves release_dates releases \ - || >&2 echo "Dependency checker still FAILING!" + -c cves release_dates releases # Run pip requirements tests echo "check pip..." diff --git a/ci/run_envoy_docker.sh b/ci/run_envoy_docker.sh index 7caaf87ee474..13e940e15d57 100755 --- a/ci/run_envoy_docker.sh +++ b/ci/run_envoy_docker.sh @@ -80,7 +80,9 @@ if ! is_windows; then VOLUMES+=(-v "${SHARED_TMP_DIR}":"${SHARED_TMP_DIR}") fi -time docker pull "${ENVOY_BUILD_IMAGE}" +if [[ -n "${ENVOY_DOCKER_PULL}" ]]; then + time docker pull "${ENVOY_BUILD_IMAGE}" +fi # Since we specify an explicit hash, docker-run will pull from the remote repo if missing. diff --git a/configs/BUILD b/configs/BUILD index 43d71e05ac10..17b5cf99b5fd 100644 --- a/configs/BUILD +++ b/configs/BUILD @@ -63,7 +63,7 @@ genrule( ], outs = ["example_configs.tar"], cmd = ( - "$(location configgen.sh) $(location configgen) $(@D) " + + "$(location configgen.sh) $(location configgen) example_configs.tar $(@D) " + "$(locations :configs) " + "$(locations //examples:configs) " + "$(locations //examples:certs) " + @@ -79,6 +79,30 @@ genrule( ], ) +genrule( + name = "example_contrib_configs", + srcs = [ + "//docs:contrib_configs", + "//contrib:configs", + "//examples:contrib_configs", + "//examples:certs", + "//test/config/integration/certs", + ], + outs = ["example_contrib_configs.tar"], + cmd = ( + "$(location configgen.sh) NO_CONFIGGEN example_contrib_configs.tar $(@D) " + + "$(locations //contrib:configs) " + + "$(locations //docs:contrib_configs) " + + "$(locations //examples:contrib_configs) " + + "$(locations //examples:certs) " + + "$(locations //test/config/integration/certs)" + ), + tools = [ + "configgen.sh", + ":configgen", + ], +) + py_binary( name = "example_configs_validation", srcs = ["example_configs_validation.py"], diff --git a/configs/configgen.sh b/configs/configgen.sh index 364e352b4c7a..72347b1faecc 100755 --- a/configs/configgen.sh +++ b/configs/configgen.sh @@ -4,6 +4,8 @@ set -e CONFIGGEN="$1" shift +TARGETFILE="$1" +shift OUT_DIR="$1" shift @@ -20,7 +22,7 @@ for FILE in "$@"; do *.pem|*.der) cp "$FILE" "$OUT_DIR/certs" ;; - *.lua|*.wasm) + *.lua|*.wasm|*.so) cp "$FILE" "$OUT_DIR/lib" ;; *.pb) @@ -41,4 +43,4 @@ done # shellcheck disable=SC2035 # TODO(mattklein123): I can't make this work when using the shellcheck suggestions. Try # to fix this. -(cd "$OUT_DIR"; tar -hcf example_configs.tar -- $(ls *.yaml certs/*.pem certs/*.der protos/*.pb lib/*.wasm lib/*.lua 2>/dev/null)) +(cd "$OUT_DIR"; tar -hcf "$TARGETFILE" -- $(ls *.yaml certs/*.pem certs/*.der protos/*.pb lib/*.so lib/*.wasm lib/*.lua 2>/dev/null)) diff --git a/contrib/BUILD b/contrib/BUILD index ceedb6dfcaac..a775974a7d2b 100644 --- a/contrib/BUILD +++ b/contrib/BUILD @@ -12,3 +12,14 @@ json_data( name = "contrib_extensions_build_config", data = CONTRIB_EXTENSIONS, ) + +filegroup( + name = "configs", + srcs = select({ + "//bazel:windows_x86_64": [], + "//conditions:default": [ + "//contrib/golang/filters/http/test/test_data/dummy:testing_shared_objects", + ], + }), + visibility = ["//visibility:public"], +) diff --git a/contrib/exe/BUILD b/contrib/exe/BUILD index 1cf08ac42163..8a99bc43ff76 100644 --- a/contrib/exe/BUILD +++ b/contrib/exe/BUILD @@ -30,11 +30,11 @@ envoy_cc_test( name = "example_configs_test", size = "large", data = [ - "//examples:contrib_configs", + "//configs:example_contrib_configs", "//test/config_test:example_configs_test_setup.sh", ], env = { - "EXAMPLE_CONFIGS_TAR_PATH": "envoy/examples/example_configs.tar", + "EXAMPLE_CONFIGS_TAR_PATH": "envoy/configs/example_contrib_configs.tar", "DISABLE_TEST_MERGE": "true", }, deps = [ diff --git a/contrib/golang/filters/http/source/cgo.cc b/contrib/golang/filters/http/source/cgo.cc index ba4cc297618c..2b4bc2780701 100644 --- a/contrib/golang/filters/http/source/cgo.cc +++ b/contrib/golang/filters/http/source/cgo.cc @@ -22,105 +22,111 @@ absl::string_view copyGoString(void* str) { extern "C" { -void envoyGoFilterHandlerWrapper(void* r, std::function&)> f) { +CAPIStatus envoyGoFilterHandlerWrapper(void* r, + std::function&)> f) { auto req = reinterpret_cast(r); auto weakFilter = req->weakFilter(); if (auto filter = weakFilter.lock()) { - f(filter); + return f(filter); } + return CAPIStatus::CAPIFilterIsGone; } -void envoyGoFilterHttpContinue(void* r, int status) { - envoyGoFilterHandlerWrapper(r, [status](std::shared_ptr& filter) { - filter->continueStatus(static_cast(status)); +CAPIStatus envoyGoFilterHttpContinue(void* r, int status) { + return envoyGoFilterHandlerWrapper(r, [status](std::shared_ptr& filter) -> CAPIStatus { + return filter->continueStatus(static_cast(status)); }); } -void envoyGoFilterHttpSendLocalReply(void* r, int response_code, void* body_text, void* headers, - long long int grpc_status, void* details) { - envoyGoFilterHandlerWrapper(r, [response_code, body_text, headers, grpc_status, - details](std::shared_ptr& filter) { - UNREFERENCED_PARAMETER(headers); - auto grpcStatus = static_cast(grpc_status); - filter->sendLocalReply(static_cast(response_code), copyGoString(body_text), nullptr, - grpcStatus, copyGoString(details)); - }); +CAPIStatus envoyGoFilterHttpSendLocalReply(void* r, int response_code, void* body_text, + void* headers, long long int grpc_status, + void* details) { + return envoyGoFilterHandlerWrapper( + r, + [response_code, body_text, headers, grpc_status, + details](std::shared_ptr& filter) -> CAPIStatus { + UNREFERENCED_PARAMETER(headers); + auto grpcStatus = static_cast(grpc_status); + return filter->sendLocalReply(static_cast(response_code), + copyGoString(body_text), nullptr, grpcStatus, + copyGoString(details)); + }); } // unsafe API, without copy memory from c to go. -void envoyGoFilterHttpGetHeader(void* r, void* key, void* value) { - envoyGoFilterHandlerWrapper(r, [key, value](std::shared_ptr& filter) { - auto keyStr = copyGoString(key); - auto v = filter->getHeader(keyStr); - if (v.has_value()) { - auto goValue = reinterpret_cast(value); - goValue->p = v.value().data(); - goValue->n = v.value().length(); - } - }); +CAPIStatus envoyGoFilterHttpGetHeader(void* r, void* key, void* value) { + return envoyGoFilterHandlerWrapper(r, + [key, value](std::shared_ptr& filter) -> CAPIStatus { + auto keyStr = copyGoString(key); + auto goValue = reinterpret_cast(value); + return filter->getHeader(keyStr, goValue); + }); } -void envoyGoFilterHttpCopyHeaders(void* r, void* strs, void* buf) { - envoyGoFilterHandlerWrapper(r, [strs, buf](std::shared_ptr& filter) { +CAPIStatus envoyGoFilterHttpCopyHeaders(void* r, void* strs, void* buf) { + return envoyGoFilterHandlerWrapper(r, [strs, buf](std::shared_ptr& filter) -> CAPIStatus { auto goStrs = reinterpret_cast(strs); auto goBuf = reinterpret_cast(buf); - filter->copyHeaders(goStrs, goBuf); + return filter->copyHeaders(goStrs, goBuf); }); } -void envoyGoFilterHttpSetHeader(void* r, void* key, void* value) { - envoyGoFilterHandlerWrapper(r, [key, value](std::shared_ptr& filter) { - auto keyStr = copyGoString(key); - auto valueStr = copyGoString(value); - filter->setHeader(keyStr, valueStr); - }); +CAPIStatus envoyGoFilterHttpSetHeader(void* r, void* key, void* value) { + return envoyGoFilterHandlerWrapper(r, + [key, value](std::shared_ptr& filter) -> CAPIStatus { + auto keyStr = copyGoString(key); + auto valueStr = copyGoString(value); + return filter->setHeader(keyStr, valueStr); + }); } -void envoyGoFilterHttpRemoveHeader(void* r, void* key) { - envoyGoFilterHandlerWrapper(r, [key](std::shared_ptr& filter) { +CAPIStatus envoyGoFilterHttpRemoveHeader(void* r, void* key) { + return envoyGoFilterHandlerWrapper(r, [key](std::shared_ptr& filter) -> CAPIStatus { // TODO: it's safe to skip copy auto keyStr = copyGoString(key); - filter->removeHeader(keyStr); + return filter->removeHeader(keyStr); }); } -void envoyGoFilterHttpGetBuffer(void* r, unsigned long long int buffer_ptr, void* data) { - envoyGoFilterHandlerWrapper(r, [buffer_ptr, data](std::shared_ptr& filter) { - auto buffer = reinterpret_cast(buffer_ptr); - filter->copyBuffer(buffer, reinterpret_cast(data)); - }); +CAPIStatus envoyGoFilterHttpGetBuffer(void* r, unsigned long long int buffer_ptr, void* data) { + return envoyGoFilterHandlerWrapper( + r, [buffer_ptr, data](std::shared_ptr& filter) -> CAPIStatus { + auto buffer = reinterpret_cast(buffer_ptr); + return filter->copyBuffer(buffer, reinterpret_cast(data)); + }); } -void envoyGoFilterHttpSetBufferHelper(void* r, unsigned long long int buffer_ptr, void* data, - int length, bufferAction action) { - envoyGoFilterHandlerWrapper( - r, [buffer_ptr, data, length, action](std::shared_ptr& filter) { +CAPIStatus envoyGoFilterHttpSetBufferHelper(void* r, unsigned long long int buffer_ptr, void* data, + int length, bufferAction action) { + return envoyGoFilterHandlerWrapper( + r, [buffer_ptr, data, length, action](std::shared_ptr& filter) -> CAPIStatus { auto buffer = reinterpret_cast(buffer_ptr); auto value = absl::string_view(reinterpret_cast(data), length); - filter->setBufferHelper(buffer, value, action); + return filter->setBufferHelper(buffer, value, action); }); } -void envoyGoFilterHttpCopyTrailers(void* r, void* strs, void* buf) { - envoyGoFilterHandlerWrapper(r, [strs, buf](std::shared_ptr& filter) { +CAPIStatus envoyGoFilterHttpCopyTrailers(void* r, void* strs, void* buf) { + return envoyGoFilterHandlerWrapper(r, [strs, buf](std::shared_ptr& filter) -> CAPIStatus { auto goStrs = reinterpret_cast(strs); auto goBuf = reinterpret_cast(buf); - filter->copyTrailers(goStrs, goBuf); + return filter->copyTrailers(goStrs, goBuf); }); } -void envoyGoFilterHttpSetTrailer(void* r, void* key, void* value) { - envoyGoFilterHandlerWrapper(r, [key, value](std::shared_ptr& filter) { - auto keyStr = copyGoString(key); - auto valueStr = copyGoString(value); - filter->setTrailer(keyStr, valueStr); - }); +CAPIStatus envoyGoFilterHttpSetTrailer(void* r, void* key, void* value) { + return envoyGoFilterHandlerWrapper(r, + [key, value](std::shared_ptr& filter) -> CAPIStatus { + auto keyStr = copyGoString(key); + auto valueStr = copyGoString(value); + return filter->setTrailer(keyStr, valueStr); + }); } -void envoyGoFilterHttpGetStringValue(void* r, int id, void* value) { - envoyGoFilterHandlerWrapper(r, [id, value](std::shared_ptr& filter) { +CAPIStatus envoyGoFilterHttpGetStringValue(void* r, int id, void* value) { + return envoyGoFilterHandlerWrapper(r, [id, value](std::shared_ptr& filter) -> CAPIStatus { auto valueStr = reinterpret_cast(value); - filter->getStringValue(id, valueStr); + return filter->getStringValue(id, valueStr); }); } diff --git a/contrib/golang/filters/http/source/go/pkg/api/api.h b/contrib/golang/filters/http/source/go/pkg/api/api.h index ebfeb23d77ee..b28fe29d2b5c 100644 --- a/contrib/golang/filters/http/source/go/pkg/api/api.h +++ b/contrib/golang/filters/http/source/go/pkg/api/api.h @@ -23,23 +23,32 @@ typedef enum { // NOLINT(modernize-use-using) Prepend, } bufferAction; -void envoyGoFilterHttpContinue(void* r, int status); -void envoyGoFilterHttpSendLocalReply(void* r, int response_code, void* body_text, void* headers, - long long int grpc_status, void* details); - -void envoyGoFilterHttpGetHeader(void* r, void* key, void* value); -void envoyGoFilterHttpCopyHeaders(void* r, void* strs, void* buf); -void envoyGoFilterHttpSetHeader(void* r, void* key, void* value); -void envoyGoFilterHttpRemoveHeader(void* r, void* key); - -void envoyGoFilterHttpGetBuffer(void* r, unsigned long long int buffer, void* value); -void envoyGoFilterHttpSetBufferHelper(void* r, unsigned long long int buffer, void* data, - int length, bufferAction action); - -void envoyGoFilterHttpCopyTrailers(void* r, void* strs, void* buf); -void envoyGoFilterHttpSetTrailer(void* r, void* key, void* value); - -void envoyGoFilterHttpGetStringValue(void* r, int id, void* value); +// The return value of C Api that invoking from Go. +typedef enum { // NOLINT(modernize-use-using) + CAPIOK = 0, + CAPIFilterIsGone = -1, + CAPIFilterIsDestroy = -2, + CAPINotInGo = -3, + CAPIInvalidPhase = -4, +} CAPIStatus; + +CAPIStatus envoyGoFilterHttpContinue(void* r, int status); +CAPIStatus envoyGoFilterHttpSendLocalReply(void* r, int response_code, void* body_text, + void* headers, long long int grpc_status, void* details); + +CAPIStatus envoyGoFilterHttpGetHeader(void* r, void* key, void* value); +CAPIStatus envoyGoFilterHttpCopyHeaders(void* r, void* strs, void* buf); +CAPIStatus envoyGoFilterHttpSetHeader(void* r, void* key, void* value); +CAPIStatus envoyGoFilterHttpRemoveHeader(void* r, void* key); + +CAPIStatus envoyGoFilterHttpGetBuffer(void* r, unsigned long long int buffer, void* value); +CAPIStatus envoyGoFilterHttpSetBufferHelper(void* r, unsigned long long int buffer, void* data, + int length, bufferAction action); + +CAPIStatus envoyGoFilterHttpCopyTrailers(void* r, void* strs, void* buf); +CAPIStatus envoyGoFilterHttpSetTrailer(void* r, void* key, void* value); + +CAPIStatus envoyGoFilterHttpGetStringValue(void* r, int id, void* value); void envoyGoFilterHttpFinalize(void* r, int reason); diff --git a/contrib/golang/filters/http/source/go/pkg/http/capi_impl.go b/contrib/golang/filters/http/source/go/pkg/http/capi_impl.go index 751d18813d1a..bab1a257295a 100644 --- a/contrib/golang/filters/http/source/go/pkg/http/capi_impl.go +++ b/contrib/golang/filters/http/source/go/pkg/http/capi_impl.go @@ -46,8 +46,26 @@ const ( type httpCApiImpl struct{} +// Only CAPIOK is expected, otherwise, it means unexpected stage when invoke C API, +// panic here and it will be recover in the Go entry function (TODO). +func handleCApiStatus(status C.CAPIStatus) { + switch status { + case C.CAPIOK: + return + case C.CAPIFilterIsGone: + panic("request has been finished") + case C.CAPIFilterIsDestroy: + panic("golang filter has been destroyed") + case C.CAPINotInGo: + panic("not proccessing Go") + case C.CAPIInvalidPhase: + panic("invalid phase, maybe headers/buffer already continued") + } +} + func (c *httpCApiImpl) HttpContinue(r unsafe.Pointer, status uint64) { - C.envoyGoFilterHttpContinue(r, C.int(status)) + res := C.envoyGoFilterHttpContinue(r, C.int(status)) + handleCApiStatus(res) } func (c *httpCApiImpl) HttpSendLocalReply(r unsafe.Pointer, response_code int, body_text string, headers map[string]string, grpc_status int64, details string) { @@ -56,11 +74,13 @@ func (c *httpCApiImpl) HttpSendLocalReply(r unsafe.Pointer, response_code int, b for k, v := range headers { strs = append(strs, k, v) } - C.envoyGoFilterHttpSendLocalReply(r, C.int(response_code), unsafe.Pointer(&body_text), unsafe.Pointer(&strs), C.longlong(grpc_status), unsafe.Pointer(&details)) + res := C.envoyGoFilterHttpSendLocalReply(r, C.int(response_code), unsafe.Pointer(&body_text), unsafe.Pointer(&strs), C.longlong(grpc_status), unsafe.Pointer(&details)) + handleCApiStatus(res) } func (c *httpCApiImpl) HttpGetHeader(r unsafe.Pointer, key *string, value *string) { - C.envoyGoFilterHttpGetHeader(r, unsafe.Pointer(key), unsafe.Pointer(value)) + res := C.envoyGoFilterHttpGetHeader(r, unsafe.Pointer(key), unsafe.Pointer(value)) + handleCApiStatus(res) } func (c *httpCApiImpl) HttpCopyHeaders(r unsafe.Pointer, num uint64, bytes uint64) map[string]string { @@ -75,7 +95,8 @@ func (c *httpCApiImpl) HttpCopyHeaders(r unsafe.Pointer, num uint64, bytes uint6 sHeader := (*reflect.SliceHeader)(unsafe.Pointer(&strs)) bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) - C.envoyGoFilterHttpCopyHeaders(r, unsafe.Pointer(sHeader.Data), unsafe.Pointer(bHeader.Data)) + res := C.envoyGoFilterHttpCopyHeaders(r, unsafe.Pointer(sHeader.Data), unsafe.Pointer(bHeader.Data)) + handleCApiStatus(res) m := make(map[string]string, num) for i := uint64(0); i < num*2; i += 2 { @@ -88,11 +109,13 @@ func (c *httpCApiImpl) HttpCopyHeaders(r unsafe.Pointer, num uint64, bytes uint6 } func (c *httpCApiImpl) HttpSetHeader(r unsafe.Pointer, key *string, value *string) { - C.envoyGoFilterHttpSetHeader(r, unsafe.Pointer(key), unsafe.Pointer(value)) + res := C.envoyGoFilterHttpSetHeader(r, unsafe.Pointer(key), unsafe.Pointer(value)) + handleCApiStatus(res) } func (c *httpCApiImpl) HttpRemoveHeader(r unsafe.Pointer, key *string) { - C.envoyGoFilterHttpRemoveHeader(r, unsafe.Pointer(key)) + res := C.envoyGoFilterHttpRemoveHeader(r, unsafe.Pointer(key)) + handleCApiStatus(res) } func (c *httpCApiImpl) HttpGetBuffer(r unsafe.Pointer, bufferPtr uint64, value *string, length uint64) { @@ -101,7 +124,8 @@ func (c *httpCApiImpl) HttpGetBuffer(r unsafe.Pointer, bufferPtr uint64, value * sHeader := (*reflect.StringHeader)(unsafe.Pointer(value)) sHeader.Data = bHeader.Data sHeader.Len = int(length) - C.envoyGoFilterHttpGetBuffer(r, C.ulonglong(bufferPtr), unsafe.Pointer(bHeader.Data)) + res := C.envoyGoFilterHttpGetBuffer(r, C.ulonglong(bufferPtr), unsafe.Pointer(bHeader.Data)) + handleCApiStatus(res) } func (c *httpCApiImpl) HttpSetBufferHelper(r unsafe.Pointer, bufferPtr uint64, value string, action api.BufferAction) { @@ -115,7 +139,8 @@ func (c *httpCApiImpl) HttpSetBufferHelper(r unsafe.Pointer, bufferPtr uint64, v case api.PrependBuffer: act = C.Prepend } - C.envoyGoFilterHttpSetBufferHelper(r, C.ulonglong(bufferPtr), unsafe.Pointer(sHeader.Data), C.int(sHeader.Len), act) + res := C.envoyGoFilterHttpSetBufferHelper(r, C.ulonglong(bufferPtr), unsafe.Pointer(sHeader.Data), C.int(sHeader.Len), act) + handleCApiStatus(res) } func (c *httpCApiImpl) HttpCopyTrailers(r unsafe.Pointer, num uint64, bytes uint64) map[string]string { @@ -128,7 +153,8 @@ func (c *httpCApiImpl) HttpCopyTrailers(r unsafe.Pointer, num uint64, bytes uint sHeader := (*reflect.SliceHeader)(unsafe.Pointer(&strs)) bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) - C.envoyGoFilterHttpCopyTrailers(r, unsafe.Pointer(sHeader.Data), unsafe.Pointer(bHeader.Data)) + res := C.envoyGoFilterHttpCopyTrailers(r, unsafe.Pointer(sHeader.Data), unsafe.Pointer(bHeader.Data)) + handleCApiStatus(res) m := make(map[string]string, num) for i := uint64(0); i < num*2; i += 2 { @@ -140,12 +166,14 @@ func (c *httpCApiImpl) HttpCopyTrailers(r unsafe.Pointer, num uint64, bytes uint } func (c *httpCApiImpl) HttpSetTrailer(r unsafe.Pointer, key *string, value *string) { - C.envoyGoFilterHttpSetTrailer(r, unsafe.Pointer(key), unsafe.Pointer(value)) + res := C.envoyGoFilterHttpSetTrailer(r, unsafe.Pointer(key), unsafe.Pointer(value)) + handleCApiStatus(res) } func (c *httpCApiImpl) HttpGetRouteName(r unsafe.Pointer) string { var value string - C.envoyGoFilterHttpGetStringValue(r, ValueRouteName, unsafe.Pointer(&value)) + res := C.envoyGoFilterHttpGetStringValue(r, ValueRouteName, unsafe.Pointer(&value)) + handleCApiStatus(res) // copy the memory from c to Go. return strings.Clone(value) } diff --git a/contrib/golang/filters/http/source/golang_filter.cc b/contrib/golang/filters/http/source/golang_filter.cc index 40a515c8f00a..75c63428ac7d 100644 --- a/contrib/golang/filters/http/source/golang_filter.cc +++ b/contrib/golang/filters/http/source/golang_filter.cc @@ -489,12 +489,22 @@ void Filter::sendLocalReplyInternal( state.sendLocalReply(response_code, body_text, modify_headers, grpc_status, details); } -void Filter::sendLocalReply(Http::Code response_code, absl::string_view body_text, - std::function modify_headers, - Grpc::Status::GrpcStatus grpc_status, absl::string_view details) { +CAPIStatus +Filter::sendLocalReply(Http::Code response_code, absl::string_view body_text, + std::function modify_headers, + Grpc::Status::GrpcStatus grpc_status, absl::string_view details) { + Thread::LockGuard lock(mutex_); + if (has_destroyed_) { + ENVOY_LOG(debug, "golang filter has been destroyed"); + return CAPIStatus::CAPIFilterIsDestroy; + } + auto& state = getProcessorState(); + if (!state.isProcessingInGo()) { + ENVOY_LOG(debug, "golang filter is not processing Go"); + return CAPIStatus::CAPINotInGo; + } ENVOY_LOG(debug, "sendLocalReply, response code: {}", int(response_code)); - auto& state = getProcessorState(); auto weak_ptr = weak_from_this(); state.getDispatcher().post( [this, &state, weak_ptr, response_code, body_text, modify_headers, grpc_status, details] { @@ -508,16 +518,26 @@ void Filter::sendLocalReply(Http::Code response_code, absl::string_view body_tex ENVOY_LOG(debug, "golang filter has gone or destroyed in sendLocalReply"); } }); + return CAPIStatus::CAPIOK; }; -void Filter::continueStatus(GolangStatus status) { - // TODO: skip post event to dispatcher, and return continue in the caller, - // when it's invoked in the current envoy thread, for better performance & latency. +CAPIStatus Filter::continueStatus(GolangStatus status) { + Thread::LockGuard lock(mutex_); + if (has_destroyed_) { + ENVOY_LOG(debug, "golang filter has been destroyed"); + return CAPIStatus::CAPIFilterIsDestroy; + } auto& state = getProcessorState(); + if (!state.isProcessingInGo()) { + ENVOY_LOG(debug, "golang filter is not processing Go"); + return CAPIStatus::CAPINotInGo; + } ENVOY_LOG(debug, "golang filter continue from Go, status: {}, state: {}, phase: {}", int(status), state.stateStr(), state.phaseStr()); auto weak_ptr = weak_from_this(); + // TODO: skip post event to dispatcher, and return continue in the caller, + // when it's invoked in the current envoy thread, for better performance & latency. state.getDispatcher().post([this, &state, weak_ptr, status] { ASSERT(state.isThreadSafe()); // TODO: do not need lock here, since it's the work thread now. @@ -529,22 +549,33 @@ void Filter::continueStatus(GolangStatus status) { ENVOY_LOG(debug, "golang filter has gone or destroyed in continueStatus event"); } }); + return CAPIStatus::CAPIOK; } -absl::optional Filter::getHeader(absl::string_view key) { +CAPIStatus Filter::getHeader(absl::string_view key, GoString* goValue) { Thread::LockGuard lock(mutex_); if (has_destroyed_) { ENVOY_LOG(debug, "golang filter has been destroyed"); - return ""; + return CAPIStatus::CAPIFilterIsDestroy; } auto& state = getProcessorState(); + if (!state.isProcessingInGo()) { + ENVOY_LOG(debug, "golang filter is not processing Go"); + return CAPIStatus::CAPINotInGo; + } auto m = state.isProcessingHeader() ? headers_ : trailers_; + if (m == nullptr) { + ENVOY_LOG(debug, "invoking cgo api at invalid phase: {}", __func__); + return CAPIStatus::CAPIInvalidPhase; + } auto result = m->get(Http::LowerCaseString(key)); - if (result.empty()) { - return absl::nullopt; + if (!result.empty()) { + auto str = result[0]->value().getStringView(); + goValue->p = str.data(); + goValue->n = str.length(); } - return result[0]->value().getStringView(); + return CAPIStatus::CAPIOK; } void copyHeaderMapToGo(Http::HeaderMap& m, GoString* go_strs, char* go_buf) { @@ -574,43 +605,79 @@ void copyHeaderMapToGo(Http::HeaderMap& m, GoString* go_strs, char* go_buf) { }); } -void Filter::copyHeaders(GoString* go_strs, char* go_buf) { +CAPIStatus Filter::copyHeaders(GoString* go_strs, char* go_buf) { Thread::LockGuard lock(mutex_); if (has_destroyed_) { ENVOY_LOG(debug, "golang filter has been destroyed"); - return; + return CAPIStatus::CAPIFilterIsDestroy; + } + auto& state = getProcessorState(); + if (!state.isProcessingInGo()) { + ENVOY_LOG(debug, "golang filter is not processing Go"); + return CAPIStatus::CAPINotInGo; + } + if (headers_ == nullptr) { + ENVOY_LOG(debug, "invoking cgo api at invalid phase: {}", __func__); + return CAPIStatus::CAPIInvalidPhase; } - ASSERT(headers_ != nullptr, "headers is empty, may already continue to next filter"); copyHeaderMapToGo(*headers_, go_strs, go_buf); + return CAPIStatus::CAPIOK; } -void Filter::setHeader(absl::string_view key, absl::string_view value) { +CAPIStatus Filter::setHeader(absl::string_view key, absl::string_view value) { Thread::LockGuard lock(mutex_); if (has_destroyed_) { ENVOY_LOG(debug, "golang filter has been destroyed"); - return; + return CAPIStatus::CAPIFilterIsDestroy; + } + auto& state = getProcessorState(); + if (!state.isProcessingInGo()) { + ENVOY_LOG(debug, "golang filter is not processing Go"); + return CAPIStatus::CAPINotInGo; + } + if (headers_ == nullptr) { + ENVOY_LOG(debug, "invoking cgo api at invalid phase: {}", __func__); + return CAPIStatus::CAPIInvalidPhase; } - ASSERT(headers_ != nullptr, "headers is empty, may already continue to next filter"); headers_->setCopy(Http::LowerCaseString(key), value); onHeadersModified(); + return CAPIStatus::CAPIOK; } -void Filter::removeHeader(absl::string_view key) { +CAPIStatus Filter::removeHeader(absl::string_view key) { Thread::LockGuard lock(mutex_); if (has_destroyed_) { ENVOY_LOG(debug, "golang filter has been destroyed"); - return; + return CAPIStatus::CAPIFilterIsDestroy; + } + auto& state = getProcessorState(); + if (!state.isProcessingInGo()) { + ENVOY_LOG(debug, "golang filter is not processing Go"); + return CAPIStatus::CAPINotInGo; + } + if (headers_ == nullptr) { + ENVOY_LOG(debug, "invoking cgo api at invalid phase: {}", __func__); + return CAPIStatus::CAPIInvalidPhase; } - ASSERT(headers_ != nullptr, "headers is empty, may already continue to next filter"); headers_->remove(Http::LowerCaseString(key)); onHeadersModified(); + return CAPIStatus::CAPIOK; } -void Filter::copyBuffer(Buffer::Instance* buffer, char* data) { +CAPIStatus Filter::copyBuffer(Buffer::Instance* buffer, char* data) { Thread::LockGuard lock(mutex_); if (has_destroyed_) { ENVOY_LOG(debug, "golang filter has been destroyed"); - return; + return CAPIStatus::CAPIFilterIsDestroy; + } + auto& state = getProcessorState(); + if (!state.isProcessingInGo()) { + ENVOY_LOG(debug, "golang filter is not processing Go"); + return CAPIStatus::CAPINotInGo; + } + if (!state.doDataList.checkExisting(buffer)) { + ENVOY_LOG(debug, "invoking cgo api at invalid phase: {}", __func__); + return CAPIStatus::CAPIInvalidPhase; } for (const Buffer::RawSlice& slice : buffer->getRawSlices()) { // data is the heap memory of go, and the length is the total length of buffer. So use memcpy is @@ -618,51 +685,85 @@ void Filter::copyBuffer(Buffer::Instance* buffer, char* data) { memcpy(data, static_cast(slice.mem_), slice.len_); // NOLINT(safe-memcpy) data += slice.len_; } + return CAPIStatus::CAPIOK; } -void Filter::setBufferHelper(Buffer::Instance* buffer, absl::string_view& value, - bufferAction action) { +CAPIStatus Filter::setBufferHelper(Buffer::Instance* buffer, absl::string_view& value, + bufferAction action) { Thread::LockGuard lock(mutex_); if (has_destroyed_) { ENVOY_LOG(debug, "golang filter has been destroyed"); - return; + return CAPIStatus::CAPIFilterIsDestroy; + } + auto& state = getProcessorState(); + if (!state.isProcessingInGo()) { + ENVOY_LOG(debug, "golang filter is not processing Go"); + return CAPIStatus::CAPINotInGo; + } + if (!state.doDataList.checkExisting(buffer)) { + ENVOY_LOG(debug, "invoking cgo api at invalid phase: {}", __func__); + return CAPIStatus::CAPIInvalidPhase; } if (action == bufferAction::Set) { buffer->drain(buffer->length()); + buffer->add(value); } else if (action == bufferAction::Prepend) { buffer->prepend(value); - return; + } else { + buffer->add(value); } - buffer->add(value); + return CAPIStatus::CAPIOK; } -void Filter::copyTrailers(GoString* go_strs, char* go_buf) { +CAPIStatus Filter::copyTrailers(GoString* go_strs, char* go_buf) { Thread::LockGuard lock(mutex_); if (has_destroyed_) { ENVOY_LOG(debug, "golang filter has been destroyed"); - return; + return CAPIStatus::CAPIFilterIsDestroy; + } + auto& state = getProcessorState(); + if (!state.isProcessingInGo()) { + ENVOY_LOG(debug, "golang filter is not processing Go"); + return CAPIStatus::CAPINotInGo; + } + if (trailers_ == nullptr) { + ENVOY_LOG(debug, "invoking cgo api at invalid phase: {}", __func__); + return CAPIStatus::CAPIInvalidPhase; } - ASSERT(trailers_ != nullptr, "trailers is empty"); copyHeaderMapToGo(*trailers_, go_strs, go_buf); + return CAPIStatus::CAPIOK; } -void Filter::setTrailer(absl::string_view key, absl::string_view value) { +CAPIStatus Filter::setTrailer(absl::string_view key, absl::string_view value) { Thread::LockGuard lock(mutex_); if (has_destroyed_) { ENVOY_LOG(debug, "golang filter has been destroyed"); - return; + return CAPIStatus::CAPIFilterIsDestroy; + } + auto& state = getProcessorState(); + if (!state.isProcessingInGo()) { + ENVOY_LOG(debug, "golang filter is not processing Go"); + return CAPIStatus::CAPINotInGo; + } + if (trailers_ == nullptr) { + ENVOY_LOG(debug, "invoking cgo api at invalid phase: {}", __func__); + return CAPIStatus::CAPIInvalidPhase; } - ASSERT(trailers_ != nullptr, "trailers is empty"); trailers_->setCopy(Http::LowerCaseString(key), value); + return CAPIStatus::CAPIOK; } -void Filter::getStringValue(int id, GoString* value_str) { +CAPIStatus Filter::getStringValue(int id, GoString* value_str) { Thread::LockGuard lock(mutex_); if (has_destroyed_) { ENVOY_LOG(debug, "golang filter has been destroyed"); - return; + return CAPIStatus::CAPIFilterIsDestroy; } auto& state = getProcessorState(); + if (!state.isProcessingInGo()) { + ENVOY_LOG(debug, "golang filter is not processing Go"); + return CAPIStatus::CAPINotInGo; + } switch (static_cast(id)) { case StringValue::RouteName: // string will copy to req->strValue, but not deep copy @@ -674,6 +775,7 @@ void Filter::getStringValue(int id, GoString* value_str) { value_str->p = req_->strValue.data(); value_str->n = req_->strValue.length(); + return CAPIStatus::CAPIOK; } /* ConfigId */ @@ -717,7 +819,8 @@ uint64_t FilterConfig::getConfigId() { ASSERT(dlib != nullptr, "load at the config parse phase, so it should not be null"); std::string str; - ASSERT(plugin_config_.SerializeToString(&str)); + auto res = plugin_config_.SerializeToString(&str); + ASSERT(res, "SerializeToString is always successful"); auto ptr = reinterpret_cast(str.data()); auto len = str.length(); config_id_ = dlib->envoyGoFilterNewHttpPluginConfig(ptr, len); @@ -763,7 +866,8 @@ uint64_t RoutePluginConfig::getMergedConfigId(uint64_t parent_id, std::string so if (config_id_ == 0) { std::string str; - ASSERT(plugin_config_.SerializeToString(&str)); + auto res = plugin_config_.SerializeToString(&str); + ASSERT(res, "SerializeToString is always successful"); auto ptr = reinterpret_cast(str.data()); auto len = str.length(); config_id_ = dlib->envoyGoFilterNewHttpPluginConfig(ptr, len); diff --git a/contrib/golang/filters/http/source/golang_filter.h b/contrib/golang/filters/http/source/golang_filter.h index 2c59f38a3aa1..861365b157b3 100644 --- a/contrib/golang/filters/http/source/golang_filter.h +++ b/contrib/golang/filters/http/source/golang_filter.h @@ -140,21 +140,22 @@ class Filter : public Http::StreamFilter, void onStreamComplete() override {} - void continueStatus(GolangStatus status); - - void sendLocalReply(Http::Code response_code, absl::string_view body_text, - std::function modify_headers, - Grpc::Status::GrpcStatus grpc_status, absl::string_view details); - - absl::optional getHeader(absl::string_view key); - void copyHeaders(GoString* go_strs, char* go_buf); - void setHeader(absl::string_view key, absl::string_view value); - void removeHeader(absl::string_view key); - void copyBuffer(Buffer::Instance* buffer, char* data); - void setBufferHelper(Buffer::Instance* buffer, absl::string_view& value, bufferAction action); - void copyTrailers(GoString* go_strs, char* go_buf); - void setTrailer(absl::string_view key, absl::string_view value); - void getStringValue(int id, GoString* value_str); + CAPIStatus continueStatus(GolangStatus status); + + CAPIStatus sendLocalReply(Http::Code response_code, absl::string_view body_text, + std::function modify_headers, + Grpc::Status::GrpcStatus grpc_status, absl::string_view details); + + CAPIStatus getHeader(absl::string_view key, GoString* goValue); + CAPIStatus copyHeaders(GoString* go_strs, char* go_buf); + CAPIStatus setHeader(absl::string_view key, absl::string_view value); + CAPIStatus removeHeader(absl::string_view key); + CAPIStatus copyBuffer(Buffer::Instance* buffer, char* data); + CAPIStatus setBufferHelper(Buffer::Instance* buffer, absl::string_view& value, + bufferAction action); + CAPIStatus copyTrailers(GoString* go_strs, char* go_buf); + CAPIStatus setTrailer(absl::string_view key, absl::string_view value); + CAPIStatus getStringValue(int id, GoString* value_str); private: ProcessorState& getProcessorState(); diff --git a/contrib/golang/filters/http/source/processor_state.cc b/contrib/golang/filters/http/source/processor_state.cc index a02b881a453f..59211e175966 100644 --- a/contrib/golang/filters/http/source/processor_state.cc +++ b/contrib/golang/filters/http/source/processor_state.cc @@ -37,6 +37,15 @@ void BufferList::clearAll() { queue_.clear(); }; +bool BufferList::checkExisting(Buffer::Instance* data) { + for (auto it = queue_.begin(); it != queue_.end(); ++it) { + if ((*it).get() == data) { + return true; + }; + } + return false; +}; + // headers_ should set to nullptr when return true. bool ProcessorState::handleHeaderGolangStatus(const GolangStatus status) { ENVOY_LOG(debug, "golang filter handle header status, state: {}, phase: {}, status: {}", diff --git a/contrib/golang/filters/http/source/processor_state.h b/contrib/golang/filters/http/source/processor_state.h index 0cb44d7c2c3a..4d2103522ad1 100644 --- a/contrib/golang/filters/http/source/processor_state.h +++ b/contrib/golang/filters/http/source/processor_state.h @@ -34,6 +34,8 @@ class BufferList : public NonCopyable { void clearLatest(); // clear all. void clearAll(); + // check the buffer instance if existing + bool checkExisting(Buffer::Instance* data); private: std::deque queue_; diff --git a/contrib/golang/filters/http/test/golang_filter_test.cc b/contrib/golang/filters/http/test/golang_filter_test.cc index 0a6255f0bd4c..9e3a1c1b3dea 100644 --- a/contrib/golang/filters/http/test/golang_filter_test.cc +++ b/contrib/golang/filters/http/test/golang_filter_test.cc @@ -39,6 +39,9 @@ namespace { class TestFilter : public Filter { public: using Filter::Filter; + void onDestroy() override { + // do nothing + } }; class GolangHttpFilterTest : public testing::Test { @@ -161,6 +164,14 @@ TEST_F(GolangHttpFilterTest, ScriptHeadersOnlyRequestHeadersOnly) { EXPECT_EQ(0, stats_store_.counter("test.golang.errors").value()); } +// setHeader at wrong stage +TEST_F(GolangHttpFilterTest, SetHeaderAtWrongStage) { + InSequence s; + setup(PASSTHROUGH, genSoPath(PASSTHROUGH), PASSTHROUGH); + + EXPECT_EQ(CAPINotInGo, filter_->setHeader("foo", "bar")); +} + } // namespace } // namespace Golang } // namespace HttpFilters diff --git a/contrib/golang/filters/http/test/golang_integration_test.cc b/contrib/golang/filters/http/test/golang_integration_test.cc index c34782efb993..fbb516718e14 100644 --- a/contrib/golang/filters/http/test/golang_integration_test.cc +++ b/contrib/golang/filters/http/test/golang_integration_test.cc @@ -33,13 +33,11 @@ name: golang library_id: %s library_path: %s plugin_name: %s - merge_policy: MERGE_VIRTUALHOST_ROUTER_FILTER plugin_config: - "@type": type.googleapis.com/udpa.type.v1.TypedStruct - type_url: typexx + "@type": type.googleapis.com/xds.type.v3.TypedStruct value: - remove: x-test-header-0 - set: foo + echo_body: "echo from go" + match_path: "/echo" )EOF"; auto yaml_string = absl::StrFormat(yaml_fmt, lib_id, lib_path, plugin_name); @@ -60,7 +58,8 @@ TEST_P(GolangIntegrationTest, Echo) { initialize(); registerTestServerPorts({"http"}); - auto path = "/localreply"; + auto path = "/echo"; + auto echo_body = "echo from go"; codec_client_ = makeHttpConnection(makeClientConnection(lookupPort("http"))); Http::TestRequestHeaderMapImpl request_headers{ {":method", "POST"}, {":path", path}, {":scheme", "http"}, {":authority", "test.com"}}; @@ -76,8 +75,8 @@ TEST_P(GolangIntegrationTest, Echo) { EXPECT_EQ("403", response->headers().getStatusValue()); // check body for echo - auto body = StringUtil::toUpper(absl::StrFormat("forbidden from go, path: %s\r\n", path)); - EXPECT_EQ(body, StringUtil::toUpper(response->body())); + auto body = absl::StrFormat("%s, path: %s\r\n", echo_body, path); + EXPECT_EQ(body, response->body()); codec_client_->close(); @@ -120,8 +119,8 @@ TEST_P(GolangIntegrationTest, Passthrough) { EXPECT_EQ("200", response->headers().getStatusValue()); // check body for pasthrough - auto body = StringUtil::toUpper(absl::StrFormat("%s%s", good, bye)); - EXPECT_EQ(body, StringUtil::toUpper(response->body())); + auto body = absl::StrFormat("%s%s", good, bye); + EXPECT_EQ(body, response->body()); codec_client_->close(); diff --git a/contrib/golang/filters/http/test/test_data/dummy/BUILD b/contrib/golang/filters/http/test/test_data/dummy/BUILD new file mode 100644 index 000000000000..8712a25cf224 --- /dev/null +++ b/contrib/golang/filters/http/test/test_data/dummy/BUILD @@ -0,0 +1,46 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary") + +licenses(["notice"]) # Apache 2 + +go_binary( + name = "my_plugin.so", + srcs = ["plugin.go"], + out = "my_plugin.so", + cgo = True, + importpath = "github.com/envoyproxy/envoy/contrib/golang/filters/http/test/test_data/dummy", + linkmode = "c-shared", + visibility = ["//visibility:public"], + deps = ["//contrib/golang/filters/http/source/go/pkg/http"], +) + +go_binary( + name = "my_other_plugin.so", + srcs = ["plugin.go"], + out = "my_other_plugin.so", + cgo = True, + importpath = "github.com/envoyproxy/envoy/contrib/golang/filters/http/test/test_data/dummy", + linkmode = "c-shared", + visibility = ["//visibility:public"], + deps = ["//contrib/golang/filters/http/source/go/pkg/http"], +) + +go_binary( + name = "my_configurable_plugin.so", + srcs = ["plugin.go"], + out = "my_configurable_plugin.so", + cgo = True, + importpath = "github.com/envoyproxy/envoy/contrib/golang/filters/http/test/test_data/dummy", + linkmode = "c-shared", + visibility = ["//visibility:public"], + deps = ["//contrib/golang/filters/http/source/go/pkg/http"], +) + +filegroup( + name = "testing_shared_objects", + srcs = [ + ":my_configurable_plugin.so", + ":my_other_plugin.so", + ":my_plugin.so", + ], + visibility = ["//visibility:public"], +) diff --git a/contrib/golang/filters/http/test/test_data/dummy/plugin.go b/contrib/golang/filters/http/test/test_data/dummy/plugin.go new file mode 100644 index 000000000000..556aa153793c --- /dev/null +++ b/contrib/golang/filters/http/test/test_data/dummy/plugin.go @@ -0,0 +1,12 @@ +// This is a NOOP plugin for the purpose of configuration testing. + +package main + +import "github.com/envoyproxy/envoy/contrib/golang/filters/http/source/go/pkg/http" + +func init() { + http.RegisterHttpFilterConfigFactory("", http.PassThroughFactory) +} + +func main() { +} diff --git a/contrib/golang/filters/http/test/test_data/echo/BUILD b/contrib/golang/filters/http/test/test_data/echo/BUILD index efb2108b4c56..58c642d6a719 100644 --- a/contrib/golang/filters/http/test/test_data/echo/BUILD +++ b/contrib/golang/filters/http/test/test_data/echo/BUILD @@ -17,7 +17,8 @@ go_binary( "//contrib/golang/filters/http/source/go/pkg/api", "//contrib/golang/filters/http/source/go/pkg/http", "//contrib/golang/filters/http/source/go/pkg/utils", - "@com_github_cncf_xds_go//udpa/type/v1:type", + "@com_github_cncf_xds_go//xds/type/v3:type", "@org_golang_google_protobuf//types/known/anypb", + "@org_golang_google_protobuf//types/known/structpb", ], ) diff --git a/contrib/golang/filters/http/test/test_data/echo/config.go b/contrib/golang/filters/http/test/test_data/echo/config.go index 2948b5aa13fd..234b3115dc85 100644 --- a/contrib/golang/filters/http/test/test_data/echo/config.go +++ b/contrib/golang/filters/http/test/test_data/echo/config.go @@ -1,8 +1,10 @@ package main import ( + xds "github.com/cncf/xds/go/xds/type/v3" "github.com/envoyproxy/envoy/contrib/golang/filters/http/source/go/pkg/api" "github.com/envoyproxy/envoy/contrib/golang/filters/http/source/go/pkg/http" + "google.golang.org/protobuf/types/known/anypb" ) const Name = "echo" @@ -11,10 +13,20 @@ func init() { http.RegisterHttpFilterConfigFactory(Name, ConfigFactory) } -func ConfigFactory(interface{}) api.StreamFilterFactory { +func ConfigFactory(config interface{}) api.StreamFilterFactory { + any, ok := config.(*anypb.Any) + if !ok { + return nil + } + + configStruct := &xds.TypedStruct{} + if err := any.UnmarshalTo(configStruct); err != nil { + return nil + } return func(callbacks api.FilterCallbackHandler) api.StreamFilter { return &filter{ callbacks: callbacks, + config: configStruct.Value, } } } diff --git a/contrib/golang/filters/http/test/test_data/echo/filter.go b/contrib/golang/filters/http/test/test_data/echo/filter.go index f02e155b1c85..3fdfa210a044 100644 --- a/contrib/golang/filters/http/test/test_data/echo/filter.go +++ b/contrib/golang/filters/http/test/test_data/echo/filter.go @@ -4,16 +4,24 @@ import ( "fmt" "github.com/envoyproxy/envoy/contrib/golang/filters/http/source/go/pkg/api" + "google.golang.org/protobuf/types/known/structpb" ) type filter struct { callbacks api.FilterCallbackHandler path string + config *structpb.Struct } func (f *filter) sendLocalReply() api.StatusType { headers := make(map[string]string) - body := fmt.Sprintf("forbidden from go, path: %s\r\n", f.path) + echoBody := "" + v, ok := f.config.AsMap()["echo_body"] + if ok { + echoBody = v.(string) + } + + body := fmt.Sprintf("%s, path: %s\r\n", echoBody, f.path) f.callbacks.SendLocalReply(403, body, headers, -1, "test-from-go") return api.LocalReply } @@ -22,7 +30,9 @@ func (f *filter) DecodeHeaders(header api.RequestHeaderMap, endStream bool) api. header.Del("x-test-header-1") f.path, _ = header.Get(":path") header.Set("rsp-header-from-go", "foo-test") - if f.path == "/localreply" { + // For the convenience of testing, it's better to in the config parse phase + matchPath, ok := f.config.AsMap()["match_path"] + if ok && f.path == matchPath.(string) { return f.sendLocalReply() } return api.Continue diff --git a/contrib/hyperscan/matching/input_matchers/source/BUILD b/contrib/hyperscan/matching/input_matchers/source/BUILD index 2baab304c900..56d987918133 100644 --- a/contrib/hyperscan/matching/input_matchers/source/BUILD +++ b/contrib/hyperscan/matching/input_matchers/source/BUILD @@ -27,6 +27,7 @@ envoy_cmake( "BUILD_AVX512VBMI": "on", "BUILD_EXAMPLES": "off", "CMAKE_INSTALL_LIBDIR": "lib", + "FAT_RUNTIME": "on", "RAGEL": "$EXT_BUILD_DEPS/ragel/bin/ragel", }, env = select({ diff --git a/contrib/kafka/filters/network/source/mesh/BUILD b/contrib/kafka/filters/network/source/mesh/BUILD index cfc0b024924f..668995d99f14 100644 --- a/contrib/kafka/filters/network/source/mesh/BUILD +++ b/contrib/kafka/filters/network/source/mesh/BUILD @@ -88,6 +88,7 @@ envoy_cc_library( deps = [ "//contrib/kafka/filters/network/source:kafka_response_lib", "//contrib/kafka/filters/network/source:tagged_fields_lib", + "//envoy/event:dispatcher_interface", ], ) diff --git a/contrib/kafka/filters/network/source/mesh/abstract_command.h b/contrib/kafka/filters/network/source/mesh/abstract_command.h index e7ee458d1e7d..6de8c6ac4b45 100644 --- a/contrib/kafka/filters/network/source/mesh/abstract_command.h +++ b/contrib/kafka/filters/network/source/mesh/abstract_command.h @@ -1,5 +1,7 @@ #pragma once +#include "envoy/event/dispatcher.h" + #include "source/common/common/logger.h" #include "contrib/kafka/filters/network/source/kafka_response.h" @@ -57,18 +59,23 @@ class AbstractRequestListener { // Notifies the listener that a new request has been received. virtual void onRequest(InFlightRequestSharedPtr request) PURE; - // Notified the listener, that the request finally has an answer ready. + // Notifies the listener, that the request finally has an answer ready. // Usually this means that the request has been sent to upstream Kafka clusters and we got answers // (unless it's something that could be responded to locally). // IMPL: we do not need to pass request here, as filters need to answer in-order. // What means that we always need to check if first answer is ready, even if the latter are // already finished. virtual void onRequestReadyForAnswer() PURE; + + // Accesses listener's dispatcher. + // Used by non-Envoy threads that need to communicate with listeners. + virtual Event::Dispatcher& dispatcher() PURE; }; /** * Helper base class for all in flight requests. * Binds request to its origin filter. + * All the fields can be accessed only by the owning dispatcher thread. */ class BaseInFlightRequest : public InFlightRequest, protected Logger::Loggable { public: diff --git a/contrib/kafka/filters/network/source/mesh/command_handlers/BUILD b/contrib/kafka/filters/network/source/mesh/command_handlers/BUILD index be021a214a3a..7b0ee1d20800 100644 --- a/contrib/kafka/filters/network/source/mesh/command_handlers/BUILD +++ b/contrib/kafka/filters/network/source/mesh/command_handlers/BUILD @@ -45,6 +45,40 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "fetch_lib", + srcs = [ + "fetch.cc", + ], + hdrs = [ + "fetch.h", + ], + tags = ["skip_on_windows"], + deps = [ + ":fetch_record_converter_lib", + "//contrib/kafka/filters/network/source:kafka_request_parser_lib", + "//contrib/kafka/filters/network/source:kafka_response_parser_lib", + "//contrib/kafka/filters/network/source/mesh:abstract_command_lib", + "//contrib/kafka/filters/network/source/mesh:shared_consumer_manager_lib", + "//source/common/common:minimal_logger_lib", + ], +) + +envoy_cc_library( + name = "fetch_record_converter_lib", + srcs = [ + "fetch_record_converter.cc", + ], + hdrs = [ + "fetch_record_converter.h", + ], + tags = ["skip_on_windows"], + deps = [ + "//contrib/kafka/filters/network/source:kafka_response_parser_lib", + "//contrib/kafka/filters/network/source/mesh:inbound_record_lib", + ], +) + envoy_cc_library( name = "list_offsets_lib", srcs = [ diff --git a/contrib/kafka/filters/network/source/mesh/command_handlers/fetch.cc b/contrib/kafka/filters/network/source/mesh/command_handlers/fetch.cc new file mode 100644 index 000000000000..9f04bc799ca9 --- /dev/null +++ b/contrib/kafka/filters/network/source/mesh/command_handlers/fetch.cc @@ -0,0 +1,188 @@ +#include "contrib/kafka/filters/network/source/mesh/command_handlers/fetch.h" + +#include + +#include "source/common/common/fmt.h" + +#include "absl/synchronization/mutex.h" +#include "contrib/kafka/filters/network/source/external/responses.h" + +namespace Envoy { +namespace Extensions { +namespace NetworkFilters { +namespace Kafka { +namespace Mesh { + +FetchRequestHolder::FetchRequestHolder(AbstractRequestListener& filter, + RecordCallbackProcessor& consumer_manager, + const std::shared_ptr> request) + : FetchRequestHolder{filter, consumer_manager, request, + FetchRecordConverterImpl::getDefaultInstance()} {} + +FetchRequestHolder::FetchRequestHolder(AbstractRequestListener& filter, + RecordCallbackProcessor& consumer_manager, + const std::shared_ptr> request, + const FetchRecordConverter& converter) + : BaseInFlightRequest{filter}, consumer_manager_{consumer_manager}, request_{request}, + dispatcher_{filter.dispatcher()}, converter_{converter} {} + +// XXX (adam.kotwasinski) This should be made configurable in future. +constexpr uint32_t FETCH_TIMEOUT_MS = 5000; + +static Event::TimerPtr registerTimeoutCallback(Event::Dispatcher& dispatcher, + const Event::TimerCb callback, + const int32_t timeout) { + auto event = dispatcher.createTimer(callback); + event->enableTimer(std::chrono::milliseconds(timeout)); + return event; +} + +void FetchRequestHolder::startProcessing() { + const TopicToPartitionsMap requested_topics = interest(); + + { + absl::MutexLock lock(&state_mutex_); + for (const auto& topic_and_partitions : requested_topics) { + const std::string& topic_name = topic_and_partitions.first; + for (const int32_t partition : topic_and_partitions.second) { + // This makes sure that all requested KafkaPartitions are tracked, + // so then output generation is simpler. + messages_[{topic_name, partition}] = {}; + } + } + } + + const auto self_reference = shared_from_this(); + consumer_manager_.processCallback(self_reference); + + Event::TimerCb callback = [this]() -> void { + // Fun fact: if the request is degenerate (no partitions requested), + // this will ensure it gets processed. + markFinishedByTimer(); + }; + timer_ = registerTimeoutCallback(dispatcher_, callback, FETCH_TIMEOUT_MS); +} + +TopicToPartitionsMap FetchRequestHolder::interest() const { + TopicToPartitionsMap result; + const std::vector& topics = request_->data_.topics_; + for (const FetchTopic& topic : topics) { + const std::string topic_name = topic.topic_; + const std::vector partitions = topic.partitions_; + for (const FetchPartition& partition : partitions) { + result[topic_name].push_back(partition.partition_); + } + } + return result; +} + +// This method is called by a Envoy-worker thread. +void FetchRequestHolder::markFinishedByTimer() { + ENVOY_LOG(trace, "Request {} timed out", toString()); + bool doCleanup = false; + { + absl::MutexLock lock(&state_mutex_); + timer_ = nullptr; + if (!finished_) { + finished_ = true; + doCleanup = true; + } + } + if (doCleanup) { + cleanup(true); + } +} + +// XXX (adam.kotwasinski) This should be made configurable in future. +// Right now the Fetch request is going to send up to 3 records. +// In future this should tranform into some kind of method that's invoked inside 'receive' calls, +// as Kafka can have limits on records per partition. +constexpr int32_t MINIMAL_MSG_CNT = 3; + +// This method is called by: +// - Kafka-consumer thread - when have the records delivered, +// - dispatcher thread - when we start processing and check whether anything was cached. +CallbackReply FetchRequestHolder::receive(InboundRecordSharedPtr message) { + absl::MutexLock lock(&state_mutex_); + if (!finished_) { + // Store a new record. + const KafkaPartition kp = {message->topic_, message->partition_}; + messages_[kp].push_back(message); + + // Count all the records currently stored within this request. + uint32_t current_messages = 0; + for (const auto& e : messages_) { + current_messages += e.second.size(); + } + + if (current_messages < MINIMAL_MSG_CNT) { + // We can consume more in future. + return CallbackReply::AcceptedAndWantMore; + } else { + // We have all we needed, we can finish processing. + finished_ = true; + cleanup(false); + return CallbackReply::AcceptedAndFinished; + } + } else { + // This fetch request has finished processing, so it will not accept a record. + return CallbackReply::Rejected; + } +} + +std::string FetchRequestHolder::toString() const { + return fmt::format("[Fetch id={}]", request_->request_header_.correlation_id_); +} + +void FetchRequestHolder::cleanup(bool unregister) { + ENVOY_LOG(trace, "Cleanup starting for {}", toString()); + if (unregister) { + const auto self_reference = shared_from_this(); + consumer_manager_.removeCallback(self_reference); + } + + // Our request is ready and can be sent downstream. + // However, the caller here could be a Kafka-consumer worker thread (not an Envoy worker one), + // so we need to use dispatcher to notify the filter that we are finished. + auto notifyCallback = [this]() -> void { + timer_ = nullptr; + filter_.onRequestReadyForAnswer(); + }; + // Impl note: usually this will be invoked by non-Envoy thread, + // so let's not optimize that this might be invoked by dispatcher callback. + dispatcher_.post(notifyCallback); + ENVOY_LOG(trace, "Cleanup finished for {}", toString()); +} + +bool FetchRequestHolder::finished() const { + absl::MutexLock lock(&state_mutex_); + return finished_; +} + +void FetchRequestHolder::abandon() { + ENVOY_LOG(trace, "Abandoning {}", toString()); + // We remove the timeout-callback and unregister this request so no deliveries happen to it. + timer_ = nullptr; + const auto self_reference = shared_from_this(); + consumer_manager_.removeCallback(self_reference); + BaseInFlightRequest::abandon(); +} + +AbstractResponseSharedPtr FetchRequestHolder::computeAnswer() const { + const auto& header = request_->request_header_; + const ResponseMetadata metadata = {header.api_key_, header.api_version_, header.correlation_id_}; + + std::vector responses; + { + absl::MutexLock lock(&state_mutex_); + responses = converter_.convert(messages_); + } + const FetchResponse data = {responses}; + return std::make_shared>(metadata, data); +} + +} // namespace Mesh +} // namespace Kafka +} // namespace NetworkFilters +} // namespace Extensions +} // namespace Envoy diff --git a/contrib/kafka/filters/network/source/mesh/command_handlers/fetch.h b/contrib/kafka/filters/network/source/mesh/command_handlers/fetch.h new file mode 100644 index 000000000000..ac11a45f6ad1 --- /dev/null +++ b/contrib/kafka/filters/network/source/mesh/command_handlers/fetch.h @@ -0,0 +1,89 @@ +#pragma once + +#include "envoy/event/dispatcher.h" +#include "envoy/event/timer.h" + +#include "absl/synchronization/mutex.h" +#include "contrib/kafka/filters/network/source/external/requests.h" +#include "contrib/kafka/filters/network/source/mesh/abstract_command.h" +#include "contrib/kafka/filters/network/source/mesh/command_handlers/fetch_record_converter.h" +#include "contrib/kafka/filters/network/source/mesh/inbound_record.h" +#include "contrib/kafka/filters/network/source/mesh/shared_consumer_manager.h" +#include "contrib/kafka/filters/network/source/mesh/upstream_kafka_consumer.h" + +namespace Envoy { +namespace Extensions { +namespace NetworkFilters { +namespace Kafka { +namespace Mesh { + +class FetchRequestHolder : public BaseInFlightRequest, + public RecordCb, + public std::enable_shared_from_this { +public: + FetchRequestHolder(AbstractRequestListener& filter, RecordCallbackProcessor& consumer_manager, + const std::shared_ptr> request); + + // Visible for testing. + FetchRequestHolder(AbstractRequestListener& filter, RecordCallbackProcessor& consumer_manager, + const std::shared_ptr>, + const FetchRecordConverter& converter); + + // AbstractInFlightRequest + void startProcessing() override; + + // AbstractInFlightRequest + bool finished() const override; + + // AbstractInFlightRequest + void abandon() override; + + // AbstractInFlightRequest + AbstractResponseSharedPtr computeAnswer() const override; + + // Invoked by timer as this requests's time runs out. + // It is possible that this request has already been finished (there was data to send), + // then this method does nothing. + void markFinishedByTimer(); + + // RecordCb + CallbackReply receive(InboundRecordSharedPtr message) override; + + // RecordCb + TopicToPartitionsMap interest() const override; + + // RecordCb + std::string toString() const override; + +private: + // Invoked internally when we want to mark this Fetch request as done. + // This means: we are no longer interested in future messages and might need to unregister + // ourselves. + void cleanup(bool unregister); + + // Provides access to upstream-pointing consumers. + RecordCallbackProcessor& consumer_manager_; + // Original request. + const std::shared_ptr> request_; + + mutable absl::Mutex state_mutex_; + // Whether this request has finished processing and is ready for sending upstream. + bool finished_ ABSL_GUARDED_BY(state_mutex_) = false; + // The messages to send downstream. + std::map> + messages_ ABSL_GUARDED_BY(state_mutex_); + + // Filter's dispatcher. + Event::Dispatcher& dispatcher_; + // Timeout timer (invalidated when request is finished). + Event::TimerPtr timer_; + + // Translates librdkafka objects into bytes to be sent downstream. + const FetchRecordConverter& converter_; +}; + +} // namespace Mesh +} // namespace Kafka +} // namespace NetworkFilters +} // namespace Extensions +} // namespace Envoy diff --git a/contrib/kafka/filters/network/source/mesh/command_handlers/fetch_record_converter.cc b/contrib/kafka/filters/network/source/mesh/command_handlers/fetch_record_converter.cc new file mode 100644 index 000000000000..a2c2d3a68669 --- /dev/null +++ b/contrib/kafka/filters/network/source/mesh/command_handlers/fetch_record_converter.cc @@ -0,0 +1,24 @@ +#include "contrib/kafka/filters/network/source/mesh/command_handlers/fetch_record_converter.h" + +namespace Envoy { +namespace Extensions { +namespace NetworkFilters { +namespace Kafka { +namespace Mesh { + +std::vector +FetchRecordConverterImpl::convert(const InboundRecordsMap&) const { + + // TODO (adam.kotwasinski) This needs to be actually implemented. + return {}; +} + +const FetchRecordConverter& FetchRecordConverterImpl::getDefaultInstance() { + CONSTRUCT_ON_FIRST_USE(FetchRecordConverterImpl); +} + +} // namespace Mesh +} // namespace Kafka +} // namespace NetworkFilters +} // namespace Extensions +} // namespace Envoy diff --git a/contrib/kafka/filters/network/source/mesh/command_handlers/fetch_record_converter.h b/contrib/kafka/filters/network/source/mesh/command_handlers/fetch_record_converter.h new file mode 100644 index 000000000000..fe5103a8b814 --- /dev/null +++ b/contrib/kafka/filters/network/source/mesh/command_handlers/fetch_record_converter.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include + +#include "contrib/kafka/filters/network/source/external/responses.h" +#include "contrib/kafka/filters/network/source/kafka_types.h" +#include "contrib/kafka/filters/network/source/mesh/inbound_record.h" + +namespace Envoy { +namespace Extensions { +namespace NetworkFilters { +namespace Kafka { +namespace Mesh { + +using InboundRecordsMap = std::map>; + +/** + * Dependency injection class responsible for converting received records into serializable form + * that we can put into Fetch responses. + */ +class FetchRecordConverter { +public: + virtual ~FetchRecordConverter() = default; + + // Converts received records into the serialized form. + virtual std::vector convert(const InboundRecordsMap& arg) const PURE; +}; + +/** + * Proper implementation. + */ +class FetchRecordConverterImpl : public FetchRecordConverter { +public: + // FetchRecordConverter + std::vector convert(const InboundRecordsMap& arg) const override; + + // Default singleton accessor. + static const FetchRecordConverter& getDefaultInstance(); +}; + +} // namespace Mesh +} // namespace Kafka +} // namespace NetworkFilters +} // namespace Extensions +} // namespace Envoy diff --git a/contrib/kafka/filters/network/source/mesh/filter.cc b/contrib/kafka/filters/network/source/mesh/filter.cc index 10c0ef796162..b863e410630c 100644 --- a/contrib/kafka/filters/network/source/mesh/filter.cc +++ b/contrib/kafka/filters/network/source/mesh/filter.cc @@ -90,6 +90,10 @@ void KafkaMeshFilter::onRequestReadyForAnswer() { } } +Event::Dispatcher& KafkaMeshFilter::dispatcher() { + return read_filter_callbacks_->connection().dispatcher(); +} + void KafkaMeshFilter::abandonAllInFlightRequests() { for (const auto& request : requests_in_flight_) { request->abandon(); diff --git a/contrib/kafka/filters/network/source/mesh/filter.h b/contrib/kafka/filters/network/source/mesh/filter.h index a6b4ec80cdd4..d38d0257e92c 100644 --- a/contrib/kafka/filters/network/source/mesh/filter.h +++ b/contrib/kafka/filters/network/source/mesh/filter.h @@ -1,6 +1,7 @@ #pragma once #include "envoy/common/time.h" +#include "envoy/event/dispatcher.h" #include "envoy/network/filter.h" #include "envoy/stats/scope.h" @@ -75,6 +76,7 @@ class KafkaMeshFilter : public Network::ReadFilter, // AbstractRequestListener void onRequest(InFlightRequestSharedPtr request) override; void onRequestReadyForAnswer() override; + Event::Dispatcher& dispatcher() override; std::list& getRequestsInFlightForTest(); diff --git a/contrib/kafka/filters/network/test/mesh/abstract_command_unit_test.cc b/contrib/kafka/filters/network/test/mesh/abstract_command_unit_test.cc index 48661edf9751..34d725edae2f 100644 --- a/contrib/kafka/filters/network/test/mesh/abstract_command_unit_test.cc +++ b/contrib/kafka/filters/network/test/mesh/abstract_command_unit_test.cc @@ -13,6 +13,7 @@ class MockAbstractRequestListener : public AbstractRequestListener { public: MOCK_METHOD(void, onRequest, (InFlightRequestSharedPtr)); MOCK_METHOD(void, onRequestReadyForAnswer, ()); + MOCK_METHOD(Event::Dispatcher&, dispatcher, ()); }; class Testee : public BaseInFlightRequest { diff --git a/contrib/kafka/filters/network/test/mesh/command_handlers/BUILD b/contrib/kafka/filters/network/test/mesh/command_handlers/BUILD index b65189eeff7b..154b7bfb696d 100644 --- a/contrib/kafka/filters/network/test/mesh/command_handlers/BUILD +++ b/contrib/kafka/filters/network/test/mesh/command_handlers/BUILD @@ -28,6 +28,17 @@ envoy_cc_test( ], ) +envoy_cc_test( + name = "fetch_unit_test", + srcs = ["fetch_unit_test.cc"], + tags = ["skip_on_windows"], + deps = [ + "//contrib/kafka/filters/network/source/mesh/command_handlers:fetch_lib", + "//test/mocks/network:network_mocks", + "//test/mocks/stats:stats_mocks", + ], +) + envoy_cc_test( name = "list_offsets_unit_test", srcs = ["list_offsets_unit_test.cc"], diff --git a/contrib/kafka/filters/network/test/mesh/command_handlers/api_versions_unit_test.cc b/contrib/kafka/filters/network/test/mesh/command_handlers/api_versions_unit_test.cc index 2a572bec507b..4d9cb58a6995 100644 --- a/contrib/kafka/filters/network/test/mesh/command_handlers/api_versions_unit_test.cc +++ b/contrib/kafka/filters/network/test/mesh/command_handlers/api_versions_unit_test.cc @@ -13,6 +13,7 @@ class MockAbstractRequestListener : public AbstractRequestListener { public: MOCK_METHOD(void, onRequest, (InFlightRequestSharedPtr)); MOCK_METHOD(void, onRequestReadyForAnswer, ()); + MOCK_METHOD(Event::Dispatcher&, dispatcher, ()); }; TEST(ApiVersionsTest, shouldBeAlwaysReadyForAnswer) { diff --git a/contrib/kafka/filters/network/test/mesh/command_handlers/fetch_unit_test.cc b/contrib/kafka/filters/network/test/mesh/command_handlers/fetch_unit_test.cc new file mode 100644 index 000000000000..8a34d4a06ea1 --- /dev/null +++ b/contrib/kafka/filters/network/test/mesh/command_handlers/fetch_unit_test.cc @@ -0,0 +1,186 @@ +#include "test/mocks/event/mocks.h" + +#include "contrib/kafka/filters/network/source/mesh/command_handlers/fetch.h" +#include "contrib/kafka/filters/network/source/mesh/command_handlers/fetch_record_converter.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace NetworkFilters { +namespace Kafka { +namespace Mesh { +namespace { + +using testing::NiceMock; +using testing::Return; +using testing::ReturnRef; + +class MockAbstractRequestListener : public AbstractRequestListener { +public: + MOCK_METHOD(void, onRequest, (InFlightRequestSharedPtr)); + MOCK_METHOD(void, onRequestReadyForAnswer, ()); + MOCK_METHOD(Event::Dispatcher&, dispatcher, ()); +}; + +class MockRecordCallbackProcessor : public RecordCallbackProcessor { +public: + MOCK_METHOD(void, processCallback, (const RecordCbSharedPtr&)); + MOCK_METHOD(void, removeCallback, (const RecordCbSharedPtr&)); +}; + +class MockFetchRecordConverter : public FetchRecordConverter { +public: + MOCK_METHOD(std::vector, convert, (const InboundRecordsMap&), (const)); +}; + +class FetchUnitTest : public testing::Test { +protected: + constexpr static int64_t TEST_CORRELATION_ID = 123456; + + NiceMock filter_; + NiceMock dispatcher_; + NiceMock callback_processor_; + MockFetchRecordConverter converter_; + + FetchUnitTest() { ON_CALL(filter_, dispatcher).WillByDefault(ReturnRef(dispatcher_)); } + + std::shared_ptr makeTestee() { + const RequestHeader header = {FETCH_REQUEST_API_KEY, 0, TEST_CORRELATION_ID, absl::nullopt}; + // Our request refers to aaa-0, aaa-1, bbb-10, bbb-20. + const FetchTopic t1 = {"aaa", {{0, 0, 0}, {1, 0, 0}}}; + const FetchTopic t2 = {"bbb", {{10, 0, 0}, {20, 0, 0}}}; + const FetchRequest data = {0, 0, 0, {t1, t2}}; + const auto message = std::make_shared>(header, data); + return std::make_shared(filter_, callback_processor_, message, converter_); + } +}; + +TEST_F(FetchUnitTest, ShouldRegisterCallbackAndTimer) { + // given + const auto testee = makeTestee(); + EXPECT_CALL(callback_processor_, processCallback(_)); + EXPECT_CALL(dispatcher_, createTimer_(_)); + + // when + testee->startProcessing(); + + // then + ASSERT_FALSE(testee->finished()); +} + +TEST_F(FetchUnitTest, ShouldReturnProperInterest) { + // given + const auto testee = makeTestee(); + + // when + const TopicToPartitionsMap result = testee->interest(); + + // then + const TopicToPartitionsMap expected = {{"aaa", {0, 1}}, {"bbb", {10, 20}}}; + ASSERT_EQ(result, expected); +} + +TEST_F(FetchUnitTest, ShouldCleanupAfterTimer) { + // given + const auto testee = makeTestee(); + testee->startProcessing(); + + EXPECT_CALL(callback_processor_, removeCallback(_)); + EXPECT_CALL(dispatcher_, post(_)); + + // when + testee->markFinishedByTimer(); + + // then + ASSERT_TRUE(testee->finished()); +} + +// Helper method to generate records. +InboundRecordSharedPtr makeRecord() { return std::make_shared("aaa", 0, 0); } + +TEST_F(FetchUnitTest, ShouldReceiveRecords) { + // given + const auto testee = makeTestee(); + testee->startProcessing(); + + // Will be invoked by the third record (delivery was finished). + EXPECT_CALL(dispatcher_, post(_)); + // It is invoker that removes the callback - not us. + EXPECT_CALL(callback_processor_, removeCallback(_)).Times(0); + + // when - 1 + const auto res1 = testee->receive(makeRecord()); + // then - first record got stored. + ASSERT_EQ(res1, CallbackReply::AcceptedAndWantMore); + ASSERT_FALSE(testee->finished()); + + // when - 2 + const auto res2 = testee->receive(makeRecord()); + // then - second record got stored. + ASSERT_EQ(res2, CallbackReply::AcceptedAndWantMore); + ASSERT_FALSE(testee->finished()); + + // when - 3 + const auto res3 = testee->receive(makeRecord()); + // then - third record got stored and no more will be accepted. + ASSERT_EQ(res3, CallbackReply::AcceptedAndFinished); + ASSERT_TRUE(testee->finished()); + + // when - 4 + const auto res4 = testee->receive(makeRecord()); + // then - fourth record was rejected. + ASSERT_EQ(res4, CallbackReply::Rejected); +} + +TEST_F(FetchUnitTest, ShouldRejectRecordsAfterTimer) { + // given + const auto testee = makeTestee(); + testee->startProcessing(); + testee->markFinishedByTimer(); + + // when + const auto res = testee->receive(makeRecord()); + + // then + ASSERT_EQ(res, CallbackReply::Rejected); +} + +TEST_F(FetchUnitTest, ShouldUnregisterItselfWhenAbandoned) { + // given + const auto testee = makeTestee(); + testee->startProcessing(); + + EXPECT_CALL(callback_processor_, removeCallback(_)); + + // when + testee->abandon(); + + // then - expectations are met. +} + +TEST_F(FetchUnitTest, ShouldComputeAnswer) { + // given + const auto testee = makeTestee(); + testee->startProcessing(); + + std::vector ftr = {{"aaa", {}}, {"bbb", {}}}; + EXPECT_CALL(converter_, convert(_)).WillOnce(Return(ftr)); + + // when + const AbstractResponseSharedPtr answer = testee->computeAnswer(); + + // then + ASSERT_EQ(answer->metadata_.correlation_id_, TEST_CORRELATION_ID); + const auto response = std::dynamic_pointer_cast>(answer); + ASSERT_TRUE(response); + const std::vector responses = response->data_.responses_; + ASSERT_EQ(responses, ftr); +} + +} // namespace +} // namespace Mesh +} // namespace Kafka +} // namespace NetworkFilters +} // namespace Extensions +} // namespace Envoy diff --git a/contrib/kafka/filters/network/test/mesh/command_handlers/list_offsets_unit_test.cc b/contrib/kafka/filters/network/test/mesh/command_handlers/list_offsets_unit_test.cc index dc5bbd25ae19..4e3820876337 100644 --- a/contrib/kafka/filters/network/test/mesh/command_handlers/list_offsets_unit_test.cc +++ b/contrib/kafka/filters/network/test/mesh/command_handlers/list_offsets_unit_test.cc @@ -13,6 +13,7 @@ class MockAbstractRequestListener : public AbstractRequestListener { public: MOCK_METHOD(void, onRequest, (InFlightRequestSharedPtr)); MOCK_METHOD(void, onRequestReadyForAnswer, ()); + MOCK_METHOD(Event::Dispatcher&, dispatcher, ()); }; TEST(ListOffsetsTest, shouldBeAlwaysReadyForAnswer) { diff --git a/contrib/kafka/filters/network/test/mesh/command_handlers/metadata_unit_test.cc b/contrib/kafka/filters/network/test/mesh/command_handlers/metadata_unit_test.cc index 132946cd8401..e40aa2923e22 100644 --- a/contrib/kafka/filters/network/test/mesh/command_handlers/metadata_unit_test.cc +++ b/contrib/kafka/filters/network/test/mesh/command_handlers/metadata_unit_test.cc @@ -16,6 +16,7 @@ class MockAbstractRequestListener : public AbstractRequestListener { public: MOCK_METHOD(void, onRequest, (InFlightRequestSharedPtr)); MOCK_METHOD(void, onRequestReadyForAnswer, ()); + MOCK_METHOD(Event::Dispatcher&, dispatcher, ()); }; class MockUpstreamKafkaConfiguration : public UpstreamKafkaConfiguration { diff --git a/contrib/kafka/filters/network/test/mesh/command_handlers/produce_unit_test.cc b/contrib/kafka/filters/network/test/mesh/command_handlers/produce_unit_test.cc index f2e1abf4afa1..f98fec3fb5e1 100644 --- a/contrib/kafka/filters/network/test/mesh/command_handlers/produce_unit_test.cc +++ b/contrib/kafka/filters/network/test/mesh/command_handlers/produce_unit_test.cc @@ -23,6 +23,7 @@ class MockAbstractRequestListener : public AbstractRequestListener { public: MOCK_METHOD(void, onRequest, (InFlightRequestSharedPtr)); MOCK_METHOD(void, onRequestReadyForAnswer, ()); + MOCK_METHOD(Event::Dispatcher&, dispatcher, ()); }; class MockRecordExtractor : public RecordExtractor { @@ -53,7 +54,6 @@ class ProduceUnitTest : public testing::Test { // (as ProduceRequests with no topics/records make no sense). TEST_F(ProduceUnitTest, ShouldHandleProduceRequestWithNoRecords) { // given - MockRecordExtractor extractor; const std::vector records = {}; EXPECT_CALL(extractor_, extractRecords(_)).WillOnce(Return(records)); diff --git a/contrib/kafka/filters/network/test/mesh/request_processor_unit_test.cc b/contrib/kafka/filters/network/test/mesh/request_processor_unit_test.cc index e01534a1fbe4..ece19dd6d413 100644 --- a/contrib/kafka/filters/network/test/mesh/request_processor_unit_test.cc +++ b/contrib/kafka/filters/network/test/mesh/request_processor_unit_test.cc @@ -23,6 +23,7 @@ class MockAbstractRequestListener : public AbstractRequestListener { public: MOCK_METHOD(void, onRequest, (InFlightRequestSharedPtr)); MOCK_METHOD(void, onRequestReadyForAnswer, ()); + MOCK_METHOD(Event::Dispatcher&, dispatcher, ()); }; class MockUpstreamKafkaFacade : public UpstreamKafkaFacade { diff --git a/distribution/BUILD b/distribution/BUILD index 0001d7e1cb23..8862eb4a174a 100644 --- a/distribution/BUILD +++ b/distribution/BUILD @@ -10,9 +10,9 @@ MAINTAINER = "Envoy maintainers " genrule( name = "envoy-bin", - srcs = ["//source/exe:envoy-static.stripped"], + srcs = ["//source/exe:envoy-static"], outs = ["envoy"], - cmd = "cp -L $< $@", + cmd = "strip $(location //source/exe:envoy-static) -o $@", ) envoy_pkg_distros( diff --git a/docs/BUILD b/docs/BUILD index 713507141a14..7e757fd06af9 100644 --- a/docs/BUILD +++ b/docs/BUILD @@ -21,6 +21,7 @@ filegroup( srcs = glob( ["root/**/*.yaml"], exclude = [ + "root/_configs/go/*", "root/**/envoy-dynamic-cds-demo.yaml", "root/**/envoy-dynamic-lds-demo.yaml", "root/**/envoy-dynamic-filesystem-demo.yaml", @@ -39,6 +40,14 @@ filegroup( }), ) +filegroup( + name = "contrib_configs", + srcs = select({ + "//bazel:windows_x86_64": [], + "//conditions:default": glob(["root/_configs/go/*.yaml"]), + }), +) + filegroup( name = "proto_examples", srcs = glob(["root/**/*.pb"]), diff --git a/docs/root/_configs/go/golang-with-config.yaml b/docs/root/_configs/go/golang-with-config.yaml new file mode 100644 index 000000000000..d00da5867028 --- /dev/null +++ b/docs/root/_configs/go/golang-with-config.yaml @@ -0,0 +1,54 @@ +static_resources: + listeners: + - name: listener_0 + address: + socket_address: + protocol: TCP + address: 0.0.0.0 + port_value: 8080 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + codec_type: AUTO + stat_prefix: ingress_http + http_filters: + - name: envoy.filters.http.golang + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.Config + library_id: my-configurable-plugin-id + library_path: "lib/my_configurable_plugin.so" + plugin_name: my_configurable_plugin + plugin_config: + "@type": type.googleapis.com/xds.type.v3.TypedStruct + value: + foo: bar + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + route_config: + name: route_0 + virtual_hosts: + - name: service_0 + domains: ["*"] + routes: + - match: + prefix: "/" + route: + cluster: cluster_0 + + clusters: + - name: cluster_0 + type: LOGICAL_DNS + lb_policy: ROUND_ROBIN + dns_lookup_family: V4_ONLY + load_assignment: + cluster_name: service_cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: service.local + port_value: 80 diff --git a/docs/root/_configs/go/golang-with-per-route-config.yaml b/docs/root/_configs/go/golang-with-per-route-config.yaml new file mode 100644 index 000000000000..f44a43a907da --- /dev/null +++ b/docs/root/_configs/go/golang-with-per-route-config.yaml @@ -0,0 +1,59 @@ +static_resources: + listeners: + - name: listener_0 + address: + socket_address: + protocol: TCP + address: 0.0.0.0 + port_value: 8080 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + codec_type: AUTO + stat_prefix: ingress_http + http_filters: + - name: envoy.filters.http.golang + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.Config + library_id: my-configurable-plugin-id + library_path: "lib/my_configurable_plugin.so" + plugin_name: my_configurable_plugin + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + route_config: + name: route_0 + virtual_hosts: + - name: service_0 + domains: ["*"] + routes: + - match: + prefix: "/" + route: + cluster: cluster_0 + typed_per_filter_config: + envoy.filters.http.golang: + "@type": type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.ConfigsPerRoute + plugins_config: + my_configurable_plugin: + config: + "@type": type.googleapis.com/xds.type.v3.TypedStruct + value: + foo: route_bar + + clusters: + - name: cluster_0 + type: LOGICAL_DNS + lb_policy: ROUND_ROBIN + dns_lookup_family: V4_ONLY + load_assignment: + cluster_name: service_cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: service.local + port_value: 80 diff --git a/docs/root/_configs/go/golang-with-per-virtualhost-config.yaml b/docs/root/_configs/go/golang-with-per-virtualhost-config.yaml new file mode 100644 index 000000000000..cfea9d10c68f --- /dev/null +++ b/docs/root/_configs/go/golang-with-per-virtualhost-config.yaml @@ -0,0 +1,59 @@ +static_resources: + listeners: + - name: listener_0 + address: + socket_address: + protocol: TCP + address: 0.0.0.0 + port_value: 8080 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + codec_type: AUTO + stat_prefix: ingress_http + http_filters: + - name: envoy.filters.http.golang + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.Config + library_id: my-configurable-plugin-id + library_path: "lib/my_configurable_plugin.so" + plugin_name: my_configurable_plugin + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + route_config: + name: route_0 + virtual_hosts: + - name: service_0 + domains: ["*"] + routes: + - match: + prefix: "/" + route: + cluster: cluster_0 + typed_per_filter_config: + envoy.filters.http.golang: + "@type": type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.ConfigsPerRoute + plugins_config: + my_configurable_plugin: + config: + "@type": type.googleapis.com/xds.type.v3.TypedStruct + value: + foo: vh_bar + + clusters: + - name: cluster_0 + type: LOGICAL_DNS + lb_policy: ROUND_ROBIN + dns_lookup_family: V4_ONLY + load_assignment: + cluster_name: service_cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: service.local + port_value: 80 diff --git a/docs/root/_configs/go/golang.yaml b/docs/root/_configs/go/golang.yaml new file mode 100644 index 000000000000..5b44cbfed966 --- /dev/null +++ b/docs/root/_configs/go/golang.yaml @@ -0,0 +1,59 @@ +static_resources: + listeners: + - name: listener_0 + address: + socket_address: + protocol: TCP + address: 0.0.0.0 + port_value: 8080 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + codec_type: AUTO + stat_prefix: ingress_http + http_filters: + - name: envoy.filters.http.golang + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.Config + library_id: my-plugin-id + library_path: "lib/my_plugin.so" + plugin_name: my_plugin + - name: envoy.filters.http.header_to_metadata + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.header_to_metadata.v3.Config + - name: envoy.filters.http.golang + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.Config + library_id: my-other-plugin-id + library_path: "lib/my_other_plugin.so" + plugin_name: my_other_plugin + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + route_config: + name: route_0 + virtual_hosts: + - name: service_0 + domains: ["*"] + routes: + - match: + prefix: "/" + route: + cluster: cluster_0 + + clusters: + - name: cluster_0 + type: LOGICAL_DNS + lb_policy: ROUND_ROBIN + dns_lookup_family: V4_ONLY + load_assignment: + cluster_name: service_cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: service.local + port_value: 80 diff --git a/docs/root/configuration/http/http_conn_man/response_code_details.rst b/docs/root/configuration/http/http_conn_man/response_code_details.rst index 73551a9f1c73..0c083f0565c5 100644 --- a/docs/root/configuration/http/http_conn_man/response_code_details.rst +++ b/docs/root/configuration/http/http_conn_man/response_code_details.rst @@ -21,7 +21,7 @@ Below are the list of reasons the HttpConnectionManager or Router filter may sen absolute_path_rejected, The request was rejected due to using an absolute path on a route not supporting them. admin_filter_response, The response was generated by the admin filter. cluster_not_found, The request was rejected by the router filter because there was no cluster found for the selected route. - downstream_local_disconnect, The client connection was locally closed for an unspecified reason. + downstream_local_disconnect, The client connection was locally closed for the provided reason. downstream_remote_disconnect, The client disconnected unexpectedly. duration_timeout, The max connection duration was exceeded. direct_response, A direct response was generated by the router filter. diff --git a/docs/root/configuration/http/http_filters/golang_filter.rst b/docs/root/configuration/http/http_filters/golang_filter.rst index b5c3b6c38bdc..2cfd19bf4b3b 100644 --- a/docs/root/configuration/http/http_filters/golang_filter.rst +++ b/docs/root/configuration/http/http_filters/golang_filter.rst @@ -3,37 +3,122 @@ Golang ====== -Overview --------- - The HTTP Golang filter allows `Golang `_ to be run during both the request -and response flows and makes it easier to extend Envoy. See the `Envoy's GoLang extension proposal documentation -`_ for more details. +and response flows and makes it easier to extend Envoy. + +Go plugins used by this filter can be recompiled independently of Envoy. + +See the `Envoy's Golang extension proposal documentation +`_ +for more details on the filter's implementation. + +.. warning:: + The Envoy Golang filter is designed to be run with the `GODEBUG=cgocheck=0` environment variable set. + + This disables the cgo pointer check. + + Failure to set this environment variable will cause Envoy to crash! + +Developing a Go plugin +---------------------- + +Envoy's Go plugins must implement the :repo:`StreamFilter API `. + +Building a Go plugin +~~~~~~~~~~~~~~~~~~~~ + +.. attention:: + When building a Go plugin dynamic library, you **must** use a Go version consistent + with Envoy's version of glibc. + +One way to ensure a compatible Go version is to use the Go binary provided by Envoy's bazel setup: + +.. code-block:: console + $ bazel run @go_sdk//:bin/go -- version + ... + go version goX.YZ linux/amd64 + +For example, to build the ``.so`` for a ``foo`` plugin, you might run: + +.. code-block:: console + + $ bazel run @go_sdk//:bin/go build --buildmode=c-shared -v -o path/to/output/libfoo.so path/to/src/foo Configuration ------------- -* This filter should be configured with the type URL ``type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.Config``. -* :ref:`v3 API reference ` +.. tip:: + This filter should be configured with the type URL + :ref:`type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.Config `. -A simple example of configuring Golang HTTP filter that default `echo` go plugin as follow: +A prebuilt Golang HTTP filter ``my_plugin.so`` might be configured as follows: -.. code-block:: yaml +.. literalinclude:: /_configs/go/golang.yaml + :language: yaml + :linenos: + :lines: 16-23 + :lineno-start: 16 + :emphasize-lines: 2-7 + :caption: :download:`golang.yaml ` - name: envoy.filters.http.golang - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.Config - library_id: echo-id - library_path: "contrib/golang/filters/http/test/test_data/echo/filter.so" - plugin_name: echo +An :ref:`HttpConnectionManager +` can +have multiple Go plugins in its :ref:`http_filters +`: -.. attention:: +.. literalinclude:: /_configs/go/golang.yaml + :language: yaml + :linenos: + :lines: 16-33 + :lineno-start: 16 + :emphasize-lines: 2-7, 9-14 + :caption: :download:`golang.yaml ` + +This can be useful if, for example, you have one plugin that provides authentication, and another +that provides connection limiting. + +Extensible plugin configuration +------------------------------- + +Envoy's Go plugins can specify and use their own configuration. + +Below is a very simple example of how such a plugin might be configured in Envoy: + +.. literalinclude:: /_configs/go/golang-with-config.yaml + :language: yaml + :linenos: + :lines: 17-27 + :lineno-start: 17 + :emphasize-lines: 7-10 + :caption: :download:`golang-with-config.yaml ` + +See the :repo:`StreamFilter API ` +for more information about how the plugin's configuration data can be accessed. + +Per-route plugin configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Go plugins can be configured on a +:ref:`per-route ` basis, as in the example below: + +.. literalinclude:: /_configs/go/golang-with-per-route-config.yaml + :language: yaml + :linenos: + :lines: 16-44 + :lineno-start: 16 + :emphasize-lines: 2-7, 21-29 + :caption: :download:`golang-with-per-route-config.yaml ` - The go plugin dynamic library built needs to be consistent with the envoy version of glibc. +Per-virtualhost plugin configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Complete example ----------------- +Go plugins can also be configured on a +:ref:`per-virtualhost ` basis: -A complete example using Docker is available in :repo:`/contrib/golang/filters/http/test/test_data/echo` and run -``bazel build //contrib/golang/filters/http/test/test_data/echo:filter.so``. +.. literalinclude:: /_configs/go/golang-with-per-virtualhost-config.yaml + :language: yaml + :linenos: + :lines: 16-44 + :emphasize-lines: 2-7, 21-29 + :caption: :download:`golang-with-per-virtualhost-config.yaml ` diff --git a/docs/root/configuration/http/http_filters/lua_filter.rst b/docs/root/configuration/http/http_filters/lua_filter.rst index e75dc1b69102..bba7150edce0 100644 --- a/docs/root/configuration/http/http_filters/lua_filter.rst +++ b/docs/root/configuration/http/http_filters/lua_filter.rst @@ -423,6 +423,8 @@ the supported keys are: If the *return_duplicate_headers* is set to false (default), the returned *headers* is table with value type of string. If the *return_duplicate_headers* is set to true, the returned *headers* is table with value type of string or value type of table. +- *send_xff* is a boolean flag that decides whether the *x-forwarded-for* header is sent to target server. + The default value is true. For example, the following upstream response headers have repeated headers. diff --git a/docs/root/configuration/observability/access_log/usage.rst b/docs/root/configuration/observability/access_log/usage.rst index f7d3e41476c8..a6fe387aa35b 100644 --- a/docs/root/configuration/observability/access_log/usage.rst +++ b/docs/root/configuration/observability/access_log/usage.rst @@ -387,6 +387,19 @@ The following command operators are supported: Renders a numeric value in typed JSON logs. +%ROUNDTRIP_DURATION% + HTTP/3 (QUIC) + Total duration in milliseconds of the request from the start time to receiving the final ack from + the downstream. + + HTTP/1 and HTTP/2 + Not implemented ("-"). + + TCP/UDP + Not implemented ("-"). + + Renders a numeric value in typed JSON logs. + %RESPONSE_TX_DURATION% HTTP Total duration in milliseconds of the request from the first byte read from the upstream host to the last diff --git a/envoy/api/os_sys_calls.h b/envoy/api/os_sys_calls.h index 6a26a19edc70..e7603a281f2a 100644 --- a/envoy/api/os_sys_calls.h +++ b/envoy/api/os_sys_calls.h @@ -39,8 +39,6 @@ struct InterfaceAddress { using InterfaceAddressVector = std::vector; -using AlternateGetifaddrs = std::function; - class OsSysCalls { public: virtual ~OsSysCalls() = default; @@ -277,16 +275,6 @@ class OsSysCalls { */ virtual SysCallIntResult getifaddrs(InterfaceAddressVector& interfaces) PURE; - /** - * allows a platform to override getifaddrs or provide an implementation if one does not exist - * natively. - * - * @arg alternate_getifaddrs function pointer to implementation. - */ - virtual void setAlternateGetifaddrs(AlternateGetifaddrs alternate_getifaddrs) { - alternate_getifaddrs_ = alternate_getifaddrs; - } - /** * @see man getaddrinfo */ @@ -297,9 +285,6 @@ class OsSysCalls { * @see man freeaddrinfo */ virtual void freeaddrinfo(addrinfo* res) PURE; - -protected: - absl::optional alternate_getifaddrs_{}; }; using OsSysCallsPtr = std::unique_ptr; diff --git a/envoy/http/BUILD b/envoy/http/BUILD index 5c675b0e44d6..4fe58c22ded9 100644 --- a/envoy/http/BUILD +++ b/envoy/http/BUILD @@ -29,6 +29,7 @@ envoy_cc_library( hdrs = ["async_client.h"], external_deps = ["abseil_optional"], deps = [ + ":filter_interface", ":message_interface", "//envoy/event:dispatcher_interface", "//envoy/tracing:http_tracer_interface", @@ -46,6 +47,7 @@ envoy_cc_library( ":metadata_interface", ":protocol_interface", ":stream_reset_handler_interface", + "//envoy/access_log:access_log_interface", "//envoy/buffer:buffer_interface", "//envoy/grpc:status", "//envoy/network:address_interface", diff --git a/envoy/http/async_client.h b/envoy/http/async_client.h index 36a8430cf509..6e1e0fd00b7d 100644 --- a/envoy/http/async_client.h +++ b/envoy/http/async_client.h @@ -3,8 +3,11 @@ #include #include +#include "envoy/buffer/buffer.h" #include "envoy/config/route/v3/route_components.pb.h" #include "envoy/event/dispatcher.h" +#include "envoy/http/filter.h" +#include "envoy/http/header_map.h" #include "envoy/http/message.h" #include "envoy/stream_info/stream_info.h" #include "envoy/tracing/http_tracer.h" @@ -129,6 +132,8 @@ class AsyncClient { virtual void onReset() PURE; }; + using StreamDestructorCallbacks = std::function; + /** * An in-flight HTTP stream. */ @@ -163,6 +168,32 @@ class AsyncClient { */ virtual void reset() PURE; + /*** + * Register callback to be called on stream destruction. This callback must persist beyond the + * lifetime of the stream or be unregistered via removeDestructorCallback. If there's already a + * destructor callback registered, this method will ASSERT-fail. + */ + virtual void setDestructorCallback(StreamDestructorCallbacks callback) PURE; + + /*** + * Remove previously set destructor callback. Calling this without having previously set a + * Destructor callback will ASSERT-fail. + */ + virtual void removeDestructorCallback() PURE; + + /*** + * Register a callback to be called when high/low write buffer watermark events occur on the + * stream. This callback must persist beyond the lifetime of the stream or be unregistered via + * removeWatermarkCallbacks. If there's already a watermark callback registered, this method + * will ASSERT-fail. + */ + virtual void setWatermarkCallbacks(DecoderFilterWatermarkCallbacks& callbacks) PURE; + + /*** + * Remove previously set watermark callbacks. + */ + virtual void removeWatermarkCallbacks() PURE; + /*** * @returns if the stream has enough buffered outbound data to be over the configured buffer * limits @@ -170,6 +201,20 @@ class AsyncClient { virtual bool isAboveWriteBufferHighWatermark() const PURE; }; + /*** + * An in-flight HTTP request to which additional data and trailers can be sent, as well as resets + * and other stream-cancelling events. Must be terminated by sending trailers or data with + * end_stream. + */ + class OngoingRequest : public virtual Request, public virtual Stream { + public: + /*** + * Takes ownership of trailers, and sends it to the underlying stream. + * @param trailers owned trailers to pass to upstream. + */ + virtual void captureAndSendTrailers(RequestTrailerMapPtr&& trailers) PURE; + }; + virtual ~AsyncClient() = default; /** @@ -216,6 +261,16 @@ class AsyncClient { return *this; } + // Set buffer restriction and accounting for the stream. + StreamOptions& setBufferAccount(const Buffer::BufferMemoryAccountSharedPtr& account) { + account_ = account; + return *this; + } + StreamOptions& setBufferLimit(uint32_t limit) { + buffer_limit_ = limit; + return *this; + } + // this should be done with setBufferedBodyForRetry=true ? StreamOptions& setRetryPolicy(const envoy::config::route::v3::RetryPolicy& p) { retry_policy = p; @@ -258,6 +313,11 @@ class AsyncClient { envoy::config::core::v3::Metadata metadata; + // Buffer memory account for tracking bytes. + Buffer::BufferMemoryAccountSharedPtr account_{nullptr}; + + absl::optional buffer_limit_; + absl::optional retry_policy; OptRef filter_config_; @@ -318,6 +378,14 @@ class AsyncClient { sampled_ = sampled; return *this; } + RequestOptions& setBufferAccount(const Buffer::BufferMemoryAccountSharedPtr& account) { + account_ = account; + return *this; + } + RequestOptions& setBufferLimit(uint32_t limit) { + buffer_limit_ = limit; + return *this; + } // For gmock test bool operator==(const RequestOptions& src) const { @@ -350,7 +418,20 @@ class AsyncClient { const RequestOptions& options) PURE; /** - * Start an HTTP stream asynchronously. + * Starts a new OngoingRequest asynchronously with the given headers. + * + * @param request_headers headers to send. + * @param callbacks the callbacks to be notified of request status. + * @param options the data struct to control the request sending. + * @return a request handle or nullptr if no request could be created. See note attached to + * `send`. Calling startRequest will not trigger end stream. For header-only requests, `send` + * should be called instead. + */ + virtual OngoingRequest* startRequest(RequestHeaderMapPtr&& request_headers, Callbacks& callbacks, + const RequestOptions& options) PURE; + + /** + * Start an HTTP stream asynchronously, without an associated HTTP request. * @param callbacks the callbacks to be notified of stream status. * @param options the data struct to control the stream. * @return a stream handle or nullptr if no stream could be started. NOTE: In this case diff --git a/envoy/http/codec.h b/envoy/http/codec.h index 236a4da6f250..7a906210ab09 100644 --- a/envoy/http/codec.h +++ b/envoy/http/codec.h @@ -4,6 +4,7 @@ #include #include +#include "envoy/access_log/access_log.h" #include "envoy/buffer/buffer.h" #include "envoy/common/pure.h" #include "envoy/grpc/status.h" @@ -174,6 +175,22 @@ class ResponseEncoder : public virtual StreamEncoder { * @param decoder new request decoder. */ virtual void setRequestDecoder(RequestDecoder& decoder) PURE; + + /** + * Set headers, trailers, and stream info for deferred logging. This allows HCM to hand off + * stream-level details to the codec for logging after the stream may be destroyed (e.g. on + * receiving the final ack packet from the client). Note that headers and trailers are const + * as they will not be modified after this point. + * @param request_header_map Request headers for this stream. + * @param response_header_map Response headers for this stream. + * @param response_trailer_map Response trailers for this stream. + * @param stream_info Stream info for this stream. + */ + virtual void + setDeferredLoggingHeadersAndTrailers(Http::RequestHeaderMapConstSharedPtr request_header_map, + Http::ResponseHeaderMapConstSharedPtr response_header_map, + Http::ResponseTrailerMapConstSharedPtr response_trailer_map, + StreamInfo::StreamInfo& stream_info) PURE; }; /** @@ -211,7 +228,7 @@ class RequestDecoder : public virtual StreamDecoder { * @param headers supplies the decoded headers map. * @param end_stream supplies whether this is a header only request. */ - virtual void decodeHeaders(RequestHeaderMapPtr&& headers, bool end_stream) PURE; + virtual void decodeHeaders(RequestHeaderMapSharedPtr&& headers, bool end_stream) PURE; /** * Called with a decoded trailers frame. This implicitly ends the stream. @@ -236,6 +253,11 @@ class RequestDecoder : public virtual StreamDecoder { * @return StreamInfo::StreamInfo& the stream_info for this stream. */ virtual StreamInfo::StreamInfo& streamInfo() PURE; + + /** + * @return List of shared pointers to access loggers for this stream. + */ + virtual std::list accessLogHandlers() PURE; }; /** diff --git a/envoy/http/filter.h b/envoy/http/filter.h index 9741a896e957..defb3c04306c 100644 --- a/envoy/http/filter.h +++ b/envoy/http/filter.h @@ -429,11 +429,33 @@ class StreamFilterCallbacks { virtual OptRef downstreamCallbacks() PURE; }; +class DecoderFilterWatermarkCallbacks { +public: + virtual ~DecoderFilterWatermarkCallbacks() = default; + + /** + * Called when the buffer for a decoder filter or any buffers the filter sends data to go over + * their high watermark. + * + * In the case of a filter such as the router filter, which spills into multiple buffers (codec, + * connection etc.) this may be called multiple times. Any such filter is responsible for calling + * the low watermark callbacks an equal number of times as the respective buffers are drained. + */ + virtual void onDecoderFilterAboveWriteBufferHighWatermark() PURE; + + /** + * Called when a decoder filter or any buffers the filter sends data to go from over its high + * watermark to under its low watermark. + */ + virtual void onDecoderFilterBelowWriteBufferLowWatermark() PURE; +}; /** - * Stream decoder filter callbacks add additional callbacks that allow a decoding filter to restart - * decoding if they decide to hold data (e.g. for buffering or rate limiting). + * Stream decoder filter callbacks add additional callbacks that allow a + * decoding filter to restart decoding if they decide to hold data (e.g. for + * buffering or rate limiting). */ -class StreamDecoderFilterCallbacks : public virtual StreamFilterCallbacks { +class StreamDecoderFilterCallbacks : public virtual StreamFilterCallbacks, + public virtual DecoderFilterWatermarkCallbacks { public: /** * Continue iterating through the filter chain with buffered headers and body data. This routine @@ -626,22 +648,6 @@ class StreamDecoderFilterCallbacks : public virtual StreamFilterCallbacks { */ virtual void encodeMetadata(MetadataMapPtr&& metadata_map) PURE; - /** - * Called when the buffer for a decoder filter or any buffers the filter sends data to go over - * their high watermark. - * - * In the case of a filter such as the router filter, which spills into multiple buffers (codec, - * connection etc.) this may be called multiple times. Any such filter is responsible for calling - * the low watermark callbacks an equal number of times as the respective buffers are drained. - */ - virtual void onDecoderFilterAboveWriteBufferHighWatermark() PURE; - - /** - * Called when a decoder filter or any buffers the filter sends data to go from over its high - * watermark to under its low watermark. - */ - virtual void onDecoderFilterBelowWriteBufferLowWatermark() PURE; - /** * This routine can be called by a filter to subscribe to watermark events on the downstream * stream and downstream connection. diff --git a/envoy/http/header_map.h b/envoy/http/header_map.h index 4f53c91c5696..0c8ca1c5d706 100644 --- a/envoy/http/header_map.h +++ b/envoy/http/header_map.h @@ -718,6 +718,8 @@ class RequestHeaderMap INLINE_REQ_NUMERIC_HEADERS(DEFINE_INLINE_NUMERIC_HEADER) }; using RequestHeaderMapPtr = std::unique_ptr; +using RequestHeaderMapSharedPtr = std::shared_ptr; +using RequestHeaderMapConstSharedPtr = std::shared_ptr; using RequestHeaderMapOptRef = OptRef; using RequestHeaderMapOptConstRef = OptRef; @@ -748,6 +750,8 @@ class ResponseHeaderMap INLINE_RESP_NUMERIC_HEADERS(DEFINE_INLINE_NUMERIC_HEADER) }; using ResponseHeaderMapPtr = std::unique_ptr; +using ResponseHeaderMapSharedPtr = std::shared_ptr; +using ResponseHeaderMapConstSharedPtr = std::shared_ptr; using ResponseHeaderMapOptRef = OptRef; using ResponseHeaderMapOptConstRef = OptRef; @@ -757,6 +761,8 @@ class ResponseTrailerMap public HeaderMap, public CustomInlineHeaderBase {}; using ResponseTrailerMapPtr = std::unique_ptr; +using ResponseTrailerMapSharedPtr = std::shared_ptr; +using ResponseTrailerMapConstSharedPtr = std::shared_ptr; using ResponseTrailerMapOptRef = OptRef; using ResponseTrailerMapOptConstRef = OptRef; diff --git a/envoy/network/connection.h b/envoy/network/connection.h index 554bee0e1174..d6fbfda0cdc7 100644 --- a/envoy/network/connection.h +++ b/envoy/network/connection.h @@ -135,9 +135,17 @@ class Connection : public Event::DeferredDeletable, /** * Close the connection. + * @param type the connection close type. */ virtual void close(ConnectionCloseType type) PURE; + /** + * Close the connection. + * @param type the connection close type. + * @param details the reason the connection is being closed. + */ + virtual void close(ConnectionCloseType type, absl::string_view details) PURE; + /** * @return Event::Dispatcher& the dispatcher backing this connection. */ @@ -311,11 +319,17 @@ class Connection : public Event::DeferredDeletable, virtual void setDelayedCloseTimeout(std::chrono::milliseconds timeout) PURE; /** - * @return std::string the failure reason of the underlying transport socket, if no failure - * occurred an empty string is returned. + * @return absl::string_view the failure reason of the underlying transport socket, if no failure + * occurred an empty string view is returned. */ virtual absl::string_view transportFailureReason() const PURE; + /** + * @return absl::string_view the local close reason of the underlying socket, if local close + * did not occur an empty string view is returned. + */ + virtual absl::string_view localCloseReason() const PURE; + /** * Instructs the connection to start using secure transport. * Note: Not all underlying transport sockets support such operation. diff --git a/envoy/network/proxy_protocol.h b/envoy/network/proxy_protocol.h index 12f7323c4ccc..04267db8f7e3 100644 --- a/envoy/network/proxy_protocol.h +++ b/envoy/network/proxy_protocol.h @@ -1,13 +1,25 @@ #pragma once +#include +#include +#include + #include "envoy/network/address.h" namespace Envoy { namespace Network { +struct ProxyProtocolTLV { + const uint8_t type; + const std::vector value; +}; + +using ProxyProtocolTLVVector = std::vector; + struct ProxyProtocolData { const Network::Address::InstanceConstSharedPtr src_addr_; const Network::Address::InstanceConstSharedPtr dst_addr_; + const ProxyProtocolTLVVector tlv_vector_{}; std::string asStringForHash() const { return std::string(src_addr_ ? src_addr_->asString() : "null") + (dst_addr_ ? dst_addr_->asString() : "null"); diff --git a/envoy/network/socket.h b/envoy/network/socket.h index 680c712bf358..4fd3e92ff167 100644 --- a/envoy/network/socket.h +++ b/envoy/network/socket.h @@ -107,6 +107,11 @@ class ConnectionInfoProvider { * @return ja3 fingerprint hash of the downstream connection, if any. */ virtual absl::string_view ja3Hash() const PURE; + + /** + * @return roundTripTime of the connection + */ + virtual const absl::optional& roundTripTime() const PURE; }; class ConnectionInfoSetter : public ConnectionInfoProvider { @@ -168,6 +173,11 @@ class ConnectionInfoSetter : public ConnectionInfoProvider { * @param JA3 fingerprint. */ virtual void setJA3Hash(const absl::string_view ja3_hash) PURE; + + /** + * @param milliseconds of round trip time of previous connection + */ + virtual void setRoundTripTime(std::chrono::milliseconds roundTripTime) PURE; }; using ConnectionInfoSetterSharedPtr = std::shared_ptr; diff --git a/envoy/router/shadow_writer.h b/envoy/router/shadow_writer.h index ffebed86d0ba..89f2920e8448 100644 --- a/envoy/router/shadow_writer.h +++ b/envoy/router/shadow_writer.h @@ -23,10 +23,24 @@ class ShadowWriter { * Shadow a request. * @param cluster supplies the cluster name to shadow to. * @param message supplies the complete request to shadow. - * @param timeout supplies the shadowed request timeout. + * @param options supplies the request options for the underlying asynchronous request. */ virtual void shadow(const std::string& cluster, Http::RequestMessagePtr&& request, const Http::AsyncClient::RequestOptions& options) PURE; + + /** + * Initialize shadowing a request. Differs from the above in that additional + * data can be passed to the returned handle after the headers have been sent. + * @param cluster supplies the cluster name to shadow to. + * @param headers supplies the headers for initializing the shadow. + * @param options supplies the request options for the underlying asynchronous request. + * @return OngoingRequest* pointer owned by the AsyncClient which can have additional data and + * trailers sent to it. Can be null if the stream is immediately + * cancelled. + */ + virtual Http::AsyncClient::OngoingRequest* + streamingShadow(const std::string& cluster, Http::RequestHeaderMapPtr&& headers, + const Http::AsyncClient::RequestOptions& options) PURE; }; using ShadowWriterPtr = std::unique_ptr; diff --git a/envoy/stats/scope.h b/envoy/stats/scope.h index e30465ad22b8..1116281d9302 100644 --- a/envoy/stats/scope.h +++ b/envoy/stats/scope.h @@ -28,14 +28,6 @@ using TextReadoutOptConstRef = absl::optional; using ScopeSharedPtr = std::shared_ptr; -// TODO(#20911): Until 2022, scopes were generally captured by the creator as -// unique_ptr. This has changed to std::shared_ptr, and to make -// this transition work we made ScopePtr an alias for ScopeSharedPtr. All -// references in the Envoy repo are now removed, but there remain references in -// external repositories, so we'll leave this alias until we have some -// confidence that external repositories are cleaned up. -using ScopePtr ABSL_DEPRECATED("Use ScopeSharedPtr() instead.") = ScopeSharedPtr; - template using IterateFn = std::function&)>; /** diff --git a/envoy/stats/store.h b/envoy/stats/store.h index 38d7810cdddb..c44f51debca0 100644 --- a/envoy/stats/store.h +++ b/envoy/stats/store.h @@ -152,10 +152,6 @@ class Store { virtual bool iterate(const IterateFn& fn) const PURE; virtual bool iterate(const IterateFn& fn) const PURE; - // TODO(#24007): Remove this operator overload: it is not needed anymore. Once - // #24567, #24843, and #24861 have landed we can remove this API. - operator Scope&() { return *rootScope(); } - // Delegate some methods to the root scope; these are exposed to make it more // convenient to use stats_macros.h. We may consider dropping them if desired, // when we resolve #24007 or in the next follow-up. diff --git a/envoy/stream_info/stream_info.h b/envoy/stream_info/stream_info.h index c4a72b3221fe..87eaee4b9c2f 100644 --- a/envoy/stream_info/stream_info.h +++ b/envoy/stream_info/stream_info.h @@ -176,8 +176,8 @@ struct ResponseCodeDetailValues { const std::string FilterChainNotFound = "filter_chain_not_found"; // The client disconnected unexpectedly. const std::string DownstreamRemoteDisconnect = "downstream_remote_disconnect"; - // The client connection was locally closed for an unspecified reason. - const std::string DownstreamLocalDisconnect = "downstream_local_disconnect"; + // The client connection was locally closed for the given reason. + const std::string DownstreamLocalDisconnect = "downstream_local_disconnect({})"; // The max connection duration was exceeded. const std::string DurationTimeout = "duration_timeout"; // The max request downstream header duration was exceeded. @@ -205,6 +205,36 @@ struct ResponseCodeDetailValues { using ResponseCodeDetails = ConstSingleton; +/** + * Constants for the locally closing a connection. This is used in response code + * details field of StreamInfo for details sent by core (non-extension) code. + * This is incomplete as some details may be + * + * Custom extensions can define additional values provided they are appropriately + * scoped to avoid collisions. + */ +struct LocalCloseReasonValues { + const std::string DeferredCloseOnDrainedConnection = "deferred_close_on_drained_connection"; + const std::string IdleTimeoutOnConnection = "on_idle_timeout"; + const std::string CloseForConnectRequestOrTcpTunneling = + "close_for_connect_request_or_tcp_tunneling"; + const std::string Http2PingTimeout = "http2_ping_timeout"; + const std::string Http2ConnectionProtocolViolation = "http2_connection_protocol_violation"; + const std::string TransportSocketTimeout = "transport_socket_timeout"; + const std::string TriggeredDelayedCloseTimeout = "triggered_delayed_close_timeout"; + const std::string TcpProxyInitializationFailure = "tcp_initializion_failure:"; + const std::string TcpSessionIdleTimeout = "tcp_session_idle_timeout"; + const std::string MaxConnectionDurationReached = "max_connection_duration_reached"; + const std::string ClosingUpstreamTcpDueToDownstreamRemoteClose = + "closing_upstream_tcp_connection_due_to_downstream_remote_close"; + const std::string ClosingUpstreamTcpDueToDownstreamLocalClose = + "closing_upstream_tcp_connection_due_to_downstream_local_close"; + const std::string NonPooledTcpConnectionHostHealthFailure = + "non_pooled_tcp_connection_host_health_failure"; +}; + +using LocalCloseReasons = ConstSingleton; + struct UpstreamTiming { /** * Sets the time when the first byte of the request was sent upstream. @@ -285,6 +315,9 @@ class DownstreamTiming { absl::optional downstreamHandshakeComplete() const { return downstream_handshake_complete_; } + absl::optional lastDownstreamAckReceived() const { + return last_downstream_ack_received_; + } void onLastDownstreamRxByteReceived(TimeSource& time_source) { ASSERT(!last_downstream_rx_byte_received_); @@ -302,6 +335,10 @@ class DownstreamTiming { // An existing value can be overwritten, e.g. in resumption case. downstream_handshake_complete_ = time_source.monotonicTime(); } + void onLastDownstreamAckReceived(TimeSource& time_source) { + ASSERT(!last_downstream_ack_received_); + last_downstream_ack_received_ = time_source.monotonicTime(); + } private: absl::flat_hash_map timings_; @@ -313,6 +350,8 @@ class DownstreamTiming { absl::optional last_downstream_tx_byte_sent_; // The time the TLS handshake completed. Set at connection level. absl::optional downstream_handshake_complete_; + // The time the final ack was received from the client. + absl::optional last_downstream_ack_received_; }; // Measure the number of bytes sent and received for a stream. diff --git a/envoy/upstream/cluster_manager.h b/envoy/upstream/cluster_manager.h index 0bb896daeacc..a28e19c9ac8c 100644 --- a/envoy/upstream/cluster_manager.h +++ b/envoy/upstream/cluster_manager.h @@ -369,15 +369,6 @@ class ClusterManager { */ virtual Config::SubscriptionFactory& subscriptionFactory() PURE; - /** - * Obtain multiplexed subscription factory for the cluster manager. - * This factory shares mux per management server per xds resource type which reduces number of - * concurrent active grpc streams. - * - * @return Config::SubscriptionFactory& multiplexed subscription factory. - */ - virtual Config::SubscriptionFactory& multiplexedSubscriptionFactory() PURE; - /** * Returns a struct with all the Stats::StatName objects needed by * Clusters. This helps factor out some relatively heavy name diff --git a/envoy/upstream/upstream.h b/envoy/upstream/upstream.h index 14f43b786691..2c88abf4674d 100644 --- a/envoy/upstream/upstream.h +++ b/envoy/upstream/upstream.h @@ -968,7 +968,7 @@ class ClusterInfo : public Http::FilterChainFactory { /** * @return the type of cluster, only used for custom discovery types. */ - virtual const absl::optional& + virtual OptRef clusterType() const PURE; /** @@ -1007,8 +1007,7 @@ class ClusterInfo : public Http::FilterChainFactory { * @return const absl::optional& the configuration * for the upstream, if a custom upstream is configured. */ - virtual const absl::optional& - upstreamConfig() const PURE; + virtual OptRef upstreamConfig() const PURE; /** * @return Whether the cluster is currently in maintenance mode and should not be routed to. diff --git a/examples/BUILD b/examples/BUILD index 6affb627bcae..278a9f2c154e 100644 --- a/examples/BUILD +++ b/examples/BUILD @@ -27,7 +27,7 @@ filegroup( ), ) -genrule( +filegroup( name = "contrib_configs", srcs = glob( [ @@ -39,13 +39,6 @@ genrule( "**/*docker-compose*.yaml", ], ), - outs = ["example_configs.tar"], - cmd = ( - "$(location //configs:configgen.sh) NO_CONFIGGEN $(@D) $(SRCS)" - ), - tools = [ - "//configs:configgen.sh", - ], ) filegroup( diff --git a/examples/ext_authz/auth/grpc-service/go.mod b/examples/ext_authz/auth/grpc-service/go.mod index 204dcd0ebd84..c4dcffeb156e 100644 --- a/examples/ext_authz/auth/grpc-service/go.mod +++ b/examples/ext_authz/auth/grpc-service/go.mod @@ -6,5 +6,5 @@ require ( github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1 github.com/golang/protobuf v1.5.2 google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 - google.golang.org/grpc v1.52.0 + google.golang.org/grpc v1.52.3 ) diff --git a/examples/ext_authz/auth/grpc-service/go.sum b/examples/ext_authz/auth/grpc-service/go.sum index 02f79fde5dec..96b3982fd332 100644 --- a/examples/ext_authz/auth/grpc-service/go.sum +++ b/examples/ext_authz/auth/grpc-service/go.sum @@ -988,8 +988,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.52.0 h1:kd48UiU7EHsV4rnLyOJRuP/Il/UHE7gdDAQ+SZI7nZk= -google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= +google.golang.org/grpc v1.52.3 h1:pf7sOysg4LdgBqduXveGKrcEwbStiK2rtfghdzlUYDQ= +google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/examples/ext_authz/auth/http-service/Dockerfile b/examples/ext_authz/auth/http-service/Dockerfile index 9e4d3b77ae8a..91a49d80f1bb 100644 --- a/examples/ext_authz/auth/http-service/Dockerfile +++ b/examples/ext_authz/auth/http-service/Dockerfile @@ -1,4 +1,4 @@ -FROM node:alpine@sha256:ab3603cb7934b21f1ffb522b1a1d538809516c6e4cd73b144716bc1830aad1a6 +FROM node:alpine@sha256:4619ec6c9a43ab4edfa12cf96745319c3ca43aff9dd630ab20e684dd3632318e COPY . /app CMD ["node", "/app/http-service/server"] diff --git a/examples/grpc-bridge/client/requirements.txt b/examples/grpc-bridge/client/requirements.txt index 6bcdeaff1d12..18e4c21e9385 100644 --- a/examples/grpc-bridge/client/requirements.txt +++ b/examples/grpc-bridge/client/requirements.txt @@ -61,82 +61,72 @@ grpcio==1.51.1 \ # via # -r requirements.in # grpcio-tools -grpcio-tools==1.48.2 \ - --hash=sha256:0119aabd9ceedfdf41b56b9fdc8284dd85a7f589d087f2694d743f346a368556 \ - --hash=sha256:072234859f6069dc43a6be8ad6b7d682f4ba1dc2e2db2ebf5c75f62eee0f6dfb \ - --hash=sha256:0fb6c1c1e56eb26b224adc028a4204b6ad0f8b292efa28067dff273bbc8b27c4 \ - --hash=sha256:189be2a9b672300ca6845d94016bdacc052fdbe9d1ae9e85344425efae2ff8ef \ - --hash=sha256:21ff50e321736eba22210bf9b94e05391a9ac345f26e7df16333dc75d63e74fb \ - --hash=sha256:3c8749dca04a8d302862ceeb1dfbdd071ee13b281395975f24405a347e5baa57 \ - --hash=sha256:4fa4300b1be59b046492ed3c5fdb59760bc6433f44c08f50de900f9552ec7461 \ - --hash=sha256:516eedd5eb7af6326050bc2cfceb3a977b9cc1144f283c43cc4956905285c912 \ - --hash=sha256:51be91b7c7056ff9ee48b1eccd4a2840b0126230803a5e09dfc082a5b16a91c1 \ - --hash=sha256:5410d6b601d1404835e34466bd8aee37213489b36ee1aad2276366e265ff29d4 \ - --hash=sha256:55fdebc73fb580717656b1bafa4f8eca448726a7aa22726a6c0a7895d2f0f088 \ - --hash=sha256:6cc298fbfe584de8876a85355efbcf796dfbcfac5948c9560f5df82e79336e2a \ - --hash=sha256:6d9753944e5a6b6b78b76ce9d2ae0fe3f748008c1849deb7fadcb64489d6553b \ - --hash=sha256:70564521e86a0de35ea9ac6daecff10cb46860aec469af65869974807ce8e98b \ - --hash=sha256:7307dd2408b82ea545ae63502ec03036b025f449568556ea9a056e06129a7a4e \ - --hash=sha256:80f450272316ca0924545f488c8492649ca3aeb7044d4bf59c426dcdee527f7c \ - --hash=sha256:84a84d601a238572d049d3108e04fe4c206536e81076d56e623bd525a1b38def \ - --hash=sha256:8588819b22d0de3aa1951e1991cc3e4b9aa105eecf6e3e24eb0a2fc8ab958b3e \ - --hash=sha256:8902a035708555cddbd61b5467cea127484362decc52de03f061a1a520fe90cd \ - --hash=sha256:8a5614251c46da07549e24f417cf989710250385e9d80deeafc53a0ee7df6325 \ - --hash=sha256:8e0d74403484eb77e8df2566a64b8b0b484b5c87903678c381634dd72f252d5e \ - --hash=sha256:92acc3e10ba2b0dcb90a88ae9fe1cc0ffba6868545207e4ff20ca95284f8e3c9 \ - --hash=sha256:9443f5c30bac449237c3cf99da125f8d6e6c01e17972bc683ee73b75dea95573 \ - --hash=sha256:9771d4d317dca029dfaca7ec9282d8afe731c18bc536ece37fd39b8a974cc331 \ - --hash=sha256:a415fbec67d4ff7efe88794cbe00cf548d0f0a5484cceffe0a0c89d47694c491 \ - --hash=sha256:a43d26714933f23de93ea0bf9c86c66a6ede709b8ca32e357f9e2181703e64ae \ - --hash=sha256:ace0035766fe01a1b096aa050be9f0a9f98402317e7aeff8bfe55349be32a407 \ - --hash=sha256:ae56f133b05b7e5d780ef7e032dd762adad7f3dc8f64adb43ff5bfabd659f435 \ - --hash=sha256:bdbbe63f6190187de5946891941629912ac8196701ed2253fa91624a397822ec \ - --hash=sha256:cabc8b0905cedbc3b2b7b2856334fa35cce3d4bc79ae241cacd8cca8940a5c85 \ - --hash=sha256:cb75bac0cd43858cb759ef103fe68f8c540cb58b63dda127e710228fec3007b8 \ - --hash=sha256:d18599ab572b2f15a8f3db49503272d1bb4fcabb4b4d1214ef03aca1816b20a0 \ - --hash=sha256:d18ef2adc05a8ef9e58ac46357f6d4ce7e43e077c7eda0a4425773461f9d0e6e \ - --hash=sha256:d598ccde6338b2cfbb3124f34c95f03394209013f9b1ed4a5360a736853b1c27 \ - --hash=sha256:d77e8b1613876e0d8fd17709509d4ceba13492816426bd156f7e88a4c47e7158 \ - --hash=sha256:d886a9e052a038642b3af5d18e6f2085d1656d9788e202dc23258cf3a751e7ca \ - --hash=sha256:d96e96ae7361aa51c9cd9c73b677b51f691f98df6086860fcc3c45852d96b0b0 \ - --hash=sha256:dcaaecdd5e847de5c1d533ea91522bf56c9e6b2dc98cdc0d45f0a1c26e846ea2 \ - --hash=sha256:e0403e095b343431195db1305248b50019ad55d3dd310254431af87e14ef83a2 \ - --hash=sha256:e20d7885a40e68a2bda92908acbabcdf3c14dd386c3845de73ba139e9df1f132 \ - --hash=sha256:e5bb396d63495667d4df42e506eed9d74fc9a51c99c173c04395fe7604c848f1 \ - --hash=sha256:e712a6d00606ad19abdeae852a7e521d6f6d0dcea843708fecf3a38be16a851e \ - --hash=sha256:e7e7668f89fd598c5469bb58e16bfd12b511d9947ccc75aec94da31f62bc3758 \ - --hash=sha256:f0feb4f2b777fa6377e977faa89c26359d4f31953de15e035505b92f41aa6906 \ - --hash=sha256:f75973a42c710999acd419968bc79f00327e03e855bbe82c6529e003e49af660 \ - --hash=sha256:f766050e491d0b3203b6b85638015f543816a2eb7d089fc04e86e00f6de0e31d +grpcio-tools==1.51.1 \ + --hash=sha256:048793747339f327ea091d8f022c6756d89713d8080dffde5ce7380cc348ea8e \ + --hash=sha256:055819992ddd30c642a7fd6f344a03747be3afa95cb910f8a2e5efaabd41cde5 \ + --hash=sha256:0a218f64e667f3332b74080bdc5440aaf0fa6700ae07a0b54ecf085aaef2aa9f \ + --hash=sha256:14e82c2b3ee7e300611c2c729d411b3b911e4cca5f4ec14787457a2fb72ff9d4 \ + --hash=sha256:15b8acf4eaa0ebe37e2f69108de49efd935b7abe9c7e58ba737490b99906aa76 \ + --hash=sha256:16b8b915625dc6eb2ea7efdfb06f1fae44a9066c9016453a2ca120c034f33090 \ + --hash=sha256:1c44b57a6770b78a1eafe355878ff1ec59a2fa07455a2cbd522c071eedae04d4 \ + --hash=sha256:2281180490c475d09b7aa05dabafa5e09de9902176931e7295113f636c2b5360 \ + --hash=sha256:27113b354f7587684eb55125733e6e5be1f489458abfe12344dabd918d8dcc54 \ + --hash=sha256:331a897306adeec3c67470431ea8d8b4972b689d32966f94506d91f4dac20952 \ + --hash=sha256:392ad4cd004f7b843cf7d916d9a15b2d6585965bfef235be1c88d8f8649777e5 \ + --hash=sha256:3a671466158ed74c07ee070fb940ed783acf59ba6e6e53cb4de8fd63819c6c7f \ + --hash=sha256:40ef70e8c5d0310dedff9af502b520b4c7e215bce94094527fb959150a0c594a \ + --hash=sha256:4957f1ffa16598aa5379505fcbaeb47d65693a46b0817f4ee61db76707092aeb \ + --hash=sha256:49624394805568acd7d767dea5a00d970fca5ad8f395fe0161eeea0de5133eba \ + --hash=sha256:4e3249a2ec435b3b972610c66c8a714c188844500d564c910f57a2771dc61978 \ + --hash=sha256:531586c5598a99658249f3c5e92826d6d2bb117abd6ffc88527d1e1d9eaef924 \ + --hash=sha256:566809d9942e78821b279af70f3cf159a328127f9f3d5fee8d83ad8b2d27b2fe \ + --hash=sha256:64d8ad369417759f5fdb8ffb7cbd6374fecc06ab51c9a226dee9bbd7d311c3b5 \ + --hash=sha256:674b340f2f7bb2adbc3f15144bd37ce5ea83239f78b68dbbd0ea3cba00107e2b \ + --hash=sha256:67b304282cad38642587ebae68617e450e1ad4fa1c0c8b19e9e30274dbb32716 \ + --hash=sha256:6b83d7fc2597c6d392c225177d1fbbcff74900f8cc40b33236987fd1ff841330 \ + --hash=sha256:6d6626a6e4dbe843df96dc8c08dd244d2191a75324f54bfa4ebaa3e76b0b1958 \ + --hash=sha256:6e72a30be1746ea0749a8486d0ca0120c0b2757fe84fc246a5144b1ef66d7b89 \ + --hash=sha256:794f26a09b70f4f101df5cf54c6c12dc1b65747ab1dee5bda02c2991389ade56 \ + --hash=sha256:79c06d2577cb4d977922bbf01234de3b20f73d1784d3cbe3179deee1bdb9a60b \ + --hash=sha256:87bc5f3e3698c65907d397003c64d25c3ea84e3d6aa46dac133bd98bf66835ee \ + --hash=sha256:8e62d23d3fed9d4f81738f98dd193dbd2e21aed4a8f0dd715e75b5439e649727 \ + --hash=sha256:98777b5031f1b3c58b688815ffa83435c103b2152c26eb144f80f4a4bb34addb \ + --hash=sha256:9906fb6bf6d9c30c23d85153f12d130f44325afe8f9ebe58aa7a6c82ecade9d8 \ + --hash=sha256:9dfe6c12b0e2c07f6a4a91a9912ef4e5bd007672533891a44e6f433ffbf7c3b1 \ + --hash=sha256:a66b3a5d18a7615f0f828b72e2d2935751459c89cc4725e56bdfb3d2cd93281f \ + --hash=sha256:aab24a342642329de38139cb26f8492882ca0d8551bb87f6530bcc613945a0d0 \ + --hash=sha256:b4fb8ed6d29f2d6cf03ef99ffaad635bbc132a59be77013691392fe557e67144 \ + --hash=sha256:c4649af7f5d9553975ee66b6bfae20a84be779f13e163fa835e782961895e63c \ + --hash=sha256:ccd37165d7a3e93f460096a2eb62b7a9c1ebe5c424eaee42d8e92740d0c8f6bc \ + --hash=sha256:d5e033c04b416afcddd5231b3ff94a34fb5d26fba2416eb940e69b05f22cfd25 \ + --hash=sha256:d7b186183515ad6b8584ffe4bd820b72b00f6e7d121fb1c36294edeea9092313 \ + --hash=sha256:d8cc862a1ad30f94528d66cc6f95fb9e659005e568313e54a23550535b649573 \ + --hash=sha256:de51a0a71845b854f6a5967756c893c96bd03e37f39e5dce87b4f409dac36ee2 \ + --hash=sha256:e9abc03d67793b1bf33dc766caa69a3333f9db029869ba6e8fc6cd9c251c0080 \ + --hash=sha256:ecf1494cb695afead36995534f787761ee33fb9e116b23030113a37fe6057a83 \ + --hash=sha256:f06bb0753b7cecbff154b523cfb8f45dee2c31b0a4c72bed7da44c57f1cba113 \ + --hash=sha256:f336ad9be661d92fa45940e74e8ff3d78e67ebe9b4f7ea8774b2d680c17aeb6c \ + --hash=sha256:f6caf36e7752728329a28f93afec7c4ec9015fc1c6e4460bd1eb0f3737e1c55a # via -r requirements.in idna==3.2 \ --hash=sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a \ --hash=sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3 # via requests -protobuf==3.20.3 \ - --hash=sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7 \ - --hash=sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c \ - --hash=sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2 \ - --hash=sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b \ - --hash=sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050 \ - --hash=sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9 \ - --hash=sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7 \ - --hash=sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454 \ - --hash=sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480 \ - --hash=sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469 \ - --hash=sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c \ - --hash=sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e \ - --hash=sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db \ - --hash=sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905 \ - --hash=sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b \ - --hash=sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86 \ - --hash=sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4 \ - --hash=sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402 \ - --hash=sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7 \ - --hash=sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4 \ - --hash=sha256:e67f9af1b607eb3a89aafc9bc68a9d1172aae788b2445cb9fd781bd97531f1f1 \ - --hash=sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99 \ - --hash=sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee +protobuf==4.21.12 \ + --hash=sha256:1f22ac0ca65bb70a876060d96d914dae09ac98d114294f77584b0d2644fa9c30 \ + --hash=sha256:237216c3326d46808a9f7c26fd1bd4b20015fb6867dc5d263a493ef9a539293b \ + --hash=sha256:27f4d15021da6d2b706ddc3860fac0a5ddaba34ab679dc182b60a8bb4e1121cc \ + --hash=sha256:299ea899484ee6f44604deb71f424234f654606b983cb496ea2a53e3c63ab791 \ + --hash=sha256:3d164928ff0727d97022957c2b849250ca0e64777ee31efd7d6de2e07c494717 \ + --hash=sha256:6ab80df09e3208f742c98443b6166bcb70d65f52cfeb67357d52032ea1ae9bec \ + --hash=sha256:78a28c9fa223998472886c77042e9b9afb6fe4242bd2a2a5aced88e3f4422aa7 \ + --hash=sha256:7cd532c4566d0e6feafecc1059d04c7915aec8e182d1cf7adee8b24ef1e2e6ab \ + --hash=sha256:89f9149e4a0169cddfc44c74f230d7743002e3aa0b9472d8c28f0388102fc4c2 \ + --hash=sha256:a53fd3f03e578553623272dc46ac2f189de23862e68565e83dde203d41b76fc5 \ + --hash=sha256:b135410244ebe777db80298297a97fbb4c862c881b4403b71bac9d4107d61fd1 \ + --hash=sha256:b98d0148f84e3a3c569e19f52103ca1feacdac0d2df8d6533cf983d1fda28462 \ + --hash=sha256:d1736130bce8cf131ac7957fa26880ca19227d4ad68b4888b3be0dea1f95df97 \ + --hash=sha256:f45460f9ee70a0ec1b6694c6e4e348ad2019275680bd68a1d9314b8c7e01e574 # via # -r requirements.in # grpcio-tools diff --git a/examples/grpc-bridge/docker-compose.yaml b/examples/grpc-bridge/docker-compose.yaml index d4bde502d48d..737fcdf6ed62 100644 --- a/examples/grpc-bridge/docker-compose.yaml +++ b/examples/grpc-bridge/docker-compose.yaml @@ -31,7 +31,8 @@ services: build: context: client environment: - - CLIENT_PROXY=http://kv-client-proxy:9911 + CLIENT_PROXY: http://kv-client-proxy:9911 + PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION: python networks: - envoymesh diff --git a/examples/load-reporting-service/go.mod b/examples/load-reporting-service/go.mod index 6eba67089113..9b8a10c1a3a1 100644 --- a/examples/load-reporting-service/go.mod +++ b/examples/load-reporting-service/go.mod @@ -5,5 +5,5 @@ go 1.13 require ( github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1 github.com/golang/protobuf v1.5.2 - google.golang.org/grpc v1.52.0 + google.golang.org/grpc v1.52.3 ) diff --git a/examples/load-reporting-service/go.sum b/examples/load-reporting-service/go.sum index 02f79fde5dec..96b3982fd332 100644 --- a/examples/load-reporting-service/go.sum +++ b/examples/load-reporting-service/go.sum @@ -988,8 +988,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.52.0 h1:kd48UiU7EHsV4rnLyOJRuP/Il/UHE7gdDAQ+SZI7nZk= -google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= +google.golang.org/grpc v1.52.3 h1:pf7sOysg4LdgBqduXveGKrcEwbStiK2rtfghdzlUYDQ= +google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/examples/mysql/Dockerfile-mysql b/examples/mysql/Dockerfile-mysql index ff3558b674dc..4628caab7a54 100644 --- a/examples/mysql/Dockerfile-mysql +++ b/examples/mysql/Dockerfile-mysql @@ -1 +1 @@ -FROM mysql:8.0.32@sha256:6f54880f928070a036aa3874d4a3fa203adc28688eb89e9f926a0dcacbce3378 +FROM mysql:8.0.32@sha256:19b05df6eb4b7ed6f274c0552f053ff0c00842a40dcf05941225c429a716683d diff --git a/examples/shared/build/Dockerfile b/examples/shared/build/Dockerfile index b830ce5990fe..758d23a51d57 100644 --- a/examples/shared/build/Dockerfile +++ b/examples/shared/build/Dockerfile @@ -1,12 +1,8 @@ -FROM envoyproxy/envoy-build-ubuntu:b0ff77ae3f25b0bf595f9b8bba46b489723ab446 +FROM envoyproxy/envoy-build-ubuntu:7304f974de2724617b7492ccb4c9c58cd420353a ENV DEBIAN_FRONTEND=noninteractive -RUN curl -sL https://packages.cloud.google.com/apt/doc/apt-key.gpg \ - | apt-key --keyring /usr/share/keyrings/cloud.google.gpg add - \ - && curl -fsSL https://apt.kitware.com/keys/kitware-archive-latest.asc \ - | apt-key add - \ - && apt-get update \ +RUN apt-get update \ && apt-get install --no-install-recommends -y -qq gosu \ && groupadd -f envoygroup \ && useradd -g envoygroup -m -d /home/envoybuild envoybuild diff --git a/examples/wasm-cc/build-entrypoint.sh b/examples/shared/build/build-entrypoint.sh similarity index 100% rename from examples/wasm-cc/build-entrypoint.sh rename to examples/shared/build/build-entrypoint.sh diff --git a/examples/wasm-cc/docker-compose-wasm.yaml b/examples/wasm-cc/docker-compose-wasm.yaml index a0392e577802..113f9b11b67c 100644 --- a/examples/wasm-cc/docker-compose-wasm.yaml +++ b/examples/wasm-cc/docker-compose-wasm.yaml @@ -8,7 +8,7 @@ services: bash -c " bazel build //examples/wasm-cc:envoy_filter_http_wasm_updated_example.wasm && cp -a bazel-bin/examples/wasm-cc/*updated*.wasm /output" - entrypoint: /source/examples/wasm-cc/build-entrypoint.sh + entrypoint: /source/examples/shared/build/build-entrypoint.sh environment: - BUILD_UID=${UID:-1000} - TEST_TMPDIR=${ENVOY_TEST_TMPDIR:-/tmp} @@ -25,7 +25,7 @@ services: bash -c " bazel build //examples/wasm-cc:envoy_filter_http_wasm_example.wasm && cp -a bazel-bin/examples/wasm-cc/* /output" - entrypoint: /source/examples/wasm-cc/build-entrypoint.sh + entrypoint: /source/examples/shared/build/build-entrypoint.sh environment: - BUILD_UID=${UID:-1000} - TEST_TMPDIR=${ENVOY_TEST_TMPDIR:-/tmp} diff --git a/mobile/.bazelrc b/mobile/.bazelrc index f457b6e5ba32..f222862224a2 100644 --- a/mobile/.bazelrc +++ b/mobile/.bazelrc @@ -3,7 +3,6 @@ try-import ../.bazelrc # Common flags for all builds build --platform_mappings=bazel/platform_mappings -build --define=google_grpc=disabled build --define=hot_restart=disabled build --define=tcmalloc=disabled build --define=admin_html=disabled @@ -57,7 +56,7 @@ build:dbg-common --copt="-fdebug-compilation-dir" --copt="/proc/self/cwd" build:ios --define=manual_stamp=manual_stamp # Default flags for builds targeting Android -build:android --define=logger=android --define=include_ifaddrs=true +build:android --define=logger=android # Default flags for Android debug builds build:dbg-android --config=dbg-common @@ -111,6 +110,9 @@ build:release-common --config=sizeopt --define=admin_functionality=disabled # Set default symbols visibility to hidden to reduce .dynstr and the symbol table size build:release-common --copt=-fvisibility=hidden +# Disable google_grpc in production by default +build:release-common --define=google_grpc=disabled + # Enable automatic extension factory registration for release builds build:release-common --define=static_extension_registration=enabled @@ -121,7 +123,6 @@ build:release-ios --compilation_mode=opt # Flags for release builds targeting Android or the JVM # Release does not use the option --define=logger=android -build:release-android --define=include_ifaddrs=true build:release-android --config=release-common build:release-android --compilation_mode=opt diff --git a/mobile/bazel/BUILD b/mobile/bazel/BUILD index 6219701b5441..dd8b74a8522d 100644 --- a/mobile/bazel/BUILD +++ b/mobile/bazel/BUILD @@ -17,11 +17,6 @@ kt_jvm_library( ], ) -config_setting( - name = "include_ifaddrs", - values = {"define": "include_ifaddrs=true"}, -) - config_setting( name = "exclude_certificates", values = {"define": "exclude_certificates=true"}, diff --git a/mobile/bazel/apple_test.bzl b/mobile/bazel/apple_test.bzl index 37510959b36d..0e2b8df7f7fa 100644 --- a/mobile/bazel/apple_test.bzl +++ b/mobile/bazel/apple_test.bzl @@ -18,7 +18,7 @@ load("//bazel:config.bzl", "MINIMUM_IOS_VERSION") # ], # ) # -def envoy_mobile_swift_test(name, srcs, data = [], deps = [], tags = [], repository = "", visibility = []): +def envoy_mobile_swift_test(name, srcs, data = [], deps = [], tags = [], repository = "", visibility = [], flaky = False): test_lib_name = name + "_lib" swift_library( name = test_lib_name, @@ -39,9 +39,10 @@ def envoy_mobile_swift_test(name, srcs, data = [], deps = [], tags = [], reposit minimum_os_version = MINIMUM_IOS_VERSION, tags = tags, visibility = visibility, + flaky = flaky, ) -def envoy_mobile_objc_test(name, srcs, data = [], deps = [], tags = [], visibility = []): +def envoy_mobile_objc_test(name, srcs, data = [], deps = [], tags = [], visibility = [], flaky = False): test_lib_name = name + "_lib" native.objc_library( name = test_lib_name, @@ -58,4 +59,5 @@ def envoy_mobile_objc_test(name, srcs, data = [], deps = [], tags = [], visibili minimum_os_version = MINIMUM_IOS_VERSION, tags = tags, visibility = visibility, + flaky = flaky, ) diff --git a/mobile/bazel/envoy_mobile_test_extensions.bzl b/mobile/bazel/envoy_mobile_test_extensions.bzl index e773c3927779..8c3692df9035 100644 --- a/mobile/bazel/envoy_mobile_test_extensions.bzl +++ b/mobile/bazel/envoy_mobile_test_extensions.bzl @@ -7,4 +7,5 @@ TEST_EXTENSIONS = [ "//library/common/extensions/filters/http/test_kv_store:config", "//library/common/extensions/filters/http/test_logger:config", "//library/common/extensions/filters/http/test_read:config", + "//library/common/extensions/filters/http/test_remote_response:config", ] diff --git a/mobile/ci/mac_ci_setup.sh b/mobile/ci/mac_ci_setup.sh index 185df1cea096..41c5e3c26873 100755 --- a/mobile/ci/mac_ci_setup.sh +++ b/mobile/ci/mac_ci_setup.sh @@ -10,18 +10,18 @@ set -e # a list of pre-installed tools in the macOS image. export HOMEBREW_NO_AUTO_UPDATE=1 -HOMEBREW_RETRY_ATTEMPTS=10 -HOMEBREW_RETRY_INTERVAL=3 +RETRY_ATTEMPTS=10 +RETRY_INTERVAL=3 function retry () { local returns=1 i=1 - while ((i<=HOMEBREW_RETRY_ATTEMPTS)); do + while ((i<=RETRY_ATTEMPTS)); do if "$@"; then returns=0 break else - sleep "$HOMEBREW_RETRY_INTERVAL"; + sleep "$RETRY_INTERVAL"; ((i++)) fi done @@ -45,18 +45,17 @@ if ! retry brew update; then echo "Failed to update homebrew" fi -DEPS="automake cmake coreutils libtool wget ninja" +DEPS="automake cmake coreutils libtool ninja" for DEP in ${DEPS} do is_installed "${DEP}" || install "${DEP}" done -./bazelw version - -pip3 install slackclient # https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md#xcode sudo xcode-select --switch /Applications/Xcode_14.1.app +retry ./bazelw version + if [[ "${1:-}" == "--android" ]]; then # Download and set up ndk 21 after GitHub update # https://github.com/actions/virtual-environments/issues/5595 diff --git a/mobile/docs/requirements.txt b/mobile/docs/requirements.txt index d065fc8b9044..8fd06cf26f44 100644 --- a/mobile/docs/requirements.txt +++ b/mobile/docs/requirements.txt @@ -99,9 +99,9 @@ charset-normalizer==3.0.1 \ docutils==0.19 \ --hash=sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6 \ --hash=sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc -idna==3.3 \ - --hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \ - --hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d +idna==3.4 \ + --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ + --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 imagesize==1.4.1 \ --hash=sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b \ --hash=sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a diff --git a/mobile/docs/root/intro/version_history.rst b/mobile/docs/root/intro/version_history.rst index 357e30429439..ac7f0deed129 100644 --- a/mobile/docs/root/intro/version_history.rst +++ b/mobile/docs/root/intro/version_history.rst @@ -22,6 +22,7 @@ Bugfixes: Features: +- api: Add a constructor which takes a URL to C++ RequestEngineBuilder. - api: add option to support platform provided certificates validation interfaces on iOS and Android. (:issue `#2144 <2144>`) - api: Add a ``setPerTryIdleTimeoutSeconds()`` method to C++ EngineBuilder. - swift/kotlin: add an option to enable DNS cache by calling ``enableDNSCache(_:)`` method. diff --git a/mobile/examples/cc/fetch_client/BUILD b/mobile/examples/cc/fetch_client/BUILD new file mode 100644 index 000000000000..15b0e1892882 --- /dev/null +++ b/mobile/examples/cc/fetch_client/BUILD @@ -0,0 +1,45 @@ +load( + "@envoy//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_library( + name = "fetch_client_lib", + srcs = [ + "fetch_client.cc", + ], + hdrs = [ + "fetch_client.h", + ], + repository = "@envoy", + deps = [ + "//library/cc:engine_builder_lib", + "//library/common/http:client_lib", + "//library/common/http:header_utility_lib", + "//library/common/types:c_types_lib", + "@envoy//envoy/http:header_map_interface", + "@envoy//source/common/http:header_map_lib", + ], +) + +cc_binary( + name = "fetch_client", + srcs = ["fetch_client_main.cc"], + deps = [ + ":fetch_client_lib", + "@envoy//source/common/api:api_lib", + "@envoy//source/common/common:random_generator_lib", + "@envoy//source/common/common:thread_lib", + "@envoy//source/common/event:real_time_system_lib", + "@envoy//source/common/stats:allocator_lib", + "@envoy//source/common/stats:thread_local_store_lib", + "@envoy//source/exe:platform_header_lib", + "@envoy//source/exe:platform_impl_lib", + "@envoy//source/exe:process_wide_lib", + ], +) diff --git a/mobile/examples/cc/fetch_client/fetch_client.cc b/mobile/examples/cc/fetch_client/fetch_client.cc new file mode 100644 index 000000000000..b5cb65f6eb88 --- /dev/null +++ b/mobile/examples/cc/fetch_client/fetch_client.cc @@ -0,0 +1,109 @@ +#include "examples/cc/fetch_client/fetch_client.h" + +#include + +#include "source/common/api/api_impl.h" +#include "source/common/common/random_generator.h" +#include "source/common/common/thread.h" +#include "source/common/event/real_time_system.h" +#include "source/common/http/utility.h" +#include "source/common/stats/allocator_impl.h" +#include "source/common/stats/thread_local_store.h" +#include "source/exe/platform_impl.h" +#include "source/exe/process_wide.h" + +#include "library/common/data/utility.h" + +namespace Envoy { + +Fetch::Fetch() + : logging_context_(spdlog::level::level_enum::info, Envoy::Logger::Logger::DEFAULT_LOG_FORMAT, + lock_, false), + stats_allocator_(symbol_table_), store_root_(stats_allocator_), + api_(std::make_unique(platform_impl_.threadFactory(), store_root_, + time_system_, platform_impl_.fileSystem(), + random_generator_, bootstrap_)) { + Envoy::Event::Libevent::Global::initialize(); +} + +void Fetch::fetch(const std::vector& urls) { + absl::Notification engine_running; + dispatcher_ = api_->allocateDispatcher("fetch_client"); + Thread::ThreadPtr envoy_thread = api_->threadFactory().createThread( + [this, &engine_running]() -> void { runEngine(engine_running); }); + engine_running.WaitForNotification(); + for (const absl::string_view url : urls) { + sendRequest(url); + } + dispatcher_->exit(); + envoy_thread->join(); + engine_->terminate(); +} + +void Fetch::sendRequest(const absl::string_view url_string) { + Envoy::Http::Utility::Url url; + if (!url.initialize(url_string, /*is_connect_request=*/false)) { + std::cerr << "Unable to parse url: '" << url_string << "'\n"; + return; + } + std::cout << "Fetching url: " << url.toString() << "\n"; + + absl::Notification request_finished; + Platform::StreamPrototypeSharedPtr stream_prototype = + engine_->streamClient()->newStreamPrototype(); + stream_prototype->setOnHeaders( + [](Platform::ResponseHeadersSharedPtr headers, bool, envoy_stream_intel intel) { + std::cerr << "Received headers on connection: " << intel.connection_id << "\n"; + + const Platform::RawHeaderMap raw_headers = headers->allHeaders(); + for (const auto& [key, header] : raw_headers) { + for (const auto& val : header) { + std::cout << key << ": " << val << "\n"; + } + } + }); + stream_prototype->setOnData([](envoy_data c_data, bool fin) { + std::cout << Data::Utility::copyToString(c_data); + if (fin) { + std::cout << "Received final data\n"; + } + release_envoy_data(c_data); + }); + stream_prototype->setOnComplete( + [&request_finished](envoy_stream_intel, envoy_final_stream_intel final_intel) { + std::cerr << "Request finished after " + << final_intel.stream_end_ms - final_intel.stream_start_ms << "ms\n"; + request_finished.Notify(); + }); + stream_prototype->setOnError([&request_finished](Platform::EnvoyErrorSharedPtr, + envoy_stream_intel, + envoy_final_stream_intel final_intel) { + std::cerr << "Request failed after " << final_intel.stream_end_ms - final_intel.stream_start_ms + << "ms\n"; + request_finished.Notify(); + }); + stream_prototype->setOnCancel( + [&request_finished](envoy_stream_intel, envoy_final_stream_intel final_intel) { + std::cerr << "Request cancelled after " + << final_intel.stream_end_ms - final_intel.stream_start_ms << "ms\n"; + request_finished.Notify(); + }); + + Platform::StreamSharedPtr stream = stream_prototype->start(/*explicit_flow_control=*/false); + + Platform::RequestHeadersBuilder builder(Platform::RequestMethod::GET, std::string(url.scheme()), + std::string(url.hostAndPort()), + std::string(url.pathAndQueryParams())); + + stream->sendHeaders(std::make_shared(builder.build()), true); + request_finished.WaitForNotification(); +} + +void Fetch::runEngine(absl::Notification& engine_running) { + Platform::EngineBuilder engine_builder; + engine_builder.setOnEngineRunning([&engine_running]() { engine_running.Notify(); }); + engine_ = engine_builder.build(); + dispatcher_->run(Event::Dispatcher::RunType::RunUntilExit); +} + +} // namespace Envoy diff --git a/mobile/examples/cc/fetch_client/fetch_client.h b/mobile/examples/cc/fetch_client/fetch_client.h new file mode 100644 index 000000000000..3b58c0aec96e --- /dev/null +++ b/mobile/examples/cc/fetch_client/fetch_client.h @@ -0,0 +1,50 @@ +#include +#include + +#include "envoy/http/header_map.h" + +#include "source/common/api/api_impl.h" +#include "source/common/common/random_generator.h" +#include "source/common/common/thread.h" +#include "source/common/event/real_time_system.h" +#include "source/common/http/header_map_impl.h" +#include "source/common/stats/allocator_impl.h" +#include "source/common/stats/thread_local_store.h" +#include "source/exe/platform_impl.h" +#include "source/exe/process_wide.h" + +#include "absl/synchronization/notification.h" +#include "library/cc/engine_builder.h" +#include "library/cc/stream.h" +#include "library/cc/stream_prototype.h" +#include "library/common/http/client.h" +#include "library/common/types/c_types.h" + +namespace Envoy { + +class Fetch { +public: + Fetch(); + + void fetch(const std::vector& urls); + +private: + void runEngine(absl::Notification& engine_running); + void sendRequest(const absl::string_view url); + + Envoy::Thread::MutexBasicLockable lock_; + Envoy::Logger::Context logging_context_; + Envoy::PlatformImpl platform_impl_; + Envoy::Stats::SymbolTableImpl symbol_table_; + Envoy::Event::RealTimeSystem time_system_; // NO_CHECK_FORMAT(real_time) + Envoy::Stats::AllocatorImpl stats_allocator_; + Envoy::Stats::ThreadLocalStoreImpl store_root_; + Envoy::Random::RandomGeneratorImpl random_generator_; + envoy::config::bootstrap::v3::Bootstrap bootstrap_; + Api::ApiPtr api_; + + Event::DispatcherPtr dispatcher_; + Platform::EngineSharedPtr engine_; +}; + +} // namespace Envoy diff --git a/mobile/examples/cc/fetch_client/fetch_client_main.cc b/mobile/examples/cc/fetch_client/fetch_client_main.cc new file mode 100644 index 000000000000..11ae9a0af2c1 --- /dev/null +++ b/mobile/examples/cc/fetch_client/fetch_client_main.cc @@ -0,0 +1,21 @@ +#include "examples/cc/fetch_client/fetch_client.h" + +extern const char build_scm_revision[]; +extern const char build_scm_status[]; + +const char build_scm_revision[] = "0"; +const char build_scm_status[] = "test"; + +// Fetches each URL specified on the command line in series, +// and prints the contents to standard out. +int main(int argc, char** argv) { + Envoy::Fetch client; + std::vector urls; + // Start at 1 to skip the command name. + for (int i = 1; i < argc; ++i) { + urls.push_back(argv[i]); + } + client.fetch(urls); + + exit(0); +} diff --git a/mobile/library/cc/BUILD b/mobile/library/cc/BUILD index bef74483e867..724165c9473e 100644 --- a/mobile/library/cc/BUILD +++ b/mobile/library/cc/BUILD @@ -12,11 +12,33 @@ envoy_cc_library( hdrs = [ "engine_builder.h", ], + copts = select({ + "//bazel:exclude_certificates": ["-DEXCLUDE_CERTIFICATES"], + "//conditions:default": [], + }), repository = "@envoy", visibility = ["//visibility:public"], deps = [ ":envoy_engine_cc_lib_no_stamp", "@envoy//source/common/common:assert_lib", + "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", + "@envoy_api//envoy/config/metrics/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/compression/brotli/decompressor/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/compression/gzip/decompressor/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/filters/http/alternate_protocols_cache/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/filters/http/decompressor/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/filters/http/dynamic_forward_proxy/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/http/header_formatters/preserve_case/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/network/dns_resolver/apple/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/network/dns_resolver/getaddrinfo/v3:pkg_cc_proto", + "@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/config:config_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", + "@envoy_mobile//library/common/extensions/filters/http/network_configuration:filter_cc_proto", + "@envoy_mobile//library/common/extensions/filters/http/socket_tag:filter_cc_proto", ], ) @@ -83,6 +105,7 @@ envoy_cc_library( "//library/common/api:c_types", "//library/common/data:utility_lib", "//library/common/extensions/key_value/platform:config", + "@envoy//source/common/http:utility_lib", ], ) diff --git a/mobile/library/cc/engine_builder.cc b/mobile/library/cc/engine_builder.cc index e0c2a650deac..6416ea44da82 100644 --- a/mobile/library/cc/engine_builder.cc +++ b/mobile/library/cc/engine_builder.cc @@ -2,11 +2,32 @@ #include +#include "envoy/config/metrics/v3/metrics_service.pb.h" +#include "envoy/extensions/compression/brotli/decompressor/v3/brotli.pb.h" +#include "envoy/extensions/compression/gzip/decompressor/v3/gzip.pb.h" +#include "envoy/extensions/filters/http/alternate_protocols_cache/v3/alternate_protocols_cache.pb.h" +#include "envoy/extensions/filters/http/decompressor/v3/decompressor.pb.h" +#include "envoy/extensions/filters/http/dynamic_forward_proxy/v3/dynamic_forward_proxy.pb.h" +#include "envoy/extensions/http/header_formatters/preserve_case/v3/preserve_case.pb.h" +#include "envoy/extensions/network/dns_resolver/apple/v3/apple_dns_resolver.pb.h" +#include "envoy/extensions/network/dns_resolver/getaddrinfo/v3/getaddrinfo_dns_resolver.pb.h" +#include "envoy/extensions/transport_sockets/http_11_proxy/v3/upstream_http_11_connect.pb.h" +#include "envoy/extensions/transport_sockets/quic/v3/quic_transport.pb.h" +#include "envoy/extensions/transport_sockets/raw_buffer/v3/raw_buffer.pb.h" + #include "source/common/common/assert.h" +#include "source/extensions/clusters/dynamic_forward_proxy/cluster.h" #include "absl/strings/str_join.h" #include "absl/strings/str_replace.h" #include "fmt/core.h" +#include "library/common/config/internal.h" +#include "library/common/engine.h" +#include "library/common/extensions/cert_validator/platform_bridge/platform_bridge.pb.h" +#include "library/common/extensions/filters/http/local_error/filter.pb.h" +#include "library/common/extensions/filters/http/network_configuration/filter.pb.h" +#include "library/common/extensions/filters/http/socket_tag/filter.pb.h" +#include "library/common/extensions/key_value/platform/platform.pb.h" #include "library/common/main_interface.h" namespace Envoy { @@ -18,11 +39,40 @@ void insertCustomFilter(const std::string& filter_config, std::string& config_te absl::StrReplaceAll({{"#{custom_filters}", absl::StrCat("#{custom_filters}\n", filter_config)}}, &config_template); } + +// Note that updates to the config.cc bootstrap will require a matching update in +// generateBootstrap() below +bool generatedStringMatchesGeneratedBoostrap( + const std::string& config_str, const envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + Thread::SkipAsserts skip; + ProtobufMessage::StrictValidationVisitorImpl visitor; + envoy::config::bootstrap::v3::Bootstrap config_bootstrap; + MessageUtil::loadFromYaml(absl::StrCat(config_header, config_str), config_bootstrap, visitor); + + Protobuf::util::MessageDifferencer differencer; + differencer.set_message_field_comparison(Protobuf::util::MessageDifferencer::EQUIVALENT); + differencer.set_repeated_field_comparison(Protobuf::util::MessageDifferencer::AS_LIST); + + bool same = differencer.Compare(config_bootstrap, bootstrap); + + if (!same) { + std::cerr << MessageUtil::getYamlStringFromMessage(config_bootstrap); + std::cerr << "============================================"; + std::cerr << MessageUtil::getYamlStringFromMessage(bootstrap); + } + return same; +} + } // namespace EngineBuilder::EngineBuilder(std::string config_template) - : callbacks_(std::make_shared()), config_template_(config_template) {} -EngineBuilder::EngineBuilder() : EngineBuilder(std::string(config_template)) {} + : callbacks_(std::make_shared()), config_template_(config_template), + config_bootstrap_incompatible_(true) {} + +EngineBuilder::EngineBuilder() : EngineBuilder(std::string(config_template)) { + // Using the default config template is bootstrap compatible. + config_bootstrap_incompatible_ = false; +} EngineBuilder& EngineBuilder::addLogLevel(LogLevel log_level) { log_level_ = log_level; @@ -34,8 +84,8 @@ EngineBuilder& EngineBuilder::setOnEngineRunning(std::function closure) return *this; } -EngineBuilder& EngineBuilder::addStatsSinks(const std::vector& stat_sinks) { - stat_sinks_ = stat_sinks; +EngineBuilder& EngineBuilder::addStatsSinks(std::vector stats_sinks) { + stats_sinks_ = std::move(stats_sinks); return *this; } @@ -70,8 +120,8 @@ EngineBuilder& EngineBuilder::addDnsQueryTimeoutSeconds(int dns_query_timeout_se return *this; } -EngineBuilder& EngineBuilder::addDnsPreresolveHostnames(std::string dns_preresolve_hostnames) { - dns_preresolve_hostnames_ = std::move(dns_preresolve_hostnames); +EngineBuilder& EngineBuilder::addDnsPreresolveHostnames(const std::vector& hostnames) { + dns_preresolve_hostnames_ = std::move(hostnames); return *this; } @@ -103,8 +153,8 @@ EngineBuilder& EngineBuilder::addStatsFlushSeconds(int stats_flush_seconds) { return *this; } -EngineBuilder& EngineBuilder::addVirtualClusters(std::string virtual_clusters) { - virtual_clusters_ = std::move(virtual_clusters); +EngineBuilder& EngineBuilder::addVirtualCluster(std::string virtual_cluster) { + virtual_clusters_.push_back(std::move(virtual_cluster)); return *this; } @@ -169,6 +219,16 @@ EngineBuilder& EngineBuilder::enableHttp3(bool http3_on) { return *this; } +EngineBuilder& EngineBuilder::setForceAlwaysUsev6(bool value) { + always_use_v6_ = value; + return *this; +} + +EngineBuilder& EngineBuilder::setSkipDnsLookupForProxiedRequests(bool value) { + skip_dns_lookups_for_proxied_requests_ = value; + return *this; +} + EngineBuilder& EngineBuilder::enableInterfaceBinding(bool interface_binding_on) { enable_interface_binding_ = interface_binding_on; return *this; @@ -184,12 +244,34 @@ EngineBuilder& EngineBuilder::enforceTrustChainVerification(bool trust_chain_ver return *this; } +EngineBuilder& EngineBuilder::addRtdsLayer(const std::string& layer_name, int timeout_seconds) { + rtds_layer_name_ = layer_name; + rtds_timeout_seconds_ = timeout_seconds; + return *this; +} +EngineBuilder& EngineBuilder::setAggregatedDiscoveryService(const std::string& api_type, + const std::string& address, + const int port) { +#ifndef ENVOY_GOOGLE_GRPC + throw std::runtime_error("google_grpc must be enabled in bazel to use ADS"); +#endif + ads_api_type_ = api_type; + ads_address_ = address; + ads_port_ = port; + return *this; +} + EngineBuilder& EngineBuilder::enablePlatformCertificatesValidation(bool platform_certificates_validation_on) { platform_certificates_validation_on_ = platform_certificates_validation_on; return *this; } +EngineBuilder& EngineBuilder::enableDnsCache(bool dns_cache_on) { + dns_cache_on_ = dns_cache_on; + return *this; +} + EngineBuilder& EngineBuilder::addStringAccessor(std::string name, StringAccessorSharedPtr accessor) { string_accessors_[std::move(name)] = std::move(accessor); @@ -202,18 +284,45 @@ EngineBuilder& EngineBuilder::addNativeFilter(std::string name, std::string type } EngineBuilder& EngineBuilder::addPlatformFilter(std::string name) { - platform_filters_.push_back(std::move(name)); + addNativeFilter( + "envoy.filters.http.platform_bridge", + absl::StrCat( + "{'@type': " + "type.googleapis.com/envoymobile.extensions.filters.http.platform_bridge.PlatformBridge, " + "platform_filter_name: ", + name, "}")); return *this; } std::string EngineBuilder::generateConfigStr() const { + if (!config_override_for_tests_.empty()) { + return config_override_for_tests_; + } + + std::string preresolve_hostnames = "["; + std::string maybe_comma = ""; + for (auto& hostname : dns_preresolve_hostnames_) { + absl::StrAppend(&preresolve_hostnames, maybe_comma, "{address: ", hostname, + ", port_value: 443}"); + maybe_comma = ","; + } + absl::StrAppend(&preresolve_hostnames, "]"); + + std::string virtual_clusters = "["; + maybe_comma = ""; + for (auto& cluster : virtual_clusters_) { + absl::StrAppend(&virtual_clusters, maybe_comma, cluster); + maybe_comma = ","; + } + absl::StrAppend(&virtual_clusters, "]"); + std::vector> replacements { {"connect_timeout", fmt::format("{}s", connect_timeout_seconds_)}, {"dns_fail_base_interval", fmt::format("{}s", dns_failure_refresh_seconds_base_)}, {"dns_fail_max_interval", fmt::format("{}s", dns_failure_refresh_seconds_max_)}, {"dns_lookup_family", enable_happy_eyeballs_ ? "ALL" : "V4_PREFERRED"}, {"dns_min_refresh_rate", fmt::format("{}s", dns_min_refresh_seconds_)}, - {"dns_preresolve_hostnames", dns_preresolve_hostnames_}, + {"dns_preresolve_hostnames", preresolve_hostnames}, {"dns_refresh_rate", fmt::format("{}s", dns_refresh_seconds_)}, {"dns_query_timeout", fmt::format("{}s", dns_query_timeout_seconds_)}, {"enable_drain_post_dns_refresh", enable_drain_post_dns_refresh_ ? "true" : "false"}, @@ -233,14 +342,21 @@ std::string EngineBuilder::generateConfigStr() const { {"trust_chain_verification", enforce_trust_chain_verification_ ? "VERIFY_TRUST_CHAIN" : "ACCEPT_UNTRUSTED"}, {"per_try_idle_timeout", fmt::format("{}s", per_try_idle_timeout_seconds_)}, - {"virtual_clusters", virtual_clusters_}, + {"virtual_clusters", virtual_clusters}, + {"skip_dns_lookup_for_proxied_requests", + skip_dns_lookups_for_proxied_requests_ ? "true" : "false"}, #if defined(__ANDROID_API__) {"force_ipv6", "true"}, +#else + {"force_ipv6", always_use_v6_ ? "true" : "false"}, #endif }; if (!stats_domain_.empty()) { replacements.push_back({"stats_domain", stats_domain_}); } + if (dns_cache_on_) { + replacements.push_back({"persistent_dns_cache_config", persistent_dns_cache_config_insert}); + } // NOTE: this does not include support for custom filters // which are not yet supported in the C++ platform implementation @@ -249,7 +365,8 @@ std::string EngineBuilder::generateConfigStr() const { for (const auto& [key, value] : replacements) { config_builder << "- &" << key << " " << value << std::endl; } - std::vector stat_sinks = stat_sinks_; + + std::vector stat_sinks = stats_sinks_; if (!stats_domain_.empty()) { stat_sinks.push_back("*base_metrics_service"); } @@ -265,15 +382,15 @@ std::string EngineBuilder::generateConfigStr() const { config_builder << cert_validation_template << std::endl; std::string config_template = config_template_; + if (socket_tagging_filter_) { + insertCustomFilter(socket_tag_config_insert, config_template); + } if (gzip_filter_) { insertCustomFilter(gzip_config_insert, config_template); } if (brotli_filter_) { insertCustomFilter(brotli_config_insert, config_template); } - if (socket_tagging_filter_) { - insertCustomFilter(socket_tag_config_insert, config_template); - } if (enable_http3_) { insertCustomFilter(alternate_protocols_cache_filter_insert, config_template); } @@ -285,10 +402,19 @@ std::string EngineBuilder::generateConfigStr() const { insertCustomFilter(filter_config, config_template); } - for (const std::string& name : platform_filters_) { - std::string filter_config = - absl::StrReplaceAll(platform_filter_template, {{"{{ platform_filter_name }}", name}}); - insertCustomFilter(filter_config, config_template); + if (!rtds_layer_name_.empty() && ads_api_type_.empty()) { + throw std::runtime_error("ADS must be configured when using RTDS"); + } + if (!rtds_layer_name_.empty()) { + std::string rtds_layer = + fmt::format(rtds_layer_insert, rtds_layer_name_, rtds_layer_name_, rtds_timeout_seconds_); + absl::StrReplaceAll({{"#{custom_layers}", absl::StrCat("#{custom_layers}\n", rtds_layer)}}, + &config_template); + } + if (!ads_api_type_.empty()) { + std::string custom_ads = fmt::format(ads_insert, ads_api_type_, ads_address_, ads_port_); + absl::StrReplaceAll({{"#{custom_ads}", absl::StrCat("#{custom_ads}\n", custom_ads)}}, + &config_template); } config_builder << config_template; @@ -301,9 +427,528 @@ std::string EngineBuilder::generateConfigStr() const { if (config_str.find("{{") != std::string::npos) { throw std::runtime_error("could not resolve all template keys in config"); } + return config_str; } +std::unique_ptr +EngineBuilder::generateBootstrapAndCompareForTests(std::string yaml) const { + auto bootstrap = generateBootstrap(); + RELEASE_ASSERT(generatedStringMatchesGeneratedBoostrap(yaml, *generateBootstrap()), + "Failed equivalence"); + return bootstrap; +} + +std::unique_ptr EngineBuilder::generateBootstrap() const { + // The yaml utilities have non-relevant thread asserts. + Thread::SkipAsserts skip; + ASSERT(!config_bootstrap_incompatible_); + + auto bootstrap = std::make_unique(); + + // Set up the HCM + envoy::extensions::filters::network::http_connection_manager::v3::EnvoyMobileHttpConnectionManager + api_listener_config; + auto* hcm = api_listener_config.mutable_config(); + hcm->set_stat_prefix("hcm"); + hcm->set_server_header_transformation( + envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager:: + PASS_THROUGH); + hcm->mutable_stream_idle_timeout()->set_seconds(stream_idle_timeout_seconds_); + auto* route_config = hcm->mutable_route_config(); + route_config->set_name("api_router"); + auto* remote_service = route_config->add_virtual_hosts(); + remote_service->set_name("remote_service"); + remote_service->add_domains("127.0.0.1"); + auto* route = remote_service->add_routes(); + route->mutable_match()->set_prefix("/"); + route->mutable_direct_response()->set_status(404); + route->mutable_direct_response()->mutable_body()->set_inline_string("not found"); + route->add_request_headers_to_remove("x-forwarded-proto"); + route->add_request_headers_to_remove("x-envoy-mobile-cluster"); + auto* api_service = route_config->add_virtual_hosts(); + api_service->set_name("api"); + api_service->set_include_attempt_count_in_response(true); + api_service->add_domains("*"); + // Virtual clusters + for (auto& cluster : virtual_clusters_) { + MessageUtil::loadFromYaml(cluster, *api_service->add_virtual_clusters(), + ProtobufMessage::getStrictValidationVisitor()); + } + + route = api_service->add_routes(); + route->mutable_match()->set_prefix("/"); + route->add_request_headers_to_remove("x-forwarded-proto"); + route->add_request_headers_to_remove("x-envoy-mobile-cluster"); + auto* route_to = route->mutable_route(); + route_to->set_cluster_header("x-envoy-mobile-cluster"); + route_to->mutable_timeout()->set_seconds(0); + route_to->mutable_retry_policy()->mutable_per_try_idle_timeout()->set_seconds( + per_try_idle_timeout_seconds_); + auto* backoff = route_to->mutable_retry_policy()->mutable_retry_back_off(); + backoff->mutable_base_interval()->set_nanos(250000000); + backoff->mutable_max_interval()->set_seconds(60); + + for (auto filter = native_filter_chain_.rbegin(); filter != native_filter_chain_.rend(); + ++filter) { + auto* native_filter = hcm->add_http_filters(); + native_filter->set_name((*filter).name_); + MessageUtil::loadFromYaml((*filter).typed_config_, *native_filter->mutable_typed_config(), + ProtobufMessage::getStrictValidationVisitor()); + } + + // Set up the optional filters + if (enable_http3_) { + envoy::extensions::filters::http::alternate_protocols_cache::v3::FilterConfig cache_config; + cache_config.mutable_alternate_protocols_cache_options()->set_name( + "default_alternate_protocols_cache"); + auto* cache_filter = hcm->add_http_filters(); + cache_filter->set_name("alternate_protocols_cache"); + cache_filter->mutable_typed_config()->PackFrom(cache_config); + } + + if (brotli_filter_) { + envoy::extensions::compression::brotli::decompressor::v3::Brotli brotli_config; + envoy::extensions::filters::http::decompressor::v3::Decompressor decompressor_config; + decompressor_config.mutable_decompressor_library()->set_name("text_optimized"); + decompressor_config.mutable_decompressor_library()->mutable_typed_config()->PackFrom( + brotli_config); + auto* common_request = + decompressor_config.mutable_request_direction_config()->mutable_common_config(); + common_request->mutable_enabled()->mutable_default_value(); + common_request->mutable_enabled()->set_runtime_key("request_decompressor_enabled"); + decompressor_config.mutable_response_direction_config() + ->mutable_common_config() + ->set_ignore_no_transform_header(true); + auto* brotli_filter = hcm->add_http_filters(); + brotli_filter->set_name("envoy.filters.http.decompressor"); + brotli_filter->mutable_typed_config()->PackFrom(decompressor_config); + } + if (gzip_filter_) { + envoy::extensions::compression::gzip::decompressor::v3::Gzip gzip_config; + gzip_config.mutable_window_bits()->set_value(15); + envoy::extensions::filters::http::decompressor::v3::Decompressor decompressor_config; + decompressor_config.mutable_decompressor_library()->set_name("gzip"); + decompressor_config.mutable_decompressor_library()->mutable_typed_config()->PackFrom( + gzip_config); + auto* common_request = + decompressor_config.mutable_request_direction_config()->mutable_common_config(); + common_request->mutable_enabled()->mutable_default_value(); + common_request->mutable_enabled()->set_runtime_key("request_decompressor_enabled"); + decompressor_config.mutable_response_direction_config() + ->mutable_common_config() + ->set_ignore_no_transform_header(true); + auto* gzip_filter = hcm->add_http_filters(); + gzip_filter->set_name("envoy.filters.http.decompressor"); + gzip_filter->mutable_typed_config()->PackFrom(decompressor_config); + } + if (socket_tagging_filter_) { + envoymobile::extensions::filters::http::socket_tag::SocketTag tag_config; + auto* tag_filter = hcm->add_http_filters(); + tag_filter->set_name("envoy.filters.http.socket_tag"); + tag_filter->mutable_typed_config()->PackFrom(tag_config); + } + + // Set up the always-present filters + envoymobile::extensions::filters::http::network_configuration::NetworkConfiguration + network_config; + network_config.set_enable_drain_post_dns_refresh(enable_drain_post_dns_refresh_); + network_config.set_enable_interface_binding(enable_interface_binding_); + auto* network_filter = hcm->add_http_filters(); + network_filter->set_name("envoy.filters.http.network_configuration"); + network_filter->mutable_typed_config()->PackFrom(network_config); + + envoymobile::extensions::filters::http::local_error::LocalError local_config; + auto* local_filter = hcm->add_http_filters(); + local_filter->set_name("envoy.filters.http.local_error"); + local_filter->mutable_typed_config()->PackFrom(local_config); + + envoy::extensions::filters::http::dynamic_forward_proxy::v3::FilterConfig dfp_config; + auto* dns_cache_config = dfp_config.mutable_dns_cache_config(); + dns_cache_config->set_name("base_dns_cache"); + if (enable_happy_eyeballs_) { + dns_cache_config->set_dns_lookup_family(envoy::config::cluster::v3::Cluster::ALL); + } else { + dns_cache_config->set_dns_lookup_family(envoy::config::cluster::v3::Cluster::V4_PREFERRED); + } + dns_cache_config->mutable_host_ttl()->set_seconds(86400); + dns_cache_config->mutable_dns_min_refresh_rate()->set_seconds(dns_min_refresh_seconds_); + dns_cache_config->mutable_dns_refresh_rate()->set_seconds(dns_refresh_seconds_); + dns_cache_config->mutable_dns_failure_refresh_rate()->mutable_base_interval()->set_seconds( + dns_failure_refresh_seconds_base_); + dns_cache_config->mutable_dns_failure_refresh_rate()->mutable_max_interval()->set_seconds( + dns_failure_refresh_seconds_max_); + dns_cache_config->mutable_dns_query_timeout()->set_seconds(dns_query_timeout_seconds_); + if (dns_cache_on_) { + envoymobile::extensions::key_value::platform::PlatformKeyValueStoreConfig kv_config; + kv_config.set_key("dns_persistent_cache"); + kv_config.mutable_save_interval()->set_seconds(0); + kv_config.set_max_entries(100); + dns_cache_config->mutable_key_value_config()->mutable_config()->set_name( + "envoy.key_value.platform"); + dns_cache_config->mutable_key_value_config() + ->mutable_config() + ->mutable_typed_config() + ->PackFrom(kv_config); + } + +#if defined(__APPLE__) + envoy::extensions::network::dns_resolver::apple::v3::AppleDnsResolverConfig resolver_config; + dns_cache_config->mutable_typed_dns_resolver_config()->set_name( + "envoy.network.dns_resolver.apple"); +#else + envoy::extensions::network::dns_resolver::getaddrinfo::v3::GetAddrInfoDnsResolverConfig + resolver_config; + dns_cache_config->mutable_typed_dns_resolver_config()->set_name( + "envoy.network.dns_resolver.getaddrinfo"); +#endif + dns_cache_config->mutable_typed_dns_resolver_config()->mutable_typed_config()->PackFrom( + resolver_config); + + for (auto& hostname : dns_preresolve_hostnames_) { + envoy::config::core::v3::SocketAddress* address = dns_cache_config->add_preresolve_hostnames(); + address->set_address(hostname); + address->set_port_value(443); + } + + auto* dfp_filter = hcm->add_http_filters(); + dfp_filter->set_name("envoy.filters.http.dynamic_forward_proxy"); + dfp_filter->mutable_typed_config()->PackFrom(dfp_config); + + auto* router_filter = hcm->add_http_filters(); + envoy::extensions::filters::http::router::v3::Router router_config; + router_filter->set_name("envoy.router"); + router_filter->mutable_typed_config()->PackFrom(router_config); + + auto* static_resources = bootstrap->mutable_static_resources(); + + // Finally create the base listener, and point it at the HCM. + auto* base_listener = static_resources->add_listeners(); + base_listener->set_name("base_api_listener"); + auto* base_address = base_listener->mutable_address(); + base_address->mutable_socket_address()->set_protocol(envoy::config::core::v3::SocketAddress::TCP); + base_address->mutable_socket_address()->set_address("0.0.0.0"); + base_address->mutable_socket_address()->set_port_value(10000); + base_listener->mutable_per_connection_buffer_limit_bytes()->set_value(10485760); + base_listener->mutable_api_listener()->mutable_api_listener()->PackFrom(api_listener_config); + + // Basic TLS config. + envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext tls_socket; + tls_socket.mutable_common_tls_context()->mutable_tls_params()->set_tls_maximum_protocol_version( + envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_3); + auto* validation = tls_socket.mutable_common_tls_context()->mutable_validation_context(); + if (enforce_trust_chain_verification_) { + validation->set_trust_chain_verification(envoy::extensions::transport_sockets::tls::v3:: + CertificateValidationContext::VERIFY_TRUST_CHAIN); + } else { + validation->set_trust_chain_verification(envoy::extensions::transport_sockets::tls::v3:: + CertificateValidationContext::ACCEPT_UNTRUSTED); + } + if (platform_certificates_validation_on_) { + envoy_mobile::extensions::cert_validator::platform_bridge::PlatformBridgeCertValidator + validator; + validation->mutable_custom_validator_config()->set_name( + "envoy_mobile.cert_validator.platform_bridge_cert_validator"); + validation->mutable_custom_validator_config()->mutable_typed_config()->PackFrom(validator); + + } else { + const char* inline_certs = "" +#ifndef EXCLUDE_CERTIFICATES +#include "library/common/config/certificates.inc" +#endif + ""; + // The certificates in certificates.inc are prefixed with 2 spaces per + // line to be ingressed into YAML. + std::string certs = inline_certs; + absl::StrReplaceAll({{"\n ", "\n"}}, &certs); + validation->mutable_trusted_ca()->set_inline_string(certs); + } + envoy::extensions::transport_sockets::http_11_proxy::v3::Http11ProxyUpstreamTransport + ssl_proxy_socket; + ssl_proxy_socket.mutable_transport_socket()->set_name("envoy.transport_sockets.tls"); + ssl_proxy_socket.mutable_transport_socket()->mutable_typed_config()->PackFrom(tls_socket); + + envoy::config::core::v3::TransportSocket base_tls_socket; + base_tls_socket.set_name("envoy.transport_sockets.http_11_proxy"); + base_tls_socket.mutable_typed_config()->PackFrom(ssl_proxy_socket); + + // Stats cluster + auto* stats_cluster = static_resources->add_clusters(); + stats_cluster->set_name("stats"); + stats_cluster->set_type(envoy::config::cluster::v3::Cluster::LOGICAL_DNS); + stats_cluster->mutable_connect_timeout()->set_seconds(connect_timeout_seconds_); + stats_cluster->mutable_dns_refresh_rate()->set_seconds(dns_refresh_seconds_); + stats_cluster->mutable_transport_socket()->CopyFrom(base_tls_socket); + stats_cluster->mutable_load_assignment()->set_cluster_name("stats"); + auto* address = stats_cluster->mutable_load_assignment() + ->add_endpoints() + ->add_lb_endpoints() + ->mutable_endpoint() + ->mutable_address(); + if (stats_domain_.empty()) { + address->mutable_socket_address()->set_address("127.0.0.1"); + } else { + address->mutable_socket_address()->set_address(stats_domain_); + } + address->mutable_socket_address()->set_port_value(443); + envoy::extensions::upstreams::http::v3::HttpProtocolOptions h2_protocol_options; + h2_protocol_options.mutable_explicit_http_config()->mutable_http2_protocol_options(); + (*stats_cluster->mutable_typed_extension_protocol_options()) + ["envoy.extensions.upstreams.http.v3.HttpProtocolOptions"] + .PackFrom(h2_protocol_options); + stats_cluster->mutable_wait_for_warm_on_init(); + + // Base cluster config (DFP cluster config) + auto* base_cluster = static_resources->add_clusters(); + envoy::extensions::clusters::dynamic_forward_proxy::v3::ClusterConfig base_cluster_config; + envoy::config::cluster::v3::Cluster::CustomClusterType base_cluster_type; + base_cluster_config.mutable_dns_cache_config()->CopyFrom(*dns_cache_config); + base_cluster_type.set_name("envoy.clusters.dynamic_forward_proxy"); + base_cluster_type.mutable_typed_config()->PackFrom(base_cluster_config); + + auto* upstream_opts = base_cluster->mutable_upstream_connection_options(); + upstream_opts->set_set_local_interface_name_on_upstream_connections(true); + upstream_opts->mutable_tcp_keepalive()->mutable_keepalive_interval()->set_value(5); + upstream_opts->mutable_tcp_keepalive()->mutable_keepalive_probes()->set_value(1); + upstream_opts->mutable_tcp_keepalive()->mutable_keepalive_time()->set_value(10); + + auto* circuit_breaker_settings = base_cluster->mutable_circuit_breakers(); + auto* breaker1 = circuit_breaker_settings->add_thresholds(); + breaker1->set_priority(envoy::config::core::v3::RoutingPriority::DEFAULT); + breaker1->mutable_retry_budget()->mutable_budget_percent()->set_value(100); + breaker1->mutable_retry_budget()->mutable_min_retry_concurrency()->set_value(0xffffffff); + auto* breaker2 = circuit_breaker_settings->add_per_host_thresholds(); + breaker2->set_priority(envoy::config::core::v3::RoutingPriority::DEFAULT); + breaker2->mutable_max_connections()->set_value(max_connections_per_host_); + + envoy::extensions::upstreams::http::v3::HttpProtocolOptions alpn_options; + alpn_options.mutable_upstream_http_protocol_options()->set_auto_sni(true); + alpn_options.mutable_upstream_http_protocol_options()->set_auto_san_validation(true); + auto* h2_options = alpn_options.mutable_auto_config()->mutable_http2_protocol_options(); + if (h2_connection_keepalive_idle_interval_milliseconds_ > 1000) { + h2_options->mutable_connection_keepalive()->mutable_connection_idle_interval()->set_seconds( + h2_connection_keepalive_idle_interval_milliseconds_ / 1000); + } else { + h2_options->mutable_connection_keepalive()->mutable_connection_idle_interval()->set_nanos( + h2_connection_keepalive_idle_interval_milliseconds_ * 1000 * 1000); + } + h2_options->mutable_connection_keepalive()->mutable_timeout()->set_seconds( + h2_connection_keepalive_timeout_seconds_); + h2_options->mutable_max_concurrent_streams()->set_value(100); + + envoy::extensions::http::header_formatters::preserve_case::v3::PreserveCaseFormatterConfig + preserve_case_config; + preserve_case_config.set_forward_reason_phrase(false); + preserve_case_config.set_formatter_type_on_envoy_headers( + envoy::extensions::http::header_formatters::preserve_case::v3::PreserveCaseFormatterConfig:: + DEFAULT); + + auto* h1_options = alpn_options.mutable_auto_config()->mutable_http_protocol_options(); + auto* formatter = h1_options->mutable_header_key_format()->mutable_stateful_formatter(); + formatter->set_name("preserve_case"); + formatter->mutable_typed_config()->PackFrom(preserve_case_config); + + // Base cluster + base_cluster->set_name("base"); + base_cluster->mutable_connect_timeout()->set_seconds(connect_timeout_seconds_); + base_cluster->set_lb_policy(envoy::config::cluster::v3::Cluster::CLUSTER_PROVIDED); + (*base_cluster->mutable_typed_extension_protocol_options()) + ["envoy.extensions.upstreams.http.v3.HttpProtocolOptions"] + .PackFrom(alpn_options); + base_cluster->mutable_cluster_type()->CopyFrom(base_cluster_type); + base_cluster->mutable_transport_socket()->set_name("envoy.transport_sockets.http_11_proxy"); + base_cluster->mutable_transport_socket()->mutable_typed_config()->PackFrom(ssl_proxy_socket); + + // Base cleartext cluster set up + envoy::extensions::transport_sockets::raw_buffer::v3::RawBuffer raw_buffer; + envoy::extensions::transport_sockets::http_11_proxy::v3::Http11ProxyUpstreamTransport + cleartext_proxy_socket; + cleartext_proxy_socket.mutable_transport_socket()->mutable_typed_config()->PackFrom(raw_buffer); + cleartext_proxy_socket.mutable_transport_socket()->set_name("envoy.transport_sockets.raw_buffer"); + envoy::extensions::upstreams::http::v3::HttpProtocolOptions h1_protocol_options; + h1_protocol_options.mutable_upstream_http_protocol_options()->set_auto_sni(true); + h1_protocol_options.mutable_upstream_http_protocol_options()->set_auto_san_validation(true); + h1_protocol_options.mutable_explicit_http_config()->mutable_http_protocol_options()->CopyFrom( + *alpn_options.mutable_auto_config()->mutable_http_protocol_options()); + + // Base cleartext cluster. + auto* base_clear = static_resources->add_clusters(); + base_clear->set_name("base_clear"); + base_clear->mutable_connect_timeout()->set_seconds(connect_timeout_seconds_); + base_clear->set_lb_policy(envoy::config::cluster::v3::Cluster::CLUSTER_PROVIDED); + base_clear->mutable_cluster_type()->CopyFrom(base_cluster_type); + base_clear->mutable_transport_socket()->set_name("envoy.transport_sockets.http_11_proxy"); + base_clear->mutable_transport_socket()->mutable_typed_config()->PackFrom(cleartext_proxy_socket); + base_clear->mutable_upstream_connection_options()->CopyFrom( + *base_cluster->mutable_upstream_connection_options()); + base_clear->mutable_circuit_breakers()->CopyFrom(*base_cluster->mutable_circuit_breakers()); + (*base_clear->mutable_typed_extension_protocol_options()) + ["envoy.extensions.upstreams.http.v3.HttpProtocolOptions"] + .PackFrom(h1_protocol_options); + + // Edit and re-pack + tls_socket.mutable_common_tls_context()->add_alpn_protocols("h2"); + ssl_proxy_socket.mutable_transport_socket()->mutable_typed_config()->PackFrom(tls_socket); + + // Base h2 cluster. + auto* base_h2 = static_resources->add_clusters(); + base_h2->set_name("base_h2"); + h2_protocol_options.mutable_explicit_http_config()->mutable_http2_protocol_options()->CopyFrom( + *alpn_options.mutable_auto_config()->mutable_http2_protocol_options()); + h2_protocol_options.mutable_upstream_http_protocol_options()->set_auto_sni(true); + h2_protocol_options.mutable_upstream_http_protocol_options()->set_auto_san_validation(true); + base_h2->mutable_connect_timeout()->set_seconds(connect_timeout_seconds_); + base_h2->set_lb_policy(envoy::config::cluster::v3::Cluster::CLUSTER_PROVIDED); + base_h2->mutable_cluster_type()->CopyFrom(base_cluster_type); + base_h2->mutable_transport_socket()->set_name("envoy.transport_sockets.http_11_proxy"); + base_h2->mutable_transport_socket()->mutable_typed_config()->PackFrom(ssl_proxy_socket); + base_h2->mutable_upstream_connection_options()->CopyFrom( + *base_cluster->mutable_upstream_connection_options()); + base_h2->mutable_circuit_breakers()->CopyFrom(*base_cluster->mutable_circuit_breakers()); + (*base_h2->mutable_typed_extension_protocol_options()) + ["envoy.extensions.upstreams.http.v3.HttpProtocolOptions"] + .PackFrom(h2_protocol_options); + + // Base h3 cluster set up + envoy::extensions::transport_sockets::quic::v3::QuicUpstreamTransport h3_inner_socket; + tls_socket.mutable_common_tls_context()->mutable_alpn_protocols()->Clear(); + h3_inner_socket.mutable_upstream_tls_context()->CopyFrom(tls_socket); + envoy::extensions::transport_sockets::http_11_proxy::v3::Http11ProxyUpstreamTransport + h3_proxy_socket; + h3_proxy_socket.mutable_transport_socket()->mutable_typed_config()->PackFrom(h3_inner_socket); + h3_proxy_socket.mutable_transport_socket()->set_name("envoy.transport_sockets.quic"); + alpn_options.mutable_auto_config()->mutable_http3_protocol_options(); + alpn_options.mutable_auto_config()->mutable_alternate_protocols_cache_options()->set_name( + "default_alternate_protocols_cache"); + + // Base h3 cluster + auto* base_h3 = static_resources->add_clusters(); + base_h3->set_name("base_h3"); + base_h3->mutable_connect_timeout()->set_seconds(connect_timeout_seconds_); + base_h3->set_lb_policy(envoy::config::cluster::v3::Cluster::CLUSTER_PROVIDED); + base_h3->mutable_cluster_type()->CopyFrom(base_cluster_type); + base_h3->mutable_transport_socket()->set_name("envoy.transport_sockets.http_11_proxy"); + base_h3->mutable_transport_socket()->mutable_typed_config()->PackFrom(h3_proxy_socket); + base_h3->mutable_upstream_connection_options()->CopyFrom( + *base_cluster->mutable_upstream_connection_options()); + base_h3->mutable_circuit_breakers()->CopyFrom(*base_cluster->mutable_circuit_breakers()); + (*base_h3->mutable_typed_extension_protocol_options()) + ["envoy.extensions.upstreams.http.v3.HttpProtocolOptions"] + .PackFrom(alpn_options); + + // Set up stats. + bootstrap->mutable_stats_flush_interval()->set_seconds(stats_flush_seconds_); + bootstrap->mutable_stats_sinks(); + auto* list = bootstrap->mutable_stats_config()->mutable_stats_matcher()->mutable_inclusion_list(); + list->add_patterns()->set_prefix("cluster.base.upstream_rq_"); + list->add_patterns()->set_prefix("cluster.base_h2.upstream_rq_"); + list->add_patterns()->set_prefix("cluster.stats.upstream_rq_"); + list->add_patterns()->set_prefix("cluster.base.upstream_cx_"); + list->add_patterns()->set_prefix("cluster.base_h2.upstream_cx_"); + list->add_patterns()->set_prefix("cluster.stats.upstream_cx_"); + list->add_patterns()->set_exact("cluster.base.http2.keepalive_timeout"); + list->add_patterns()->set_exact("cluster.base_h2.http2.keepalive_timeout"); + list->add_patterns()->set_exact("cluster.stats.http2.keepalive_timeout"); + list->add_patterns()->set_prefix("http.hcm.downstream_rq_"); + list->add_patterns()->set_prefix("http.hcm.decompressor."); + list->add_patterns()->set_prefix("pulse."); + list->add_patterns()->set_prefix("runtime.load_success"); + list->add_patterns()->mutable_safe_regex()->set_regex( + "^vhost\\.[\\w]+\\.vcluster\\.[\\w]+?\\.upstream_rq_(?:[12345]xx|[3-5][0-9][0-9]|retry|" + "total)"); + bootstrap->mutable_stats_config()->mutable_use_all_default_tags()->set_value(false); + + // Set up watchdog + auto* watchdog = bootstrap->mutable_watchdogs(); + watchdog->mutable_main_thread_watchdog()->mutable_megamiss_timeout()->set_seconds(60); + watchdog->mutable_main_thread_watchdog()->mutable_miss_timeout()->set_seconds(60); + watchdog->mutable_worker_watchdog()->mutable_megamiss_timeout()->set_seconds(60); + watchdog->mutable_worker_watchdog()->mutable_miss_timeout()->set_seconds(60); + + // Set up node + auto* node = bootstrap->mutable_node(); + node->set_id("envoy-mobile"); + node->set_cluster("envoy-mobile"); + ProtobufWkt::Struct& metadata = *node->mutable_metadata(); + (*metadata.mutable_fields())["app_id"].set_string_value(app_id_); + (*metadata.mutable_fields())["app_version"].set_string_value(app_version_); + (*metadata.mutable_fields())["device_os"].set_string_value(device_os_); + + // Set up runtime. + auto* runtime = bootstrap->mutable_layered_runtime()->add_layers(); + runtime->set_name("static_layer_0"); + ProtobufWkt::Struct envoy_layer; + ProtobufWkt::Struct& runtime_values = + *(*envoy_layer.mutable_fields())["envoy"].mutable_struct_value(); + ProtobufWkt::Struct& flags = + *(*runtime_values.mutable_fields())["reloadable_features"].mutable_struct_value(); + (*flags.mutable_fields())["always_use_v6"].set_bool_value(always_use_v6_); + (*flags.mutable_fields())["skip_dns_lookup_for_proxied_requests"].set_bool_value( + skip_dns_lookups_for_proxied_requests_); + (*runtime_values.mutable_fields())["disallow_global_stats"].set_bool_value("true"); + ProtobufWkt::Struct& overload_values = + *(*envoy_layer.mutable_fields())["overload"].mutable_struct_value(); + (*overload_values.mutable_fields())["global_downstream_max_connections"].set_string_value( + "4294967295"); + runtime->mutable_static_layer()->MergeFrom(envoy_layer); + + bootstrap->mutable_typed_dns_resolver_config()->CopyFrom( + *dns_cache_config->mutable_typed_dns_resolver_config()); + + for (const std::string& sink_yaml : stats_sinks_) { + auto* sink = bootstrap->add_stats_sinks(); + MessageUtil::loadFromYaml(sink_yaml, *sink, ProtobufMessage::getStrictValidationVisitor()); + } + if (!stats_domain_.empty()) { + envoy::config::metrics::v3::MetricsServiceConfig metrics_config; + metrics_config.mutable_grpc_service()->mutable_envoy_grpc()->set_cluster_name("stats"); + metrics_config.mutable_report_counters_as_deltas()->set_value(true); + metrics_config.set_transport_api_version(envoy::config::core::v3::ApiVersion::V3); + metrics_config.set_emit_tags_as_labels(true); + auto* sink = bootstrap->add_stats_sinks(); + sink->set_name("envoy.metrics_service"); + sink->mutable_typed_config()->PackFrom(metrics_config); + } + + if (!rtds_layer_name_.empty() && ads_api_type_.empty()) { + throw std::runtime_error("ADS must be configured when using RTDS"); + } + if (!rtds_layer_name_.empty()) { + auto* layered_runtime = bootstrap->mutable_layered_runtime(); + auto* layer = layered_runtime->add_layers(); + layer->set_name(rtds_layer_name_); + auto* rtds_layer = layer->mutable_rtds_layer(); + rtds_layer->set_name(rtds_layer_name_); + auto* rtds_config = rtds_layer->mutable_rtds_config(); + rtds_config->mutable_ads(); + rtds_config->set_resource_api_version(envoy::config::core::v3::ApiVersion::V3); + rtds_config->mutable_initial_fetch_timeout()->set_seconds(rtds_timeout_seconds_); + } + if (!ads_api_type_.empty()) { + std::string target_uri = fmt::format(R"({}:{})", ads_address_, ads_port_); + auto* ads_config = bootstrap->mutable_dynamic_resources()->mutable_ads_config(); + ads_config->set_transport_api_version(envoy::config::core::v3::ApiVersion::V3); + ads_config->set_set_node_on_first_message_only(true); + envoy::config::core::v3::ApiConfigSource::ApiType api_type_enum; + envoy::config::core::v3::ApiConfigSource::ApiType_Parse(ads_api_type_, &api_type_enum); + ads_config->set_api_type(api_type_enum); + ads_config->add_grpc_services()->mutable_google_grpc()->set_target_uri(target_uri); + } + + // Admin + if (admin_interface_enabled_) { + auto* admin_address = bootstrap->mutable_admin()->mutable_address()->mutable_socket_address(); + admin_address->set_address("::1"); + admin_address->set_port_value(9901); + } + + // Check equivalence in debug mode. + RELEASE_ASSERT(generatedStringMatchesGeneratedBoostrap(generateConfigStr(), *bootstrap), + "Native C++ checks failed"); + + return bootstrap; +} + EngineSharedPtr EngineBuilder::build() { envoy_logger null_logger; null_logger.log = nullptr; @@ -313,11 +958,13 @@ EngineSharedPtr EngineBuilder::build() { envoy_event_tracker null_tracker{}; std::string config_str; - if (config_override_for_tests_.empty()) { - config_str = generateConfigStr(); + std::unique_ptr bootstrap; + if (!config_bootstrap_incompatible_) { + bootstrap = generateBootstrap(); } else { - config_str = config_override_for_tests_; + config_str = generateConfigStr(); } + envoy_engine_t envoy_engine = init_engine(callbacks_->asEnvoyEngineCallbacks(), null_logger, null_tracker); @@ -335,12 +982,25 @@ EngineSharedPtr EngineBuilder::build() { register_platform_api(name.c_str(), api); } - run_engine(envoy_engine, config_str.c_str(), logLevelToString(log_level_).c_str(), - admin_address_path_for_tests_.c_str()); + Engine* engine = new Engine(envoy_engine); + + if (auto cast_engine = reinterpret_cast(envoy_engine)) { + auto options = std::make_unique(); + if (bootstrap) { + options->setConfigProto(std::move(bootstrap)); + } else { + options->setConfigYaml(absl::StrCat(config_header, config_str)); + } + options->setLogLevel(options->parseAndValidateLogLevel(logLevelToString(log_level_).c_str())); + options->setConcurrency(1); + if (!admin_address_path_for_tests_.empty()) { + options->setAdminAddressPath(admin_address_path_for_tests_); + } + cast_engine->run(std::move(options)); + } // we can't construct via std::make_shared // because Engine is only constructible as a friend - Engine* engine = new Engine(envoy_engine); auto engine_ptr = EngineSharedPtr(engine); return engine_ptr; } diff --git a/mobile/library/cc/engine_builder.h b/mobile/library/cc/engine_builder.h index 7d2e3b41a492..8d3077ae2a89 100644 --- a/mobile/library/cc/engine_builder.h +++ b/mobile/library/cc/engine_builder.h @@ -5,6 +5,8 @@ #include #include +#include "envoy/config/bootstrap/v3/bootstrap.pb.h" + #include "absl/container/flat_hash_map.h" #include "engine.h" #include "engine_callbacks.h" @@ -15,22 +17,30 @@ namespace Envoy { namespace Platform { +// The C++ Engine builder supports 2 ways of building Envoy Mobile config, the 'legacy mode' +// which uses a yaml config header, blocks of well known yaml configs, and uses string manipulation +// to glue them together, and the 'bootstrap mode' which creates a structured bootstrap proto and +// modifies it to produce the same config. We retain the legacy mode to be able to regression +// test that changes to the config yaml are reflected in generateBootstrap, until all languages use +// the C++ bootstrap builder. +// +// Bootstrap mode will be used unless the config_template constructor is used. class EngineBuilder { public: - EngineBuilder(std::string config_template); EngineBuilder(); + // This constructor is not compatible with bootstrap mode. + EngineBuilder(std::string config_template); + // Use the experimental non-YAML config mode which uses the bootstrap proto directly. EngineBuilder& addLogLevel(LogLevel log_level); EngineBuilder& setOnEngineRunning(std::function closure); - EngineBuilder& addStatsSinks(const std::vector& stat_sinks); EngineBuilder& addGrpcStatsDomain(std::string stats_domain); EngineBuilder& addConnectTimeoutSeconds(int connect_timeout_seconds); EngineBuilder& addDnsRefreshSeconds(int dns_refresh_seconds); EngineBuilder& addDnsFailureRefreshSeconds(int base, int max); EngineBuilder& addDnsQueryTimeoutSeconds(int dns_query_timeout_seconds); EngineBuilder& addDnsMinRefreshSeconds(int dns_min_refresh_seconds); - EngineBuilder& addDnsPreresolveHostnames(std::string dns_preresolve_hostnames); EngineBuilder& addMaxConnectionsPerHost(int max_connections_per_host); EngineBuilder& useDnsSystemResolver(bool use_system_resolver); EngineBuilder& addH2ConnectionKeepaliveIdleIntervalMilliseconds( @@ -38,13 +48,8 @@ class EngineBuilder { EngineBuilder& addH2ConnectionKeepaliveTimeoutSeconds(int h2_connection_keepalive_timeout_seconds); EngineBuilder& addStatsFlushSeconds(int stats_flush_seconds); - EngineBuilder& addVirtualClusters(std::string virtual_clusters); - EngineBuilder& addKeyValueStore(std::string name, KeyValueStoreSharedPtr key_value_store); - EngineBuilder& addStringAccessor(std::string name, StringAccessorSharedPtr accessor); - EngineBuilder& addNativeFilter(std::string name, std::string typed_config); // Configures Envoy to use the PlatformBridge filter named `name`. An instance of // envoy_http_filter must be registered as a platform API with the same name. - EngineBuilder& addPlatformFilter(std::string name); EngineBuilder& setAppVersion(std::string app_version); EngineBuilder& setAppId(std::string app_id); EngineBuilder& setDeviceOs(std::string app_id); @@ -53,21 +58,45 @@ class EngineBuilder { EngineBuilder& enableGzip(bool gzip_on); EngineBuilder& enableBrotli(bool brotli_on); EngineBuilder& enableSocketTagging(bool socket_tagging_on); - EngineBuilder& enableAdminInterface(bool admin_interface_on); EngineBuilder& enableHappyEyeballs(bool happy_eyeballs_on); EngineBuilder& enableHttp3(bool http3_on); EngineBuilder& enableInterfaceBinding(bool interface_binding_on); EngineBuilder& enableDrainPostDnsRefresh(bool drain_post_dns_refresh_on); EngineBuilder& enforceTrustChainVerification(bool trust_chain_verification_on); EngineBuilder& enablePlatformCertificatesValidation(bool platform_certificates_validation_on); + // Adds an RTDS layer to default config. Requires that ADS be configured + EngineBuilder& addRtdsLayer(const std::string& layer_name, int timeout_seconds = 5); + // Adds an ADS layer. + EngineBuilder& setAggregatedDiscoveryService(const std::string& api_type, + const std::string& address, const int port); + EngineBuilder& enableDnsCache(bool dns_cache_on); + EngineBuilder& setForceAlwaysUsev6(bool value); + EngineBuilder& setSkipDnsLookupForProxiedRequests(bool value); + EngineBuilder& addDnsPreresolveHostnames(const std::vector& hostnames); + EngineBuilder& addNativeFilter(std::string name, std::string typed_config); + EngineBuilder& enableAdminInterface(bool admin_interface_on); + EngineBuilder& addStatsSinks(std::vector stat_sinks); + EngineBuilder& addPlatformFilter(std::string name); + EngineBuilder& addVirtualCluster(std::string virtual_cluster); + + // These functions don't affect YAML but instead perform registrations. + EngineBuilder& addKeyValueStore(std::string name, KeyValueStoreSharedPtr key_value_store); + EngineBuilder& addStringAccessor(std::string name, StringAccessorSharedPtr accessor); - // this is separated from build() for the sake of testability + // This is separated from build() for the sake of testability std::string generateConfigStr() const; + std::unique_ptr generateBootstrap() const; EngineSharedPtr build(); + std::unique_ptr + generateBootstrapAndCompareForTests(std::string yaml) const; + protected: - void setOverrideConfigForTests(std::string config) { config_override_for_tests_ = config; } + void setOverrideConfigForTests(std::string config) { + config_bootstrap_incompatible_ = true; + config_override_for_tests_ = config; + } void setAdminAddressPathForTests(std::string admin) { admin_address_path_for_tests_ = admin; } private: @@ -90,7 +119,6 @@ class EngineBuilder { int dns_failure_refresh_seconds_base_ = 2; int dns_failure_refresh_seconds_max_ = 10; int dns_query_timeout_seconds_ = 25; - std::string dns_preresolve_hostnames_ = "[]"; bool use_system_resolver_ = true; int h2_connection_keepalive_idle_interval_milliseconds_ = 100000000; int h2_connection_keepalive_timeout_seconds_ = 10; @@ -98,7 +126,6 @@ class EngineBuilder { std::string app_version_ = "unspecified"; std::string app_id_ = "unspecified"; std::string device_os_ = "unspecified"; - std::string virtual_clusters_ = "[]"; std::string config_override_for_tests_ = ""; std::string admin_address_path_for_tests_ = ""; int stream_idle_timeout_seconds_ = 15; @@ -107,6 +134,12 @@ class EngineBuilder { bool brotli_filter_ = false; bool socket_tagging_filter_ = false; bool platform_certificates_validation_on_ = false; + std::string rtds_layer_name_ = ""; + int rtds_timeout_seconds_; + std::string ads_api_type_ = ""; + std::string ads_address_ = ""; + int ads_port_; + bool dns_cache_on_ = false; absl::flat_hash_map key_value_stores_{}; @@ -117,13 +150,18 @@ class EngineBuilder { bool enforce_trust_chain_verification_ = true; bool h2_extend_keepalive_timeout_ = false; bool enable_http3_ = true; + bool always_use_v6_ = false; int dns_min_refresh_seconds_ = 60; int max_connections_per_host_ = 7; - std::vector stat_sinks_; + std::vector stats_sinks_; std::vector native_filter_chain_; - std::vector platform_filters_; + std::vector dns_preresolve_hostnames_; + std::vector virtual_clusters_; + absl::flat_hash_map string_accessors_; + bool config_bootstrap_incompatible_ = false; + bool skip_dns_lookups_for_proxied_requests_ = false; }; using EngineBuilderSharedPtr = std::shared_ptr; diff --git a/mobile/library/cc/request_headers_builder.cc b/mobile/library/cc/request_headers_builder.cc index 4c35b0bdb550..c664d9f70472 100644 --- a/mobile/library/cc/request_headers_builder.cc +++ b/mobile/library/cc/request_headers_builder.cc @@ -1,10 +1,27 @@ #include "request_headers_builder.h" +#include "source/common/http/utility.h" + namespace Envoy { namespace Platform { RequestHeadersBuilder::RequestHeadersBuilder(RequestMethod request_method, std::string scheme, std::string authority, std::string path) { + initialize(request_method, std::move(scheme), std::move(authority), std::move(path)); +} + +RequestHeadersBuilder::RequestHeadersBuilder(RequestMethod request_method, absl::string_view url) { + Envoy::Http::Utility::Url parsed_url; + if (!parsed_url.initialize(url, /*is_connect_request=*/false)) { + initialize(request_method, "", "", ""); + return; + } + initialize(request_method, std::string(parsed_url.scheme()), + std::string(parsed_url.hostAndPort()), std::string(parsed_url.pathAndQueryParams())); +} + +void RequestHeadersBuilder::initialize(RequestMethod request_method, std::string scheme, + std::string authority, std::string path) { internalSet(":method", {requestMethodToString(request_method)}); internalSet(":scheme", {std::move(scheme)}); internalSet(":authority", {std::move(authority)}); diff --git a/mobile/library/cc/request_headers_builder.h b/mobile/library/cc/request_headers_builder.h index c5bc245b1ad3..d71f1f5525df 100644 --- a/mobile/library/cc/request_headers_builder.h +++ b/mobile/library/cc/request_headers_builder.h @@ -18,11 +18,16 @@ class RequestHeadersBuilder : public HeadersBuilder { public: RequestHeadersBuilder(RequestMethod request_method, std::string scheme, std::string authority, std::string path); + RequestHeadersBuilder(RequestMethod request_method, absl::string_view url); RequestHeadersBuilder& addRetryPolicy(const RetryPolicy& retry_policy); RequestHeadersBuilder& addUpstreamHttpProtocol(UpstreamHttpProtocol upstream_http_protocol); RequestHeaders build() const; + +private: + void initialize(RequestMethod request_method, std::string scheme, std::string authority, + std::string path); }; using RequestHeadersBuilderSharedPtr = std::shared_ptr; diff --git a/mobile/library/common/config/config.cc b/mobile/library/common/config/config.cc index 52c2e037ada5..d1cca59c0fd7 100644 --- a/mobile/library/common/config/config.cc +++ b/mobile/library/common/config/config.cc @@ -367,7 +367,7 @@ const char* config_template = R"( typed_dns_resolver_config: name: *dns_resolver_name typed_config: *dns_resolver_config - +#{custom_ads} static_resources: listeners: #{custom_listeners} @@ -506,6 +506,7 @@ stats_sinks: *stats_sinks - prefix: http.hcm.downstream_rq_ - prefix: http.hcm.decompressor. - prefix: pulse. + - prefix: runtime.load_success - safe_regex: regex: '^vhost\.[\w]+\.vcluster\.[\w]+?\.upstream_rq_(?:[12345]xx|[3-5][0-9][0-9]|retry|total)' use_all_default_tags: @@ -538,5 +539,27 @@ stats_sinks: *stats_sinks R"( overload: global_downstream_max_connections: 0xffffffff # uint32 max +#{custom_layers} )"; + +const char* rtds_layer_insert = R"( + - name: {} + rtds_layer: + name: {} + rtds_config: + initial_fetch_timeout: + seconds: {} + resource_api_version: V3 + ads: {{}})"; + +const char* ads_insert = R"( +dynamic_resources: + ads_config: + transport_api_version: V3 + api_type: {} + set_node_on_first_message_only: true + grpc_services: + google_grpc: + target_uri: '{}:{}')"; + // clang-format on diff --git a/mobile/library/common/config/templates.h b/mobile/library/common/config/templates.h index 3edbf78320d4..a89feaf4ce70 100644 --- a/mobile/library/common/config/templates.h +++ b/mobile/library/common/config/templates.h @@ -102,3 +102,13 @@ extern const char* default_cert_validation_context_template; * chain. */ extern const char* platform_cert_validation_context_template; + +/** + * Config template for an RTDS layer + */ +extern const char* rtds_layer_insert; + +/** + * ADS config + */ +extern const char* ads_insert; diff --git a/mobile/library/common/engine.cc b/mobile/library/common/engine.cc index b4b432035163..5877de038ac4 100644 --- a/mobile/library/common/engine.cc +++ b/mobile/library/common/engine.cc @@ -6,7 +6,6 @@ #include "library/common/bridge/utility.h" #include "library/common/config/internal.h" #include "library/common/data/utility.h" -#include "library/common/network/android.h" #include "library/common/stats/utility.h" namespace Envoy { @@ -29,13 +28,24 @@ envoy_status_t Engine::run(const std::string config, const std::string log_level // std::thread, main_thread_ is the same object after this call, but its state is replaced with // that of the temporary. The temporary object's state becomes the default state, which does // nothing. - main_thread_ = std::thread(&Engine::main, this, std::string(config), std::string(log_level), - admin_address_path); + auto options = std::make_unique(); + options->setConfigYaml(absl::StrCat(config_header, config)); + if (!log_level.empty()) { + options->setLogLevel(options->parseAndValidateLogLevel(log_level.c_str())); + } + options->setConcurrency(1); + if (!admin_address_path.empty()) { + options->setAdminAddressPath(admin_address_path); + } + return run(std::move(options)); +} + +envoy_status_t Engine::run(std::unique_ptr&& options) { + main_thread_ = std::thread(&Engine::main, this, std::move(options)); return ENVOY_SUCCESS; } -envoy_status_t Engine::main(const std::string config, const std::string log_level, - const std::string admin_address_path) { +envoy_status_t Engine::main(std::unique_ptr&& options) { // Using unique_ptr ensures main_common's lifespan is strictly scoped to this function. std::unique_ptr main_common; { @@ -65,16 +75,6 @@ envoy_status_t Engine::main(const std::string config, const std::string log_leve std::make_unique(log_mutex_, Logger::Registry::getSink()); } - auto options = std::make_unique(); - options->setConfigYaml(absl::StrCat(config_header, config)); - if (!log_level.empty()) { - options->setLogLevel(options->parseAndValidateLogLevel(log_level.c_str())); - } - options->setConcurrency(1); - if (!admin_address_path.empty()) { - options->setAdminAddressPath(admin_address_path); - } - main_common = std::make_unique(std::move(options)); server_ = main_common->server(); event_dispatcher_ = &server_->dispatcher(); @@ -101,9 +101,6 @@ envoy_status_t Engine::main(const std::string config, const std::string log_leve connectivity_manager_ = Network::ConnectivityManagerFactory{server_->serverFactoryContext()}.get(); - if (Api::OsSysCallsSingleton::get().supportsGetifaddrs()) { - Envoy::Network::Android::Utility::setAlternateGetifaddrs(); - } auto v4_interfaces = connectivity_manager_->enumerateV4Interfaces(); auto v6_interfaces = connectivity_manager_->enumerateV6Interfaces(); logInterfaces("netconf_get_v4_interfaces", v4_interfaces); diff --git a/mobile/library/common/engine.h b/mobile/library/common/engine.h index 8b41fb1b1d90..fbf94229357d 100644 --- a/mobile/library/common/engine.h +++ b/mobile/library/common/engine.h @@ -39,6 +39,7 @@ class Engine : public Logger::Loggable { */ envoy_status_t run(std::string config, std::string log_level, const std::string admin_address_path); + envoy_status_t run(std::unique_ptr&& options); /** * Immediately terminate the engine, if running. @@ -100,7 +101,7 @@ class Engine : public Logger::Loggable { Stats::Store& getStatsStore(); private: - envoy_status_t main(std::string config, std::string log_level, std::string admin_address_path); + envoy_status_t main(std::unique_ptr&& options); static void logInterfaces(absl::string_view event, std::vector& interfaces); diff --git a/mobile/library/common/extensions/filters/http/test_remote_response/BUILD b/mobile/library/common/extensions/filters/http/test_remote_response/BUILD new file mode 100644 index 000000000000..64ac60a4b6be --- /dev/null +++ b/mobile/library/common/extensions/filters/http/test_remote_response/BUILD @@ -0,0 +1,48 @@ +load( + "@envoy//bazel:envoy_build_system.bzl", + "envoy_cc_extension", + "envoy_extension_package", + "envoy_proto_library", +) + +licenses(["notice"]) # Apache 2 + +envoy_extension_package() + +envoy_proto_library( + name = "filter", + srcs = ["filter.proto"], +) + +envoy_cc_extension( + name = "test_remote_response_filter_lib", + srcs = ["filter.cc"], + hdrs = ["filter.h"], + repository = "@envoy", + deps = [ + ":filter_cc_proto", + "//library/common/http:header_utility_lib", + "//library/common/http:internal_headers_lib", + "//library/common/types:c_types_lib", + "@envoy//envoy/http:codes_interface", + "@envoy//envoy/http:filter_interface", + "@envoy//source/common/grpc:common_lib", + "@envoy//source/common/grpc:status_lib", + "@envoy//source/common/http:codes_lib", + "@envoy//source/common/http:header_map_lib", + "@envoy//source/common/http:headers_lib", + "@envoy//source/common/http:utility_lib", + "@envoy//source/extensions/filters/http/common:pass_through_filter_lib", + ], +) + +envoy_cc_extension( + name = "config", + srcs = ["config.cc"], + hdrs = ["config.h"], + repository = "@envoy", + deps = [ + ":test_remote_response_filter_lib", + "@envoy//source/extensions/filters/http/common:factory_base_lib", + ], +) diff --git a/mobile/library/common/extensions/filters/http/test_remote_response/config.cc b/mobile/library/common/extensions/filters/http/test_remote_response/config.cc new file mode 100644 index 000000000000..abdcd38f18ef --- /dev/null +++ b/mobile/library/common/extensions/filters/http/test_remote_response/config.cc @@ -0,0 +1,28 @@ +#include "library/common/extensions/filters/http/test_remote_response/config.h" + +#include "library/common/extensions/filters/http/test_remote_response/filter.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace TestRemoteResponse { + +Http::FilterFactoryCb TestRemoteResponseFilterFactory::createFilterFactoryFromProtoTyped( + const envoymobile::extensions::filters::http::test_remote_response::TestRemoteResponse&, + const std::string&, Server::Configuration::FactoryContext&) { + + return [](Http::FilterChainFactoryCallbacks& callbacks) -> void { + callbacks.addStreamFilter(std::make_shared()); + }; +} + +/** + * Static registration for the TestRemoteResponse filter. @see NamedHttpFilterConfigFactory. + */ +REGISTER_FACTORY(TestRemoteResponseFilterFactory, + Server::Configuration::NamedHttpFilterConfigFactory); + +} // namespace TestRemoteResponse +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/mobile/library/common/extensions/filters/http/test_remote_response/config.h b/mobile/library/common/extensions/filters/http/test_remote_response/config.h new file mode 100644 index 000000000000..11a8c60ad84a --- /dev/null +++ b/mobile/library/common/extensions/filters/http/test_remote_response/config.h @@ -0,0 +1,34 @@ +#include + +#include "source/extensions/filters/http/common/factory_base.h" + +#include "library/common/extensions/filters/http/test_remote_response/filter.pb.h" +#include "library/common/extensions/filters/http/test_remote_response/filter.pb.validate.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace TestRemoteResponse { + +/** + * Config registration for the test_remote_response filter. @see NamedHttpFilterConfigFactory. + */ +class TestRemoteResponseFilterFactory + : public Common::FactoryBase< + envoymobile::extensions::filters::http::test_remote_response::TestRemoteResponse> { +public: + TestRemoteResponseFilterFactory() : FactoryBase("test_remote_response") {} + +private: + ::Envoy::Http::FilterFactoryCb createFilterFactoryFromProtoTyped( + const envoymobile::extensions::filters::http::test_remote_response::TestRemoteResponse& + config, + const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override; +}; + +DECLARE_FACTORY(TestRemoteResponseFilterFactory); + +} // namespace TestRemoteResponse +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/mobile/library/common/extensions/filters/http/test_remote_response/filter.cc b/mobile/library/common/extensions/filters/http/test_remote_response/filter.cc new file mode 100644 index 000000000000..60f50f6eb093 --- /dev/null +++ b/mobile/library/common/extensions/filters/http/test_remote_response/filter.cc @@ -0,0 +1,46 @@ +#include "library/common/extensions/filters/http/test_remote_response/filter.h" + +#include "envoy/http/header_map.h" +#include "envoy/server/filter_config.h" + +#include "source/common/buffer/buffer_impl.h" +#include "source/common/http/header_map_impl.h" + +#include "library/common/http/header_utility.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace TestRemoteResponse { + +Http::FilterHeadersStatus TestRemoteResponseFilter::decodeHeaders(Http::RequestHeaderMap&, + bool end_stream) { + if (end_stream) { + sendResponse(); + } + return Http::FilterHeadersStatus::StopIteration; +} +Http::FilterDataStatus TestRemoteResponseFilter::decodeData(Buffer::Instance&, bool end_stream) { + if (end_stream) { + sendResponse(); + } + return Http::FilterDataStatus::StopIterationAndBuffer; +} +Http::FilterTrailersStatus TestRemoteResponseFilter::decodeTrailers(Http::RequestTrailerMap&) { + sendResponse(); + return Http::FilterTrailersStatus::StopIteration; +} + +void TestRemoteResponseFilter::sendResponse() { + Http::ResponseHeaderMapPtr headers{ + Http::createHeaderMap({{Http::Headers::get().Status, "200"}})}; + decoder_callbacks_->encodeHeaders(std::move(headers), false, + StreamInfo::ResponseCodeDetails::get().ViaUpstream); + Buffer::OwnedImpl body("data"); + decoder_callbacks_->encodeData(body, true); +} + +} // namespace TestRemoteResponse +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/mobile/library/common/extensions/filters/http/test_remote_response/filter.h b/mobile/library/common/extensions/filters/http/test_remote_response/filter.h new file mode 100644 index 000000000000..7da43b31b8db --- /dev/null +++ b/mobile/library/common/extensions/filters/http/test_remote_response/filter.h @@ -0,0 +1,33 @@ +#pragma once + +#include "envoy/http/filter.h" + +#include "source/common/common/logger.h" +#include "source/extensions/filters/http/common/pass_through_filter.h" + +#include "library/common/extensions/filters/http/test_remote_response/filter.pb.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace TestRemoteResponse { + +/** + * Filter to test data sent from upstream. This fakes sending a response similar + * to how the router does but is hermetic as no network connection is created. + */ +class TestRemoteResponseFilter final : public Http::PassThroughFilter, + public Logger::Loggable { +public: + // StreamFilterBase + Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap&, bool end_stream) override; + Http::FilterDataStatus decodeData(Buffer::Instance&, bool end_stream) override; + Http::FilterTrailersStatus decodeTrailers(Http::RequestTrailerMap&) override; + + void sendResponse(); +}; + +} // namespace TestRemoteResponse +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/mobile/library/common/extensions/filters/http/test_remote_response/filter.proto b/mobile/library/common/extensions/filters/http/test_remote_response/filter.proto new file mode 100644 index 000000000000..07c2779fad2a --- /dev/null +++ b/mobile/library/common/extensions/filters/http/test_remote_response/filter.proto @@ -0,0 +1,6 @@ +syntax = "proto3"; + +package envoymobile.extensions.filters.http.test_remote_response; + +message TestRemoteResponse { +} diff --git a/mobile/library/common/http/client.h b/mobile/library/common/http/client.h index 4a2d459df034..1b6329a45968 100644 --- a/mobile/library/common/http/client.h +++ b/mobile/library/common/http/client.h @@ -164,6 +164,10 @@ class Client : public Logger::Loggable { } bool streamErrorOnInvalidHttpMessage() const override { return false; } void setRequestDecoder(RequestDecoder& /*decoder*/) override{}; + void setDeferredLoggingHeadersAndTrailers(Http::RequestHeaderMapConstSharedPtr, + Http::ResponseHeaderMapConstSharedPtr, + Http::ResponseTrailerMapConstSharedPtr, + StreamInfo::StreamInfo&) override {} void encodeMetadata(const MetadataMapVector&) override { PANIC("not implemented"); } diff --git a/mobile/library/common/network/BUILD b/mobile/library/common/network/BUILD index 5e3179a399d5..a214ff895b07 100644 --- a/mobile/library/common/network/BUILD +++ b/mobile/library/common/network/BUILD @@ -8,24 +8,11 @@ envoy_cc_library( name = "connectivity_manager_lib", srcs = [ "connectivity_manager.cc", - "android.cc", - ] + select({ - "//bazel:include_ifaddrs": [ - "//third_party:android/ifaddrs-android.h", - "//third_party:android/LocalArray.h", - "//third_party:android/ScopedFd.h", - ], - "//conditions:default": [], - }), + ], hdrs = [ - "android.h", "connectivity_manager.h", "proxy_settings.h", ], - copts = select({ - "//bazel:include_ifaddrs": ["-DINCLUDE_IFADDRS"], - "//conditions:default": [], - }), repository = "@envoy", deps = [ "//library/common/network:src_addr_socket_option_lib", diff --git a/mobile/library/common/network/android.cc b/mobile/library/common/network/android.cc deleted file mode 100644 index 5c9a5ec8bd0e..000000000000 --- a/mobile/library/common/network/android.cc +++ /dev/null @@ -1,78 +0,0 @@ -#include "library/common/network/android.h" - -#include - -#include "envoy/common/platform.h" - -#include "source/common/api/os_sys_calls_impl.h" -#include "source/common/common/assert.h" -#include "source/common/common/scalar_to_byte_vector.h" -#include "source/common/common/utility.h" -#include "source/common/network/addr_family_aware_socket_option_impl.h" -#include "source/common/network/address_impl.h" -#include "source/extensions/common/dynamic_forward_proxy/dns_cache_manager_impl.h" - -#include "library/common/network/src_addr_socket_option_impl.h" - -namespace Envoy { -namespace Network { -namespace Android { -namespace Utility { - -#if defined(INCLUDE_IFADDRS) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wold-style-cast" -namespace { -#include "third_party/android/ifaddrs-android.h" -} -#pragma clang diagnostic pop -#endif - -void setAlternateGetifaddrs() { -#if defined(INCLUDE_IFADDRS) - auto& os_syscalls = Api::OsSysCallsSingleton::get(); - ENVOY_BUG(!os_syscalls.supportsGetifaddrs(), - "setAlternateGetifaddrs should only be called when supportsGetifaddrs is false"); - - Api::AlternateGetifaddrs android_getifaddrs = - [](Api::InterfaceAddressVector& interfaces) -> Api::SysCallIntResult { - struct ifaddrs* ifaddr; - struct ifaddrs* ifa; - - const int rc = getifaddrs(&ifaddr); - if (rc == -1) { - return {rc, errno}; - } - - for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == nullptr) { - continue; - } - - if (ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6) { - const sockaddr_storage* ss = reinterpret_cast(ifa->ifa_addr); - size_t ss_len = - ifa->ifa_addr->sa_family == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6); - StatusOr address = - Address::addressFromSockAddr(*ss, ss_len, ifa->ifa_addr->sa_family == AF_INET6); - if (address.ok()) { - interfaces.emplace_back(ifa->ifa_name, ifa->ifa_flags, *address); - } - } - } - - if (ifaddr) { - freeifaddrs(ifaddr); - } - - return {rc, 0}; - }; - - os_syscalls.setAlternateGetifaddrs(android_getifaddrs); -#endif -} - -} // namespace Utility -} // namespace Android -} // namespace Network -} // namespace Envoy diff --git a/mobile/library/common/network/android.h b/mobile/library/common/network/android.h deleted file mode 100644 index 148cbcb97e63..000000000000 --- a/mobile/library/common/network/android.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -namespace Envoy { -namespace Network { -namespace Android { -namespace Utility { -/** - * Sets an alternate `getifaddrs` implementation than the one defined - * in Envoy by default. - */ -void setAlternateGetifaddrs(); -} // namespace Utility -} // namespace Android -} // namespace Network -} // namespace Envoy diff --git a/mobile/library/java/io/envoyproxy/envoymobile/engine/AndroidEngineImpl.java b/mobile/library/java/io/envoyproxy/envoymobile/engine/AndroidEngineImpl.java index a15a9bd1ac3b..cfde853fa03b 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/engine/AndroidEngineImpl.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/engine/AndroidEngineImpl.java @@ -33,9 +33,14 @@ public EnvoyHTTPStream startStream(EnvoyHTTPCallbacks callbacks, boolean explici return envoyEngine.startStream(callbacks, explicitFlowControl); } - public int runWithTemplate(String configurationYAML, EnvoyConfiguration envoyConfiguration, - String logLevel) { - return envoyEngine.runWithTemplate(configurationYAML, envoyConfiguration, logLevel); + @Override + public void performRegistration(EnvoyConfiguration envoyConfiguration) { + envoyEngine.performRegistration(envoyConfiguration); + } + + @Override + public int runWithYaml(String configurationYAML, String logLevel) { + return envoyEngine.runWithYaml(configurationYAML, logLevel); } @Override diff --git a/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyConfiguration.java b/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyConfiguration.java index 08bbd6837cd8..d5bc1fd7ac30 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyConfiguration.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyConfiguration.java @@ -11,6 +11,7 @@ import io.envoyproxy.envoymobile.engine.types.EnvoyHTTPFilterFactory; import io.envoyproxy.envoymobile.engine.types.EnvoyStringAccessor; import io.envoyproxy.envoymobile.engine.types.EnvoyKeyValueStore; +import io.envoyproxy.envoymobile.engine.JniLibrary; /* Typed configuration that may be used for starting Envoy. */ public class EnvoyConfiguration { @@ -133,6 +134,7 @@ public EnvoyConfiguration( Map stringAccessors, Map keyValueStores, List statSinks, Boolean enableSkipDNSLookupForProxiedRequests, boolean enablePlatformCertificatesValidation) { + JniLibrary.load(); this.adminInterfaceEnabled = adminInterfaceEnabled; this.grpcStatsDomain = grpcStatsDomain; this.connectTimeoutSeconds = connectTimeoutSeconds; @@ -169,30 +171,20 @@ public EnvoyConfiguration( this.enablePlatformCertificatesValidation = enablePlatformCertificatesValidation; this.enableSkipDNSLookupForProxiedRequests = enableSkipDNSLookupForProxiedRequests; } - /** - * Resolves the provided configuration template using properties on this - * configuration. + * Creates configuration YAML based on the configuration of the class * - * @param configTemplate the template configuration to resolve. - * @param platformFilterTemplate helper template to build platform http filters. - * @param nativeFilterTemplate helper template to build native http filters. - * @param altProtocolCacheFilterInsert helper insert to include the alt protocol cache filter. - * @param gzipFilterInsert helper to include to enable gzip compression. - * @param brotliFilterInsert helper to include to enable brotli compression. - * @param socketTagFilterInsert helper to include to enable socket tagging. - * @param persistentDNSCacheConfigInsert helper to include to enable DNS cache. - * @param certValidationTemplate helper template to enable cert validation. - * @return String, the resolved template. - * @throws ConfigurationException, when the template provided is not fully + * @return String, the resolved yaml. + * @throws ConfigurationException, when the yaml provided is not fully * resolved. */ - String resolveTemplate(final String configTemplate, final String platformFilterTemplate, - final String nativeFilterTemplate, - final String altProtocolCacheFilterInsert, final String gzipFilterInsert, - final String brotliFilterInsert, final String socketTagFilterInsert, - final String persistentDNSCacheConfigInsert, - final String certValidationTemplate) { + String createYaml() { + final String configTemplate = JniLibrary.configTemplate(); + final String certValidationTemplate = + JniLibrary.certValidationTemplate(enablePlatformCertificatesValidation); + final String platformFilterTemplate = JniLibrary.platformFilterTemplate(); + final String nativeFilterTemplate = JniLibrary.nativeFilterTemplate(); + final StringBuilder customFiltersBuilder = new StringBuilder(); for (EnvoyHTTPFilterFactory filterFactory : httpPlatformFilterFactories) { @@ -208,17 +200,21 @@ String resolveTemplate(final String configTemplate, final String platformFilterT } if (enableHttp3) { + final String altProtocolCacheFilterInsert = JniLibrary.altProtocolCacheFilterInsert(); customFiltersBuilder.append(altProtocolCacheFilterInsert); } if (enableGzip) { + final String gzipFilterInsert = JniLibrary.gzipConfigInsert(); customFiltersBuilder.append(gzipFilterInsert); } if (enableBrotli) { + final String brotliFilterInsert = JniLibrary.brotliConfigInsert(); customFiltersBuilder.append(brotliFilterInsert); } if (enableSocketTagging) { + final String socketTagFilterInsert = JniLibrary.socketTagConfigInsert(); customFiltersBuilder.append(socketTagFilterInsert); } @@ -257,6 +253,7 @@ String resolveTemplate(final String configTemplate, final String platformFilterT .append("\n"); if (enableDNSCache) { + final String persistentDNSCacheConfigInsert = JniLibrary.persistentDNSCacheConfigInsert(); configBuilder.append( String.format("- &persistent_dns_cache_config %s\n", persistentDNSCacheConfigInsert)); } diff --git a/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyEngine.java b/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyEngine.java index a5dc655fdecb..0c0ba7e66384 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyEngine.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyEngine.java @@ -22,22 +22,31 @@ public interface EnvoyEngine { */ void terminate(); + /** + * Performs any registrations necessary before running Envoy. + * + * The envoyConfiguration is used to determined what to register. + * + * @param envoyConfiguration The EnvoyConfiguration used to start Envoy. + */ + void performRegistration(EnvoyConfiguration envoyConfiguration); + /** * Run the Envoy engine with the provided yaml string and log level. * - * The envoyConfiguration is used to resolve the configurationYAML. + * This does not perform registration, and performRegistration() may need to be called first. * * @param configurationYAML The configuration yaml with which to start Envoy. - * @param envoyConfiguration The EnvoyConfiguration used to start Envoy. * @param logLevel The log level to use when starting Envoy. * @return A status indicating if the action was successful. */ - int runWithTemplate(String configurationYAML, EnvoyConfiguration envoyConfiguration, - String logLevel); + int runWithYaml(String configurationYAML, String logLevel); /** * Run the Envoy engine with the provided EnvoyConfiguration and log level. * + * This automatically performs any necessary registrations. + * * @param envoyConfiguration The EnvoyConfiguration used to start Envoy. * @param logLevel The log level to use when starting Envoy. * @return A status indicating if the action was successful. diff --git a/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyEngineImpl.java b/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyEngineImpl.java index e272fac9567d..caf6524e8c69 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyEngineImpl.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyEngineImpl.java @@ -65,18 +65,12 @@ public String dumpStats() { } /** - * Run the Envoy engine with the provided yaml string and log level. - * - * The envoyConfiguration is used to resolve the configurationYAML. + * Performs various JNI registration prior to engine running. * - * @param configurationYAML The configuration yaml with which to start Envoy. * @param envoyConfiguration The EnvoyConfiguration used to start Envoy. - * @param logLevel The log level to use when starting Envoy. - * @return A status indicating if the action was successful. */ @Override - public int runWithTemplate(String configurationYAML, EnvoyConfiguration envoyConfiguration, - String logLevel) { + public void performRegistration(EnvoyConfiguration envoyConfiguration) { for (EnvoyHTTPFilterFactory filterFactory : envoyConfiguration.httpPlatformFilterFactories) { JniLibrary.registerFilterFactory(filterFactory.getFilterName(), new JvmFilterFactoryContext(filterFactory)); @@ -93,16 +87,21 @@ public int runWithTemplate(String configurationYAML, EnvoyConfiguration envoyCon JniLibrary.registerKeyValueStore(entry.getKey(), new JvmKeyValueStoreContext(entry.getValue())); } + } - return runWithResolvedYAML( - envoyConfiguration.resolveTemplate( - configurationYAML, JniLibrary.platformFilterTemplate(), - JniLibrary.nativeFilterTemplate(), JniLibrary.altProtocolCacheFilterInsert(), - JniLibrary.gzipConfigInsert(), JniLibrary.brotliConfigInsert(), - JniLibrary.socketTagConfigInsert(), JniLibrary.persistentDNSCacheConfigInsert(), - JniLibrary.certValidationTemplate( - envoyConfiguration.enablePlatformCertificatesValidation)), - logLevel); + /** + * Run the Envoy engine with the provided yaml string and log level. + * + * This does not perform registration, and performRegistration may need to be called first. + * + * @param configurationYAML The configuration yaml with which to start Envoy. + * @param logLevel The log level to use when starting Envoy. + * @return A status indicating if the action was successful. + * TODO(alyssawilk) change all the status returns to EnvoyStatus. + */ + @Override + public int runWithYaml(String configurationYAML, String logLevel) { + return runWithResolvedYAML(configurationYAML, logLevel); } /** @@ -114,7 +113,9 @@ public int runWithTemplate(String configurationYAML, EnvoyConfiguration envoyCon */ @Override public int runWithConfig(EnvoyConfiguration envoyConfiguration, String logLevel) { - return runWithTemplate(JniLibrary.configTemplate(), envoyConfiguration, logLevel); + performRegistration(envoyConfiguration); + + return runWithResolvedYAML(envoyConfiguration.createYaml(), logLevel); } private int runWithResolvedYAML(String configurationYAML, String logLevel) { diff --git a/mobile/library/kotlin/io/envoyproxy/envoymobile/EngineImpl.kt b/mobile/library/kotlin/io/envoyproxy/envoymobile/EngineImpl.kt index 893338418bc0..066ac6794a65 100644 --- a/mobile/library/kotlin/io/envoyproxy/envoymobile/EngineImpl.kt +++ b/mobile/library/kotlin/io/envoyproxy/envoymobile/EngineImpl.kt @@ -26,7 +26,8 @@ class EngineImpl constructor( streamClient = StreamClientImpl(envoyEngine) pulseClient = PulseClientImpl(envoyEngine) if (configurationYAML != null) { - envoyEngine.runWithTemplate(configurationYAML, envoyConfiguration, logLevel.level) + envoyEngine.performRegistration(envoyConfiguration) + envoyEngine.runWithYaml(configurationYAML, logLevel.level) } else { envoyEngine.runWithConfig(envoyConfiguration, logLevel.level) } diff --git a/mobile/library/kotlin/io/envoyproxy/envoymobile/mocks/MockEnvoyEngine.kt b/mobile/library/kotlin/io/envoyproxy/envoymobile/mocks/MockEnvoyEngine.kt index 43b4e1fd5c4b..07e614c0217d 100644 --- a/mobile/library/kotlin/io/envoyproxy/envoymobile/mocks/MockEnvoyEngine.kt +++ b/mobile/library/kotlin/io/envoyproxy/envoymobile/mocks/MockEnvoyEngine.kt @@ -13,9 +13,10 @@ import io.envoyproxy.envoymobile.engine.types.EnvoyStringAccessor internal class MockEnvoyEngine : EnvoyEngine { override fun runWithConfig(envoyConfiguration: EnvoyConfiguration?, logLevel: String?): Int = 0 - override fun runWithTemplate( + override fun performRegistration(envoyConfiguration: EnvoyConfiguration) = Unit + + override fun runWithYaml( configurationYAML: String, - envoyConfiguration: EnvoyConfiguration, logLevel: String ): Int = 0 diff --git a/mobile/library/python/envoy_engine.pyi b/mobile/library/python/envoy_engine.pyi index 092e17b552a1..1663047df812 100644 --- a/mobile/library/python/envoy_engine.pyi +++ b/mobile/library/python/envoy_engine.pyi @@ -21,7 +21,7 @@ class EngineBuilder: def add_stats_flush_seconds(self, stats_flush_seconds: int) -> "EngineBuilder": ... def set_app_version(self, app_version: str) -> "EngineBuilder": ... def set_app_id(self, app_id: str) -> "EngineBuilder": ... - def add_virtual_clusters(self, virtual_clusters: str) -> "EngineBuilder": ... + def add_virtual_cluster(self, virtual_cluster: str) -> "EngineBuilder": ... def build(self) -> "Engine": ... # TODO: add after filter integration diff --git a/mobile/library/python/module_definition.cc b/mobile/library/python/module_definition.cc index be2321863f98..a673db79502b 100644 --- a/mobile/library/python/module_definition.cc +++ b/mobile/library/python/module_definition.cc @@ -62,7 +62,7 @@ PYBIND11_MODULE(envoy_engine, m) { .def("add_stats_flush_seconds", &EngineBuilder::addStatsFlushSeconds) .def("set_app_version", &EngineBuilder::setAppVersion) .def("set_app_id", &EngineBuilder::setAppId) - .def("add_virtual_clusters", &EngineBuilder::addVirtualClusters) + .def("add_virtual_cluster", &EngineBuilder::addVirtualCluster) // TODO(crockeo): add after filter integration // .def("add_platform_filter", &EngineBuilder::addPlatformFilter) // .def("add_native_filter", &EngineBuilder::addNativeFilter) diff --git a/mobile/test/cc/unit/BUILD b/mobile/test/cc/unit/BUILD index a204d7026672..069035caf7c4 100644 --- a/mobile/test/cc/unit/BUILD +++ b/mobile/test/cc/unit/BUILD @@ -1,4 +1,4 @@ -load("@envoy//bazel:envoy_build_system.bzl", "envoy_cc_test", "envoy_package") +load("@envoy//bazel:envoy_build_system.bzl", "envoy_cc_benchmark_binary", "envoy_cc_test", "envoy_package") licenses(["notice"]) # Apache 2 @@ -14,3 +14,27 @@ envoy_cc_test( "@envoy_build_config//:extension_registry", ], ) + +envoy_cc_test( + name = "request_headers_builder_test", + srcs = ["request_headers_builder_test.cc"], + repository = "@envoy", + deps = [ + "//library/cc:envoy_engine_cc_lib_no_stamp", + "@envoy_build_config//:extension_registry", + ], +) + +envoy_cc_benchmark_binary( + name = "envoy_config_benchmark", + srcs = ["envoy_config_benchmark.cc"], + external_deps = [ + "benchmark", + ], + repository = "@envoy", + deps = [ + "//library/cc:engine_builder_lib", + "//library/cc:envoy_engine_cc_lib_no_stamp", + "@envoy_build_config//:extension_registry", + ], +) diff --git a/mobile/test/cc/unit/envoy_config_benchmark.cc b/mobile/test/cc/unit/envoy_config_benchmark.cc new file mode 100644 index 000000000000..a2376903d42b --- /dev/null +++ b/mobile/test/cc/unit/envoy_config_benchmark.cc @@ -0,0 +1,47 @@ +#include +#include + +#include "test/test_common/utility.h" + +#include "absl/strings/str_replace.h" +#include "absl/synchronization/notification.h" +#include "gtest/gtest.h" +#include "library/cc/engine_builder.h" +#include "library/cc/log_level.h" +#include "library/common/api/external.h" +#include "library/common/config/internal.h" +#include "library/common/data/utility.h" + +#if defined(__APPLE__) +#include "source/extensions/network/dns_resolver/apple/apple_dns_impl.h" +#endif + +#include "benchmark/benchmark.h" +namespace Envoy { +// NOLINTNEXTLINE(readability-identifier-naming) +static void BM_LoadFromString(benchmark::State& state) { + Platform::EngineBuilder engine_builder; + ProtobufMessage::ValidationVisitor& validation_visitor = + ProtobufMessage::getStrictValidationVisitor(); + + for (auto _ : state) { // NOLINT + std::string config_yaml = engine_builder.generateConfigStr(); + envoy::config::bootstrap::v3::Bootstrap bootstrap; + MessageUtil::loadFromYaml(absl::StrCat(config_header, config_yaml), bootstrap, + validation_visitor); + } +} + +static void BM_LoadFromProto(benchmark::State& state) { + Platform::EngineBuilder engine_builder; + + for (auto _ : state) { // NOLINT + auto bootstrap = engine_builder.generateBootstrap(); + envoy::config::bootstrap::v3::Bootstrap envoy_bootstrap; + envoy_bootstrap.CopyFrom(*bootstrap); + } +} +} // namespace Envoy + +BENCHMARK(Envoy::BM_LoadFromString)->Unit(benchmark::kMillisecond); +BENCHMARK(Envoy::BM_LoadFromProto)->Unit(benchmark::kMillisecond); diff --git a/mobile/test/cc/unit/envoy_config_test.cc b/mobile/test/cc/unit/envoy_config_test.cc index 65e1e4f37540..c0e5b534e7ce 100644 --- a/mobile/test/cc/unit/envoy_config_test.cc +++ b/mobile/test/cc/unit/envoy_config_test.cc @@ -33,13 +33,15 @@ TEST(TestConfig, ConfigIsApplied) { .addDnsMinRefreshSeconds(567) .addDnsFailureRefreshSeconds(789, 987) .addDnsQueryTimeoutSeconds(321) - .addDnsPreresolveHostnames("[hostname]") .addH2ConnectionKeepaliveIdleIntervalMilliseconds(222) .addH2ConnectionKeepaliveTimeoutSeconds(333) .addStatsFlushSeconds(654) - .addVirtualClusters("[virtual-clusters]") .setAppVersion("1.2.3") .setAppId("1234-1234-1234") + .enableDnsCache(true) + .addDnsPreresolveHostnames({"lyft.com", "google.com"}) + .enableAdminInterface(true) + .setForceAlwaysUsev6(true) .setDeviceOs("probably-ubuntu-on-CI"); std::string config_str = engine_builder.generateConfigStr(); @@ -50,18 +52,23 @@ TEST(TestConfig, ConfigIsApplied) { "- &dns_fail_max_interval 987s", "- &dns_min_refresh_rate 567s", "- &dns_query_timeout 321s", - "- &dns_preresolve_hostnames [hostname]", "- &h2_connection_keepalive_idle_interval 0.222s", "- &h2_connection_keepalive_timeout 333s", "- &stats_flush_interval 654s", - "- &virtual_clusters [virtual-clusters]", + " key: dns_persistent_cache", + "- &force_ipv6 true", ("- &metadata { device_os: probably-ubuntu-on-CI, " "app_version: 1.2.3, app_id: 1234-1234-1234 }"), R"(- &validation_context trusted_ca:)"}; for (const auto& string : must_contain) { - ASSERT_NE(config_str.find(string), std::string::npos) << "'" << string << "' not found"; + ASSERT_NE(config_str.find(string), std::string::npos) + << "'" << string << "' not found in" << config_str; } + envoy::config::bootstrap::v3::Bootstrap bootstrap; + TestUtility::loadFromYaml(absl::StrCat(config_header, config_str), bootstrap); + engine_builder.generateBootstrap(); + EXPECT_TRUE(TestUtility::protoEqual(bootstrap, *engine_builder.generateBootstrap())); } TEST(TestConfig, ConfigIsValid) { @@ -78,6 +85,8 @@ TEST(TestConfig, ConfigIsValid) { ASSERT_THAT(bootstrap.DebugString(), HasSubstr("envoy.network.dns_resolver.getaddrinfo")); ASSERT_THAT(bootstrap.DebugString(), Not(HasSubstr("envoy.network.dns_resolver.apple"))); #endif + + EXPECT_TRUE(TestUtility::protoEqual(bootstrap, *engine_builder.generateBootstrap())); } TEST(TestConfig, SetGzip) { @@ -88,6 +97,7 @@ TEST(TestConfig, SetGzip) { envoy::config::bootstrap::v3::Bootstrap bootstrap; TestUtility::loadFromYaml(absl::StrCat(config_header, config_str), bootstrap); ASSERT_THAT(bootstrap.DebugString(), Not(HasSubstr("envoy.filters.http.decompressor"))); + EXPECT_TRUE(TestUtility::protoEqual(bootstrap, *engine_builder.generateBootstrap())); engine_builder.enableGzip(true); config_str = engine_builder.generateConfigStr(); @@ -103,11 +113,13 @@ TEST(TestConfig, SetBrotli) { envoy::config::bootstrap::v3::Bootstrap bootstrap; TestUtility::loadFromYaml(absl::StrCat(config_header, config_str), bootstrap); ASSERT_THAT(bootstrap.DebugString(), Not(HasSubstr("brotli.decompressor.v3.Brotli"))); + EXPECT_TRUE(TestUtility::protoEqual(bootstrap, *engine_builder.generateBootstrap())); engine_builder.enableBrotli(true); config_str = engine_builder.generateConfigStr(); TestUtility::loadFromYaml(absl::StrCat(config_header, config_str), bootstrap); ASSERT_THAT(bootstrap.DebugString(), HasSubstr("brotli.decompressor.v3.Brotli")); + EXPECT_TRUE(TestUtility::protoEqual(bootstrap, *engine_builder.generateBootstrap())); } TEST(TestConfig, SetSocketTag) { @@ -118,11 +130,13 @@ TEST(TestConfig, SetSocketTag) { envoy::config::bootstrap::v3::Bootstrap bootstrap; TestUtility::loadFromYaml(absl::StrCat(config_header, config_str), bootstrap); ASSERT_THAT(bootstrap.DebugString(), Not(HasSubstr("http.socket_tag.SocketTag"))); + EXPECT_TRUE(TestUtility::protoEqual(bootstrap, *engine_builder.generateBootstrap())); engine_builder.enableSocketTagging(true); config_str = engine_builder.generateConfigStr(); TestUtility::loadFromYaml(absl::StrCat(config_header, config_str), bootstrap); ASSERT_THAT(bootstrap.DebugString(), HasSubstr("http.socket_tag.SocketTag")); + EXPECT_TRUE(TestUtility::protoEqual(bootstrap, *engine_builder.generateBootstrap())); } TEST(TestConfig, SetAltSvcCache) { @@ -144,11 +158,13 @@ TEST(TestConfig, StreamIdleTimeout) { ASSERT_THAT(config_str, HasSubstr("&stream_idle_timeout 15s")); envoy::config::bootstrap::v3::Bootstrap bootstrap; TestUtility::loadFromYaml(absl::StrCat(config_header, config_str), bootstrap); + EXPECT_TRUE(TestUtility::protoEqual(bootstrap, *engine_builder.generateBootstrap())); engine_builder.setStreamIdleTimeoutSeconds(42); config_str = engine_builder.generateConfigStr(); ASSERT_THAT(config_str, HasSubstr("&stream_idle_timeout 42s")); TestUtility::loadFromYaml(absl::StrCat(config_header, config_str), bootstrap); + EXPECT_TRUE(TestUtility::protoEqual(bootstrap, *engine_builder.generateBootstrap())); } TEST(TestConfig, PerTryIdleTimeout) { @@ -158,11 +174,13 @@ TEST(TestConfig, PerTryIdleTimeout) { ASSERT_THAT(config_str, HasSubstr("&per_try_idle_timeout 15s")); envoy::config::bootstrap::v3::Bootstrap bootstrap; TestUtility::loadFromYaml(absl::StrCat(config_header, config_str), bootstrap); + EXPECT_TRUE(TestUtility::protoEqual(bootstrap, *engine_builder.generateBootstrap())); engine_builder.setPerTryIdleTimeoutSeconds(42); config_str = engine_builder.generateConfigStr(); ASSERT_THAT(config_str, HasSubstr("&per_try_idle_timeout 42s")); TestUtility::loadFromYaml(absl::StrCat(config_header, config_str), bootstrap); + EXPECT_TRUE(TestUtility::protoEqual(bootstrap, *engine_builder.generateBootstrap())); } TEST(TestConfig, EnableAdminInterface) { @@ -186,11 +204,13 @@ TEST(TestConfig, EnableInterfaceBinding) { ASSERT_THAT(config_str, HasSubstr("&enable_interface_binding false")); envoy::config::bootstrap::v3::Bootstrap bootstrap; TestUtility::loadFromYaml(absl::StrCat(config_header, config_str), bootstrap); + EXPECT_TRUE(TestUtility::protoEqual(bootstrap, *engine_builder.generateBootstrap())); engine_builder.enableInterfaceBinding(true); config_str = engine_builder.generateConfigStr(); ASSERT_THAT(config_str, HasSubstr("&enable_interface_binding true")); TestUtility::loadFromYaml(absl::StrCat(config_header, config_str), bootstrap); + EXPECT_TRUE(TestUtility::protoEqual(bootstrap, *engine_builder.generateBootstrap())); } TEST(TestConfig, EnableDrainPostDnsRefresh) { @@ -271,8 +291,8 @@ TEST(TestConfig, AddStatsSinks) { engine_builder.addStatsSinks({statsdSinkConfig(1), statsdSinkConfig(2)}); config_str = engine_builder.generateConfigStr(); - ASSERT_THAT(config_str, - HasSubstr("&stats_sinks [" + statsdSinkConfig(1) + "," + statsdSinkConfig(2) + "]")); + ASSERT_THAT(config_str, HasSubstr(statsdSinkConfig(1))); + ASSERT_THAT(config_str, HasSubstr(statsdSinkConfig(2))); TestUtility::loadFromYaml(absl::StrCat(config_header, config_str), bootstrap); } @@ -303,6 +323,17 @@ TEST(TestConfig, RemainingTemplatesThrows) { } } +TEST(TestConfig, RtdsWithoutAds) { + EngineBuilder engine_builder; + engine_builder.addRtdsLayer("some rtds layer"); + try { + engine_builder.generateConfigStr(); + FAIL() << "Expected std::runtime_error"; + } catch (std::runtime_error& err) { + EXPECT_EQ(err.what(), std::string("ADS must be configured when using RTDS")); + } +} + TEST(TestConfig, EnablePlatformCertificatesValidation) { EngineBuilder engine_builder; envoy::config::bootstrap::v3::Bootstrap bootstrap; @@ -340,24 +371,27 @@ class TestStringAccessor : public StringAccessor { mutable int count_ = 0; }; -TEST(TestConfig, AddNativeFilter) { +TEST(TestConfig, AddNativeFilters) { EngineBuilder engine_builder; - std::string filter_name = "envoy.filters.http.buffer"; + std::string filter_name1 = "envoy.filters.http.buffer1"; + std::string filter_name2 = "envoy.filters.http.buffer2"; std::string filter_config = "{\"@type\":\"type.googleapis.com/envoy.extensions.filters.http.buffer.v3.Buffer\"," "\"max_request_bytes\":5242880}"; std::string config_str = engine_builder.generateConfigStr(); - ASSERT_THAT(config_str, Not(HasSubstr("- name: " + filter_name))); + ASSERT_THAT(config_str, Not(HasSubstr("- name: " + filter_name1))); ASSERT_THAT(config_str, Not(HasSubstr(" typed_config: " + filter_config))); envoy::config::bootstrap::v3::Bootstrap bootstrap; TestUtility::loadFromYaml(absl::StrCat(config_header, config_str), bootstrap); - engine_builder.addNativeFilter(filter_name, filter_config); + engine_builder.addNativeFilter(filter_name1, filter_config); + engine_builder.addNativeFilter(filter_name2, filter_config); config_str = engine_builder.generateConfigStr(); - ASSERT_THAT(config_str, HasSubstr("- name: " + filter_name)); + ASSERT_THAT(config_str, HasSubstr("- name: " + filter_name1)); + ASSERT_THAT(config_str, HasSubstr("- name: " + filter_name2)); ASSERT_THAT(config_str, HasSubstr(" typed_config: " + filter_config)); TestUtility::loadFromYaml(absl::StrCat(config_header, config_str), bootstrap); } @@ -397,5 +431,22 @@ TEST(TestConfig, DISABLED_StringAccessors) { EXPECT_EQ(data_string, Data::Utility::copyToString(data)); release_envoy_data(data); } + +TEST(TestConfig, AddVirtualCluster) { + EngineBuilder engine_builder; + envoy::config::bootstrap::v3::Bootstrap bootstrap; + engine_builder.addVirtualCluster( + "{headers: [{name: ':method', string_match: {exact: POST}}], name: cluster1}"); + std::string config_str = engine_builder.generateConfigStr(); + TestUtility::loadFromYaml(absl::StrCat(config_header, config_str), bootstrap); + ASSERT_THAT(config_str, HasSubstr("cluster1")); + + engine_builder.addVirtualCluster( + "{headers: [{name: ':method', string_match: {exact: GET}}], name: cluster2}"); + config_str = engine_builder.generateConfigStr(); + TestUtility::loadFromYaml(absl::StrCat(config_header, config_str), bootstrap); + ASSERT_THAT(config_str, HasSubstr("cluster2")); +} + } // namespace } // namespace Envoy diff --git a/mobile/test/cc/unit/request_headers_builder_test.cc b/mobile/test/cc/unit/request_headers_builder_test.cc new file mode 100644 index 000000000000..11ae924f5a6b --- /dev/null +++ b/mobile/test/cc/unit/request_headers_builder_test.cc @@ -0,0 +1,40 @@ +#include +#include + +#include "gtest/gtest.h" +#include "library/cc/request_headers_builder.h" + +namespace Envoy { +namespace Platform { +namespace { + +TEST(RequestHeadersBuilderTest, ConstructsFromPieces) { + RequestHeadersBuilder builder(RequestMethod::POST, "https", "www.example.com", "/"); + RequestHeaders headers = builder.build(); + EXPECT_EQ(RequestMethod::POST, headers.requestMethod()); + EXPECT_EQ("https", headers.scheme()); + EXPECT_EQ("www.example.com", headers.authority()); + EXPECT_EQ("/", headers.path()); +} + +TEST(RequestHeadersBuilderTest, ConstructsFromUrl) { + RequestHeadersBuilder builder(RequestMethod::POST, "https://www.example.com/"); + RequestHeaders headers = builder.build(); + EXPECT_EQ(RequestMethod::POST, headers.requestMethod()); + EXPECT_EQ("https", headers.scheme()); + EXPECT_EQ("www.example.com", headers.authority()); + EXPECT_EQ("/", headers.path()); +} + +TEST(RequestHeadersBuilderTest, ConstructsFromInvalidUrl) { + RequestHeadersBuilder builder(RequestMethod::POST, "root@example.com"); + RequestHeaders headers = builder.build(); + EXPECT_EQ(RequestMethod::POST, headers.requestMethod()); + EXPECT_EQ("", headers.scheme()); + EXPECT_EQ("", headers.authority()); + EXPECT_EQ("", headers.path()); +} + +} // namespace +} // namespace Platform +} // namespace Envoy diff --git a/mobile/test/common/extensions/cert_validator/platform_bridge/platform_bridge_cert_validator_test.cc b/mobile/test/common/extensions/cert_validator/platform_bridge/platform_bridge_cert_validator_test.cc index 2535e6e91c0b..718c09a3809c 100644 --- a/mobile/test/common/extensions/cert_validator/platform_bridge/platform_bridge_cert_validator_test.cc +++ b/mobile/test/common/extensions/cert_validator/platform_bridge/platform_bridge_cert_validator_test.cc @@ -61,7 +61,7 @@ class PlatformBridgeCertValidatorTest protected: PlatformBridgeCertValidatorTest() : api_(Api::createApiForTest()), dispatcher_(api_->allocateDispatcher("test_thread")), - stats_(generateSslStats(test_store_)), ssl_ctx_(SSL_CTX_new(TLS_method())), + stats_(generateSslStats(*test_store_.rootScope())), ssl_ctx_(SSL_CTX_new(TLS_method())), callback_(std::make_unique()), is_server_(false) { mock_validator_ = std::make_unique(); main_thread_id_ = std::this_thread::get_id(); diff --git a/mobile/test/common/http/client_test.cc b/mobile/test/common/http/client_test.cc index 600d73404bce..1fae40a03463 100644 --- a/mobile/test/common/http/client_test.cc +++ b/mobile/test/common/http/client_test.cc @@ -153,7 +153,7 @@ class ClientTest : public testing::TestWithParam { NiceMock random_; Stats::IsolatedStoreImpl stats_store_; bool explicit_flow_control_{GetParam()}; - Client http_client_{api_listener_, dispatcher_, stats_store_, random_}; + Client http_client_{api_listener_, dispatcher_, *stats_store_.rootScope(), random_}; envoy_stream_t stream_ = 1; }; diff --git a/mobile/test/common/integration/base_client_integration_test.cc b/mobile/test/common/integration/base_client_integration_test.cc index b79e974646d3..60958466b4aa 100644 --- a/mobile/test/common/integration/base_client_integration_test.cc +++ b/mobile/test/common/integration/base_client_integration_test.cc @@ -201,9 +201,8 @@ void BaseClientIntegrationTest::createEnvoy() { } } - finalizeConfigWithPorts(config_helper_, ports, use_lds_); - if (override_builder_config_) { + finalizeConfigWithPorts(config_helper_, ports, use_lds_); ASSERT_FALSE(config_helper_.bootstrap().has_admin()) << "Bootstrap config should not have `admin` configured in Envoy Mobile"; builder_.setOverrideConfigForTests( diff --git a/mobile/test/common/integration/quic_test_server.cc b/mobile/test/common/integration/quic_test_server.cc index d42cc4ec412a..b4ae1159409b 100644 --- a/mobile/test/common/integration/quic_test_server.cc +++ b/mobile/test/common/integration/quic_test_server.cc @@ -34,7 +34,7 @@ QuicTestServer::QuicTestServer() : api_(Api::createApiForTest(stats_store_, time_system_)), version_(Network::Address::IpVersion::v4), upstream_config_(time_system_), port_(0) { ON_CALL(factory_context_, api()).WillByDefault(testing::ReturnRef(*api_)); - ON_CALL(factory_context_, scope()).WillByDefault(testing::ReturnRef(stats_store_)); + ON_CALL(factory_context_, scope()).WillByDefault(testing::ReturnRef(*stats_store_.rootScope())); upstream_config_.udp_fake_upstream_ = FakeUpstreamConfig::UdpConfig(); } diff --git a/mobile/test/common/integration/rtds_integration_test.cc b/mobile/test/common/integration/rtds_integration_test.cc index 92db68851d7c..728b1ab81510 100644 --- a/mobile/test/common/integration/rtds_integration_test.cc +++ b/mobile/test/common/integration/rtds_integration_test.cc @@ -9,51 +9,30 @@ namespace Envoy { namespace { -envoy::config::bootstrap::v3::LayeredRuntime layeredRuntimeConfig(const std::string& api_type) { - const std::string yaml = fmt::format(R"EOF( - layers: - - name: some_static_layer - static_layer: - envoy.reloadable_features.test_feature_false: True - - name: some_rtds_layer - rtds_layer: - name: some_rtds_layer - rtds_config: - initial_fetch_timeout: - seconds: 1 - resource_api_version: V3 - api_config_source: - api_type: {} - transport_api_version: V3 - grpc_services: - envoy_grpc: - cluster_name: {} - set_node_on_first_message_only: true - )EOF", - api_type, XDS_CLUSTER); - - envoy::config::bootstrap::v3::LayeredRuntime config; - TestUtility::loadFromYaml(yaml, config); - return config; -} - class RtdsIntegrationTest : public XdsIntegrationTest { public: - RtdsIntegrationTest() { - config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { - // Add the layered runtime config, which includes the RTDS layer. - const std::string api_type = sotw_or_delta_ == Grpc::SotwOrDelta::Sotw || - sotw_or_delta_ == Grpc::SotwOrDelta::UnifiedSotw - ? "GRPC" - : "DELTA_GRPC"; - bootstrap.mutable_layered_runtime()->MergeFrom(layeredRuntimeConfig(api_type)); - }); - } - void initialize() override { XdsIntegrationTest::initialize(); + + default_request_headers_.setScheme("http"); initializeXdsStream(); } + void createEnvoy() override { + // Add the layered runtime config, which includes the RTDS layer. + const std::string api_type = sotw_or_delta_ == Grpc::SotwOrDelta::Sotw || + sotw_or_delta_ == Grpc::SotwOrDelta::UnifiedSotw + ? "GRPC" + : "DELTA_GRPC"; + builder_.addRtdsLayer("some_rtds_layer", 1); + + builder_.setAggregatedDiscoveryService(api_type, + Network::Test::getLoopbackAddressUrlString(ipVersion()), + fake_upstreams_[1]->localAddress()->ip()->port()); + XdsIntegrationTest::createEnvoy(); + } + + // using http1 because the h1 cluster has a plaintext socket + void SetUp() override { setUpstreamProtocol(Http::CodecType::HTTP1); } }; INSTANTIATE_TEST_SUITE_P(IpVersionsClientTypeDelta, RtdsIntegrationTest, @@ -72,10 +51,11 @@ TEST_P(RtdsIntegrationTest, RtdsReload) { EXPECT_EQ(cc_.on_complete_calls, 1); EXPECT_EQ(cc_.on_cancel_calls, 0); EXPECT_EQ(cc_.on_error_calls, 0); - EXPECT_EQ(cc_.on_header_consumed_bytes_from_response, 13); - EXPECT_EQ(cc_.on_complete_received_byte_count, 41); + EXPECT_EQ(cc_.on_header_consumed_bytes_from_response, 27); + EXPECT_EQ(cc_.on_complete_received_byte_count, 67); // Check that the Runtime config is from the static layer. - EXPECT_TRUE(Runtime::runtimeFeatureEnabled("envoy.reloadable_features.test_feature_false")); + EXPECT_FALSE(Runtime::runtimeFeatureEnabled( + "envoy.reloadable_features.skip_dns_lookup_for_proxied_requests")); const std::string load_success_counter = "runtime.load_success"; uint64_t load_success_value = getCounterValue(load_success_counter); @@ -85,8 +65,7 @@ TEST_P(RtdsIntegrationTest, RtdsReload) { auto some_rtds_layer = TestUtility::parseYaml(R"EOF( name: some_rtds_layer layer: - envoy.reloadable_features.test_feature_false: False - envoy.reloadable_features.test_feature_true: False + envoy.reloadable_features.skip_dns_lookup_for_proxied_requests: True )EOF"); sendDiscoveryResponse( Config::TypeUrl::get().Runtime, {some_rtds_layer}, {some_rtds_layer}, {}, "1"); @@ -94,8 +73,8 @@ TEST_P(RtdsIntegrationTest, RtdsReload) { ASSERT_TRUE(waitForCounterGe(load_success_counter, load_success_value + 1)); // Verify that the Runtime config values are from the RTDS response. - EXPECT_FALSE(Runtime::runtimeFeatureEnabled("envoy.reloadable_features.test_feature_false")); - EXPECT_FALSE(Runtime::runtimeFeatureEnabled("envoy.reloadable_features.test_feature_true")); + EXPECT_TRUE(Runtime::runtimeFeatureEnabled( + "envoy.reloadable_features.skip_dns_lookup_for_proxied_requests")); } } // namespace diff --git a/mobile/test/common/integration/sds_integration_test.cc b/mobile/test/common/integration/sds_integration_test.cc index cec65d59139a..37c88ca6f9ef 100644 --- a/mobile/test/common/integration/sds_integration_test.cc +++ b/mobile/test/common/integration/sds_integration_test.cc @@ -20,10 +20,26 @@ const envoy::service::secret::v3::SdsDummy _sds_dummy; class SdsIntegrationTest : public XdsIntegrationTest { public: SdsIntegrationTest() { + override_builder_config_ = true; ExtensionRegistry::registerFactories(); skip_tag_extraction_rule_check_ = true; upstream_tls_ = true; + config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + // The default stats config has overenthusiastic filters. + bootstrap.clear_stats_config(); + + // Add two clusters by default: + // - base_h2: An HTTP2 cluster with one fake upstream endpoint, for accepting requests from + // EM. + // - xds_cluster.lyft.com: An xDS management server cluster, with one fake upstream endpoint. + bootstrap.mutable_static_resources()->clear_clusters(); + bootstrap.mutable_static_resources()->add_clusters()->MergeFrom( + createSingleEndpointClusterConfig("base_h2")); + bootstrap.mutable_static_resources()->add_clusters()->MergeFrom( + createSingleEndpointClusterConfig(std::string(XDS_CLUSTER))); + }); + config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { // Change the base_h2 cluster to use SSL and SDS. auto* transport_socket = diff --git a/mobile/test/common/integration/xds_integration_test.cc b/mobile/test/common/integration/xds_integration_test.cc index 7881b32b45f1..0ba662f6bae0 100644 --- a/mobile/test/common/integration/xds_integration_test.cc +++ b/mobile/test/common/integration/xds_integration_test.cc @@ -3,6 +3,8 @@ #include "envoy/config/bootstrap/v3/bootstrap.pb.h" #include "envoy/config/cluster/v3/cluster.pb.h" +#include "source/common/grpc/google_grpc_creds_impl.h" + #include "test/common/grpc/grpc_client_integration.h" #include "test/common/integration/base_client_integration_test.h" #include "test/test_common/environment.h" @@ -17,8 +19,9 @@ using ::testing::AssertionResult; using ::testing::AssertionSuccess; XdsIntegrationTest::XdsIntegrationTest() : BaseClientIntegrationTest(ipVersion()) { - override_builder_config_ = true; // The builder does not yet have RTDS support. - expect_dns_ = false; // TODO(alyssawilk) debug. + Grpc::forceRegisterDefaultGoogleGrpcCredentialsFactory(); + override_builder_config_ = false; + expect_dns_ = false; // TODO(alyssawilk) debug. create_xds_upstream_ = true; sotw_or_delta_ = sotwOrDelta(); @@ -27,21 +30,6 @@ XdsIntegrationTest::XdsIntegrationTest() : BaseClientIntegrationTest(ipVersion() config_helper_.addRuntimeOverride("envoy.reloadable_features.unified_mux", "true"); } - // Set up the basic bootstrap config for xDS. - config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { - // The default stats config has overenthusiastic filters. - bootstrap.clear_stats_config(); - - // Add two clusters by default: - // - base_h2: An HTTP2 cluster with one fake upstream endpoint, for accepting requests from EM. - // - xds_cluster.lyft.com: An xDS management server cluster, with one fake upstream endpoint. - bootstrap.mutable_static_resources()->clear_clusters(); - bootstrap.mutable_static_resources()->add_clusters()->MergeFrom( - createSingleEndpointClusterConfig("base_h2")); - bootstrap.mutable_static_resources()->add_clusters()->MergeFrom( - createSingleEndpointClusterConfig(std::string(XDS_CLUSTER))); - }); - // xDS upstream is created separately in the test infra, and there's only one non-xDS cluster. setUpstreamCount(1); } diff --git a/mobile/test/java/io/envoyproxy/envoymobile/engine/BUILD b/mobile/test/java/io/envoyproxy/envoymobile/engine/BUILD index 7fc60638e945..d809fc46af59 100644 --- a/mobile/test/java/io/envoyproxy/envoymobile/engine/BUILD +++ b/mobile/test/java/io/envoyproxy/envoymobile/engine/BUILD @@ -1,12 +1,16 @@ -load("@envoy_mobile//bazel:kotlin_test.bzl", "envoy_mobile_android_test", "envoy_mobile_kt_test") +load("@envoy_mobile//bazel:kotlin_test.bzl", "envoy_mobile_android_test", "envoy_mobile_jni_kt_test", "envoy_mobile_kt_test") licenses(["notice"]) # Apache 2 -envoy_mobile_kt_test( +envoy_mobile_jni_kt_test( name = "envoy_configuration_test", srcs = [ "EnvoyConfigurationTest.kt", ], + native_deps = [ + "//test/common/jni:libenvoy_jni_with_test_extensions.so", + "//test/common/jni:libenvoy_jni_with_test_extensions_jnilib", + ], deps = [ "//library/kotlin/io/envoyproxy/envoymobile:envoy_interfaces_lib", ], diff --git a/mobile/test/java/io/envoyproxy/envoymobile/engine/EnvoyConfigurationTest.kt b/mobile/test/java/io/envoyproxy/envoymobile/engine/EnvoyConfigurationTest.kt index 1c70cddc0804..98f24507bb77 100644 --- a/mobile/test/java/io/envoyproxy/envoymobile/engine/EnvoyConfigurationTest.kt +++ b/mobile/test/java/io/envoyproxy/envoymobile/engine/EnvoyConfigurationTest.kt @@ -1,59 +1,65 @@ package io.envoyproxy.envoymobile.engine +import io.envoyproxy.envoymobile.engine.types.EnvoyHTTPFilter +import io.envoyproxy.envoymobile.engine.types.EnvoyHTTPFilterFactory import io.envoyproxy.envoymobile.engine.EnvoyConfiguration.TrustChainVerification +import io.envoyproxy.envoymobile.engine.JniLibrary +import io.envoyproxy.envoymobile.engine.types.EnvoyStreamIntel +import io.envoyproxy.envoymobile.engine.types.EnvoyFinalStreamIntel +import io.envoyproxy.envoymobile.engine.types.EnvoyHTTPFilterCallbacks +import java.nio.ByteBuffer import org.assertj.core.api.Assertions.assertThat import org.junit.Assert.fail import org.junit.Test +import java.util.regex.Pattern -private const val TEST_CONFIG = -""" -fixture_template: -- name: mock - filters: -#{custom_filters} -""" - -private const val PLATFORM_FILTER_CONFIG = -""" - - platform_filter_name: {{ platform_filter_name }} -""" - -private const val NATIVE_FILTER_CONFIG = -""" - - name: {{ native_filter_name }} - typed_config: {{ native_filter_typed_config }} -""" - -private const val APCF_INSERT = -""" - - name: AlternateProtocolsCacheFilter -""" - -private const val GZIP_INSERT = -""" - - name: GzipFilter -""" - -private const val BROTLI_INSERT = -""" - - name: BrotliFilter -""" - -private const val SOCKET_TAG_INSERT = -""" - - name: SocketTag -""" - -private const val CERT_VALIDATION_TEMPLATE = -""" - custom_validator_config: - name: "dumb_validator" -""" - -private const val PERSISTENT_DNS_CACHE_INSERT = -""" - config: persistent_dns_cache -""" +class TestFilter : EnvoyHTTPFilter { +override fun onRequestHeaders(headers: MutableMap>, endStream: Boolean, streamIntel: EnvoyStreamIntel): Array { + return emptyArray() +} +override fun onRequestData(data: ByteBuffer, endStream: Boolean, streamIntel: EnvoyStreamIntel): Array { + return emptyArray() +} +override fun onRequestTrailers(trailers: MutableMap>, streamIntel: EnvoyStreamIntel): Array { + return emptyArray() +} +override fun onResponseHeaders(headers: MutableMap>, endStream: Boolean, streamIntel: EnvoyStreamIntel): Array { + return emptyArray() +} +override fun onResponseData(data: ByteBuffer, endStream: Boolean, streamIntel: EnvoyStreamIntel): Array { + return emptyArray() +} +override fun onResponseTrailers(trailers: MutableMap>, streamIntel: EnvoyStreamIntel): Array { + return emptyArray() +} +override fun setRequestFilterCallbacks(callbacks: EnvoyHTTPFilterCallbacks) { +} +override fun setResponseFilterCallbacks(callbacks: EnvoyHTTPFilterCallbacks) { +} +override fun onCancel( streamIntel: EnvoyStreamIntel, finalStreamIntel: EnvoyFinalStreamIntel) { +} +override fun onComplete( streamIntel: EnvoyStreamIntel, finalStreamIntel: EnvoyFinalStreamIntel) { +} +override fun onError(errorCode: Int, message: String, attemptCount: Int, streamIntel: EnvoyStreamIntel, finalStreamIntel: EnvoyFinalStreamIntel) { +} +override fun onResumeRequest(headers: MutableMap>, data: ByteBuffer, trailers: MutableMap>, endStream: Boolean, streamIntel: EnvoyStreamIntel): Array { + return emptyArray() +} +override fun onResumeResponse(headers: MutableMap>, data: ByteBuffer, trailers: MutableMap>, endStream: Boolean, streamIntel: EnvoyStreamIntel): Array { + return emptyArray() +} +} + +class TestEnvoyHTTPFilterFactory(name : String) : EnvoyHTTPFilterFactory { + private var filterName = name + override fun getFilterName(): String { + return filterName + } + + override fun create(): EnvoyHTTPFilter { + return TestFilter() + } +} class EnvoyConfigurationTest { @@ -66,7 +72,7 @@ class EnvoyConfigurationTest { dnsFailureRefreshSecondsMax: Int = 456, dnsQueryTimeoutSeconds: Int = 321, dnsMinRefreshSeconds: Int = 12, - dnsPreresolveHostnames: String = "[hostname]", + dnsPreresolveHostnames: String = "[{address: hostname, port_value: 443}]", enableDNSCache: Boolean = false, enableDrainPostDnsRefresh: Boolean = false, enableHttp3: Boolean = true, @@ -84,8 +90,11 @@ class EnvoyConfigurationTest { appVersion: String = "v1.2.3", appId: String = "com.example.myapp", trustChainVerification: TrustChainVerification = TrustChainVerification.VERIFY_TRUST_CHAIN, - virtualClusters: String = "[test]", + virtualClusters: String = "[{name: test}]", + filterChain: MutableList = mutableListOf(EnvoyNativeFilterConfig("buffer_filter_1", "{'@type': 'type.googleapis.com/envoy.extensions.filters.http.buffer.v3.Buffer'}"), EnvoyNativeFilterConfig("buffer_filter_2", "{'@type': 'type.googleapis.com/envoy.extensions.filters.http.buffer.v3.Buffer'}")), + platformFilterFactories: MutableList = mutableListOf(TestEnvoyHTTPFilterFactory("name1"), TestEnvoyHTTPFilterFactory("name2")), enableSkipDNSLookupForProxiedRequests: Boolean = false, + statSinks: List = emptyList(), enablePlatformCertificatesValidation: Boolean = false ): EnvoyConfiguration { return EnvoyConfiguration( @@ -116,24 +125,22 @@ class EnvoyConfigurationTest { appId, trustChainVerification, virtualClusters, - listOf(EnvoyNativeFilterConfig("filter_name", "test_config")), - emptyList(), + filterChain, + platformFilterFactories, emptyMap(), emptyMap(), - emptyList(), + statSinks, enableSkipDNSLookupForProxiedRequests, enablePlatformCertificatesValidation ) } @Test - fun `configuration resolves with values`() { + fun `configuration default values`() { + JniLibrary.loadTestLibrary() val envoyConfiguration = buildTestEnvoyConfiguration() - val resolvedTemplate = envoyConfiguration.resolveTemplate( - TEST_CONFIG, PLATFORM_FILTER_CONFIG, NATIVE_FILTER_CONFIG, APCF_INSERT, GZIP_INSERT, BROTLI_INSERT, SOCKET_TAG_INSERT, PERSISTENT_DNS_CACHE_INSERT, - CERT_VALIDATION_TEMPLATE - ) + val resolvedTemplate = envoyConfiguration.createYaml() assertThat(resolvedTemplate).contains("&connect_timeout 123s") assertThat(resolvedTemplate).doesNotContain("admin: *admin_interface") @@ -145,9 +152,8 @@ class EnvoyConfigurationTest { assertThat(resolvedTemplate).contains("&dns_query_timeout 321s") assertThat(resolvedTemplate).contains("&dns_lookup_family V4_PREFERRED") assertThat(resolvedTemplate).contains("&dns_min_refresh_rate 12s") - assertThat(resolvedTemplate).contains("&dns_preresolve_hostnames [hostname]") + assertThat(resolvedTemplate).contains("&dns_preresolve_hostnames [{address: hostname, port_value: 443}]") assertThat(resolvedTemplate).contains("&enable_drain_post_dns_refresh false") - assertThat(resolvedTemplate).doesNotContain(PERSISTENT_DNS_CACHE_INSERT); // Interface Binding assertThat(resolvedTemplate).contains("&enable_interface_binding false") @@ -160,13 +166,14 @@ class EnvoyConfigurationTest { assertThat(resolvedTemplate).contains("&h2_connection_keepalive_timeout 333s") // H3 - assertThat(resolvedTemplate).contains(APCF_INSERT); + assertThat(resolvedTemplate).contains("http3_protocol_options:"); + assertThat(resolvedTemplate).contains("name: alternate_protocols_cache"); // Gzip - assertThat(resolvedTemplate).contains(GZIP_INSERT); + assertThat(resolvedTemplate).contains("type.googleapis.com/envoy.extensions.compression.gzip.decompressor.v3.Gzip"); // Brotli - assertThat(resolvedTemplate).doesNotContain(BROTLI_INSERT); + assertThat(resolvedTemplate).doesNotContain("type.googleapis.com/envoy.extensions.compression.brotli.decompressor.v3.Brotli"); // Per Host Limits assertThat(resolvedTemplate).contains("&max_connections_per_host 543") @@ -176,11 +183,12 @@ class EnvoyConfigurationTest { assertThat(resolvedTemplate).contains("app_version: v1.2.3") assertThat(resolvedTemplate).contains("app_id: com.example.myapp") - assertThat(resolvedTemplate).contains("&virtual_clusters [test]") + assertThat(resolvedTemplate).contains("virtual_clusters [{name: test}]") // Stats assertThat(resolvedTemplate).contains("&stats_domain stats.example.com") assertThat(resolvedTemplate).contains("&stats_flush_interval 567s") + assertThat(resolvedTemplate).contains("stats.example.com"); // Idle timeouts assertThat(resolvedTemplate).contains("&stream_idle_timeout 678s") @@ -190,68 +198,71 @@ class EnvoyConfigurationTest { assertThat(resolvedTemplate).contains("&trust_chain_verification VERIFY_TRUST_CHAIN") // Filters - assertThat(resolvedTemplate).contains("filter_name") - assertThat(resolvedTemplate).contains("test_config") + assertThat(resolvedTemplate).contains("buffer_filter_1") + assertThat(resolvedTemplate).contains("type.googleapis.com/envoy.extensions.filters.http.buffer.v3.Buffer") // Cert Validation - assertThat(resolvedTemplate).contains("custom_validator_config") + assertThat(resolvedTemplate).contains("trusted_ca:") // Proxying assertThat(resolvedTemplate).contains("&skip_dns_lookup_for_proxied_requests false") + + // Validate ordering between filters and platform filters + assertThat(resolvedTemplate).matches(Pattern.compile(".*name1.*name2.*buffer_filter_1.*buffer_filter_2.*", Pattern.DOTALL)); } @Test fun `configuration resolves with alternate values`() { + JniLibrary.loadTestLibrary() val envoyConfiguration = buildTestEnvoyConfiguration( + adminInterfaceEnabled = true, + grpcStatsDomain = "", enableDrainPostDnsRefresh = true, enableDNSCache = true, enableHappyEyeballs = true, enableHttp3 = false, enableGzip = false, enableBrotli = true, + enableSocketTagging = true, enableInterfaceBinding = true, enableSkipDNSLookupForProxiedRequests = true, - enablePlatformCertificatesValidation = true + enablePlatformCertificatesValidation = true, + dnsPreresolveHostnames = "", + virtualClusters = "", + filterChain = mutableListOf(), + trustChainVerification = TrustChainVerification.ACCEPT_UNTRUSTED ) - val resolvedTemplate = envoyConfiguration.resolveTemplate( - TEST_CONFIG, PLATFORM_FILTER_CONFIG, NATIVE_FILTER_CONFIG, APCF_INSERT, GZIP_INSERT, BROTLI_INSERT, SOCKET_TAG_INSERT, PERSISTENT_DNS_CACHE_INSERT, -CERT_VALIDATION_TEMPLATE - ) + val resolvedTemplate = envoyConfiguration.createYaml() - // DNS - assertThat(resolvedTemplate).contains("&dns_lookup_family ALL") + // adminInterfaceEnabled = true + assertThat(resolvedTemplate).contains("admin: *admin_interface") + + // enableDrainPostDnsRefresh = true assertThat(resolvedTemplate).contains("&enable_drain_post_dns_refresh true") - assertThat(resolvedTemplate).contains("config: persistent_dns_cache") - // H3 - assertThat(resolvedTemplate).doesNotContain(APCF_INSERT); + // enableDNSCache = true + assertThat(resolvedTemplate).contains("key: dns_persistent_cache") - // Gzip - assertThat(resolvedTemplate).doesNotContain(GZIP_INSERT); + // enableHappyEyeballs = true + assertThat(resolvedTemplate).contains("&dns_lookup_family ALL") - // Brotli - assertThat(resolvedTemplate).contains(BROTLI_INSERT); + // enableHttp3 = false + assertThat(resolvedTemplate).doesNotContain("name: alternate_protocols_cache"); - // Interface Binding - assertThat(resolvedTemplate).contains("&enable_interface_binding true") + // enableGzip = false + assertThat(resolvedTemplate).doesNotContain("type.googleapis.com/envoy.extensions.compression.gzip.decompressor.v3.Gzip"); - // Cert Validation - assertThat(resolvedTemplate).contains("custom_validator_config") + // enableBrotli = true + assertThat(resolvedTemplate).contains("type.googleapis.com/envoy.extensions.compression.brotli.decompressor.v3.Brotli"); - // Proxying - assertThat(resolvedTemplate).contains("&skip_dns_lookup_for_proxied_requests true") - } + // enableInterfaceBinding = true + assertThat(resolvedTemplate).contains("&enable_interface_binding true") - @Test - fun `resolve templates with invalid templates will throw on build`() { - val envoyConfiguration = buildTestEnvoyConfiguration() + // enableSkipDNSLookupForProxiedRequests = true + assertThat(resolvedTemplate).contains("&skip_dns_lookup_for_proxied_requests true") - try { - envoyConfiguration.resolveTemplate("{{ missing }}", "", "", "", "", "", "", "", "") - fail("Unresolved configuration keys should trigger exception.") - } catch (e: EnvoyConfiguration.ConfigurationException) { - assertThat(e.message).contains("missing") - } + // enablePlatformCertificatesValidation = true + assertThat(resolvedTemplate).doesNotContain("trusted_ca:") } } diff --git a/mobile/test/kotlin/integration/CancelGRPCStreamTest.kt b/mobile/test/kotlin/integration/CancelGRPCStreamTest.kt index 1ce3deacf4da..d442a3ac722a 100644 --- a/mobile/test/kotlin/integration/CancelGRPCStreamTest.kt +++ b/mobile/test/kotlin/integration/CancelGRPCStreamTest.kt @@ -1,6 +1,6 @@ package test.kotlin.integration -import io.envoyproxy.envoymobile.Custom +import io.envoyproxy.envoymobile.Standard import io.envoyproxy.envoymobile.EngineBuilder import io.envoyproxy.envoymobile.EnvoyError import io.envoyproxy.envoymobile.FilterDataStatus @@ -23,50 +23,8 @@ import org.assertj.core.api.Assertions.assertThat import org.junit.Test private val filterName = "cancel_validation_filter" -private val config = -""" -static_resources: - listeners: - - name: base_api_listener - address: - socket_address: { protocol: TCP, address: 0.0.0.0, port_value: 10000 } - api_listener: - api_listener: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.EnvoyMobileHttpConnectionManager - config: - stat_prefix: api_hcm - route_config: - name: api_router - virtual_hosts: - - name: api - domains: ["*"] - routes: - - match: { prefix: "/" } - route: { cluster: fake_remote } - http_filters: - - name: envoy.filters.http.local_error - typed_config: - "@type": type.googleapis.com/envoymobile.extensions.filters.http.local_error.LocalError - - name: envoy.filters.http.platform_bridge - typed_config: - "@type": type.googleapis.com/envoymobile.extensions.filters.http.platform_bridge.PlatformBridge - platform_filter_name: $filterName - - name: envoy.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - clusters: - - name: fake_remote - connect_timeout: 0.25s - type: STATIC - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: fake_remote - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: { address: 127.0.0.1, port_value: ${(10001..11000).random()} } -""" +private const val pbfType = "type.googleapis.com/envoymobile.extensions.filters.http.platform_bridge.PlatformBridge" +private const val localErrorFilterType = "type.googleapis.com/envoymobile.extensions.filters.http.local_error.LocalError" class CancelGRPCStreamTest { @@ -113,11 +71,13 @@ class CancelGRPCStreamTest { @Test fun `cancel grpc stream calls onCancel callback`() { - val engine = EngineBuilder(Custom(config)) + val engine = EngineBuilder(Standard()) .addPlatformFilter( name = filterName, factory = { CancelValidationFilter(filterExpectation) } ) + .addNativeFilter("envoy.filters.http.platform_bridge", "{'@type': $pbfType, platform_filter_name: $filterName}") + .addNativeFilter("envoy.filters.http.local_error", "{'@type': $localErrorFilterType}") .setOnEngineRunning {} .build() diff --git a/mobile/test/kotlin/integration/CancelStreamTest.kt b/mobile/test/kotlin/integration/CancelStreamTest.kt index 9964f4770e6c..2ff6843bfcbb 100644 --- a/mobile/test/kotlin/integration/CancelStreamTest.kt +++ b/mobile/test/kotlin/integration/CancelStreamTest.kt @@ -1,6 +1,6 @@ package test.kotlin.integration -import io.envoyproxy.envoymobile.Custom +import io.envoyproxy.envoymobile.Standard import io.envoyproxy.envoymobile.EngineBuilder import io.envoyproxy.envoymobile.EnvoyError import io.envoyproxy.envoymobile.FilterDataStatus @@ -22,57 +22,7 @@ import java.util.concurrent.TimeUnit import org.assertj.core.api.Assertions.assertThat import org.junit.Test -private const val emhcmType = - "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.EnvoyMobileHttpConnectionManager" -private const val lefType = - "type.googleapis.com/envoymobile.extensions.filters.http.local_error.LocalError" -private const val pbfType = "type.googleapis.com/envoymobile.extensions.filters.http.platform_bridge.PlatformBridge" -private const val filterName = "cancel_validation_filter" -private val remotePort = (10001..11000).random() -private val config = -""" -static_resources: - listeners: - - name: base_api_listener - address: - socket_address: { protocol: TCP, address: 0.0.0.0, port_value: 10000 } - api_listener: - api_listener: - "@type": $emhcmType - config: - stat_prefix: api_hcm - route_config: - name: api_router - virtual_hosts: - - name: api - domains: ["*"] - routes: - - match: { prefix: "/" } - route: { cluster: fake_remote } - http_filters: - - name: envoy.filters.http.local_error - typed_config: - "@type": $lefType - - name: envoy.filters.http.platform_bridge - typed_config: - "@type": $pbfType - platform_filter_name: $filterName - - name: envoy.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - clusters: - - name: fake_remote - connect_timeout: 0.25s - type: STATIC - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: fake_remote - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: { address: 127.0.0.1, port_value: $remotePort } -""" +private const val testResponseFilterType = "type.googleapis.com/envoymobile.extensions.filters.http.test_remote_response.TestRemoteResponse" class CancelStreamTest { @@ -119,11 +69,12 @@ class CancelStreamTest { @Test fun `cancel stream calls onCancel callback`() { - val engine = EngineBuilder(Custom(config)) + val engine = EngineBuilder(Standard()) .addPlatformFilter( name = "cancel_validation_filter", factory = { CancelValidationFilter(filterExpectation) } ) + .addNativeFilter("test_remote_response", "{'@type': $testResponseFilterType}") .setOnEngineRunning {} .build() diff --git a/mobile/test/kotlin/integration/GRPCReceiveErrorTest.kt b/mobile/test/kotlin/integration/GRPCReceiveErrorTest.kt index d0d017989020..c6c21abd7382 100644 --- a/mobile/test/kotlin/integration/GRPCReceiveErrorTest.kt +++ b/mobile/test/kotlin/integration/GRPCReceiveErrorTest.kt @@ -1,6 +1,6 @@ package test.kotlin.integration -import io.envoyproxy.envoymobile.Custom +import io.envoyproxy.envoymobile.Standard import io.envoyproxy.envoymobile.EngineBuilder import io.envoyproxy.envoymobile.EnvoyError import io.envoyproxy.envoymobile.FilterDataStatus @@ -27,38 +27,6 @@ private const val pbfType = "type.googleapis.com/envoymobile.extensions.filters. private const val localErrorFilterType = "type.googleapis.com/envoymobile.extensions.filters.http.local_error.LocalError" private const val filterName = "error_validation_filter" -private const val config = -""" -static_resources: - listeners: - - name: base_api_listener - address: - socket_address: { protocol: TCP, address: 0.0.0.0, port_value: 10000 } - api_listener: - api_listener: - "@type": $hcmType - config: - stat_prefix: hcm - route_config: - name: api_router - virtual_hosts: - - name: api - domains: ["*"] - routes: - - match: { prefix: "/" } - direct_response: { status: 503 } - http_filters: - - name: envoy.filters.http.platform_bridge - typed_config: - "@type": $pbfType - platform_filter_name: $filterName - - name: envoy.filters.http.local_error - typed_config: - "@type": $localErrorFilterType - - name: envoy.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router -""" class GRPCReceiveErrorTest { init { @@ -114,11 +82,12 @@ class GRPCReceiveErrorTest { path = "/pb.api.v1.Foo/GetBar" ).build() - val engine = EngineBuilder(Custom(config)) + val engine = EngineBuilder(Standard()) .addPlatformFilter( name = filterName, factory = { ErrorValidationFilter(filterReceivedError, filterNotCancelled) } ) + .addNativeFilter("envoy.filters.http.platform_bridge", "{'@type': $pbfType, platform_filter_name: $filterName}") .setOnEngineRunning {} .build() diff --git a/mobile/test/kotlin/integration/KeyValueStoreTest.kt b/mobile/test/kotlin/integration/KeyValueStoreTest.kt index e6d6ba12d605..c52369628076 100644 --- a/mobile/test/kotlin/integration/KeyValueStoreTest.kt +++ b/mobile/test/kotlin/integration/KeyValueStoreTest.kt @@ -1,6 +1,6 @@ package test.kotlin.integration -import io.envoyproxy.envoymobile.Custom +import io.envoyproxy.envoymobile.Standard import io.envoyproxy.envoymobile.EngineBuilder import io.envoyproxy.envoymobile.KeyValueStore import io.envoyproxy.envoymobile.RequestHeadersBuilder @@ -14,48 +14,10 @@ import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.fail import org.junit.Test -private const val apiListenerType = - "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.EnvoyMobileHttpConnectionManager" private const val assertionFilterType = "type.googleapis.com/envoymobile.extensions.filters.http.assertion.Assertion" +private const val testResponseFilterType = "type.googleapis.com/envoymobile.extensions.filters.http.test_remote_response.TestRemoteResponse" private const val testKey = "foo" private const val testValue = "bar" -private const val config = -""" -static_resources: - listeners: - - name: base_api_listener - address: - socket_address: - protocol: TCP - address: 0.0.0.0 - port_value: 10000 - api_listener: - api_listener: - "@type": $apiListenerType - config: - stat_prefix: hcm - route_config: - name: api_router - virtual_hosts: - - name: api - domains: - - "*" - routes: - - match: - prefix: "/" - direct_response: - status: 200 - http_filters: - - name: envoy.filters.http.test_kv_store - typed_config: - "@type": type.googleapis.com/envoymobile.extensions.filters.http.test_kv_store.TestKeyValueStore - kv_store_name: envoy.key_value.platform_test - test_key: $testKey - test_value: $testValue - - name: envoy.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router -""" class KeyValueStoreTest { @@ -74,8 +36,10 @@ class KeyValueStoreTest { override fun save(key: String, value: String) { saveExpectation.countDown() } } - val engine = EngineBuilder(Custom(config)) + val engine = EngineBuilder(Standard()) .addKeyValueStore("envoy.key_value.platform_test", testKeyValueStore) + .addNativeFilter("envoy.filters.http.test_kv_store", "{'@type': type.googleapis.com/envoymobile.extensions.filters.http.test_kv_store.TestKeyValueStore, kv_store_name: envoy.key_value.platform_test, test_key: $testKey, test_value: $testValue}") + .addNativeFilter("test_remote_response", "{'@type': $testResponseFilterType}") .build() val client = engine.streamClient() diff --git a/mobile/test/kotlin/integration/ReceiveDataTest.kt b/mobile/test/kotlin/integration/ReceiveDataTest.kt index 4059e14587c3..1914430839c9 100644 --- a/mobile/test/kotlin/integration/ReceiveDataTest.kt +++ b/mobile/test/kotlin/integration/ReceiveDataTest.kt @@ -1,6 +1,6 @@ package test.kotlin.integration -import io.envoyproxy.envoymobile.Custom +import io.envoyproxy.envoymobile.Standard import io.envoyproxy.envoymobile.EngineBuilder import io.envoyproxy.envoymobile.RequestHeadersBuilder import io.envoyproxy.envoymobile.RequestMethod @@ -13,51 +13,7 @@ import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.fail import org.junit.Test -private const val apiListenerType = - "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.EnvoyMobileHttpConnectionManager" -private const val assertionFilterType = "type.googleapis.com/envoymobile.extensions.filters.http.assertion.Assertion" -private const val assertionResponseBody = "response_body" -private const val config = -""" -static_resources: - listeners: - - name: base_api_listener - address: - socket_address: - protocol: TCP - address: 0.0.0.0 - port_value: 10000 - api_listener: - api_listener: - "@type": $apiListenerType - config: - stat_prefix: hcm - route_config: - name: api_router - virtual_hosts: - - name: api - domains: - - "*" - routes: - - match: - prefix: "/" - direct_response: - status: 200 - body: - inline_string: $assertionResponseBody - http_filters: - - name: envoy.filters.http.assertion - typed_config: - "@type": $assertionFilterType - match_config: - http_request_headers_match: - headers: - - name: ":authority" - exact_match: example.com - - name: envoy.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router -""" +private const val testResponseFilterType = "type.googleapis.com/envoymobile.extensions.filters.http.test_remote_response.TestRemoteResponse" class ReceiveDataTest { @@ -68,7 +24,9 @@ class ReceiveDataTest { @Test fun `response headers and response data call onResponseHeaders and onResponseData`() { - val engine = EngineBuilder(Custom(config)).build() + val engine = EngineBuilder(Standard()) + .addNativeFilter("test_remote_response", "{'@type': $testResponseFilterType}") + .build() val client = engine.streamClient() val requestHeaders = RequestHeadersBuilder( @@ -106,6 +64,6 @@ class ReceiveDataTest { assertThat(dataExpectation.count).isEqualTo(0) assertThat(status).isEqualTo(200) - assertThat(body!!.array().toString(Charsets.UTF_8)).isEqualTo(assertionResponseBody) + assertThat(body!!.array().toString(Charsets.UTF_8)).isEqualTo("data") } } diff --git a/mobile/test/kotlin/integration/ReceiveErrorTest.kt b/mobile/test/kotlin/integration/ReceiveErrorTest.kt index b233e15e1d33..0f279c3b87d5 100644 --- a/mobile/test/kotlin/integration/ReceiveErrorTest.kt +++ b/mobile/test/kotlin/integration/ReceiveErrorTest.kt @@ -1,6 +1,6 @@ package test.kotlin.integration -import io.envoyproxy.envoymobile.Custom +import io.envoyproxy.envoymobile.Standard import io.envoyproxy.envoymobile.EngineBuilder import io.envoyproxy.envoymobile.EnvoyError import io.envoyproxy.envoymobile.FilterDataStatus @@ -20,42 +20,9 @@ import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.fail import org.junit.Test -private const val hcmType = "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.EnvoyMobileHttpConnectionManager" private const val pbfType = "type.googleapis.com/envoymobile.extensions.filters.http.platform_bridge.PlatformBridge" private const val localErrorFilterType = "type.googleapis.com/envoymobile.extensions.filters.http.local_error.LocalError" private const val filterName = "error_validation_filter" -private const val config = -""" -static_resources: - listeners: - - name: base_api_listener - address: - socket_address: { protocol: TCP, address: 0.0.0.0, port_value: 10000 } - api_listener: - api_listener: - "@type": $hcmType - config: - stat_prefix: hcm - route_config: - name: api_router - virtual_hosts: - - name: api - domains: ["*"] - routes: - - match: { prefix: "/" } - direct_response: { status: 503 } - http_filters: - - name: envoy.filters.http.platform_bridge - typed_config: - "@type": $pbfType - platform_filter_name: $filterName - - name: envoy.filters.http.local_error - typed_config: - "@type": $localErrorFilterType - - name: envoy.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router -""" class ReceiveErrorTest { init { @@ -107,16 +74,18 @@ class ReceiveErrorTest { fun `errors on stream call onError callback`() { val requestHeader = GRPCRequestHeadersBuilder( scheme = "https", - authority = "example.com", + authority = "doesnotexist.example.com", path = "/test" ).build() - val engine = EngineBuilder(Custom(config)) + val engine = EngineBuilder(Standard()) .addPlatformFilter( name = filterName, factory = { ErrorValidationFilter(filterReceivedError, filterNotCancelled) } ) .setOnEngineRunning {} + .addNativeFilter("envoy.filters.http.platform_bridge", "{'@type': $pbfType, platform_filter_name: $filterName}") + .addNativeFilter("envoy.filters.http.local_error", "{'@type': $localErrorFilterType}") .build() var errorCode: Int? = null diff --git a/mobile/test/kotlin/integration/ResetConnectivityStateTest.kt b/mobile/test/kotlin/integration/ResetConnectivityStateTest.kt index b2dab9c894fc..3daa367c2a47 100644 --- a/mobile/test/kotlin/integration/ResetConnectivityStateTest.kt +++ b/mobile/test/kotlin/integration/ResetConnectivityStateTest.kt @@ -1,6 +1,6 @@ package test.kotlin.integration -import io.envoyproxy.envoymobile.Custom +import io.envoyproxy.envoymobile.Standard import io.envoyproxy.envoymobile.EngineBuilder import io.envoyproxy.envoymobile.RequestHeadersBuilder import io.envoyproxy.envoymobile.RequestMethod @@ -13,48 +13,11 @@ import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.fail import org.junit.Test +private const val testResponseFilterType = "type.googleapis.com/envoymobile.extensions.filters.http.test_remote_response.TestRemoteResponse" private val apiListenerType = "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.EnvoyMobileHttpConnectionManager" private val assertionFilterType = "type.googleapis.com/envoymobile.extensions.filters.http.assertion.Assertion" -private val config = -""" -static_resources: - listeners: - - name: base_api_listener - address: - socket_address: - protocol: TCP - address: 0.0.0.0 - port_value: 10000 - api_listener: - api_listener: - "@type": $apiListenerType - config: - stat_prefix: hcm - route_config: - name: api_router - virtual_hosts: - - name: api - domains: - - "*" - routes: - - match: - prefix: "/" - direct_response: - status: 200 - http_filters: - - name: envoy.filters.http.assertion - typed_config: - "@type": $assertionFilterType - match_config: - http_request_headers_match: - headers: - - name: ":authority" - exact_match: example.com - - name: envoy.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router -""" +// This test doesn't do what it advertises (https://github.com/envoyproxy/envoy/issues/25180) class ResetConnectivityStateTest { init { @@ -65,7 +28,9 @@ class ResetConnectivityStateTest { fun `successful request after connection drain`() { val headersExpectation = CountDownLatch(2) - val engine = EngineBuilder(Custom(config)).build() + val engine = EngineBuilder(Standard()) + .addNativeFilter("test_remote_response", "{'@type': $testResponseFilterType}") + .build() val client = engine.streamClient() val requestHeaders = RequestHeadersBuilder( @@ -85,6 +50,9 @@ class ResetConnectivityStateTest { resultEndStream1 = endStream headersExpectation.countDown() } + .setOnResponseData { _, endStream, _ -> + resultEndStream1 = endStream + } .setOnError { _, _ -> fail("Unexpected error") } .start() .sendHeaders(requestHeaders, true) @@ -101,6 +69,9 @@ class ResetConnectivityStateTest { resultEndStream2 = endStream headersExpectation.countDown() } + .setOnResponseData { _, endStream, _ -> + resultEndStream2 = endStream + } .setOnError { _, _ -> fail("Unexpected error") } .start() .sendHeaders(requestHeaders, true) diff --git a/mobile/test/kotlin/integration/SendDataTest.kt b/mobile/test/kotlin/integration/SendDataTest.kt index 925e9f3bea0b..8d6f93acbebd 100644 --- a/mobile/test/kotlin/integration/SendDataTest.kt +++ b/mobile/test/kotlin/integration/SendDataTest.kt @@ -1,6 +1,6 @@ package test.kotlin.integration -import io.envoyproxy.envoymobile.Custom +import io.envoyproxy.envoymobile.Standard import io.envoyproxy.envoymobile.EngineBuilder import io.envoyproxy.envoymobile.RequestHeadersBuilder import io.envoyproxy.envoymobile.RequestMethod @@ -13,52 +13,9 @@ import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.fail import org.junit.Test -private const val apiListenerType = - "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.EnvoyMobileHttpConnectionManager" +private const val testResponseFilterType = "type.googleapis.com/envoymobile.extensions.filters.http.test_remote_response.TestRemoteResponse" private const val assertionFilterType = "type.googleapis.com/envoymobile.extensions.filters.http.assertion.Assertion" private const val requestStringMatch = "match_me" -private const val config = -""" -static_resources: - listeners: - - name: base_api_listener - address: - socket_address: - protocol: TCP - address: 0.0.0.0 - port_value: 10000 - api_listener: - api_listener: - "@type": $apiListenerType - config: - stat_prefix: hcm - route_config: - name: api_router - virtual_hosts: - - name: api - domains: - - "*" - routes: - - match: - prefix: "/" - direct_response: - status: 200 - http_filters: - - name: envoy.filters.http.assertion - typed_config: - "@type": $assertionFilterType - match_config: - http_request_generic_body_match: - patterns: - - string_match: $requestStringMatch - - name: envoy.filters.http.buffer - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.buffer.v3.Buffer - max_request_bytes: 65000 - - name: envoy.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router -""" class SendDataTest { init { @@ -68,7 +25,9 @@ class SendDataTest { @Test fun `successful sending data`() { val expectation = CountDownLatch(1) - val engine = EngineBuilder(Custom(config)) + val engine = EngineBuilder(Standard()) + .addNativeFilter("envoy.filters.http.assertion", "{'@type': $assertionFilterType, match_config: {http_request_generic_body_match: {patterns: [{string_match: $requestStringMatch}]}}}") + .addNativeFilter("test_remote_response", "{'@type': $testResponseFilterType}") .setOnEngineRunning { } .build() @@ -86,13 +45,16 @@ class SendDataTest { val body = ByteBuffer.wrap(requestStringMatch.toByteArray(Charsets.UTF_8)) var responseStatus: Int? = null - var responseHeadersEndStream = false + var responseEndStream = false client.newStreamPrototype() .setOnResponseHeaders { headers, endStream, _ -> responseStatus = headers.httpStatus - responseHeadersEndStream = endStream + responseEndStream = endStream expectation.countDown() } + .setOnResponseData { _, endStream, _ -> + responseEndStream = endStream + } .setOnError { _, _ -> fail("Unexpected error") } @@ -106,6 +68,6 @@ class SendDataTest { assertThat(expectation.count).isEqualTo(0) assertThat(responseStatus).isEqualTo(200) - assertThat(responseHeadersEndStream).isTrue() + assertThat(responseEndStream).isTrue() } } diff --git a/mobile/test/kotlin/integration/SendHeadersTest.kt b/mobile/test/kotlin/integration/SendHeadersTest.kt index 20cdb99f015c..331a7d72164f 100644 --- a/mobile/test/kotlin/integration/SendHeadersTest.kt +++ b/mobile/test/kotlin/integration/SendHeadersTest.kt @@ -1,6 +1,6 @@ package test.kotlin.integration -import io.envoyproxy.envoymobile.Custom +import io.envoyproxy.envoymobile.Standard import io.envoyproxy.envoymobile.EngineBuilder import io.envoyproxy.envoymobile.RequestHeadersBuilder import io.envoyproxy.envoymobile.RequestMethod @@ -13,47 +13,7 @@ import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.fail import org.junit.Test -private val apiListenerType = "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.EnvoyMobileHttpConnectionManager" -private val assertionFilterType = "type.googleapis.com/envoymobile.extensions.filters.http.assertion.Assertion" -private val config = -""" -static_resources: - listeners: - - name: base_api_listener - address: - socket_address: - protocol: TCP - address: 0.0.0.0 - port_value: 10000 - api_listener: - api_listener: - "@type": $apiListenerType - config: - stat_prefix: hcm - route_config: - name: api_router - virtual_hosts: - - name: api - domains: - - "*" - routes: - - match: - prefix: "/" - direct_response: - status: 200 - http_filters: - - name: envoy.filters.http.assertion - typed_config: - "@type": $assertionFilterType - match_config: - http_request_headers_match: - headers: - - name: ":authority" - exact_match: example.com - - name: envoy.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router -""" +private const val testResponseFilterType = "type.googleapis.com/envoymobile.extensions.filters.http.test_remote_response.TestRemoteResponse" class SendHeadersTest { @@ -65,7 +25,9 @@ class SendHeadersTest { fun `successful sending of request headers`() { val headersExpectation = CountDownLatch(1) - val engine = EngineBuilder(Custom(config)).build() + val engine = EngineBuilder(Standard()) + .addNativeFilter("test_remote_response", "{'@type': $testResponseFilterType}") + .build() val client = engine.streamClient() val requestHeaders = RequestHeadersBuilder( @@ -85,6 +47,9 @@ class SendHeadersTest { resultEndStream = endStream headersExpectation.countDown() } + .setOnResponseData { _, endStream, _ -> + resultEndStream = endStream + } .setOnError { _, _ -> fail("Unexpected error") } .start() .sendHeaders(requestHeaders, true) diff --git a/mobile/test/kotlin/integration/SendTrailersTest.kt b/mobile/test/kotlin/integration/SendTrailersTest.kt index 1392583e1921..4850767f2cd4 100644 --- a/mobile/test/kotlin/integration/SendTrailersTest.kt +++ b/mobile/test/kotlin/integration/SendTrailersTest.kt @@ -1,6 +1,6 @@ package test.kotlin.integration -import io.envoyproxy.envoymobile.Custom +import io.envoyproxy.envoymobile.Standard import io.envoyproxy.envoymobile.EngineBuilder import io.envoyproxy.envoymobile.RequestHeadersBuilder import io.envoyproxy.envoymobile.RequestMethod @@ -14,53 +14,10 @@ import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.fail import org.junit.Test -private const val apiListenerType = "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.EnvoyMobileHttpConnectionManager" +private const val testResponseFilterType = "type.googleapis.com/envoymobile.extensions.filters.http.test_remote_response.TestRemoteResponse" private const val assertionFilterType = "type.googleapis.com/envoymobile.extensions.filters.http.assertion.Assertion" private const val matcherTrailerName = "test-trailer" private const val matcherTrailerValue = "test.code" -private const val config = -""" -static_resources: - listeners: - - name: base_api_listener - address: - socket_address: - protocol: TCP - address: 0.0.0.0 - port_value: 10000 - api_listener: - api_listener: - "@type": $apiListenerType - config: - stat_prefix: hcm - route_config: - name: api_router - virtual_hosts: - - name: api - domains: - - "*" - routes: - - match: - prefix: "/" - direct_response: - status: 200 - http_filters: - - name: envoy.filters.http.assertion - typed_config: - "@type": $assertionFilterType - match_config: - http_request_trailers_match: - headers: - - name: $matcherTrailerName - exact_match: $matcherTrailerValue - - name: envoy.filters.http.buffer - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.buffer.v3.Buffer - max_request_bytes: 65000 - - name: envoy.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router -""" class SendTrailersTest { @@ -72,7 +29,10 @@ class SendTrailersTest { fun `successful sending of trailers`() { val expectation = CountDownLatch(1) - val engine = EngineBuilder(Custom(config)) + val engine = EngineBuilder(Standard()) + .addNativeFilter("envoy.filters.http.assertion", "{'@type': $assertionFilterType, match_config: {http_request_trailers_match: {headers: [{name: 'test-trailer', exact_match: 'test.code'}]}}}") + .addNativeFilter("envoy.filters.http.buffer", "{\"@type\":\"type.googleapis.com/envoy.extensions.filters.http.buffer.v3.Buffer\",\"max_request_bytes\":65000}") + .addNativeFilter("test_remote_response", "{'@type': $testResponseFilterType}") .setOnEngineRunning { } .build() diff --git a/mobile/test/kotlin/integration/SetLoggerTest.kt b/mobile/test/kotlin/integration/SetLoggerTest.kt index 4afcd4cfeb9d..fe4b1cbc28d5 100644 --- a/mobile/test/kotlin/integration/SetLoggerTest.kt +++ b/mobile/test/kotlin/integration/SetLoggerTest.kt @@ -1,6 +1,6 @@ package test.kotlin.integration -import io.envoyproxy.envoymobile.Custom +import io.envoyproxy.envoymobile.Standard import io.envoyproxy.envoymobile.Engine import io.envoyproxy.envoymobile.EngineBuilder import io.envoyproxy.envoymobile.LogLevel @@ -12,53 +12,6 @@ import java.util.concurrent.TimeUnit import org.assertj.core.api.Assertions.assertThat import org.junit.Test -private const val apiListenerType = - "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.EnvoyMobileHttpConnectionManager" -private const val assertionFilterType = "type.googleapis.com/envoymobile.extensions.filters.http.assertion.Assertion" -private const val testLoggerFilterType = "type.googleapis.com/envoymobile.extensions.filters.http.test_logger.TestLogger" -private const val config = -""" -static_resources: - listeners: - - name: base_api_listener - address: - socket_address: - protocol: TCP - address: 0.0.0.0 - port_value: 10000 - api_listener: - api_listener: - "@type": $apiListenerType - config: - stat_prefix: hcm - route_config: - name: api_router - virtual_hosts: - - name: api - domains: - - "*" - routes: - - match: - prefix: "/" - direct_response: - status: 200 - http_filters: - - name: test_logger - typed_config: - "@type": $testLoggerFilterType - - name: envoy.filters.http.assertion - typed_config: - "@type": $assertionFilterType - match_config: - http_request_headers_match: - headers: - - name: ":authority" - exact_match: example.com - - name: envoy.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router -""" - class SetLoggerTest { init { @@ -69,8 +22,9 @@ class SetLoggerTest { fun `set logger`() { val countDownLatch = CountDownLatch(1) val logEventLatch = CountDownLatch(1) - val engine = EngineBuilder(Custom(config)) + val engine = EngineBuilder(Standard()) .addLogLevel(LogLevel.DEBUG) + .addNativeFilter("test_logger", "{\"@type\":\"type.googleapis.com/envoymobile.extensions.filters.http.test_logger.TestLogger\"}") .setLogger { msg -> if (msg.contains("starting main dispatch loop")) { countDownLatch.countDown() @@ -99,13 +53,14 @@ class SetLoggerTest { fun `engine should continue to run if no logger is set`() { val countDownLatch = CountDownLatch(1) val logEventLatch = CountDownLatch(1) - val engine = EngineBuilder(Custom(config)) + val engine = EngineBuilder(Standard()) .setEventTracker { event -> if (event["log_name"] == "event_name") { logEventLatch.countDown() } } .addLogLevel(LogLevel.DEBUG) + .addNativeFilter("test_logger", "{\"@type\":\"type.googleapis.com/envoymobile.extensions.filters.http.test_logger.TestLogger\"}") .setOnEngineRunning { countDownLatch.countDown() } diff --git a/mobile/test/kotlin/integration/StreamIdleTimeoutTest.kt b/mobile/test/kotlin/integration/StreamIdleTimeoutTest.kt index 4b787242dc37..74e08b2bdfe7 100644 --- a/mobile/test/kotlin/integration/StreamIdleTimeoutTest.kt +++ b/mobile/test/kotlin/integration/StreamIdleTimeoutTest.kt @@ -1,6 +1,6 @@ package test.kotlin.integration -import io.envoyproxy.envoymobile.Custom +import io.envoyproxy.envoymobile.Standard import io.envoyproxy.envoymobile.EngineBuilder import io.envoyproxy.envoymobile.EnvoyError import io.envoyproxy.envoymobile.FilterDataStatus @@ -23,80 +23,6 @@ import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.fail import org.junit.Test -private const val idleTimeout = "0.5s" -private const val ehcmType = - "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.EnvoyMobileHttpConnectionManager" -private const val lefType = - "type.googleapis.com/envoymobile.extensions.filters.http.local_error.LocalError" -private const val pbfType = "type.googleapis.com/envoymobile.extensions.filters.http.platform_bridge.PlatformBridge" -private const val filterName = "idle_timeout_validation_filter" -private val remotePort = (10001..11000).random() -private val config = -""" -static_resources: - listeners: - - name: fake_remote_listener - address: - socket_address: { protocol: TCP, address: 127.0.0.1, port_value: $remotePort } - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": $ehcmType - stat_prefix: remote_hcm - route_config: - name: remote_route - virtual_hosts: - - name: remote_service - domains: ["*"] - routes: - - match: { prefix: "/" } - direct_response: { status: 200 } - http_filters: - - name: envoy.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - name: base_api_listener - address: - socket_address: { protocol: TCP, address: 0.0.0.0, port_value: 10000 } - api_listener: - api_listener: - "@type": $hcmType - stat_prefix: api_hcm - stream_idle_timeout: $idleTimeout - route_config: - name: api_router - virtual_hosts: - - name: api - domains: ["*"] - routes: - - match: { prefix: "/" } - route: { cluster: fake_remote } - http_filters: - - name: envoy.filters.http.local_error - typed_config: - "@type": $lefType - - name: envoy.filters.http.platform_bridge - typed_config: - "@type": $pbfType - platform_filter_name: $filterName - - name: envoy.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - clusters: - - name: fake_remote - connect_timeout: 0.25s - type: STATIC - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: fake_remote - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: { address: 127.0.0.1, port_value: $remotePort } -""" - class CancelStreamTest { init { @@ -145,11 +71,12 @@ class CancelStreamTest { @Test fun `stream idle timeout triggers onError callbacks`() { - val engine = EngineBuilder(Custom(config)) + val engine = EngineBuilder(Standard()).build() .addPlatformFilter( name = "idle_timeout_validation_filter", factory = { IdleTimeoutValidationFilter(filterExpectation) } ) + .addStreamIdleTimeoutSeconds(1) .setOnEngineRunning {} .build() diff --git a/mobile/test/kotlin/io/envoyproxy/envoymobile/BUILD b/mobile/test/kotlin/io/envoyproxy/envoymobile/BUILD index f536fefbe56a..4f052d6aceef 100644 --- a/mobile/test/kotlin/io/envoyproxy/envoymobile/BUILD +++ b/mobile/test/kotlin/io/envoyproxy/envoymobile/BUILD @@ -1,8 +1,8 @@ -load("@envoy_mobile//bazel:kotlin_test.bzl", "envoy_mobile_kt_test") +load("@envoy_mobile//bazel:kotlin_test.bzl", "envoy_mobile_jni_kt_test", "envoy_mobile_kt_test") licenses(["notice"]) # Apache 2 -envoy_mobile_kt_test( +envoy_mobile_jni_kt_test( name = "engine_builder_test", srcs = [ "EngineBuilderTest.kt", @@ -11,6 +11,10 @@ envoy_mobile_kt_test( # TODO(lfpino): Remove this once the JVM paths are allow-listed in the sandbox. "sandboxAllowed": "False", }, + native_deps = [ + "//test/common/jni:libenvoy_jni_with_test_extensions.so", + "//test/common/jni:libenvoy_jni_with_test_extensions_jnilib", + ], deps = [ "//library/kotlin/io/envoyproxy/envoymobile:envoy_interfaces_lib", ], diff --git a/mobile/test/kotlin/io/envoyproxy/envoymobile/EngineBuilderTest.kt b/mobile/test/kotlin/io/envoyproxy/envoymobile/EngineBuilderTest.kt index eaec5e3d5270..9d81545b9e72 100644 --- a/mobile/test/kotlin/io/envoyproxy/envoymobile/EngineBuilderTest.kt +++ b/mobile/test/kotlin/io/envoyproxy/envoymobile/EngineBuilderTest.kt @@ -4,11 +4,16 @@ import io.envoyproxy.envoymobile.engine.EnvoyEngine import org.assertj.core.api.Assertions.assertThat import org.junit.Test import org.mockito.Mockito.mock +import io.envoyproxy.envoymobile.engine.JniLibrary class EngineBuilderTest { private lateinit var engineBuilder: EngineBuilder private var envoyEngine: EnvoyEngine = mock(EnvoyEngine::class.java) + init { + JniLibrary.loadTestLibrary() + } + @Test fun `adding log level builder uses log level for running Envoy`() { engineBuilder = EngineBuilder(Standard()) diff --git a/mobile/test/objective-c/BUILD b/mobile/test/objective-c/BUILD index 906bafba724c..abcead101059 100644 --- a/mobile/test/objective-c/BUILD +++ b/mobile/test/objective-c/BUILD @@ -7,6 +7,8 @@ envoy_mobile_objc_test( srcs = [ "EnvoyBridgeUtilityTest.m", ], + flaky = True, # TODO(jpsim): Fix timeouts when running these tests on CI + tags = ["no-remote-exec"], # TODO(jpsim): Re-enable remote exec visibility = ["//visibility:public"], deps = [ "//library/objective-c:envoy_objc_bridge_lib", @@ -18,6 +20,8 @@ envoy_mobile_objc_test( srcs = [ "EnvoyKeyValueStoreBridgeImplTest.m", ], + flaky = True, # TODO(jpsim): Fix timeouts when running these tests on CI + tags = ["no-remote-exec"], # TODO(jpsim): Re-enable remote exec visibility = ["//visibility:public"], deps = [ "//library/objective-c:envoy_key_value_store_bridge_impl_lib", diff --git a/mobile/test/swift/BUILD b/mobile/test/swift/BUILD index 1839c45e50ba..d449ae2b179c 100644 --- a/mobile/test/swift/BUILD +++ b/mobile/test/swift/BUILD @@ -16,6 +16,8 @@ envoy_mobile_swift_test( "RetryPolicyMapperTests.swift", "RetryPolicyTests.swift", ], + flaky = True, # TODO(jpsim): Fix timeouts when running these tests on CI + tags = ["no-remote-exec"], # TODO(jpsim): Re-enable remote exec visibility = ["//visibility:public"], deps = [ "//library/objective-c:envoy_engine_objc_lib", diff --git a/mobile/test/swift/stats/BUILD b/mobile/test/swift/stats/BUILD index c09d248d3dd3..cc37e8d4ab84 100644 --- a/mobile/test/swift/stats/BUILD +++ b/mobile/test/swift/stats/BUILD @@ -9,6 +9,8 @@ envoy_mobile_swift_test( "ElementTests.swift", "TagsBuilderTests.swift", ], + flaky = True, # TODO(jpsim): Fix timeouts when running these tests on CI + tags = ["no-remote-exec"], # TODO(jpsim): Re-enable remote exec visibility = ["//visibility:public"], deps = [ "//library/objective-c:envoy_engine_objc_lib", diff --git a/mobile/third_party/BUILD b/mobile/third_party/BUILD deleted file mode 100644 index 6a37069f016d..000000000000 --- a/mobile/third_party/BUILD +++ /dev/null @@ -1,7 +0,0 @@ -licenses(["notice"]) # Apache 2 - -exports_files([ - "android/ifaddrs-android.h", - "android/LocalArray.h", - "android/ScopedFd.h", -]) diff --git a/mobile/third_party/python/requirements.txt b/mobile/third_party/python/requirements.txt index 953ff4231fec..c525bb45feed 100644 --- a/mobile/third_party/python/requirements.txt +++ b/mobile/third_party/python/requirements.txt @@ -4,171 +4,271 @@ # # pip-compile --allow-unsafe --generate-hashes requirements.in # -attrs==22.1.0 \ - --hash=sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6 \ - --hash=sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c +attrs==22.2.0 \ + --hash=sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836 \ + --hash=sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99 # via pytest -certifi==2022.9.24 \ - --hash=sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14 \ - --hash=sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382 +certifi==2022.12.7 \ + --hash=sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3 \ + --hash=sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18 # via requests -charset-normalizer==2.1.1 \ - --hash=sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845 \ - --hash=sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f +charset-normalizer==3.0.1 \ + --hash=sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b \ + --hash=sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42 \ + --hash=sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d \ + --hash=sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b \ + --hash=sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a \ + --hash=sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59 \ + --hash=sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154 \ + --hash=sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1 \ + --hash=sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c \ + --hash=sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a \ + --hash=sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d \ + --hash=sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6 \ + --hash=sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b \ + --hash=sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b \ + --hash=sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783 \ + --hash=sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5 \ + --hash=sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918 \ + --hash=sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555 \ + --hash=sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639 \ + --hash=sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786 \ + --hash=sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e \ + --hash=sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed \ + --hash=sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820 \ + --hash=sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8 \ + --hash=sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3 \ + --hash=sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541 \ + --hash=sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14 \ + --hash=sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be \ + --hash=sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e \ + --hash=sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76 \ + --hash=sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b \ + --hash=sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c \ + --hash=sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b \ + --hash=sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3 \ + --hash=sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc \ + --hash=sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6 \ + --hash=sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59 \ + --hash=sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4 \ + --hash=sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d \ + --hash=sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d \ + --hash=sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3 \ + --hash=sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a \ + --hash=sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea \ + --hash=sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6 \ + --hash=sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e \ + --hash=sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603 \ + --hash=sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24 \ + --hash=sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a \ + --hash=sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58 \ + --hash=sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678 \ + --hash=sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a \ + --hash=sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c \ + --hash=sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6 \ + --hash=sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18 \ + --hash=sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174 \ + --hash=sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317 \ + --hash=sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f \ + --hash=sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc \ + --hash=sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837 \ + --hash=sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41 \ + --hash=sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c \ + --hash=sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579 \ + --hash=sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753 \ + --hash=sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8 \ + --hash=sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291 \ + --hash=sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087 \ + --hash=sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866 \ + --hash=sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3 \ + --hash=sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d \ + --hash=sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1 \ + --hash=sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca \ + --hash=sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e \ + --hash=sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db \ + --hash=sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72 \ + --hash=sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d \ + --hash=sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc \ + --hash=sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539 \ + --hash=sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d \ + --hash=sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af \ + --hash=sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b \ + --hash=sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602 \ + --hash=sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f \ + --hash=sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478 \ + --hash=sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c \ + --hash=sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e \ + --hash=sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479 \ + --hash=sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7 \ + --hash=sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8 # via requests -exceptiongroup==1.0.0 \ - --hash=sha256:2ac84b496be68464a2da60da518af3785fff8b7ec0d090a581604bc870bdee41 \ - --hash=sha256:affbabf13fb6e98988c38d9c5650e701569fe3c1de3233cfb61c5f33774690ad +exceptiongroup==1.1.0 \ + --hash=sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e \ + --hash=sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23 # via pytest -gevent==22.10.1 \ - --hash=sha256:03c10ca0beeab0c6be516030471ea630447ddd1f649d3335e5b162097cd4130a \ - --hash=sha256:04a920a812b6c0c36d4613a15c254ca1ce415ee75ade0df3b8941ab61ae7ce3f \ - --hash=sha256:0569e133bb620de1001ac807ad9a8abaadedd25349c6d695f80c9048a3f59d42 \ - --hash=sha256:06ea39c70ce166c4a1d4386c7fae96cb8d84ad799527b3378406051104d15443 \ - --hash=sha256:0cafb8f5399224990a5329bca3606bf399ee3604ae711b2d9238129b44551ad5 \ - --hash=sha256:1ac816f5e8e318c5fa27091ee43fcf4caaa6ae73a487e875a1a296a04c3da5af \ - --hash=sha256:1e1c609f9e4171588006bea7ff41bb830ff27c27d071bbd311f91860fb5ef4cc \ - --hash=sha256:1ec5f77f629d997668983be53bad2a90d1b858a00e43b9e75e1c9a118c3a840b \ - --hash=sha256:21dbeb6d3b47093f40ca97aab493b2fb64b6f22112f88f56d79cf6f52a8c1c16 \ - --hash=sha256:2dcfd8ef9dcca78c51dd266d0f4b48d9b7ea2592ae881bf66d8dbe59bb16631a \ - --hash=sha256:33c4cd095f99768ecc4b3bb75a12f52ea9df5c40a58671c667f86ea087a075e1 \ - --hash=sha256:36e47ca663081a71fca137b7c852e99e7ee3761082070c13aa2ae3b5b6234af6 \ - --hash=sha256:3c1cfd9f9fb6a2a5a9a04132d315db7fb819db019dea260695fe6e4012416f96 \ - --hash=sha256:3e73c9f71aa2a6795ecbec9b57282b002375e863e283558feb87b62840c8c1ac \ - --hash=sha256:3f7d11136b3ae6312effbc2ac0ed902ae718d86e7acb9a51cf927262cfb2931e \ - --hash=sha256:4b0d29fc18ee338a85396facfc508e5f26e2e0e90f4c2889f8a9e74d341ad467 \ - --hash=sha256:4be5859af086de1ed85702c0a84479387087ddf49e38332c41861b0a10e96d8f \ - --hash=sha256:4c06c0f3f4f1b147f51a934fbf541880cee769492b98c4ebd3e930b5ff862646 \ - --hash=sha256:5bc3758f0dc95007c1780d28a9fd2150416a79c50f308f62a674d78a845ea1b9 \ - --hash=sha256:5d64b208bec99adc7e0b6e08a3e2c296065c665ca725ca8da434c4ffc5aa302e \ - --hash=sha256:702a51b8f21bad1976b0893f90ade466e8c27039b846b611ad2beb8c6e6ac701 \ - --hash=sha256:7e8c4ccc544f6e6c26ab10d0d6a7be86bd522222ce40f00bfafa01289f04bffc \ - --hash=sha256:a12443b7326e40d00fb445d37bae154fd1f4693055330c6b4e68670ca3b6e6bf \ - --hash=sha256:a838437b7b629328ad457cd36df454500afe7f3df4b971a6ff85851dfcf8c844 \ - --hash=sha256:acb21bee2e66da45b8916073c8ae54c44629beb94d49120c188d27aff4ebf8dd \ - --hash=sha256:af7baec79a5f8ad1cc132d3b14edd12661c628d8094e501b089b1fe2d3df7f6e \ - --hash=sha256:be43278781d39b4081f7f4d3e8ebb1dac188c9fe98f25da817325cb12c01887a \ - --hash=sha256:cf6dd33052919de8fb56e0bea0e6a7c7d6545281fe280ea78e311621c7adb50e \ - --hash=sha256:d18fcc324f39a3b21795022eb47c7752d6e4f4ed89d8cca41f1cc604553265b3 \ - --hash=sha256:d2ea4ce36c09355379bc038be2bd50118f97d2eb6381b7096de4d05aa4c3e241 \ - --hash=sha256:d701208d4d65dbdf9feb02561a75ecc5bd28300e47b59f74033a07b593887806 \ - --hash=sha256:d8df3f628c8a9fb339b87a849dc2076e56d124e2169261fa58b4a01db3a335b6 \ - --hash=sha256:db592cfe5106730667ac36f43554e7a869d757e411f8a08116c3739cee507145 \ - --hash=sha256:df3042349c9a4460eeaec8d0e56d737cb183eed055e75a6af9dbda94aaddaf4d \ - --hash=sha256:eb0d9d6f869ba7c49d7f9b7d244dd20daec5cc87cd3e2e90209d6ed8172e0cad \ - --hash=sha256:eefcb21fda3055f2e7eaad2e8098885a7bbddd83b174b012e2142db6b2b4c09d \ - --hash=sha256:f16c6937d47593f051fc3ac7996c819919082a1e7e0dec927cdae8771d26ed45 \ - --hash=sha256:fe2c0ff095171c49f78f1d4e6dc89fa58253783c7b6dccab9f1d76e2ee391f10 +gevent==22.10.2 \ + --hash=sha256:018f93de7d5318d2fb440f846839a4464738468c3476d5c9cf7da45bb71c18bd \ + --hash=sha256:0d581f22a5be6281b11ad6309b38b18f0638cf896931223cbaa5adb904826ef6 \ + --hash=sha256:1472012493ca1fac103f700d309cb6ef7964dcdb9c788d1768266e77712f5e49 \ + --hash=sha256:172caa66273315f283e90a315921902cb6549762bdcb0587fd60cb712a9d6263 \ + --hash=sha256:17b68f4c9e20e47ad49fe797f37f91d5bbeace8765ce2707f979a8d4ec197e4d \ + --hash=sha256:1ca01da176ee37b3527a2702f7d40dbc9ffb8cfc7be5a03bfa4f9eec45e55c46 \ + --hash=sha256:1d543c9407a1e4bca11a8932916988cfb16de00366de5bf7bc9e7a3f61e60b18 \ + --hash=sha256:1e1286a76f15b5e15f1e898731d50529e249529095a032453f2c101af3fde71c \ + --hash=sha256:1e955238f59b2947631c9782a713280dd75884e40e455313b5b6bbc20b92ff73 \ + --hash=sha256:1f001cac0ba8da76abfeb392a3057f81fab3d67cc916c7df8ea977a44a2cc989 \ + --hash=sha256:1ff3796692dff50fec2f381b9152438b221335f557c4f9b811f7ded51b7a25a1 \ + --hash=sha256:2929377c8ebfb6f4d868d161cd8de2ea6b9f6c7a5fcd4f78bcd537319c16190b \ + --hash=sha256:319d8b1699b7b8134de66d656cd739b308ab9c45ace14d60ae44de7775b456c9 \ + --hash=sha256:323b207b281ba0405fea042067fa1a61662e5ac0d574ede4ebbda03efd20c350 \ + --hash=sha256:3b7eae8a0653ba95a224faaddf629a913ace408edb67384d3117acf42d7dcf89 \ + --hash=sha256:4114f0f439f0b547bb6f1d474fee99ddb46736944ad2207cef3771828f6aa358 \ + --hash=sha256:4197d423e198265eef39a0dea286ef389da9148e070310f34455ecee8172c391 \ + --hash=sha256:494c7f29e94df9a1c3157d67bb7edfa32a46eed786e04d9ee68d39f375e30001 \ + --hash=sha256:4e2f008c82dc54ec94f4de12ca6feea60e419babb48ec145456907ae61625aa4 \ + --hash=sha256:53ee7f170ed42c7561fe8aff5d381dc9a4124694e70580d0c02fba6aafc0ea37 \ + --hash=sha256:54f4bfd74c178351a4a05c5c7df6f8a0a279ff6f392b57608ce0e83c768207f9 \ + --hash=sha256:58898dbabb5b11e4d0192aae165ad286dc6742c543e1be9d30dc82753547c508 \ + --hash=sha256:59b47e81b399d49a5622f0f503c59f1ce57b7705306ea0196818951dfc2f36c8 \ + --hash=sha256:5aa99e4882a9e909b4756ee799c6fa0f79eb0542779fad4cc60efa23ec1b2aa8 \ + --hash=sha256:6c04ee32c11e9fcee47c1b431834878dc987a7a2cc4fe126ddcae3bad723ce89 \ + --hash=sha256:84c517e33ed604fa06b7d756dc0171169cc12f7fdd68eb7b17708a62eebf4516 \ + --hash=sha256:8729129edef2637a8084258cb9ec4e4d5ca45d97ac77aa7a6ff19ccb530ab731 \ + --hash=sha256:877abdb3a669576b1d51ce6a49b7260b2a96f6b2424eb93287e779a3219d20ba \ + --hash=sha256:8c192d2073e558e241f0b592c1e2b34127a4481a5be240cad4796533b88b1a98 \ + --hash=sha256:8f2477e7b0a903a01485c55bacf2089110e5f767014967ba4b287ff390ae2638 \ + --hash=sha256:96c56c280e3c43cfd075efd10b250350ed5ffd3c1514ec99a080b1b92d7c8374 \ + --hash=sha256:97cd42382421779f5d82ec5007199e8a84aa288114975429e4fd0a98f2290f10 \ + --hash=sha256:98bc510e80f45486ef5b806a1c305e0e89f0430688c14984b0dbdec03331f48b \ + --hash=sha256:990d7069f14dc40674e0d5cb43c68fd3bad8337048613b9bb94a0c4180ffc176 \ + --hash=sha256:9d85574eb729f981fea9a78998725a06292d90a3ed50ddca74530c3148c0be41 \ + --hash=sha256:a2237451c721a0f874ef89dbb4af4fdc172b76a964befaa69deb15b8fff10f49 \ + --hash=sha256:a47a4e77e2bc668856aad92a0b8de7ee10768258d93cd03968e6c7ba2e832f76 \ + --hash=sha256:a5488eba6a568b4d23c072113da4fc0feb1b5f5ede7381656dc913e0d82204e2 \ + --hash=sha256:ae90226074a6089371a95f20288431cd4b3f6b0b096856afd862e4ac9510cddd \ + --hash=sha256:b43d500d7d3c0e03070dee813335bb5315215aa1cf6a04c61093dfdd718640b3 \ + --hash=sha256:b6c144e08dfad4106effc043a026e5d0c0eff6ad031904c70bf5090c63f3a6a7 \ + --hash=sha256:d21ad79cca234cdbfa249e727500b0ddcbc7adfff6614a96e6eaa49faca3e4f2 \ + --hash=sha256:d82081656a5b9a94d37c718c8646c757e1617e389cdc533ea5e6a6f0b8b78545 \ + --hash=sha256:da4183f0b9d9a1e25e1758099220d32c51cc2c6340ee0dea3fd236b2b37598e4 \ + --hash=sha256:db562a8519838bddad0c439a2b12246bab539dd50e299ea7ff3644274a33b6a5 \ + --hash=sha256:ddaa3e310a8f1a45b5c42cf50b54c31003a3028e7d4e085059090ea0e7a5fddd \ + --hash=sha256:ed7f16613eebf892a6a744d7a4a8f345bc6f066a0ff3b413e2479f9c0a180193 \ + --hash=sha256:efc003b6c1481165af61f0aeac248e0a9ac8d880bb3acbe469b448674b2d5281 \ + --hash=sha256:f01c9adbcb605364694b11dcd0542ec468a29ac7aba2fb5665dc6caf17ba4d7e \ + --hash=sha256:f23d0997149a816a2a9045af29c66f67f405a221745b34cefeac5769ed451db8 \ + --hash=sha256:f3329bedbba4d3146ae58c667e0f9ac1e6f1e1e6340c7593976cdc60aa7d1a47 \ + --hash=sha256:f7ed2346eb9dc4344f9cb0d7963ce5b74fe16fdd031a2809bb6c2b6eba7ebcd5 # via -r requirements.in -greenlet==1.1.3.post0 \ - --hash=sha256:0120a879aa2b1ac5118bce959ea2492ba18783f65ea15821680a256dfad04754 \ - --hash=sha256:025b8de2273d2809f027d347aa2541651d2e15d593bbce0d5f502ca438c54136 \ - --hash=sha256:05ae7383f968bba4211b1fbfc90158f8e3da86804878442b4fb6c16ccbcaa519 \ - --hash=sha256:0914f02fcaa8f84f13b2df4a81645d9e82de21ed95633765dd5cc4d3af9d7403 \ - --hash=sha256:0971d37ae0eaf42344e8610d340aa0ad3d06cd2eee381891a10fe771879791f9 \ - --hash=sha256:0a954002064ee919b444b19c1185e8cce307a1f20600f47d6f4b6d336972c809 \ - --hash=sha256:0aa1845944e62f358d63fcc911ad3b415f585612946b8edc824825929b40e59e \ - --hash=sha256:104f29dd822be678ef6b16bf0035dcd43206a8a48668a6cae4d2fe9c7a7abdeb \ - --hash=sha256:11fc7692d95cc7a6a8447bb160d98671ab291e0a8ea90572d582d57361360f05 \ - --hash=sha256:17a69967561269b691747e7f436d75a4def47e5efcbc3c573180fc828e176d80 \ - --hash=sha256:2794eef1b04b5ba8948c72cc606aab62ac4b0c538b14806d9c0d88afd0576d6b \ - --hash=sha256:2c6e942ca9835c0b97814d14f78da453241837419e0d26f7403058e8db3e38f8 \ - --hash=sha256:2ccdc818cc106cc238ff7eba0d71b9c77be868fdca31d6c3b1347a54c9b187b2 \ - --hash=sha256:325f272eb997916b4a3fc1fea7313a8adb760934c2140ce13a2117e1b0a8095d \ - --hash=sha256:39464518a2abe9c505a727af7c0b4efff2cf242aa168be5f0daa47649f4d7ca8 \ - --hash=sha256:3a24f3213579dc8459e485e333330a921f579543a5214dbc935bc0763474ece3 \ - --hash=sha256:3aeac044c324c1a4027dca0cde550bd83a0c0fbff7ef2c98df9e718a5086c194 \ - --hash=sha256:3c22998bfef3fcc1b15694818fc9b1b87c6cc8398198b96b6d355a7bcb8c934e \ - --hash=sha256:467b73ce5dcd89e381292fb4314aede9b12906c18fab903f995b86034d96d5c8 \ - --hash=sha256:4a8b58232f5b72973350c2b917ea3df0bebd07c3c82a0a0e34775fc2c1f857e9 \ - --hash=sha256:4f74aa0092602da2069df0bc6553919a15169d77bcdab52a21f8c5242898f519 \ - --hash=sha256:5662492df0588a51d5690f6578f3bbbd803e7f8d99a99f3bf6128a401be9c269 \ - --hash=sha256:5c2d21c2b768d8c86ad935e404cc78c30d53dea009609c3ef3a9d49970c864b5 \ - --hash=sha256:5edf75e7fcfa9725064ae0d8407c849456553a181ebefedb7606bac19aa1478b \ - --hash=sha256:60839ab4ea7de6139a3be35b77e22e0398c270020050458b3d25db4c7c394df5 \ - --hash=sha256:62723e7eb85fa52e536e516ee2ac91433c7bb60d51099293671815ff49ed1c21 \ - --hash=sha256:64e10f303ea354500c927da5b59c3802196a07468332d292aef9ddaca08d03dd \ - --hash=sha256:66aa4e9a726b70bcbfcc446b7ba89c8cec40f405e51422c39f42dfa206a96a05 \ - --hash=sha256:695d0d8b5ae42c800f1763c9fce9d7b94ae3b878919379150ee5ba458a460d57 \ - --hash=sha256:70048d7b2c07c5eadf8393e6398595591df5f59a2f26abc2f81abca09610492f \ - --hash=sha256:7afa706510ab079fd6d039cc6e369d4535a48e202d042c32e2097f030a16450f \ - --hash=sha256:7cf37343e43404699d58808e51f347f57efd3010cc7cee134cdb9141bd1ad9ea \ - --hash=sha256:8149a6865b14c33be7ae760bcdb73548bb01e8e47ae15e013bf7ef9290ca309a \ - --hash=sha256:814f26b864ed2230d3a7efe0336f5766ad012f94aad6ba43a7c54ca88dd77cba \ - --hash=sha256:82a38d7d2077128a017094aff334e67e26194f46bd709f9dcdacbf3835d47ef5 \ - --hash=sha256:83a7a6560df073ec9de2b7cb685b199dfd12519bc0020c62db9d1bb522f989fa \ - --hash=sha256:8415239c68b2ec9de10a5adf1130ee9cb0ebd3e19573c55ba160ff0ca809e012 \ - --hash=sha256:88720794390002b0c8fa29e9602b395093a9a766b229a847e8d88349e418b28a \ - --hash=sha256:890f633dc8cb307761ec566bc0b4e350a93ddd77dc172839be122be12bae3e10 \ - --hash=sha256:8926a78192b8b73c936f3e87929931455a6a6c6c385448a07b9f7d1072c19ff3 \ - --hash=sha256:8c0581077cf2734569f3e500fab09c0ff6a2ab99b1afcacbad09b3c2843ae743 \ - --hash=sha256:8fda1139d87ce5f7bd80e80e54f9f2c6fe2f47983f1a6f128c47bf310197deb6 \ - --hash=sha256:91a84faf718e6f8b888ca63d0b2d6d185c8e2a198d2a7322d75c303e7097c8b7 \ - --hash=sha256:924df1e7e5db27d19b1359dc7d052a917529c95ba5b8b62f4af611176da7c8ad \ - --hash=sha256:949c9061b8c6d3e6e439466a9be1e787208dec6246f4ec5fffe9677b4c19fcc3 \ - --hash=sha256:9649891ab4153f217f319914455ccf0b86986b55fc0573ce803eb998ad7d6854 \ - --hash=sha256:96656c5f7c95fc02c36d4f6ef32f4e94bb0b6b36e6a002c21c39785a4eec5f5d \ - --hash=sha256:a812df7282a8fc717eafd487fccc5ba40ea83bb5b13eb3c90c446d88dbdfd2be \ - --hash=sha256:a8d24eb5cb67996fb84633fdc96dbc04f2d8b12bfcb20ab3222d6be271616b67 \ - --hash=sha256:bef49c07fcb411c942da6ee7d7ea37430f830c482bf6e4b72d92fd506dd3a427 \ - --hash=sha256:bffba15cff4802ff493d6edcf20d7f94ab1c2aee7cfc1e1c7627c05f1102eee8 \ - --hash=sha256:c0643250dd0756f4960633f5359884f609a234d4066686754e834073d84e9b51 \ - --hash=sha256:c6f90234e4438062d6d09f7d667f79edcc7c5e354ba3a145ff98176f974b8132 \ - --hash=sha256:c8c9301e3274276d3d20ab6335aa7c5d9e5da2009cccb01127bddb5c951f8870 \ - --hash=sha256:c8ece5d1a99a2adcb38f69af2f07d96fb615415d32820108cd340361f590d128 \ - --hash=sha256:cb863057bed786f6622982fb8b2c122c68e6e9eddccaa9fa98fd937e45ee6c4f \ - --hash=sha256:ccbe7129a282ec5797df0451ca1802f11578be018a32979131065565da89b392 \ - --hash=sha256:d25cdedd72aa2271b984af54294e9527306966ec18963fd032cc851a725ddc1b \ - --hash=sha256:d75afcbb214d429dacdf75e03a1d6d6c5bd1fa9c35e360df8ea5b6270fb2211c \ - --hash=sha256:d7815e1519a8361c5ea2a7a5864945906f8e386fa1bc26797b4d443ab11a4589 \ - --hash=sha256:eb6ac495dccb1520667cfea50d89e26f9ffb49fa28496dea2b95720d8b45eb54 \ - --hash=sha256:ec615d2912b9ad807afd3be80bf32711c0ff9c2b00aa004a45fd5d5dde7853d9 \ - --hash=sha256:f5e09dc5c6e1796969fd4b775ea1417d70e49a5df29aaa8e5d10675d9e11872c \ - --hash=sha256:f6661b58412879a2aa099abb26d3c93e91dedaba55a6394d1fb1512a77e85de9 \ - --hash=sha256:f7d20c3267385236b4ce54575cc8e9f43e7673fc761b069c820097092e318e3b \ - --hash=sha256:fe7c51f8a2ab616cb34bc33d810c887e89117771028e1e3d3b77ca25ddeace04 +greenlet==2.0.2 \ + --hash=sha256:03a8f4f3430c3b3ff8d10a2a86028c660355ab637cee9333d63d66b56f09d52a \ + --hash=sha256:0bf60faf0bc2468089bdc5edd10555bab6e85152191df713e2ab1fcc86382b5a \ + --hash=sha256:18a7f18b82b52ee85322d7a7874e676f34ab319b9f8cce5de06067384aa8ff43 \ + --hash=sha256:18e98fb3de7dba1c0a852731c3070cf022d14f0d68b4c87a19cc1016f3bb8b33 \ + --hash=sha256:1a819eef4b0e0b96bb0d98d797bef17dc1b4a10e8d7446be32d1da33e095dbb8 \ + --hash=sha256:26fbfce90728d82bc9e6c38ea4d038cba20b7faf8a0ca53a9c07b67318d46088 \ + --hash=sha256:2780572ec463d44c1d3ae850239508dbeb9fed38e294c68d19a24d925d9223ca \ + --hash=sha256:283737e0da3f08bd637b5ad058507e578dd462db259f7f6e4c5c365ba4ee9343 \ + --hash=sha256:2d4686f195e32d36b4d7cf2d166857dbd0ee9f3d20ae349b6bf8afc8485b3645 \ + --hash=sha256:2dd11f291565a81d71dab10b7033395b7a3a5456e637cf997a6f33ebdf06f8db \ + --hash=sha256:30bcf80dda7f15ac77ba5af2b961bdd9dbc77fd4ac6105cee85b0d0a5fcf74df \ + --hash=sha256:32e5b64b148966d9cccc2c8d35a671409e45f195864560829f395a54226408d3 \ + --hash=sha256:36abbf031e1c0f79dd5d596bfaf8e921c41df2bdf54ee1eed921ce1f52999a86 \ + --hash=sha256:3a06ad5312349fec0ab944664b01d26f8d1f05009566339ac6f63f56589bc1a2 \ + --hash=sha256:3a51c9751078733d88e013587b108f1b7a1fb106d402fb390740f002b6f6551a \ + --hash=sha256:3c9b12575734155d0c09d6c3e10dbd81665d5c18e1a7c6597df72fd05990c8cf \ + --hash=sha256:3f6ea9bd35eb450837a3d80e77b517ea5bc56b4647f5502cd28de13675ee12f7 \ + --hash=sha256:4b58adb399c4d61d912c4c331984d60eb66565175cdf4a34792cd9600f21b394 \ + --hash=sha256:4d2e11331fc0c02b6e84b0d28ece3a36e0548ee1a1ce9ddde03752d9b79bba40 \ + --hash=sha256:5454276c07d27a740c5892f4907c86327b632127dd9abec42ee62e12427ff7e3 \ + --hash=sha256:561091a7be172ab497a3527602d467e2b3fbe75f9e783d8b8ce403fa414f71a6 \ + --hash=sha256:6c3acb79b0bfd4fe733dff8bc62695283b57949ebcca05ae5c129eb606ff2d74 \ + --hash=sha256:703f18f3fda276b9a916f0934d2fb6d989bf0b4fb5a64825260eb9bfd52d78f0 \ + --hash=sha256:7492e2b7bd7c9b9916388d9df23fa49d9b88ac0640db0a5b4ecc2b653bf451e3 \ + --hash=sha256:76ae285c8104046b3a7f06b42f29c7b73f77683df18c49ab5af7983994c2dd91 \ + --hash=sha256:7cafd1208fdbe93b67c7086876f061f660cfddc44f404279c1585bbf3cdc64c5 \ + --hash=sha256:7efde645ca1cc441d6dc4b48c0f7101e8d86b54c8530141b09fd31cef5149ec9 \ + --hash=sha256:88d9ab96491d38a5ab7c56dd7a3cc37d83336ecc564e4e8816dbed12e5aaefc8 \ + --hash=sha256:8eab883b3b2a38cc1e050819ef06a7e6344d4a990d24d45bc6f2cf959045a45b \ + --hash=sha256:910841381caba4f744a44bf81bfd573c94e10b3045ee00de0cbf436fe50673a6 \ + --hash=sha256:9190f09060ea4debddd24665d6804b995a9c122ef5917ab26e1566dcc712ceeb \ + --hash=sha256:937e9020b514ceedb9c830c55d5c9872abc90f4b5862f89c0887033ae33c6f73 \ + --hash=sha256:94c817e84245513926588caf1152e3b559ff794d505555211ca041f032abbb6b \ + --hash=sha256:971ce5e14dc5e73715755d0ca2975ac88cfdaefcaab078a284fea6cfabf866df \ + --hash=sha256:9d14b83fab60d5e8abe587d51c75b252bcc21683f24699ada8fb275d7712f5a9 \ + --hash=sha256:9f35ec95538f50292f6d8f2c9c9f8a3c6540bbfec21c9e5b4b751e0a7c20864f \ + --hash=sha256:a1846f1b999e78e13837c93c778dcfc3365902cfb8d1bdb7dd73ead37059f0d0 \ + --hash=sha256:acd2162a36d3de67ee896c43effcd5ee3de247eb00354db411feb025aa319857 \ + --hash=sha256:b0ef99cdbe2b682b9ccbb964743a6aca37905fda5e0452e5ee239b1654d37f2a \ + --hash=sha256:b80f600eddddce72320dbbc8e3784d16bd3fb7b517e82476d8da921f27d4b249 \ + --hash=sha256:b864ba53912b6c3ab6bcb2beb19f19edd01a6bfcbdfe1f37ddd1778abfe75a30 \ + --hash=sha256:b9ec052b06a0524f0e35bd8790686a1da006bd911dd1ef7d50b77bfbad74e292 \ + --hash=sha256:ba2956617f1c42598a308a84c6cf021a90ff3862eddafd20c3333d50f0edb45b \ + --hash=sha256:bdfea8c661e80d3c1c99ad7c3ff74e6e87184895bbaca6ee8cc61209f8b9b85d \ + --hash=sha256:be4ed120b52ae4d974aa40215fcdfde9194d63541c7ded40ee12eb4dda57b76b \ + --hash=sha256:c4302695ad8027363e96311df24ee28978162cdcdd2006476c43970b384a244c \ + --hash=sha256:c48f54ef8e05f04d6eff74b8233f6063cb1ed960243eacc474ee73a2ea8573ca \ + --hash=sha256:c9c59a2120b55788e800d82dfa99b9e156ff8f2227f07c5e3012a45a399620b7 \ + --hash=sha256:cd021c754b162c0fb55ad5d6b9d960db667faad0fa2ff25bb6e1301b0b6e6a75 \ + --hash=sha256:d27ec7509b9c18b6d73f2f5ede2622441de812e7b1a80bbd446cb0633bd3d5ae \ + --hash=sha256:d5508f0b173e6aa47273bdc0a0b5ba055b59662ba7c7ee5119528f466585526b \ + --hash=sha256:d75209eed723105f9596807495d58d10b3470fa6732dd6756595e89925ce2470 \ + --hash=sha256:db1a39669102a1d8d12b57de2bb7e2ec9066a6f2b3da35ae511ff93b01b5d564 \ + --hash=sha256:dbfcfc0218093a19c252ca8eb9aee3d29cfdcb586df21049b9d777fd32c14fd9 \ + --hash=sha256:e0f72c9ddb8cd28532185f54cc1453f2c16fb417a08b53a855c4e6a418edd099 \ + --hash=sha256:e7c8dc13af7db097bed64a051d2dd49e9f0af495c26995c00a9ee842690d34c0 \ + --hash=sha256:ea9872c80c132f4663822dd2a08d404073a5a9b5ba6155bea72fb2a79d1093b5 \ + --hash=sha256:eff4eb9b7eb3e4d0cae3d28c283dc16d9bed6b193c2e1ace3ed86ce48ea8df19 \ + --hash=sha256:f82d4d717d8ef19188687aa32b8363e96062911e63ba22a0cff7802a8e58e5f1 \ + --hash=sha256:fc3a569657468b6f3fb60587e48356fe512c1754ca05a564f11366ac9e306526 # via gevent idna==3.4 \ --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 # via requests -iniconfig==1.1.1 \ - --hash=sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3 \ - --hash=sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32 +iniconfig==2.0.0 \ + --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ + --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 # via pytest -mypy==0.982 \ - --hash=sha256:1021c241e8b6e1ca5a47e4d52601274ac078a89845cfde66c6d5f769819ffa1d \ - --hash=sha256:14d53cdd4cf93765aa747a7399f0961a365bcddf7855d9cef6306fa41de01c24 \ - --hash=sha256:175f292f649a3af7082fe36620369ffc4661a71005aa9f8297ea473df5772046 \ - --hash=sha256:26ae64555d480ad4b32a267d10cab7aec92ff44de35a7cd95b2b7cb8e64ebe3e \ - --hash=sha256:41fd1cf9bc0e1c19b9af13a6580ccb66c381a5ee2cf63ee5ebab747a4badeba3 \ - --hash=sha256:5085e6f442003fa915aeb0a46d4da58128da69325d8213b4b35cc7054090aed5 \ - --hash=sha256:58f27ebafe726a8e5ccb58d896451dd9a662a511a3188ff6a8a6a919142ecc20 \ - --hash=sha256:6389af3e204975d6658de4fb8ac16f58c14e1bacc6142fee86d1b5b26aa52bda \ - --hash=sha256:724d36be56444f569c20a629d1d4ee0cb0ad666078d59bb84f8f887952511ca1 \ - --hash=sha256:75838c649290d83a2b83a88288c1eb60fe7a05b36d46cbea9d22efc790002146 \ - --hash=sha256:7b35ce03a289480d6544aac85fa3674f493f323d80ea7226410ed065cd46f206 \ - --hash=sha256:85f7a343542dc8b1ed0a888cdd34dca56462654ef23aa673907305b260b3d746 \ - --hash=sha256:86ebe67adf4d021b28c3f547da6aa2cce660b57f0432617af2cca932d4d378a6 \ - --hash=sha256:8ee8c2472e96beb1045e9081de8e92f295b89ac10c4109afdf3a23ad6e644f3e \ - --hash=sha256:91781eff1f3f2607519c8b0e8518aad8498af1419e8442d5d0afb108059881fc \ - --hash=sha256:a692a8e7d07abe5f4b2dd32d731812a0175626a90a223d4b58f10f458747dd8a \ - --hash=sha256:a705a93670c8b74769496280d2fe6cd59961506c64f329bb179970ff1d24f9f8 \ - --hash=sha256:c6e564f035d25c99fd2b863e13049744d96bd1947e3d3d2f16f5828864506763 \ - --hash=sha256:cebca7fd333f90b61b3ef7f217ff75ce2e287482206ef4a8b18f32b49927b1a2 \ - --hash=sha256:d6af646bd46f10d53834a8e8983e130e47d8ab2d4b7a97363e35b24e1d588947 \ - --hash=sha256:e7aeaa763c7ab86d5b66ff27f68493d672e44c8099af636d433a7f3fa5596d40 \ - --hash=sha256:eaa97b9ddd1dd9901a22a879491dbb951b5dec75c3b90032e2baa7336777363b \ - --hash=sha256:eb7a068e503be3543c4bd329c994103874fa543c1727ba5288393c21d912d795 \ - --hash=sha256:f793e3dd95e166b66d50e7b63e69e58e88643d80a3dcc3bcd81368e0478b089c +mypy==0.991 \ + --hash=sha256:0714258640194d75677e86c786e80ccf294972cc76885d3ebbb560f11db0003d \ + --hash=sha256:0c8f3be99e8a8bd403caa8c03be619544bc2c77a7093685dcf308c6b109426c6 \ + --hash=sha256:0cca5adf694af539aeaa6ac633a7afe9bbd760df9d31be55ab780b77ab5ae8bf \ + --hash=sha256:1c8cd4fb70e8584ca1ed5805cbc7c017a3d1a29fb450621089ffed3e99d1857f \ + --hash=sha256:1f7d1a520373e2272b10796c3ff721ea1a0712288cafaa95931e66aa15798813 \ + --hash=sha256:209ee89fbb0deed518605edddd234af80506aec932ad28d73c08f1400ef80a33 \ + --hash=sha256:26efb2fcc6b67e4d5a55561f39176821d2adf88f2745ddc72751b7890f3194ad \ + --hash=sha256:37bd02ebf9d10e05b00d71302d2c2e6ca333e6c2a8584a98c00e038db8121f05 \ + --hash=sha256:3a700330b567114b673cf8ee7388e949f843b356a73b5ab22dd7cff4742a5297 \ + --hash=sha256:3c0165ba8f354a6d9881809ef29f1a9318a236a6d81c690094c5df32107bde06 \ + --hash=sha256:3d80e36b7d7a9259b740be6d8d906221789b0d836201af4234093cae89ced0cd \ + --hash=sha256:4175593dc25d9da12f7de8de873a33f9b2b8bdb4e827a7cae952e5b1a342e243 \ + --hash=sha256:4307270436fd7694b41f913eb09210faff27ea4979ecbcd849e57d2da2f65305 \ + --hash=sha256:5e80e758243b97b618cdf22004beb09e8a2de1af481382e4d84bc52152d1c476 \ + --hash=sha256:641411733b127c3e0dab94c45af15fea99e4468f99ac88b39efb1ad677da5711 \ + --hash=sha256:652b651d42f155033a1967739788c436491b577b6a44e4c39fb340d0ee7f0d70 \ + --hash=sha256:6d7464bac72a85cb3491c7e92b5b62f3dcccb8af26826257760a552a5e244aa5 \ + --hash=sha256:74e259b5c19f70d35fcc1ad3d56499065c601dfe94ff67ae48b85596b9ec1461 \ + --hash=sha256:7d17e0a9707d0772f4a7b878f04b4fd11f6f5bcb9b3813975a9b13c9332153ab \ + --hash=sha256:901c2c269c616e6cb0998b33d4adbb4a6af0ac4ce5cd078afd7bc95830e62c1c \ + --hash=sha256:98e781cd35c0acf33eb0295e8b9c55cdbef64fcb35f6d3aa2186f289bed6e80d \ + --hash=sha256:a12c56bf73cdab116df96e4ff39610b92a348cc99a1307e1da3c3768bbb5b135 \ + --hash=sha256:ac6e503823143464538efda0e8e356d871557ef60ccd38f8824a4257acc18d93 \ + --hash=sha256:b8472f736a5bfb159a5e36740847808f6f5b659960115ff29c7cecec1741c648 \ + --hash=sha256:b86ce2c1866a748c0f6faca5232059f881cda6dda2a893b9a8373353cfe3715a \ + --hash=sha256:bc9ec663ed6c8f15f4ae9d3c04c989b744436c16d26580eaa760ae9dd5d662eb \ + --hash=sha256:c9166b3f81a10cdf9b49f2d594b21b31adadb3d5e9db9b834866c3258b695be3 \ + --hash=sha256:d13674f3fb73805ba0c45eb6c0c3053d218aa1f7abead6e446d474529aafc372 \ + --hash=sha256:de32edc9b0a7e67c2775e574cb061a537660e51210fbf6006b0b36ea695ae9bb \ + --hash=sha256:e62ebaad93be3ad1a828a11e90f0e76f15449371ffeecca4a0a0b9adc99abcef # via -r requirements.in mypy-extensions==0.4.3 \ --hash=sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d \ --hash=sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8 # via mypy -packaging==21.3 \ - --hash=sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb \ - --hash=sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522 +packaging==23.0 \ + --hash=sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2 \ + --hash=sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97 # via pytest pluggy==1.0.0 \ --hash=sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159 \ @@ -178,28 +278,24 @@ py-cpuinfo==9.0.0 \ --hash=sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690 \ --hash=sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5 # via pytest-benchmark -pyparsing==3.0.9 \ - --hash=sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb \ - --hash=sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc - # via packaging -pytest==7.2.0 \ - --hash=sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71 \ - --hash=sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59 +pytest==7.2.1 \ + --hash=sha256:c7c6ca206e93355074ae32f7403e8ea12163b1163c976fee7d4d84027c162be5 \ + --hash=sha256:d45e0952f3727241918b8fd0f376f5ff6b301cc0777c6f9a556935c92d8a7d42 # via # -r requirements.in # pytest-asyncio # pytest-benchmark -pytest-asyncio==0.20.1 \ - --hash=sha256:2c85a835df33fda40fe3973b451e0c194ca11bc2c007eabff90bb3d156fc172b \ - --hash=sha256:626699de2a747611f3eeb64168b3575f70439b06c3d0206e6ceaeeb956e65519 +pytest-asyncio==0.20.3 \ + --hash=sha256:83cbf01169ce3e8eb71c6c278ccb0574d1a7a3bb8eaaf5e50e0ad342afb33b36 \ + --hash=sha256:f129998b209d04fcc65c96fc85c11e5316738358909a8399e93be553d7656442 # via -r requirements.in pytest-benchmark==4.0.0 \ --hash=sha256:fb0785b83efe599a6a956361c0691ae1dbb5318018561af10f3e915caa0048d1 \ --hash=sha256:fdb7db64e31c8b277dff9850d2a2556d8b60bcb0ea6524e36e28ffd7c87f71d6 # via -r requirements.in -requests==2.28.1 \ - --hash=sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983 \ - --hash=sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349 +requests==2.28.2 \ + --hash=sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa \ + --hash=sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf # via -r requirements.in tomli==2.0.1 \ --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ @@ -211,59 +307,55 @@ typing-extensions==4.4.0 \ --hash=sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa \ --hash=sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e # via mypy -urllib3==1.26.12 \ - --hash=sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e \ - --hash=sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997 +urllib3==1.26.14 \ + --hash=sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72 \ + --hash=sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1 # via requests -zope-event==4.5.0 \ - --hash=sha256:2666401939cdaa5f4e0c08cf7f20c9b21423b95e88f4675b1443973bdb080c42 \ - --hash=sha256:5e76517f5b9b119acf37ca8819781db6c16ea433f7e2062c4afc2b6fbedb1330 +zope-event==4.6 \ + --hash=sha256:73d9e3ef750cca14816a9c322c7250b0d7c9dbc337df5d1b807ff8d3d0b9e97c \ + --hash=sha256:81d98813046fc86cc4136e3698fee628a3282f9c320db18658c21749235fce80 # via gevent -zope-interface==5.5.0 \ - --hash=sha256:006f8dd81fae28027fc28ada214855166712bf4f0bfbc5a8788f9b70982b9437 \ - --hash=sha256:03f5ae315db0d0de668125d983e2a819a554f3fdb2d53b7e934e3eb3c3c7375d \ - --hash=sha256:0eb2b3e84f48dd9cfc8621c80fba905d7e228615c67f76c7df7c716065669bb6 \ - --hash=sha256:1e3495bb0cdcea212154e558082c256f11b18031f05193ae2fb85d048848db14 \ - --hash=sha256:26c1456520fdcafecc5765bec4783eeafd2e893eabc636908f50ee31fe5c738c \ - --hash=sha256:2cb3003941f5f4fa577479ac6d5db2b940acb600096dd9ea9bf07007f5cab46f \ - --hash=sha256:37ec9ade9902f412cc7e7a32d71f79dec3035bad9bd0170226252eed88763c48 \ - --hash=sha256:3eedf3d04179774d750e8bb4463e6da350956a50ed44d7b86098e452d7ec385e \ - --hash=sha256:3f68404edb1a4fb6aa8a94675521ca26c83ebbdbb90e894f749ae0dc4ca98418 \ - --hash=sha256:423c074e404f13e6fa07f4454f47fdbb38d358be22945bc812b94289d9142374 \ - --hash=sha256:43490ad65d4c64e45a30e51a2beb7a6b63e1ff395302ad22392224eb618476d6 \ - --hash=sha256:47ff078734a1030c48103422a99e71a7662d20258c00306546441adf689416f7 \ - --hash=sha256:58a66c2020a347973168a4a9d64317bac52f9fdfd3e6b80b252be30da881a64e \ - --hash=sha256:58a975f89e4584d0223ab813c5ba4787064c68feef4b30d600f5e01de90ae9ce \ - --hash=sha256:5c6023ae7defd052cf76986ce77922177b0c2f3913bea31b5b28fbdf6cb7099e \ - --hash=sha256:6566b3d2657e7609cd8751bcb1eab1202b1692a7af223035a5887d64bb3a2f3b \ - --hash=sha256:687cab7f9ae18d2c146f315d0ca81e5ffe89a139b88277afa70d52f632515854 \ - --hash=sha256:700ebf9662cf8df70e2f0cb4988e078c53f65ee3eefd5c9d80cf988c4175c8e3 \ - --hash=sha256:740f3c1b44380658777669bcc42f650f5348e53797f2cee0d93dc9b0f9d7cc69 \ - --hash=sha256:7bdcec93f152e0e1942102537eed7b166d6661ae57835b20a52a2a3d6a3e1bf3 \ - --hash=sha256:7d9ec1e6694af39b687045712a8ad14ddcb568670d5eb1b66b48b98b9312afba \ - --hash=sha256:85dd6dd9aaae7a176948d8bb62e20e2968588fd787c29c5d0d964ab475168d3d \ - --hash=sha256:8b9f153208d74ccfa25449a0c6cb756ab792ce0dc99d9d771d935f039b38740c \ - --hash=sha256:8c791f4c203ccdbcda588ea4c8a6e4353e10435ea48ddd3d8734a26fe9714cba \ - --hash=sha256:970661ece2029915b8f7f70892e88404340fbdefd64728380cad41c8dce14ff4 \ - --hash=sha256:9cdc4e898d3b1547d018829fd4a9f403e52e51bba24be0fbfa37f3174e1ef797 \ - --hash=sha256:9dc4493aa3d87591e3d2bf1453e25b98038c839ca8e499df3d7106631b66fe83 \ - --hash=sha256:a69c28d85bb7cf557751a5214cb3f657b2b035c8c96d71080c1253b75b79b69b \ - --hash=sha256:aeac590cce44e68ee8ad0b8ecf4d7bf15801f102d564ca1b0eb1f12f584ee656 \ - --hash=sha256:be11fce0e6af6c0e8d93c10ef17b25aa7c4acb7ec644bff2596c0d639c49e20f \ - --hash=sha256:cbbf83914b9a883ab324f728de869f4e406e0cbcd92df7e0a88decf6f9ab7d5a \ - --hash=sha256:cfa614d049667bed1c737435c609c0956c5dc0dbafdc1145ee7935e4658582cb \ - --hash=sha256:d18fb0f6c8169d26044128a2e7d3c39377a8a151c564e87b875d379dbafd3930 \ - --hash=sha256:d80f6236b57a95eb19d5e47eb68d0296119e1eff6deaa2971ab8abe3af918420 \ - --hash=sha256:da7912ae76e1df6a1fb841b619110b1be4c86dfb36699d7fd2f177105cdea885 \ - --hash=sha256:df6593e150d13cfcce69b0aec5df7bc248cb91e4258a7374c129bb6d56b4e5ca \ - --hash=sha256:f70726b60009433111fe9928f5d89cbb18962411d33c45fb19eb81b9bbd26fcd +zope-interface==5.5.2 \ + --hash=sha256:008b0b65c05993bb08912f644d140530e775cf1c62a072bf9340c2249e613c32 \ + --hash=sha256:0217a9615531c83aeedb12e126611b1b1a3175013bbafe57c702ce40000eb9a0 \ + --hash=sha256:0fb497c6b088818e3395e302e426850f8236d8d9f4ef5b2836feae812a8f699c \ + --hash=sha256:17ebf6e0b1d07ed009738016abf0d0a0f80388e009d0ac6e0ead26fc162b3b9c \ + --hash=sha256:311196634bb9333aa06f00fc94f59d3a9fddd2305c2c425d86e406ddc6f2260d \ + --hash=sha256:3218ab1a7748327e08ef83cca63eea7cf20ea7e2ebcb2522072896e5e2fceedf \ + --hash=sha256:404d1e284eda9e233c90128697c71acffd55e183d70628aa0bbb0e7a3084ed8b \ + --hash=sha256:4087e253bd3bbbc3e615ecd0b6dd03c4e6a1e46d152d3be6d2ad08fbad742dcc \ + --hash=sha256:40f4065745e2c2fa0dff0e7ccd7c166a8ac9748974f960cd39f63d2c19f9231f \ + --hash=sha256:5334e2ef60d3d9439c08baedaf8b84dc9bb9522d0dacbc10572ef5609ef8db6d \ + --hash=sha256:604cdba8f1983d0ab78edc29aa71c8df0ada06fb147cea436dc37093a0100a4e \ + --hash=sha256:6373d7eb813a143cb7795d3e42bd8ed857c82a90571567e681e1b3841a390d16 \ + --hash=sha256:655796a906fa3ca67273011c9805c1e1baa047781fca80feeb710328cdbed87f \ + --hash=sha256:65c3c06afee96c654e590e046c4a24559e65b0a87dbff256cd4bd6f77e1a33f9 \ + --hash=sha256:696f3d5493eae7359887da55c2afa05acc3db5fc625c49529e84bd9992313296 \ + --hash=sha256:6e972493cdfe4ad0411fd9abfab7d4d800a7317a93928217f1a5de2bb0f0d87a \ + --hash=sha256:7579960be23d1fddecb53898035a0d112ac858c3554018ce615cefc03024e46d \ + --hash=sha256:765d703096ca47aa5d93044bf701b00bbce4d903a95b41fff7c3796e747b1f1d \ + --hash=sha256:7e66f60b0067a10dd289b29dceabd3d0e6d68be1504fc9d0bc209cf07f56d189 \ + --hash=sha256:8a2ffadefd0e7206adc86e492ccc60395f7edb5680adedf17a7ee4205c530df4 \ + --hash=sha256:959697ef2757406bff71467a09d940ca364e724c534efbf3786e86eee8591452 \ + --hash=sha256:9d783213fab61832dbb10d385a319cb0e45451088abd45f95b5bb88ed0acca1a \ + --hash=sha256:a16025df73d24795a0bde05504911d306307c24a64187752685ff6ea23897cb0 \ + --hash=sha256:a2ad597c8c9e038a5912ac3cf166f82926feff2f6e0dabdab956768de0a258f5 \ + --hash=sha256:bfee1f3ff62143819499e348f5b8a7f3aa0259f9aca5e0ddae7391d059dce671 \ + --hash=sha256:d169ccd0756c15bbb2f1acc012f5aab279dffc334d733ca0d9362c5beaebe88e \ + --hash=sha256:d514c269d1f9f5cd05ddfed15298d6c418129f3f064765295659798349c43e6f \ + --hash=sha256:d692374b578360d36568dd05efb8a5a67ab6d1878c29c582e37ddba80e66c396 \ + --hash=sha256:dbaeb9cf0ea0b3bc4b36fae54a016933d64c6d52a94810a63c00f440ecb37dd7 \ + --hash=sha256:dc26c8d44472e035d59d6f1177eb712888447f5799743da9c398b0339ed90b1b \ + --hash=sha256:e1574980b48c8c74f83578d1e77e701f8439a5d93f36a5a0af31337467c08fcf \ + --hash=sha256:e74a578172525c20d7223eac5f8ad187f10940dac06e40113d62f14f3adb1e8f \ + --hash=sha256:e945de62917acbf853ab968d8916290548df18dd62c739d862f359ecd25842a6 \ + --hash=sha256:f0980d44b8aded808bec5059018d64692f0127f10510eca71f2f0ace8fb11188 \ + --hash=sha256:f98d4bd7bbb15ca701d19b93263cc5edfd480c3475d163f137385f49e5b3a3a7 \ + --hash=sha256:fb68d212efd057596dee9e6582daded9f8ef776538afdf5feceb3059df2d2e7b # via gevent # The following packages are considered to be unsafe in a requirements file: -setuptools==65.5.0 \ - --hash=sha256:512e5536220e38146176efb833d4a62aa726b7bbff82cfbc8ba9eaa3996e0b17 \ - --hash=sha256:f62ea9da9ed6289bfe868cd6845968a2c854d1427f8548d52cae02a42b4f0356 - # via - # gevent - # zope-event - # zope-interface +setuptools==67.0.0 \ + --hash=sha256:883131c5b6efa70b9101c7ef30b2b7b780a4283d5fc1616383cdf22c83cbefe6 \ + --hash=sha256:9d790961ba6219e9ff7d9557622d2fe136816a264dd01d5997cfc057d804853d + # via gevent diff --git a/mobile/third_party/rbe_configs/config/BUILD b/mobile/third_party/rbe_configs/config/BUILD index ad1323e6681c..5e3ca512b4a7 100644 --- a/mobile/third_party/rbe_configs/config/BUILD +++ b/mobile/third_party/rbe_configs/config/BUILD @@ -43,8 +43,8 @@ platform( ], exec_properties = { # Please update both the commented tag and the sha256 - # b0ff77ae3f25b0bf595f9b8bba46b489723ab446 - "container-image": "docker://envoyproxy/envoy-build-ubuntu@sha256:6996521022f9dcd3fcf88ca3d44256f6c98712896e50815a79360791e0a174e6", + # 7304f974de2724617b7492ccb4c9c58cd420353a + "container-image": "docker://envoyproxy/envoy-build-ubuntu@sha256:c219e5cf82f0058c12a801cfc9b680f10ddc480b99bedb099fb43f55d333f7ca", "OSFamily": "Linux", "Pool": "linux", }, @@ -60,8 +60,8 @@ platform( ], exec_properties = { # Please update both the commented tag and the sha256 - # b0ff77ae3f25b0bf595f9b8bba46b489723ab446 - "container-image": "docker://envoyproxy/envoy-build-ubuntu@sha256:6996521022f9dcd3fcf88ca3d44256f6c98712896e50815a79360791e0a174e6", + # 7304f974de2724617b7492ccb4c9c58cd420353a + "container-image": "docker://envoyproxy/envoy-build-ubuntu@sha256:c219e5cf82f0058c12a801cfc9b680f10ddc480b99bedb099fb43f55d333f7ca", "OSFamily": "Linux", "Pool": "linux", # Necessary to workaround https://github.com/google/sanitizers/issues/916, otherwise, dangling threads in the diff --git a/mobile/tools/should_run_ci.sh b/mobile/tools/should_run_ci.sh index 4f388d1e69f5..fa060f9fd00d 100755 --- a/mobile/tools/should_run_ci.sh +++ b/mobile/tools/should_run_ci.sh @@ -29,6 +29,13 @@ if [[ $branch_name == "main" ]]; then exit 0 fi +if [[ $GITHUB_BASE_REF == release/* ]]; then + # Skip mobile CI jobs on PRs targeting release branches + echo "Skipping $job because the PR is targeting a release branch" + echo "run_ci_job=false" >> "$GITHUB_OUTPUT" + exit 0 +fi + base_commit="$(git merge-base origin/main HEAD)" changed_files="$(git diff "$base_commit" --name-only)" diff --git a/source/common/api/BUILD b/source/common/api/BUILD index 7da100ba35a2..0ee05a74470d 100644 --- a/source/common/api/BUILD +++ b/source/common/api/BUILD @@ -59,5 +59,6 @@ envoy_cc_library( "//envoy/api:os_sys_calls_interface", "//source/common/network:address_lib", "//source/common/singleton:threadsafe_singleton", + "//third_party/android:android_lib", ], ) diff --git a/source/common/api/posix/os_sys_calls_impl.cc b/source/common/api/posix/os_sys_calls_impl.cc index a142f949a61e..47cd78fabe1f 100644 --- a/source/common/api/posix/os_sys_calls_impl.cc +++ b/source/common/api/posix/os_sys_calls_impl.cc @@ -8,6 +8,15 @@ #include "source/common/api/os_sys_calls_impl.h" #include "source/common/network/address_impl.h" +#if defined(__ANDROID_API__) && __ANDROID_API__ < 24 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" +namespace android { +#include "third_party/android/ifaddrs-android.h" +} // namespace android +#pragma clang diagnostic pop +#endif + namespace Envoy { namespace Api { @@ -346,35 +355,39 @@ SysCallBoolResult OsSysCallsImpl::socketTcpInfo([[maybe_unused]] os_fd_t sockfd, return {false, EOPNOTSUPP}; } -bool OsSysCallsImpl::supportsGetifaddrs() const { -// TODO: eliminate this branching by upstreaming an alternative Android implementation -// e.g.: https://github.com/envoyproxy/envoy-mobile/blob/main/third_party/android/ifaddrs-android.h +bool OsSysCallsImpl::supportsGetifaddrs() const { return true; } + +SysCallIntResult OsSysCallsImpl::getifaddrs([[maybe_unused]] InterfaceAddressVector& interfaces) { #if defined(__ANDROID_API__) && __ANDROID_API__ < 24 - if (alternate_getifaddrs_.has_value()) { - return true; + struct android::ifaddrs* ifaddr; + struct android::ifaddrs* ifa; + + const int rc = android::getifaddrs(&ifaddr); + if (rc == -1) { + return {rc, errno}; } - return false; -#else - // Note: posix defaults to true regardless of whether an alternate getifaddrs has been set or not. - // This is because as far as we are aware only Android<24 lacks an implementation and thus another - // posix based platform that lacks a native getifaddrs implementation should be a programming - // error. - // - // That being said, if an alternate getifaddrs impl is set, that will be used in calls to - // OsSysCallsImpl::getifaddrs as seen below. - return true; -#endif -} -SysCallIntResult OsSysCallsImpl::getifaddrs([[maybe_unused]] InterfaceAddressVector& interfaces) { - if (alternate_getifaddrs_.has_value()) { - return alternate_getifaddrs_.value()(interfaces); + for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == nullptr) { + continue; + } + + if (ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6) { + const sockaddr_storage* ss = reinterpret_cast(ifa->ifa_addr); + size_t ss_len = + ifa->ifa_addr->sa_family == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6); + StatusOr address = + Network::Address::addressFromSockAddr(*ss, ss_len, ifa->ifa_addr->sa_family == AF_INET6); + if (address.ok()) { + interfaces.emplace_back(ifa->ifa_name, ifa->ifa_flags, *address); + } + } } -// TODO: eliminate this branching by upstreaming an alternative Android implementation -// e.g.: https://github.com/envoyproxy/envoy-mobile/blob/main/third_party/android/ifaddrs-android.h -#if defined(__ANDROID_API__) && __ANDROID_API__ < 24 - PANIC("not implemented"); + if (ifaddr) { + android::freeifaddrs(ifaddr); + } + return {rc, 0}; #else struct ifaddrs* ifaddr; struct ifaddrs* ifa; diff --git a/source/common/api/win32/os_sys_calls_impl.cc b/source/common/api/win32/os_sys_calls_impl.cc index 69502f12cff2..1465f64d2c46 100644 --- a/source/common/api/win32/os_sys_calls_impl.cc +++ b/source/common/api/win32/os_sys_calls_impl.cc @@ -455,17 +455,9 @@ SysCallBoolResult OsSysCallsImpl::socketTcpInfo([[maybe_unused]] os_fd_t sockfd, return {false, WSAEOPNOTSUPP}; } -bool OsSysCallsImpl::supportsGetifaddrs() const { - if (alternate_getifaddrs_.has_value()) { - return true; - } - return false; -} +bool OsSysCallsImpl::supportsGetifaddrs() const { return false; } SysCallIntResult OsSysCallsImpl::getifaddrs([[maybe_unused]] InterfaceAddressVector& interfaces) { - if (alternate_getifaddrs_.has_value()) { - return alternate_getifaddrs_.value()(interfaces); - } PANIC("not implemented"); } diff --git a/source/common/config/subscription_factory_impl.cc b/source/common/config/subscription_factory_impl.cc index 818d7c655b63..337c16ebefb6 100644 --- a/source/common/config/subscription_factory_impl.cc +++ b/source/common/config/subscription_factory_impl.cc @@ -3,6 +3,7 @@ #include "envoy/config/core/v3/config_source.pb.h" #include "envoy/config/xds_resources_delegate.h" +#include "source/common/config/custom_config_validators_impl.h" #include "source/common/config/filesystem_subscription_impl.h" #include "source/common/config/grpc_mux_impl.h" #include "source/common/config/grpc_subscription_impl.h" @@ -71,18 +72,66 @@ SubscriptionPtr SubscriptionFactoryImpl::subscriptionFromConfigSource( Utility::apiConfigSourceRequestTimeout(api_config_source), restMethod(type_url), type_url, callbacks, resource_decoder, stats, Utility::configSourceInitialFetchTimeout(config), validation_visitor_); - case envoy::config::core::v3::ApiConfigSource::GRPC: - case envoy::config::core::v3::ApiConfigSource::DELTA_GRPC: { + case envoy::config::core::v3::ApiConfigSource::GRPC: { + GrpcMuxSharedPtr mux; CustomConfigValidatorsPtr custom_config_validators = std::make_unique(validation_visitor_, server_, api_config_source.config_validators()); - GrpcMuxSharedPtr mux = - getOrCreateMux(api_config_source, type_url, scope, custom_config_validators); + const std::string control_plane_id = + Utility::getGrpcControlPlane(api_config_source).value_or(""); + + if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.unified_mux")) { + mux = std::make_shared( + Utility::factoryForGrpcApiConfigSource(cm_.grpcAsyncClientManager(), api_config_source, + scope, true) + ->createUncachedRawAsyncClient(), + dispatcher_, sotwGrpcMethod(type_url), api_.randomGenerator(), scope, + Utility::parseRateLimitSettings(api_config_source), local_info_, + api_config_source.set_node_on_first_message_only(), std::move(custom_config_validators), + xds_config_tracker_, xds_resources_delegate_, control_plane_id); + } else { + mux = std::make_shared( + local_info_, + Utility::factoryForGrpcApiConfigSource(cm_.grpcAsyncClientManager(), api_config_source, + scope, true) + ->createUncachedRawAsyncClient(), + dispatcher_, sotwGrpcMethod(type_url), api_.randomGenerator(), scope, + Utility::parseRateLimitSettings(api_config_source), + api_config_source.set_node_on_first_message_only(), std::move(custom_config_validators), + xds_config_tracker_, xds_resources_delegate_, control_plane_id); + } return std::make_unique( std::move(mux), callbacks, resource_decoder, stats, type_url, dispatcher_, Utility::configSourceInitialFetchTimeout(config), /*is_aggregated*/ false, options); } + case envoy::config::core::v3::ApiConfigSource::DELTA_GRPC: { + GrpcMuxSharedPtr mux; + CustomConfigValidatorsPtr custom_config_validators = + std::make_unique(validation_visitor_, server_, + api_config_source.config_validators()); + if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.unified_mux")) { + mux = std::make_shared( + Utility::factoryForGrpcApiConfigSource(cm_.grpcAsyncClientManager(), api_config_source, + scope, true) + ->createUncachedRawAsyncClient(), + dispatcher_, deltaGrpcMethod(type_url), api_.randomGenerator(), scope, + Utility::parseRateLimitSettings(api_config_source), local_info_, + api_config_source.set_node_on_first_message_only(), std::move(custom_config_validators), + xds_config_tracker_); + } else { + mux = std::make_shared( + Config::Utility::factoryForGrpcApiConfigSource(cm_.grpcAsyncClientManager(), + api_config_source, scope, true) + ->createUncachedRawAsyncClient(), + dispatcher_, deltaGrpcMethod(type_url), api_.randomGenerator(), scope, + Utility::parseRateLimitSettings(api_config_source), local_info_, + std::move(custom_config_validators), xds_config_tracker_); + } + return std::make_unique( + std::move(mux), callbacks, resource_decoder, stats, type_url, dispatcher_, + Utility::configSourceInitialFetchTimeout(config), /*is_aggregated*/ false, options); + } } throw EnvoyException("Invalid API config source API type"); } @@ -103,6 +152,7 @@ SubscriptionPtr SubscriptionFactoryImpl::collectionSubscriptionFromUrl( Stats::Scope& scope, SubscriptionCallbacks& callbacks, OpaqueResourceDecoderSharedPtr resource_decoder) { SubscriptionStats stats = Utility::generateStats(scope); + switch (collection_locator.scheme()) { case xds::core::v3::ResourceLocator::FILE: { const std::string path = Http::Utility::localPathFromFilePath(collection_locator.id()); @@ -144,12 +194,12 @@ SubscriptionPtr SubscriptionFactoryImpl::collectionSubscriptionFromUrl( Utility::parseRateLimitSettings(api_config_source), local_info_, std::move(custom_config_validators), xds_config_tracker_), callbacks, resource_decoder, stats, dispatcher_, - Utility::configSourceInitialFetchTimeout(config), false, options); + Utility::configSourceInitialFetchTimeout(config), /*is_aggregated=*/false, options); } case envoy::config::core::v3::ApiConfigSource::AGGREGATED_DELTA_GRPC: { return std::make_unique( collection_locator, cm_.adsMux(), callbacks, resource_decoder, stats, dispatcher_, - Utility::configSourceInitialFetchTimeout(config), false, options); + Utility::configSourceInitialFetchTimeout(config), /*is_aggregated=*/true, options); } default: throw EnvoyException(fmt::format("Unknown xdstp:// transport API type in {}", @@ -178,61 +228,5 @@ SubscriptionPtr SubscriptionFactoryImpl::collectionSubscriptionFromUrl( } } -GrpcMuxSharedPtr SubscriptionFactoryImpl::getOrCreateMux( - const envoy::config::core::v3::ApiConfigSource& api_config_source, absl::string_view type_url, - Stats::Scope& scope, CustomConfigValidatorsPtr& custom_config_validators) { - GrpcMuxSharedPtr mux; - switch (api_config_source.api_type()) { - case envoy::config::core::v3::ApiConfigSource::GRPC: { - const std::string control_plane_id = - Utility::getGrpcControlPlane(api_config_source).value_or(""); - if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.unified_mux")) { - mux = std::make_shared( - Utility::factoryForGrpcApiConfigSource(cm_.grpcAsyncClientManager(), api_config_source, - scope, true) - ->createUncachedRawAsyncClient(), - dispatcher_, sotwGrpcMethod(type_url), api_.randomGenerator(), scope, - Utility::parseRateLimitSettings(api_config_source), local_info_, - api_config_source.set_node_on_first_message_only(), std::move(custom_config_validators), - xds_config_tracker_, xds_resources_delegate_, control_plane_id); - } else { - mux = std::make_shared( - local_info_, - Utility::factoryForGrpcApiConfigSource(cm_.grpcAsyncClientManager(), api_config_source, - scope, true) - ->createUncachedRawAsyncClient(), - dispatcher_, sotwGrpcMethod(type_url), api_.randomGenerator(), scope, - Utility::parseRateLimitSettings(api_config_source), - api_config_source.set_node_on_first_message_only(), std::move(custom_config_validators), - xds_config_tracker_, xds_resources_delegate_, control_plane_id); - } - break; - } - case envoy::config::core::v3::ApiConfigSource::DELTA_GRPC: { - if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.unified_mux")) { - mux = std::make_shared( - Utility::factoryForGrpcApiConfigSource(cm_.grpcAsyncClientManager(), api_config_source, - scope, true) - ->createUncachedRawAsyncClient(), - dispatcher_, deltaGrpcMethod(type_url), api_.randomGenerator(), scope, - Utility::parseRateLimitSettings(api_config_source), local_info_, - api_config_source.set_node_on_first_message_only(), std::move(custom_config_validators), - xds_config_tracker_); - } else { - mux = std::make_shared( - Config::Utility::factoryForGrpcApiConfigSource(cm_.grpcAsyncClientManager(), - api_config_source, scope, true) - ->createUncachedRawAsyncClient(), - dispatcher_, deltaGrpcMethod(type_url), api_.randomGenerator(), scope, - Utility::parseRateLimitSettings(api_config_source), local_info_, - std::move(custom_config_validators), xds_config_tracker_); - } - break; - } - default: - throw EnvoyException("Unsupported api type in api config source, cannot create GRPC mux."); - } - return mux; -} } // namespace Config } // namespace Envoy diff --git a/source/common/config/subscription_factory_impl.h b/source/common/config/subscription_factory_impl.h index eebbd5c6929b..2f37e08408e7 100644 --- a/source/common/config/subscription_factory_impl.h +++ b/source/common/config/subscription_factory_impl.h @@ -12,7 +12,6 @@ #include "envoy/upstream/cluster_manager.h" #include "source/common/common/logger.h" -#include "source/common/config/custom_config_validators_impl.h" namespace Envoy { namespace Config { @@ -39,12 +38,6 @@ class SubscriptionFactoryImpl : public SubscriptionFactory, Logger::Loggable void { - ASSERT(events == Event::FileReadyType::Read); - onInotifyEvent(); - }, - Event::FileTriggerType::Edge, Event::FileReadyType::Read)) { + : file_system_(file_system) { + inotify_fd_ = inotify_init1(IN_NONBLOCK); RELEASE_ASSERT(inotify_fd_ >= 0, "Consider increasing value of user.max_inotify_watches via sysctl"); + inotify_event_ = dispatcher.createFileEvent( + inotify_fd_, + [this](uint32_t events) -> void { + ASSERT(events == Event::FileReadyType::Read); + onInotifyEvent(); + }, + Event::FileTriggerType::Edge, Event::FileReadyType::Read); } WatcherImpl::~WatcherImpl() { close(inotify_fd_); } diff --git a/source/common/formatter/substitution_formatter.cc b/source/common/formatter/substitution_formatter.cc index 5313790f661d..9f781e31a868 100644 --- a/source/common/formatter/substitution_formatter.cc +++ b/source/common/formatter/substitution_formatter.cc @@ -487,6 +487,17 @@ SubstitutionFormatParser::getKnownFormatters() { {CommandSyntaxChecker::PARAMS_REQUIRED | CommandSyntaxChecker::LENGTH_ALLOWED, [](const std::string& key, absl::optional& max_length) { return std::make_unique(key, max_length); + }}}, + {"STREAM_INFO_REQ", + {CommandSyntaxChecker::PARAMS_REQUIRED | CommandSyntaxChecker::LENGTH_ALLOWED, + [](const std::string& format, absl::optional& max_length) { + std::string main_header, alternative_header; + + SubstitutionFormatParser::parseSubcommandHeaders(format, main_header, + alternative_header); + + return std::make_unique(main_header, alternative_header, + max_length); }}}}); } @@ -913,6 +924,15 @@ const StreamInfoFormatter::FieldExtractorLookupTbl& StreamInfoFormatter::getKnow return timing.downstreamHandshakeComplete(); }); }}}, + {"ROUNDTRIP_DURATION", + {CommandSyntaxChecker::COMMAND_ONLY, + [](const std::string&, const absl::optional&) { + return std::make_unique( + [](const StreamInfo::StreamInfo& stream_info) { + StreamInfo::TimingUtility timing(stream_info); + return timing.lastDownstreamAckReceived(); + }); + }}}, {"BYTES_RECEIVED", {CommandSyntaxChecker::COMMAND_ONLY, [](const std::string&, const absl::optional&) { @@ -2183,5 +2203,22 @@ ProtobufWkt::Value EnvironmentFormatter::formatValue(const Http::RequestHeaderMa return str_; } +StreamInfoRequestHeaderFormatter::StreamInfoRequestHeaderFormatter( + const std::string& main_header, const std::string& alternative_header, + absl::optional max_length) + : HeaderFormatter(main_header, alternative_header, max_length) {} + +absl::optional StreamInfoRequestHeaderFormatter::format( + const Http::RequestHeaderMap&, const Http::ResponseHeaderMap&, const Http::ResponseTrailerMap&, + const StreamInfo::StreamInfo& stream_info, absl::string_view) const { + return HeaderFormatter::format(*stream_info.getRequestHeaders()); +} + +ProtobufWkt::Value StreamInfoRequestHeaderFormatter::formatValue( + const Http::RequestHeaderMap&, const Http::ResponseHeaderMap&, const Http::ResponseTrailerMap&, + const StreamInfo::StreamInfo& stream_info, absl::string_view) const { + return HeaderFormatter::formatValue(*stream_info.getRequestHeaders()); +} + } // namespace Formatter } // namespace Envoy diff --git a/source/common/formatter/substitution_formatter.h b/source/common/formatter/substitution_formatter.h index fc3be5fac2c6..9f635f8ac444 100644 --- a/source/common/formatter/substitution_formatter.h +++ b/source/common/formatter/substitution_formatter.h @@ -661,5 +661,25 @@ class EnvironmentFormatter : public FormatterProvider { ProtobufWkt::Value str_; }; +/** + * FormatterProvider for request headers from StreamInfo (rather than the request_headers param). + * Purely for testing. + */ +class StreamInfoRequestHeaderFormatter : public FormatterProvider, HeaderFormatter { +public: + StreamInfoRequestHeaderFormatter(const std::string& main_header, + const std::string& alternative_header, + absl::optional max_length); + + // FormatterProvider + absl::optional format(const Http::RequestHeaderMap& request_headers, + const Http::ResponseHeaderMap&, + const Http::ResponseTrailerMap&, const StreamInfo::StreamInfo&, + absl::string_view) const override; + ProtobufWkt::Value formatValue(const Http::RequestHeaderMap&, const Http::ResponseHeaderMap&, + const Http::ResponseTrailerMap&, const StreamInfo::StreamInfo&, + absl::string_view) const override; +}; + } // namespace Formatter } // namespace Envoy diff --git a/source/common/grpc/google_grpc_creds_impl.cc b/source/common/grpc/google_grpc_creds_impl.cc index dc45e45bd78c..ae49d3257a7f 100644 --- a/source/common/grpc/google_grpc_creds_impl.cc +++ b/source/common/grpc/google_grpc_creds_impl.cc @@ -2,7 +2,6 @@ #include "envoy/config/core/v3/grpc_service.pb.h" #include "envoy/grpc/google_grpc_creds.h" -#include "envoy/registry/registry.h" #include "source/common/config/datasource.h" diff --git a/source/common/grpc/google_grpc_creds_impl.h b/source/common/grpc/google_grpc_creds_impl.h index e36083432aec..676da444edb7 100644 --- a/source/common/grpc/google_grpc_creds_impl.h +++ b/source/common/grpc/google_grpc_creds_impl.h @@ -3,6 +3,7 @@ #include "envoy/api/api.h" #include "envoy/common/platform.h" #include "envoy/config/core/v3/grpc_service.pb.h" +#include "envoy/registry/registry.h" #include "grpcpp/grpcpp.h" @@ -65,6 +66,7 @@ class CredsUtility { defaultChannelCredentials(const envoy::config::core::v3::GrpcService& grpc_service_config, Api::Api& api); }; +DECLARE_FACTORY(DefaultGoogleGrpcCredentialsFactory); } // namespace Grpc } // namespace Envoy diff --git a/source/common/http/async_client_impl.cc b/source/common/http/async_client_impl.cc index c52d96a54da9..d8f1601c775a 100644 --- a/source/common/http/async_client_impl.cc +++ b/source/common/http/async_client_impl.cc @@ -52,11 +52,7 @@ AsyncClientImpl::~AsyncClientImpl() { } } -AsyncClient::Request* AsyncClientImpl::send(RequestMessagePtr&& request, - AsyncClient::Callbacks& callbacks, - const AsyncClient::RequestOptions& options) { - AsyncRequestImpl* async_request = - new AsyncRequestImpl(std::move(request), *this, callbacks, options); +template T* AsyncClientImpl::internalStartRequest(T* async_request) { async_request->initialize(); std::unique_ptr new_request{async_request}; @@ -70,6 +66,27 @@ AsyncClient::Request* AsyncClientImpl::send(RequestMessagePtr&& request, } } +template AsyncRequestImpl* +AsyncClientImpl::internalStartRequest(AsyncRequestImpl*); +template AsyncOngoingRequestImpl* +AsyncClientImpl::internalStartRequest(AsyncOngoingRequestImpl*); + +AsyncClient::Request* AsyncClientImpl::send(RequestMessagePtr&& request, + AsyncClient::Callbacks& callbacks, + const AsyncClient::RequestOptions& options) { + AsyncRequestImpl* async_request = + new AsyncRequestImpl(std::move(request), *this, callbacks, options); + return internalStartRequest(async_request); +} + +AsyncClient::OngoingRequest* +AsyncClientImpl::startRequest(RequestHeaderMapPtr&& request_headers, Callbacks& callbacks, + const AsyncClient::RequestOptions& options) { + AsyncOngoingRequestImpl* async_request = + new AsyncOngoingRequestImpl(std::move(request_headers), *this, callbacks, options); + return internalStartRequest(async_request); +} + AsyncClient::Stream* AsyncClientImpl::start(AsyncClient::StreamCallbacks& callbacks, const AsyncClient::StreamOptions& options) { std::unique_ptr new_stream{new AsyncStreamImpl(*this, callbacks, options)}; @@ -86,13 +103,13 @@ AsyncStreamImpl::AsyncStreamImpl(AsyncClientImpl& parent, AsyncClient::StreamCal tracing_config_(Tracing::EgressConfig::get()), route_(std::make_shared(parent_, options.timeout, options.hash_policy, options.retry_policy)), + account_(options.account_), buffer_limit_(options.buffer_limit_), send_xff_(options.send_xff) { - stream_info_.dynamicMetadata().MergeFrom(options.metadata); stream_info_.setIsShadow(options.is_shadow); if (options.buffer_body_for_retry) { - buffered_body_ = std::make_unique(); + buffered_body_ = std::make_unique(account_); } router_.setDecoderFilterCallbacks(*this); @@ -252,10 +269,10 @@ void AsyncStreamImpl::resetStream(Http::StreamResetReason, absl::string_view) { cleanup(); } -AsyncRequestImpl::AsyncRequestImpl(RequestMessagePtr&& request, AsyncClientImpl& parent, - AsyncClient::Callbacks& callbacks, - const AsyncClient::RequestOptions& options) - : AsyncStreamImpl(parent, *this, options), request_(std::move(request)), callbacks_(callbacks) { +AsyncRequestSharedImpl::AsyncRequestSharedImpl(AsyncClientImpl& parent, + AsyncClient::Callbacks& callbacks, + const AsyncClient::RequestOptions& options) + : AsyncStreamImpl(parent, *this, options), callbacks_(callbacks) { if (nullptr != options.parent_span_) { const std::string child_span_name = options.child_span_name_.empty() @@ -284,7 +301,12 @@ void AsyncRequestImpl::initialize() { // TODO(mattklein123): Support request trailers. } -void AsyncRequestImpl::onComplete() { +void AsyncOngoingRequestImpl::initialize() { + child_span_->injectContext(*request_headers_, nullptr); + sendHeaders(*request_headers_, false); +} + +void AsyncRequestSharedImpl::onComplete() { callbacks_.onBeforeFinalizeUpstreamSpan(*child_span_, &response_->headers()); Tracing::HttpTracerUtility::finalizeUpstreamSpan(*child_span_, streamInfo(), @@ -293,22 +315,22 @@ void AsyncRequestImpl::onComplete() { callbacks_.onSuccess(*this, std::move(response_)); } -void AsyncRequestImpl::onHeaders(ResponseHeaderMapPtr&& headers, bool) { +void AsyncRequestSharedImpl::onHeaders(ResponseHeaderMapPtr&& headers, bool) { const uint64_t response_code = Http::Utility::getResponseStatus(*headers); streamInfo().response_code_ = response_code; response_ = std::make_unique(std::move(headers)); } -void AsyncRequestImpl::onData(Buffer::Instance& data, bool) { +void AsyncRequestSharedImpl::onData(Buffer::Instance& data, bool) { streamInfo().addBytesReceived(data.length()); response_->body().move(data); } -void AsyncRequestImpl::onTrailers(ResponseTrailerMapPtr&& trailers) { +void AsyncRequestSharedImpl::onTrailers(ResponseTrailerMapPtr&& trailers) { response_->trailers(std::move(trailers)); } -void AsyncRequestImpl::onReset() { +void AsyncRequestSharedImpl::onReset() { if (!cancelled_) { // Set "error reason" tag related to reset. The tagging for "error true" is done inside the // Tracing::HttpTracerUtility::finalizeUpstreamSpan. @@ -328,7 +350,7 @@ void AsyncRequestImpl::onReset() { } } -void AsyncRequestImpl::cancel() { +void AsyncRequestSharedImpl::cancel() { cancelled_ = true; // Add tags about the cancellation. diff --git a/source/common/http/async_client_impl.h b/source/common/http/async_client_impl.h index 7271e514a3b1..ac93e208158b 100644 --- a/source/common/http/async_client_impl.h +++ b/source/common/http/async_client_impl.h @@ -9,6 +9,7 @@ #include #include +#include "envoy/buffer/buffer.h" #include "envoy/common/random_generator.h" #include "envoy/common/scope_tracker.h" #include "envoy/config/core/v3/base.pb.h" @@ -18,6 +19,7 @@ #include "envoy/http/async_client.h" #include "envoy/http/codec.h" #include "envoy/http/context.h" +#include "envoy/http/filter.h" #include "envoy/http/header_map.h" #include "envoy/http/message.h" #include "envoy/router/context.h" @@ -52,7 +54,7 @@ constexpr uint64_t kBufferLimitForRetry = 1 << 16; } // namespace class AsyncStreamImpl; -class AsyncRequestImpl; +class AsyncRequestSharedImpl; class AsyncClientImpl final : public AsyncClient { public: @@ -67,9 +69,12 @@ class AsyncClientImpl final : public AsyncClient { Request* send(RequestMessagePtr&& request, Callbacks& callbacks, const AsyncClient::RequestOptions& options) override; Stream* start(StreamCallbacks& callbacks, const AsyncClient::StreamOptions& options) override; + OngoingRequest* startRequest(RequestHeaderMapPtr&& request_headers, Callbacks& callbacks, + const AsyncClient::RequestOptions& options) override; Event::Dispatcher& dispatcher() override { return dispatcher_; } private: + template T* internalStartRequest(T* async_request); Upstream::ClusterInfoConstSharedPtr cluster_; Router::FilterConfig config_; Event::Dispatcher& dispatcher_; @@ -77,14 +82,14 @@ class AsyncClientImpl final : public AsyncClient { Singleton::Manager& singleton_manager_; friend class AsyncStreamImpl; - friend class AsyncRequestImpl; + friend class AsyncRequestSharedImpl; }; /** * Implementation of AsyncRequest. This implementation is capable of sending HTTP requests to a * ConnectionPool asynchronously. */ -class AsyncStreamImpl : public AsyncClient::Stream, +class AsyncStreamImpl : public virtual AsyncClient::Stream, public StreamDecoderFilterCallbacks, public Event::DeferredDeletable, Logger::Loggable, @@ -93,7 +98,40 @@ class AsyncStreamImpl : public AsyncClient::Stream, public: AsyncStreamImpl(AsyncClientImpl& parent, AsyncClient::StreamCallbacks& callbacks, const AsyncClient::StreamOptions& options); - ~AsyncStreamImpl() override { router_.onDestroy(); } + ~AsyncStreamImpl() override { + router_.onDestroy(); + // UpstreamRequest::cleanUp() is guaranteed to reset the high watermark calls. + ENVOY_BUG(high_watermark_calls_ == 0, "Excess high watermark calls after async stream ended."); + if (destructor_callback_.has_value()) { + (*destructor_callback_)(); + } + } + + void setDestructorCallback(AsyncClient::StreamDestructorCallbacks callback) override { + ASSERT(!destructor_callback_); + destructor_callback_.emplace(callback); + } + + void removeDestructorCallback() override { + ASSERT(destructor_callback_); + destructor_callback_.reset(); + } + + void setWatermarkCallbacks(DecoderFilterWatermarkCallbacks& callbacks) override { + ASSERT(!watermark_callbacks_); + watermark_callbacks_.emplace(callbacks); + for (uint32_t i = 0; i < high_watermark_calls_; ++i) { + watermark_callbacks_->get().onDecoderFilterAboveWriteBufferHighWatermark(); + } + } + + void removeWatermarkCallbacks() override { + ASSERT(watermark_callbacks_); + for (uint32_t i = 0; i < high_watermark_calls_; ++i) { + watermark_callbacks_->get().onDecoderFilterBelowWriteBufferLowWatermark(); + } + watermark_callbacks_.reset(); + } // Http::AsyncClient::Stream void sendHeaders(RequestHeaderMap& headers, bool end_stream) override; @@ -108,6 +146,10 @@ class AsyncStreamImpl : public AsyncClient::Stream, StreamInfo::StreamInfoImpl& streamInfo() override { return stream_info_; } AsyncClientImpl& parent_; + // Callback to listen for stream destruction. + absl::optional destructor_callback_; + // Callback to listen for low/high/overflow watermark events. + absl::optional> watermark_callbacks_; private: struct NullHedgePolicy : public Router::HedgePolicy { @@ -360,7 +402,7 @@ class AsyncStreamImpl : public AsyncClient::Stream, Upstream::ClusterInfoConstSharedPtr clusterInfo() override { return parent_.cluster_; } uint64_t streamId() const override { return stream_id_; } // TODO(kbaichoo): Plumb account from owning request filter. - Buffer::BufferMemoryAccountSharedPtr account() const override { return nullptr; } + Buffer::BufferMemoryAccountSharedPtr account() const override { return account_; } Tracing::Span& activeSpan() override { return active_span_; } OptRef tracingConfig() const override { return makeOptRef(tracing_config_); @@ -410,15 +452,25 @@ class AsyncStreamImpl : public AsyncClient::Stream, void encodeTrailers(ResponseTrailerMapPtr&& trailers) override; ResponseTrailerMapOptRef responseTrailers() const override { return {}; } void encodeMetadata(MetadataMapPtr&&) override {} - void onDecoderFilterAboveWriteBufferHighWatermark() override { ++high_watermark_calls_; } + void onDecoderFilterAboveWriteBufferHighWatermark() override { + ++high_watermark_calls_; + if (watermark_callbacks_.has_value()) { + watermark_callbacks_->get().onDecoderFilterAboveWriteBufferHighWatermark(); + } + } void onDecoderFilterBelowWriteBufferLowWatermark() override { ASSERT(high_watermark_calls_ != 0); --high_watermark_calls_; + if (watermark_callbacks_.has_value()) { + watermark_callbacks_->get().onDecoderFilterBelowWriteBufferLowWatermark(); + } } void addDownstreamWatermarkCallbacks(DownstreamWatermarkCallbacks&) override {} void removeDownstreamWatermarkCallbacks(DownstreamWatermarkCallbacks&) override {} - void setDecoderBufferLimit(uint32_t) override {} - uint32_t decoderBufferLimit() override { return 0; } + void setDecoderBufferLimit(uint32_t) override { + IS_ENVOY_BUG("decoder buffer limits should not be overridden on async streams."); + } + uint32_t decoderBufferLimit() override { return buffer_limit_.value_or(0); } bool recreateStream(const ResponseHeaderMap*) override { return false; } const ScopeTrackedObject& scope() override { return *this; } void restoreContextOnContinue(ScopeTrackedObjectStack& tracked_object_stack) override { @@ -456,6 +508,8 @@ class AsyncStreamImpl : public AsyncClient::Stream, bool local_closed_{}; bool remote_closed_{}; Buffer::InstancePtr buffered_body_; + Buffer::BufferMemoryAccountSharedPtr account_{nullptr}; + absl::optional buffer_limit_{absl::nullopt}; bool encoded_response_headers_{}; bool is_grpc_request_{}; bool is_head_request_{false}; @@ -465,25 +519,59 @@ class AsyncStreamImpl : public AsyncClient::Stream, friend class AsyncClientImplUnitTest; }; -class AsyncRequestImpl final : public AsyncClient::Request, - AsyncStreamImpl, - AsyncClient::StreamCallbacks { +class AsyncRequestSharedImpl : public virtual AsyncClient::Request, + protected AsyncStreamImpl, + protected AsyncClient::StreamCallbacks { public: - AsyncRequestImpl(RequestMessagePtr&& request, AsyncClientImpl& parent, - AsyncClient::Callbacks& callbacks, const AsyncClient::RequestOptions& options); + void cancel() final; - // AsyncClient::Request - void cancel() override; +protected: + AsyncRequestSharedImpl(AsyncClientImpl& parent, AsyncClient::Callbacks& callbacks, + const AsyncClient::RequestOptions& options); + void onHeaders(ResponseHeaderMapPtr&& headers, bool end_stream) final; + void onData(Buffer::Instance& data, bool end_stream) final; + void onTrailers(ResponseTrailerMapPtr&& trailers) final; + void onComplete() final; + void onReset() final; + + AsyncClient::Callbacks& callbacks_; + Tracing::SpanPtr child_span_; + std::unique_ptr response_; + bool cancelled_{}; +}; + +class AsyncOngoingRequestImpl final : public AsyncClient::OngoingRequest, + public AsyncRequestSharedImpl { +public: + AsyncOngoingRequestImpl(RequestHeaderMapPtr&& request_headers, AsyncClientImpl& parent, + AsyncClient::Callbacks& callbacks, + const AsyncClient::RequestOptions& options) + : AsyncRequestSharedImpl(parent, callbacks, options), + request_headers_(std::move(request_headers)) { + ASSERT(request_headers_); + } + void captureAndSendTrailers(RequestTrailerMapPtr&& trailers) override { + request_trailers_ = std::move(trailers); + sendTrailers(*request_trailers_); + } private: void initialize(); - // AsyncClient::StreamCallbacks - void onHeaders(ResponseHeaderMapPtr&& headers, bool end_stream) override; - void onData(Buffer::Instance& data, bool end_stream) override; - void onTrailers(ResponseTrailerMapPtr&& trailers) override; - void onComplete() override; - void onReset() override; + RequestHeaderMapPtr request_headers_; + RequestTrailerMapPtr request_trailers_; + + friend class AsyncClientImpl; +}; + +class AsyncRequestImpl final : public AsyncRequestSharedImpl { +public: + AsyncRequestImpl(RequestMessagePtr&& request, AsyncClientImpl& parent, + AsyncClient::Callbacks& callbacks, const AsyncClient::RequestOptions& options) + : AsyncRequestSharedImpl(parent, callbacks, options), request_(std::move(request)) {} + +private: + void initialize(); // Http::StreamDecoderFilterCallbacks void addDecodedData(Buffer::Instance&, bool) override { @@ -494,10 +582,6 @@ class AsyncRequestImpl final : public AsyncClient::Request, void modifyDecodingBuffer(std::function) override {} RequestMessagePtr request_; - AsyncClient::Callbacks& callbacks_; - std::unique_ptr response_; - bool cancelled_{}; - Tracing::SpanPtr child_span_; friend class AsyncClientImpl; }; diff --git a/source/common/http/conn_manager_impl.cc b/source/common/http/conn_manager_impl.cc index 578a6a2222a2..065d6d412129 100644 --- a/source/common/http/conn_manager_impl.cc +++ b/source/common/http/conn_manager_impl.cc @@ -55,7 +55,7 @@ namespace Envoy { namespace Http { -bool requestWasConnect(const RequestHeaderMapPtr& headers, Protocol protocol) { +bool requestWasConnect(const RequestHeaderMapSharedPtr& headers, Protocol protocol) { if (!headers) { return false; } @@ -188,8 +188,10 @@ void ConnectionManagerImpl::checkForDeferredClose(bool skip_delay_close) { close = Network::ConnectionCloseType::FlushWrite; } if (drain_state_ == DrainState::Closing && streams_.empty() && !codec_->wantsToWrite()) { + // We are closing a draining connection with no active streams and the codec has + // nothing to write. doConnectionClose(close, absl::nullopt, - StreamInfo::ResponseCodeDetails::get().DownstreamLocalDisconnect); + StreamInfo::LocalCloseReasons::get().DeferredCloseOnDrainedConnection); } } @@ -277,8 +279,29 @@ void ConnectionManagerImpl::doDeferredStreamDestroy(ActiveStream& stream) { } stream.completeRequest(); + + // Set roundtrip time in connectionInfoSetter before OnStreamComplete + absl::optional t = read_callbacks_->connection().lastRoundTripTime(); + if (t.has_value()) { + read_callbacks_->connection().connectionInfoSetter().setRoundTripTime(t.value()); + } + stream.filter_manager_.onStreamComplete(); - stream.filter_manager_.log(); + + // For HTTP/3, skip access logging here and add deferred logging info + // to stream info for QuicStatsGatherer to use later. + if (codec_ && codec_->protocol() == Protocol::Http3 && + // There was a downstream reset, log immediately. + !stream.filter_manager_.sawDownstreamReset() && + // On recreate stream, log immediately. + stream.response_encoder_ != nullptr && + Runtime::runtimeFeatureEnabled( + "envoy.reloadable_features.quic_defer_logging_to_ack_listener")) { + stream.deferHeadersAndTrailers(); + } else { + // For HTTP/1 and HTTP/2, log here as usual. + stream.filter_manager_.log(); + } stream.filter_manager_.destroyFilters(); @@ -432,9 +455,8 @@ Network::FilterStatus ConnectionManagerImpl::onNewConnection() { Buffer::OwnedImpl dummy; createCodec(dummy); ASSERT(codec_->protocol() == Protocol::Http3); - // Stop iterating through each filters for QUIC. Currently a QUIC connection - // only supports one filter, HCM, and bypasses the onData() interface. Because - // QUICHE already handles de-multiplexing. + // Stop iterating through network filters for QUIC. Currently QUIC connections bypass the + // onData() interface because QUICHE already handles de-multiplexing. return Network::FilterStatus::StopIteration; } @@ -471,14 +493,19 @@ void ConnectionManagerImpl::onEvent(Network::ConnectionEvent event) { if (event == Network::ConnectionEvent::RemoteClose || event == Network::ConnectionEvent::LocalClose) { + + std::string details; if (event == Network::ConnectionEvent::RemoteClose) { remote_close_ = true; stats_.named_.downstream_cx_destroy_remote_.inc(); + details = StreamInfo::ResponseCodeDetails::get().DownstreamRemoteDisconnect; + } else { + absl::string_view local_close_reason = read_callbacks_->connection().localCloseReason(); + ENVOY_BUG(!local_close_reason.empty(), "Local Close Reason was not set!"); + details = fmt::format(StreamInfo::ResponseCodeDetails::get().DownstreamLocalDisconnect, + StringUtil::replaceAllEmptySpace(local_close_reason)); } - absl::string_view details = - event == Network::ConnectionEvent::RemoteClose - ? StreamInfo::ResponseCodeDetails::get().DownstreamRemoteDisconnect - : StreamInfo::ResponseCodeDetails::get().DownstreamLocalDisconnect; + // TODO(mattklein123): It is technically possible that something outside of the filter causes // a local connection close, so we still guard against that here. A better solution would be to // have some type of "pre-close" callback that we could hook for cleanup that would get called @@ -530,7 +557,7 @@ void ConnectionManagerImpl::doConnectionClose( } if (close_type.has_value()) { - read_callbacks_->connection().close(close_type.value()); + read_callbacks_->connection().close(close_type.value(), details); } } @@ -545,7 +572,8 @@ void ConnectionManagerImpl::onIdleTimeout() { if (!codec_) { // No need to delay close after flushing since an idle timeout has already fired. Attempt to // write out buffered data one last time and issue a local close if successful. - doConnectionClose(Network::ConnectionCloseType::FlushWrite, absl::nullopt, ""); + doConnectionClose(Network::ConnectionCloseType::FlushWrite, absl::nullopt, + StreamInfo::LocalCloseReasons::get().IdleTimeoutOnConnection); } else if (drain_state_ == DrainState::NotDraining) { startDrainSequence(); } @@ -966,7 +994,7 @@ void ConnectionManagerImpl::ActiveStream::maybeEndDecode(bool end_stream) { // can't route select properly without full headers), checking state required to // serve error responses (connection close, head requests, etc), and // modifications which may themselves affect route selection. -void ConnectionManagerImpl::ActiveStream::decodeHeaders(RequestHeaderMapPtr&& headers, +void ConnectionManagerImpl::ActiveStream::decodeHeaders(RequestHeaderMapSharedPtr&& headers, bool end_stream) { ENVOY_STREAM_LOG(debug, "request headers complete (end_stream={}):\n{}", *this, end_stream, *headers); diff --git a/source/common/http/conn_manager_impl.h b/source/common/http/conn_manager_impl.h index 1ca41e30da58..0fc013eed51c 100644 --- a/source/common/http/conn_manager_impl.h +++ b/source/common/http/conn_manager_impl.h @@ -192,7 +192,7 @@ class ConnectionManagerImpl : Logger::Loggable, void maybeEndDecode(bool end_stream); // Http::RequestDecoder - void decodeHeaders(RequestHeaderMapPtr&& headers, bool end_stream) override; + void decodeHeaders(RequestHeaderMapSharedPtr&& headers, bool end_stream) override; void decodeTrailers(RequestTrailerMapPtr&& trailers) override; StreamInfo::StreamInfo& streamInfo() override { return filter_manager_.streamInfo(); } void sendLocalReply(Code code, absl::string_view body, @@ -201,6 +201,20 @@ class ConnectionManagerImpl : Logger::Loggable, absl::string_view details) override { return filter_manager_.sendLocalReply(code, body, modify_headers, grpc_status, details); } + std::list accessLogHandlers() override { + return filter_manager_.accessLogHandlers(); + } + // Hand off headers/trailers and stream info to the codec's response encoder, for logging later + // (i.e. possibly after this stream has been destroyed). + // + // TODO(paulsohn): Investigate whether we can move the headers/trailers and stream info required + // for logging instead of copying them (as is currently done in the HTTP/3 implementation) or + // using a shared pointer. See + // https://github.com/envoyproxy/envoy/pull/23648#discussion_r1066095564 for more details. + void deferHeadersAndTrailers() { + response_encoder_->setDeferredLoggingHeadersAndTrailers(request_headers_, response_headers_, + response_trailers_, streamInfo()); + } // ScopeTrackedObject void dumpState(std::ostream& os, int indent_level = 0) const override { @@ -371,12 +385,12 @@ class ConnectionManagerImpl : Logger::Loggable, // both locations, then refer to the FM when doing stream logs. const uint64_t stream_id_; - RequestHeaderMapPtr request_headers_; + RequestHeaderMapSharedPtr request_headers_; RequestTrailerMapPtr request_trailers_; ResponseHeaderMapPtr informational_headers_; - ResponseHeaderMapPtr response_headers_; - ResponseTrailerMapPtr response_trailers_; + ResponseHeaderMapSharedPtr response_headers_; + ResponseTrailerMapSharedPtr response_trailers_; // Note: The FM must outlive the above headers, as they are possibly accessed during filter // destruction. diff --git a/source/common/http/filter_manager.h b/source/common/http/filter_manager.h index 0ad753bc15d1..01051ed2cb00 100644 --- a/source/common/http/filter_manager.h +++ b/source/common/http/filter_manager.h @@ -583,6 +583,9 @@ class OverridableRemoteConnectionInfoSetterStreamInfo : public StreamInfo::Strea absl::string_view ja3Hash() const override { return StreamInfoImpl::downstreamAddressProvider().ja3Hash(); } + const absl::optional& roundTripTime() const override { + return StreamInfoImpl::downstreamAddressProvider().roundTripTime(); + } private: Network::Address::InstanceConstSharedPtr overridden_downstream_remote_address_; @@ -670,6 +673,8 @@ class FilterManager : public ScopeTrackedObject, } } + std::list accessLogHandlers() { return access_log_handlers_; } + void onStreamComplete() { for (auto& filter : decoder_filters_) { filter->handle_->onStreamComplete(); @@ -818,6 +823,7 @@ class FilterManager : public ScopeTrackedObject, void contextOnContinue(ScopeTrackedObjectStack& tracked_object_stack); void onDownstreamReset() { state_.saw_downstream_reset_ = true; } + bool sawDownstreamReset() { return state_.saw_downstream_reset_; } protected: struct State { @@ -1119,6 +1125,7 @@ class DownstreamFilterManager : public FilterManager { bool is_head_request, const absl::optional grpc_status); +private: OverridableRemoteConnectionInfoSetterStreamInfo stream_info_; const LocalReply::LocalReply& local_reply_; const bool avoid_reentrant_filter_invocation_during_local_reply_; diff --git a/source/common/http/http1/BUILD b/source/common/http/http1/BUILD index e4a7ba69db02..fb64e09af261 100644 --- a/source/common/http/http1/BUILD +++ b/source/common/http/http1/BUILD @@ -126,6 +126,7 @@ envoy_cc_library( deps = [ ":parser_interface", "//source/common/common:assert_lib", + "//source/common/common:regex_lib", "//source/common/http:headers_lib", "@com_github_google_quiche//:quiche_balsa_balsa_enums_lib", "@com_github_google_quiche//:quiche_balsa_balsa_frame_lib", diff --git a/source/common/http/http1/balsa_parser.cc b/source/common/http/http1/balsa_parser.cc index 2d5e3a314282..9d962c8419d3 100644 --- a/source/common/http/http1/balsa_parser.cc +++ b/source/common/http/http1/balsa_parser.cc @@ -5,6 +5,7 @@ #include #include "source/common/common/assert.h" +#include "source/common/common/regex.h" #include "source/common/http/headers.h" #include "absl/strings/match.h" @@ -103,6 +104,21 @@ bool isUrlValid(absl::string_view url, bool is_connect) { std::all_of(path_query.begin(), path_query.end(), is_valid_path_query_char); } +bool isVersionValid(absl::string_view version_input) { + // HTTP-version is defined at + // https://www.rfc-editor.org/rfc/rfc9112.html#section-2.3. HTTP/0.9 requests + // have no http-version, so empty `version_input` is also accepted. + + static const auto regex = [] { + envoy::type::matcher::v3::RegexMatcher matcher; + *matcher.mutable_google_re2() = envoy::type::matcher::v3::RegexMatcher::GoogleRE2(); + matcher.set_regex("|HTTP/[0-9]\\.[0-9]"); + return Regex::Utility::parseRegex(matcher); + }(); + + return regex->match(version_input); +} + } // anonymous namespace BalsaParser::BalsaParser(MessageType type, ParserCallbacks* connection, size_t max_header_length, @@ -242,7 +258,7 @@ void BalsaParser::ProcessTrailers(const BalsaHeaders& /*trailer*/) {} void BalsaParser::OnRequestFirstLineInput(absl::string_view /*line_input*/, absl::string_view method_input, absl::string_view request_uri, - absl::string_view /*version_input*/) { + absl::string_view version_input) { if (status_ == ParserStatus::Error) { return; } @@ -254,20 +270,29 @@ void BalsaParser::OnRequestFirstLineInput(absl::string_view /*line_input*/, const bool is_connect = method_input == Headers::get().MethodValues.Connect; if (!isUrlValid(request_uri, is_connect)) { status_ = ParserStatus::Error; - // Error message matching that of http-parser. error_message_ = "HPE_INVALID_URL"; return; } + if (!isVersionValid(version_input)) { + status_ = ParserStatus::Error; + error_message_ = "HPE_INVALID_VERSION"; + return; + } status_ = convertResult(connection_->onUrl(request_uri.data(), request_uri.size())); } void BalsaParser::OnResponseFirstLineInput(absl::string_view /*line_input*/, - absl::string_view /*version_input*/, + absl::string_view version_input, absl::string_view /*status_input*/, absl::string_view reason_input) { if (status_ == ParserStatus::Error) { return; } + if (!isVersionValid(version_input)) { + status_ = ParserStatus::Error; + error_message_ = "HPE_INVALID_VERSION"; + return; + } status_ = convertResult(connection_->onStatus(reason_input.data(), reason_input.size())); } @@ -307,7 +332,6 @@ void BalsaParser::MessageDone() { void BalsaParser::HandleError(BalsaFrameEnums::ErrorCode error_code) { status_ = ParserStatus::Error; - // Specific error messages to match http-parser behavior. switch (error_code) { case BalsaFrameEnums::UNKNOWN_TRANSFER_ENCODING: error_message_ = "unsupported transfer encoding"; diff --git a/source/common/http/http1/balsa_parser.h b/source/common/http/http1/balsa_parser.h index 4b1214089a16..4697a8627a93 100644 --- a/source/common/http/http1/balsa_parser.h +++ b/source/common/http/http1/balsa_parser.h @@ -71,6 +71,7 @@ class BalsaParser : public Parser, public quiche::BalsaVisitorInterface { bool first_byte_processed_ = false; bool headers_done_ = false; ParserStatus status_ = ParserStatus::Ok; + // An error message, often seemingly arbitrary to match http-parser behavior. absl::string_view error_message_; }; diff --git a/source/common/http/http1/codec_impl.cc b/source/common/http/http1/codec_impl.cc index ea6083972e7c..626c9364b38b 100644 --- a/source/common/http/http1/codec_impl.cc +++ b/source/common/http/http1/codec_impl.cc @@ -301,7 +301,9 @@ void StreamEncoderImpl::endEncode() { if (connect_request_ || is_tcp_tunneling_ || (is_response_to_connect_request_ && Runtime::runtimeFeatureEnabled("envoy.reloadable_features.no_delay_close_for_upgrades"))) { - connection_.connection().close(Network::ConnectionCloseType::FlushWrite); + connection_.connection().close( + Network::ConnectionCloseType::FlushWrite, + StreamInfo::LocalCloseReasons::get().CloseForConnectRequestOrTcpTunneling); } } diff --git a/source/common/http/http1/codec_impl.h b/source/common/http/http1/codec_impl.h index 1e1bd36dc131..5d3ea37e8c27 100644 --- a/source/common/http/http1/codec_impl.h +++ b/source/common/http/http1/codec_impl.h @@ -155,6 +155,10 @@ class ResponseEncoderImpl : public StreamEncoderImpl, public ResponseEncoder { bool streamErrorOnInvalidHttpMessage() const override { return stream_error_on_invalid_http_message_; } + void setDeferredLoggingHeadersAndTrailers(Http::RequestHeaderMapConstSharedPtr, + Http::ResponseHeaderMapConstSharedPtr, + Http::ResponseTrailerMapConstSharedPtr, + StreamInfo::StreamInfo&) override {} // For H/1, ResponseEncoder doesn't hold a pointer to RequestDecoder. // TODO(paulsohn): Enable H/1 codec to get a pointer to the new diff --git a/source/common/http/http2/BUILD b/source/common/http/http2/BUILD index dc7e1001cf83..7a8a816c96c2 100644 --- a/source/common/http/http2/BUILD +++ b/source/common/http/http2/BUILD @@ -93,7 +93,6 @@ envoy_cc_library( srcs = ["metadata_encoder.cc"], hdrs = ["metadata_encoder.h"], external_deps = [ - "nghttp2", "quiche_http2_adapter", ], deps = [ diff --git a/source/common/http/http2/codec_impl.cc b/source/common/http/http2/codec_impl.cc index 64bc8a7bb554..dc2526b8c944 100644 --- a/source/common/http/http2/codec_impl.cc +++ b/source/common/http/http2/codec_impl.cc @@ -560,7 +560,7 @@ void ConnectionImpl::ClientStreamImpl::decodeTrailers() { } void ConnectionImpl::ServerStreamImpl::decodeHeaders() { - auto& headers = absl::get(headers_or_trailers_); + auto& headers = absl::get(headers_or_trailers_); if (Http::Utility::isH2UpgradeRequest(*headers)) { Http::Utility::transformUpgradeRequestFromH2toH1(*headers); } @@ -754,7 +754,7 @@ void ConnectionImpl::StreamImpl::resetStream(StreamResetReason reason) { // resources. if (useDeferredReset() && local_end_stream_ && !local_end_stream_sent_ && reason != StreamResetReason::OverloadManager) { - ASSERT(parent_.getStream(stream_id_) != nullptr); + ASSERT(parent_.getStreamUnchecked(stream_id_) != nullptr); parent_.pending_deferred_reset_streams_.emplace(stream_id_, this); deferred_reset_ = reason; ENVOY_CONN_LOG(trace, "deferred reset stream", parent_.connection_); @@ -780,13 +780,6 @@ void ConnectionImpl::StreamImpl::resetStreamWorker(StreamResetReason reason) { static_cast(reasonToReset(reason))); } -MetadataEncoder& ConnectionImpl::StreamImpl::getMetadataEncoderOld() { - if (metadata_encoder_old_ == nullptr) { - metadata_encoder_old_ = std::make_unique(); - } - return *metadata_encoder_old_; -} - NewMetadataEncoder& ConnectionImpl::StreamImpl::getMetadataEncoder() { if (metadata_encoder_ == nullptr) { metadata_encoder_ = std::make_unique(); @@ -900,7 +893,8 @@ void ConnectionImpl::onKeepaliveResponseTimeout() { ENVOY_CONN_LOG_EVENT(debug, "h2_ping_timeout", "Closing connection due to keepalive timeout", connection_); stats_.keepalive_timeout_.inc(); - connection_.close(Network::ConnectionCloseType::NoFlush); + connection_.close(Network::ConnectionCloseType::NoFlush, + StreamInfo::LocalCloseReasons::get().Http2PingTimeout); } bool ConnectionImpl::slowContainsStreamId(int32_t stream_id) const { @@ -962,16 +956,21 @@ const ConnectionImpl::StreamImpl* ConnectionImpl::getStream(int32_t stream_id) c return const_cast(this)->getStream(stream_id); } -ConnectionImpl::StreamImpl* ConnectionImpl::getStreamUnchecked(int32_t stream_id) { - return static_cast(adapter_->GetStreamUserData(stream_id)); -} - ConnectionImpl::StreamImpl* ConnectionImpl::getStream(int32_t stream_id) { StreamImpl* stream = getStreamUnchecked(stream_id); SLOW_ASSERT(stream != nullptr || !slowContainsStreamId(stream_id)); return stream; } +const ConnectionImpl::StreamImpl* ConnectionImpl::getStreamUnchecked(int32_t stream_id) const { + // Delegate to the non-const version. + return const_cast(this)->getStreamUnchecked(stream_id); +} + +ConnectionImpl::StreamImpl* ConnectionImpl::getStreamUnchecked(int32_t stream_id) { + return static_cast(adapter_->GetStreamUserData(stream_id)); +} + int ConnectionImpl::onData(int32_t stream_id, const uint8_t* data, size_t len) { ASSERT(connection_.state() == Network::Connection::State::Open); StreamImpl* stream = getStream(stream_id); @@ -1091,7 +1090,7 @@ Status ConnectionImpl::onFrameReceived(const nghttp2_frame* frame) { onSettings(frame->settings); } - StreamImpl* stream = getStream(frame->hd.stream_id); + StreamImpl* stream = getStreamUnchecked(frame->hd.stream_id); if (!stream) { return okStatus(); } @@ -1164,7 +1163,7 @@ int ConnectionImpl::onFrameSend(int32_t stream_id, size_t length, uint8_t type, // an outgoing frame of this type, we will return an error code so that we can abort execution. ENVOY_CONN_LOG(trace, "sent frame type={}, stream_id={}, length={}", connection_, static_cast(type), stream_id, length); - StreamImpl* stream = getStream(stream_id); + StreamImpl* stream = getStreamUnchecked(stream_id); if (stream != nullptr) { if (type != METADATA_FRAME_TYPE) { stream->bytes_meter_->addWireBytesSent(length + H2_FRAME_HEADER_SIZE); @@ -1377,8 +1376,12 @@ Status ConnectionImpl::onStreamClose(int32_t stream_id, uint32_t error_code) { int ConnectionImpl::onMetadataReceived(int32_t stream_id, const uint8_t* data, size_t len) { ENVOY_CONN_LOG(trace, "recv {} bytes METADATA", connection_, len); - StreamImpl* stream = getStream(stream_id); + StreamImpl* stream = getStreamUnchecked(stream_id); if (!stream || stream->remote_end_stream_) { + if (!stream) { + ENVOY_CONN_LOG(debug, "no stream for stream_id {} while receiving METADATA", connection_, + stream_id); + } return 0; } @@ -1390,8 +1393,12 @@ int ConnectionImpl::onMetadataFrameComplete(int32_t stream_id, bool end_metadata ENVOY_CONN_LOG(trace, "recv METADATA frame on stream {}, end_metadata: {}", connection_, stream_id, end_metadata); - StreamImpl* stream = getStream(stream_id); + StreamImpl* stream = getStreamUnchecked(stream_id); if (!stream || stream->remote_end_stream_) { + if (!stream) { + ENVOY_CONN_LOG(debug, "no stream for stream_id {} while completing METADATA", connection_, + stream_id); + } return 0; } @@ -1401,7 +1408,7 @@ int ConnectionImpl::onMetadataFrameComplete(int32_t stream_id, bool end_metadata int ConnectionImpl::saveHeader(const nghttp2_frame* frame, HeaderString&& name, HeaderString&& value) { - StreamImpl* stream = getStream(frame->hd.stream_id); + StreamImpl* stream = getStreamUnchecked(frame->hd.stream_id); if (!stream) { // We have seen 1 or 2 crashes where we get a headers callback but there is no associated // stream data. I honestly am not sure how this can happen. However, from reading the nghttp2 @@ -1572,7 +1579,8 @@ void ConnectionImpl::scheduleProtocolConstraintViolationCallback() { void ConnectionImpl::onProtocolConstraintViolation() { // Flooded outbound queue implies that peer is not reading and it does not // make sense to try to flush pending bytes. - connection_.close(Envoy::Network::ConnectionCloseType::NoFlush); + connection_.close(Envoy::Network::ConnectionCloseType::NoFlush, + StreamInfo::LocalCloseReasons::get().Http2ConnectionProtocolViolation); } void ConnectionImpl::onUnderlyingConnectionBelowWriteBufferLowWatermark() { @@ -1835,7 +1843,7 @@ void ClientConnectionImpl::dumpStreams(std::ostream& os, int indent_level) const << current_stream_id_.value() << ":\n"; const ClientStreamImpl* client_stream = - static_cast(getStream(current_stream_id_.value())); + static_cast(getStreamUnchecked(current_stream_id_.value())); if (client_stream) { client_stream->response_decoder_.dumpState(os, indent_level + 1); } else { @@ -1877,8 +1885,8 @@ void ConnectionImpl::ServerStreamImpl::dumpState(std::ostream& os, int indent_le StreamImpl::dumpState(os, indent_level); // Dump header map - if (absl::holds_alternative(headers_or_trailers_)) { - DUMP_DETAILS(absl::get(headers_or_trailers_)); + if (absl::holds_alternative(headers_or_trailers_)) { + DUMP_DETAILS(absl::get(headers_or_trailers_)); } else { DUMP_DETAILS(absl::get(headers_or_trailers_)); } @@ -1967,10 +1975,12 @@ Status ClientConnectionImpl::trackInboundFrames(int32_t stream_id, size_t length ENVOY_CONN_LOG(trace, "error reading frame: {} received in this HTTP/2 session.", connection_, result.message()); if (isInboundFramesWithEmptyPayloadError(result)) { - ConnectionImpl::StreamImpl* stream = getStream(stream_id); + ConnectionImpl::StreamImpl* stream = getStreamUnchecked(stream_id); if (stream) { stream->setDetails(Http2ResponseCodeDetails::get().inbound_empty_frame_flood); } + // Above if is defensive, because the stream has just been created and therefore always + // exists. } } return result; @@ -2060,10 +2070,12 @@ Status ServerConnectionImpl::trackInboundFrames(int32_t stream_id, size_t length ENVOY_CONN_LOG(trace, "error reading frame: {} received in this HTTP/2 session.", connection_, result.message()); if (isInboundFramesWithEmptyPayloadError(result)) { - ConnectionImpl::StreamImpl* stream = getStream(stream_id); + ConnectionImpl::StreamImpl* stream = getStreamUnchecked(stream_id); if (stream) { stream->setDetails(Http2ResponseCodeDetails::get().inbound_empty_frame_flood); } + // Above if is defensive, because the stream has just been created and therefore always + // exists. } } return result; diff --git a/source/common/http/http2/codec_impl.h b/source/common/http/http2/codec_impl.h index fa20d235fb75..db62ebfe2cfc 100644 --- a/source/common/http/http2/codec_impl.h +++ b/source/common/http/http2/codec_impl.h @@ -290,7 +290,6 @@ class ConnectionImpl : public virtual Connection, void decodeData(); // Get MetadataEncoder for this stream. - MetadataEncoder& getMetadataEncoderOld(); NewMetadataEncoder& getMetadataEncoder(); // Get MetadataDecoder for this stream. MetadataDecoder& getMetadataDecoder(); @@ -328,7 +327,6 @@ class ConnectionImpl : public virtual Connection, HeaderMapPtr pending_trailers_to_encode_; std::unique_ptr metadata_decoder_; std::unique_ptr metadata_encoder_; - std::unique_ptr metadata_encoder_old_; absl::optional deferred_reset_; // Holds the reset reason for this stream. Useful if we have buffered data // to determine whether we should continue processing that data. @@ -493,8 +491,8 @@ class ConnectionImpl : public virtual Connection, void decodeHeaders() override; void decodeTrailers() override; HeaderMap& headers() override { - if (absl::holds_alternative(headers_or_trailers_)) { - return *absl::get(headers_or_trailers_); + if (absl::holds_alternative(headers_or_trailers_)) { + return *absl::get(headers_or_trailers_); } else { return *absl::get(headers_or_trailers_); } @@ -514,11 +512,15 @@ class ConnectionImpl : public virtual Connection, encodeTrailersBase(trailers); } void setRequestDecoder(Http::RequestDecoder& decoder) override { request_decoder_ = &decoder; } + void setDeferredLoggingHeadersAndTrailers(Http::RequestHeaderMapConstSharedPtr, + Http::ResponseHeaderMapConstSharedPtr, + Http::ResponseTrailerMapConstSharedPtr, + StreamInfo::StreamInfo&) override {} // ScopeTrackedObject void dumpState(std::ostream& os, int indent_level) const override; - absl::variant headers_or_trailers_; + absl::variant headers_or_trailers_; bool streamErrorOnInvalidHttpMessage() const override { return parent_.stream_error_on_invalid_http_messaging_; @@ -535,9 +537,10 @@ class ConnectionImpl : public virtual Connection, // edge cases (such as for METADATA frames) where nghttp2 will issue a callback for a stream_id // that is not associated with an existing stream. const StreamImpl* getStream(int32_t stream_id) const; + StreamImpl* getStream(int32_t stream_id); // Same as getStream, but without the ASSERT. + const StreamImpl* getStreamUnchecked(int32_t stream_id) const; StreamImpl* getStreamUnchecked(int32_t stream_id); - StreamImpl* getStream(int32_t stream_id); int saveHeader(const nghttp2_frame* frame, HeaderString&& name, HeaderString&& value); /** diff --git a/source/common/http/http2/metadata_encoder.cc b/source/common/http/http2/metadata_encoder.cc index 18ba87006d24..c08cf7cf1ea6 100644 --- a/source/common/http/http2/metadata_encoder.cc +++ b/source/common/http/http2/metadata_encoder.cc @@ -3,128 +3,11 @@ #include "source/common/common/assert.h" #include "absl/container/fixed_array.h" -#include "nghttp2/nghttp2.h" namespace Envoy { namespace Http { namespace Http2 { -MetadataEncoder::MetadataEncoder() { - nghttp2_hd_deflater* deflater; - const int rv = nghttp2_hd_deflate_new(&deflater, header_table_size_); - ASSERT(rv == 0); - deflater_ = Deflater(deflater); -} - -bool MetadataEncoder::createPayloadMetadataMap(const MetadataMap& metadata_map) { - ASSERT(!metadata_map.empty()); - - const uint64_t payload_size_before = payload_.length(); - const bool success = createHeaderBlockUsingNghttp2(metadata_map); - const uint64_t payload_size_after = payload_.length(); - - if (!success || payload_size_after == payload_size_before) { - ENVOY_LOG(error, "Failed to create payload."); - return false; - } - - payload_size_queue_.push_back(payload_size_after - payload_size_before); - return true; -} - -bool MetadataEncoder::createPayload(const MetadataMapVector& metadata_map_vector) { - ASSERT(payload_.length() == 0); - ASSERT(payload_size_queue_.empty()); - - for (const auto& metadata_map : metadata_map_vector) { - if (!createPayloadMetadataMap(*metadata_map)) { - return false; - } - } - return true; -} - -bool MetadataEncoder::createHeaderBlockUsingNghttp2(const MetadataMap& metadata_map) { - // Constructs input for nghttp2 deflater (encoder). Encoding method used is - // "HPACK Literal Header Field Never Indexed". - const size_t nvlen = metadata_map.size(); - absl::FixedArray nva(nvlen); - size_t i = 0; - for (const auto& header : metadata_map) { - nva[i++] = {const_cast(reinterpret_cast(header.first.data())), - const_cast(reinterpret_cast(header.second.data())), - header.first.size(), header.second.size(), NGHTTP2_NV_FLAG_NO_INDEX}; - } - - // Estimates the upper bound of output payload. - const size_t buflen = nghttp2_hd_deflate_bound(deflater_.get(), nva.begin(), nvlen); - if (buflen + payload_.length() > max_payload_size_bound_) { - ENVOY_LOG(error, "Payload size {} exceeds the max bound.", buflen); - return false; - } - auto reservation = payload_.reserveSingleSlice(buflen); - ASSERT(reservation.slice().len_ >= buflen); - - // Creates payload using nghttp2. - uint8_t* buf = reinterpret_cast(reservation.slice().mem_); - const ssize_t result = nghttp2_hd_deflate_hd(deflater_.get(), buf, buflen, nva.begin(), nvlen); - RELEASE_ASSERT(result > 0, - fmt::format("Failed to deflate metadata payload, with result {}.", result)); - - reservation.commit(result); - - return true; -} - -bool MetadataEncoder::hasNextFrame() { return payload_.length() > 0; } - -ssize_t MetadataEncoder::packNextFramePayload(uint8_t* buf, const size_t len) { - // If this RELEASE_ASSERT fires, nghttp2 has requested that we pack more - // METADATA frames than we have payload to pack. This could mean that the - // HTTP/2 codec has submitted too many METADATA frames to nghttp2, or it could - // mean that nghttp2 has called its pack_extension_callback more than once per - // METADATA frame the codec submitted. - RELEASE_ASSERT(!payload_size_queue_.empty(), - "No payload remaining to pack into a METADATA frame."); - const uint64_t current_payload_size = - std::min(METADATA_MAX_PAYLOAD_SIZE, payload_size_queue_.front()); - - // nghttp2 guarantees len is at least 16KiB. If the check fails, please verify - // NGHTTP2_MAX_PAYLOADLEN is consistent with METADATA_MAX_PAYLOAD_SIZE. - RELEASE_ASSERT(len >= current_payload_size, - fmt::format("METADATA payload buffer is too small ({}, expected at least {}).", - len, METADATA_MAX_PAYLOAD_SIZE)); - - // Copies payload to the destination memory. - payload_.copyOut(0, current_payload_size, buf); - - // Updates the remaining size of the current metadata_map. If no data left, removes the size entry - // from the queue. - payload_size_queue_.front() -= current_payload_size; - if (payload_size_queue_.front() == 0) { - payload_size_queue_.pop_front(); - } - - // Releases the payload that has been copied out. - payload_.drain(current_payload_size); - - return current_payload_size; -} - -std::vector MetadataEncoder::payloadFrameFlagBytes() { - std::vector flags; - flags.reserve(payload_size_queue_.size()); - for (uint64_t payload_size : payload_size_queue_) { - uint64_t frame_count = payload_size / METADATA_MAX_PAYLOAD_SIZE + - ((payload_size % METADATA_MAX_PAYLOAD_SIZE) == 0 ? 0 : 1); - for (uint64_t i = 0; i < frame_count - 1; ++i) { - flags.push_back(0); - } - flags.push_back(END_METADATA_FLAG); - } - return flags; -} - class BufferMetadataSource : public http2::adapter::MetadataSource { public: explicit BufferMetadataSource(Buffer::OwnedImpl payload) diff --git a/source/common/http/http2/metadata_encoder.h b/source/common/http/http2/metadata_encoder.h index 1f1295677448..21bfa8f76a01 100644 --- a/source/common/http/http2/metadata_encoder.h +++ b/source/common/http/http2/metadata_encoder.h @@ -11,7 +11,6 @@ #include "source/common/common/c_smart_ptr.h" #include "source/common/common/logger.h" -#include "nghttp2/nghttp2.h" #include "quiche/http2/adapter/data_source.h" #include "quiche/spdy/core/hpack/hpack_encoder.h" @@ -19,80 +18,6 @@ namespace Envoy { namespace Http { namespace Http2 { -/** - * A class that creates and sends METADATA payload. The METADATA payload is a group of string key - * value pairs encoded in HTTP/2 header blocks. METADATA frames are constructed in two steps: first, - * the stream submits the frames' headers to nghttp2, and later, when nghttp2 prepares to send the - * frames, it calls back into this class in order to construct their payloads. - */ -class MetadataEncoder : Logger::Loggable { -public: - MetadataEncoder(); - - /** - * Creates wire format HTTP/2 header block from a vector of metadata maps. - * @param metadata_map_vector supplies the metadata map vector to encode. - * @return whether encoding is successful. - */ - bool createPayload(const MetadataMapVector& metadata_map_vector); - - /** - * @return true if there is payload left to be packed. - */ - bool hasNextFrame(); - - /** - * Creates the metadata frame payload for the next metadata frame. - * @param buf is the pointer to the destination memory where the payload should be copied to. len - * is the largest length the memory can hold. - * @return the size of frame payload, or -1 for failure. - */ - ssize_t packNextFramePayload(uint8_t* buf, const size_t len); - - /** - * Returns a vector denoting the sequence of METADATA frames that this encoder expects to pack, - * and the flags to be set in each frame. This counts only frames that the encoder has not already - * packed; to get the full sequence of frames corresponding to the metadata map vector, call this - * before submitting any frames to nghttp2. - * @return A vector indicating the header byte in each METADATA frame, in sequence. - */ - std::vector payloadFrameFlagBytes(); - -private: - /** - * Creates wire format HTTP/2 header block from metadata_map. - * @param metadata_map supplies METADATA to encode. - * @return whether encoding is successful. - */ - bool createPayloadMetadataMap(const MetadataMap& metadata_map); - - /** - * Creates wired format header blocks using nghttp2. - * @param metadata_map supplies METADATA to encode. - * @return true if the creation succeeds. - */ - bool createHeaderBlockUsingNghttp2(const MetadataMap& metadata_map); - - // The METADATA payload to be sent. - Buffer::OwnedImpl payload_; - - // Max payload size bound. - const uint64_t max_payload_size_bound_ = 1024 * 1024; - - // Default HPACK table size. - const size_t header_table_size_ = 4096; - - // TODO(soya3129): share deflater among all encoders in the same connection. The benefit is less - // memory, and the caveat is encoding error on one stream can impact other streams. - using Deflater = CSmartPtr; - Deflater deflater_; - - // Stores the remaining payload size of each metadata_map to be packed. The payload size is needed - // so that we know where to delineate between different metadata_maps in the payload_ buffer. The - // payload size gets updated when the payload is packed into metadata frames. - std::deque payload_size_queue_; -}; - /** * A class that creates and sends METADATA payloads. A METADATA payload is a * group of string key value pairs encoded in HTTP/2 header blocks. METADATA diff --git a/source/common/network/connection_impl.cc b/source/common/network/connection_impl.cc index 2aa145f5222a..7bfef4a45e46 100644 --- a/source/common/network/connection_impl.cc +++ b/source/common/network/connection_impl.cc @@ -861,7 +861,8 @@ bool ServerConnectionImpl::initializeReadFilters() { void ServerConnectionImpl::onTransportSocketConnectTimeout() { stream_info_.setConnectionTerminationDetails(kTransportSocketConnectTimeoutTerminationDetails); - closeConnectionImmediately(); + closeConnectionImmediatelyWithDetails( + StreamInfo::LocalCloseReasons::get().TransportSocketTimeout); transport_socket_timeout_stat_->inc(); setFailureReason("connect timeout"); } diff --git a/source/common/network/connection_impl.h b/source/common/network/connection_impl.h index e958b07f62fb..2e7d81db2e03 100644 --- a/source/common/network/connection_impl.h +++ b/source/common/network/connection_impl.h @@ -67,6 +67,12 @@ class ConnectionImpl : public ConnectionImplBase, public TransportSocketCallback void enableHalfClose(bool enabled) override; bool isHalfCloseEnabled() override { return enable_half_close_; } void close(ConnectionCloseType type) final; + void close(ConnectionCloseType type, absl::string_view details) override { + if (!details.empty()) { + setLocalCloseReason(details); + } + close(type); + } std::string nextProtocol() const override { return transport_socket_->protocol(); } void noDelay(bool enable) override; void readDisable(bool disable) override; diff --git a/source/common/network/connection_impl_base.cc b/source/common/network/connection_impl_base.cc index 46bf98bcdf1f..7c3647c2d44b 100644 --- a/source/common/network/connection_impl_base.cc +++ b/source/common/network/connection_impl_base.cc @@ -70,7 +70,8 @@ void ConnectionImplBase::onDelayedCloseTimeout() { if (connection_stats_ != nullptr && connection_stats_->delayed_close_timeouts_ != nullptr) { connection_stats_->delayed_close_timeouts_->inc(); } - closeConnectionImmediately(); + closeConnectionImmediatelyWithDetails( + StreamInfo::LocalCloseReasons::get().TriggeredDelayedCloseTimeout); } } // namespace Network diff --git a/source/common/network/connection_impl_base.h b/source/common/network/connection_impl_base.h index 05a7eddee9f6..4d01684ee34e 100644 --- a/source/common/network/connection_impl_base.h +++ b/source/common/network/connection_impl_base.h @@ -38,6 +38,16 @@ class ConnectionImplBase : public FilterManagerConnection, virtual void closeConnectionImmediately() PURE; + void closeConnectionImmediatelyWithDetails(absl::string_view local_close_details) { + setLocalCloseReason(local_close_details); + closeConnectionImmediately(); + } + + absl::string_view localCloseReason() const override { return local_close_reason_; } + void setLocalCloseReason(absl::string_view local_close_reason) { + local_close_reason_ = std::string(local_close_reason); + } + // States associated with delayed closing of the connection (i.e., when the underlying socket is // not immediately close()d as a result of a ConnectionImpl::close()). enum class DelayedCloseState { @@ -55,6 +65,8 @@ class ConnectionImplBase : public FilterManagerConnection, Event::TimerPtr delayed_close_timer_; std::chrono::milliseconds delayed_close_timeout_{0}; + // Should be set with setLocalCloseReason. + std::string local_close_reason_; Event::Dispatcher& dispatcher_; const uint64_t id_; std::list callbacks_; diff --git a/source/common/network/multi_connection_base_impl.cc b/source/common/network/multi_connection_base_impl.cc index bebda20b03b6..8dcb854cdb2c 100644 --- a/source/common/network/multi_connection_base_impl.cc +++ b/source/common/network/multi_connection_base_impl.cc @@ -261,6 +261,11 @@ absl::string_view MultiConnectionBaseImpl::transportFailureReason() const { return connections_[0]->transportFailureReason(); } +absl::string_view MultiConnectionBaseImpl::localCloseReason() const { + // Note, this might change before connect finishes. + return connections_[0]->localCloseReason(); +} + bool MultiConnectionBaseImpl::startSecureTransport() { if (!connect_finished_) { per_connection_state_.start_secure_transport_ = true; @@ -311,9 +316,9 @@ void MultiConnectionBaseImpl::removeConnectionCallbacks(ConnectionCallbacks& cb) IS_ENVOY_BUG("Failed to remove connection callbacks"); } -void MultiConnectionBaseImpl::close(ConnectionCloseType type) { +void MultiConnectionBaseImpl::close(ConnectionCloseType type, absl::string_view details) { if (connect_finished_) { - connections_[0]->close(type); + connections_[0]->close(type, details); return; } @@ -325,7 +330,7 @@ void MultiConnectionBaseImpl::close(ConnectionCloseType type) { if (i != 0) { // Wait to close the final connection until the post-connection callbacks // have been added. - connections_[i]->close(ConnectionCloseType::NoFlush); + connections_[i]->close(ConnectionCloseType::NoFlush, details); } } connections_.resize(1); @@ -336,7 +341,7 @@ void MultiConnectionBaseImpl::close(ConnectionCloseType type) { connections_[0]->addConnectionCallbacks(*cb); } } - connections_[0]->close(type); + connections_[0]->close(type, details); } Event::Dispatcher& MultiConnectionBaseImpl::dispatcher() { diff --git a/source/common/network/multi_connection_base_impl.h b/source/common/network/multi_connection_base_impl.h index 9734df18a6f9..2cd85c5bf82a 100644 --- a/source/common/network/multi_connection_base_impl.h +++ b/source/common/network/multi_connection_base_impl.h @@ -120,11 +120,13 @@ class MultiConnectionBaseImpl : public ClientConnection, StreamInfo::StreamInfo& streamInfo() override; const StreamInfo::StreamInfo& streamInfo() const override; absl::string_view transportFailureReason() const override; + absl::string_view localCloseReason() const override; // Methods implemented largely by this class itself. uint64_t id() const override; Event::Dispatcher& dispatcher() override; - void close(ConnectionCloseType type) override; + void close(ConnectionCloseType type) override { close(type, ""); } + void close(ConnectionCloseType type, absl::string_view details) override; bool readEnabled() const override; bool aboveHighWatermark() const override; void hashKey(std::vector& hash_key) const override; diff --git a/source/common/network/socket_impl.h b/source/common/network/socket_impl.h index 6339142cd1ab..09964abfc517 100644 --- a/source/common/network/socket_impl.h +++ b/source/common/network/socket_impl.h @@ -67,6 +67,12 @@ class ConnectionInfoSetterImpl : public ConnectionInfoSetter { } absl::string_view ja3Hash() const override { return ja3_hash_; } void setJA3Hash(const absl::string_view ja3_hash) override { ja3_hash_ = std::string(ja3_hash); } + const absl::optional& roundTripTime() const override { + return round_trip_time_; + } + void setRoundTripTime(std::chrono::milliseconds round_trip_time) override { + round_trip_time_ = round_trip_time; + } private: Address::InstanceConstSharedPtr local_address_; @@ -79,6 +85,7 @@ class ConnectionInfoSetterImpl : public ConnectionInfoSetter { absl::optional interface_name_; Ssl::ConnectionInfoConstSharedPtr ssl_info_; std::string ja3_hash_; + absl::optional round_trip_time_; }; class SocketImpl : public virtual Socket { diff --git a/source/common/quic/BUILD b/source/common/quic/BUILD index b53a6b885ddb..30e56c067d85 100644 --- a/source/common/quic/BUILD +++ b/source/common/quic/BUILD @@ -277,6 +277,7 @@ envoy_cc_library( ":envoy_quic_utils_lib", ":quic_filter_manager_connection_lib", ":quic_stat_names_lib", + ":quic_stats_gatherer", "//source/common/buffer:buffer_lib", "//source/common/common:assert_lib", "//source/common/http:header_map_lib", @@ -590,3 +591,15 @@ envoy_cc_library( "@com_github_google_quiche//:quic_load_balancer_encoder_lib", ], ) + +envoy_cc_library( + name = "quic_stats_gatherer", + srcs = ["quic_stats_gatherer.cc"], + hdrs = ["quic_stats_gatherer.h"], + tags = ["nofips"], + deps = [ + "//envoy/access_log:access_log_interface", + "//source/common/http:header_map_lib", + "@com_github_google_quiche//:quic_core_ack_listener_interface_lib", + ], +) diff --git a/source/common/quic/envoy_quic_server_session.cc b/source/common/quic/envoy_quic_server_session.cc index 306c70dcd03d..b1a11b1da5a6 100644 --- a/source/common/quic/envoy_quic_server_session.cc +++ b/source/common/quic/envoy_quic_server_session.cc @@ -95,6 +95,9 @@ void EnvoyQuicServerSession::setUpRequestDecoder(EnvoyQuicServerStream& stream) void EnvoyQuicServerSession::OnConnectionClosed(const quic::QuicConnectionCloseFrame& frame, quic::ConnectionCloseSource source) { quic::QuicServerSessionBase::OnConnectionClosed(frame, source); + if (source == quic::ConnectionCloseSource::FROM_SELF) { + setLocalCloseReason(frame.error_details); + } onConnectionCloseEvent(frame, source, version()); if (position_.has_value()) { // Remove this connection from the map. diff --git a/source/common/quic/envoy_quic_server_stream.cc b/source/common/quic/envoy_quic_server_stream.cc index 8f64e1ac6c70..efaba2f90fc1 100644 --- a/source/common/quic/envoy_quic_server_stream.cc +++ b/source/common/quic/envoy_quic_server_stream.cc @@ -11,6 +11,7 @@ #include "source/common/http/header_utility.h" #include "source/common/quic/envoy_quic_server_session.h" #include "source/common/quic/envoy_quic_utils.h" +#include "source/common/quic/quic_stats_gatherer.h" #include "quiche/quic/core/http/quic_header_list.h" #include "quiche/quic/core/quic_session.h" @@ -36,6 +37,9 @@ EnvoyQuicServerStream::EnvoyQuicServerStream( headers_with_underscores_action_(headers_with_underscores_action) { ASSERT(static_cast(GetReceiveWindow().value()) > 8 * 1024, "Send buffer limit should be larger than 8KB."); + + stats_gatherer_ = new QuicStatsGatherer(&connection()->dispatcher().timeSource()); + set_ack_listener(stats_gatherer_); } void EnvoyQuicServerStream::encode1xxHeaders(const Http::ResponseHeaderMap& headers) { @@ -55,6 +59,7 @@ void EnvoyQuicServerStream::encodeHeaders(const Http::ResponseHeaderMap& headers { IncrementalBytesSentTracker tracker(*this, *mutableBytesMeter(), true); size_t bytes_sent = WriteHeaders(envoyHeadersToHttp2HeaderBlock(headers), end_stream, nullptr); + stats_gatherer_->addBytesSent(bytes_sent, end_stream); ENVOY_BUG(bytes_sent != 0, "Failed to encode headers."); } @@ -93,6 +98,7 @@ void EnvoyQuicServerStream::encodeData(Buffer::Instance& data, bool end_stream) { IncrementalBytesSentTracker tracker(*this, *mutableBytesMeter(), false); result = WriteBodySlices(span, end_stream); + stats_gatherer_->addBytesSent(result.bytes_consumed, end_stream); } // QUIC stream must take all. if (result.bytes_consumed == 0 && has_data) { @@ -122,6 +128,7 @@ void EnvoyQuicServerStream::encodeTrailers(const Http::ResponseTrailerMap& trail IncrementalBytesSentTracker tracker(*this, *mutableBytesMeter(), true); size_t bytes_sent = WriteTrailers(envoyHeadersToHttp2HeaderBlock(trailers), nullptr); ENVOY_BUG(bytes_sent != 0, "Failed to encode trailers."); + stats_gatherer_->addBytesSent(bytes_sent, true); } onLocalEndStream(); } @@ -381,6 +388,10 @@ void EnvoyQuicServerStream::OnClose() { return; } clearWatermarkBuffer(); + if (!stats_gatherer_->loggingDone()) { + stats_gatherer_->maybeDoDeferredLog(/* record_ack_timing */ false); + } + stats_gatherer_ = nullptr; } void EnvoyQuicServerStream::clearWatermarkBuffer() { diff --git a/source/common/quic/envoy_quic_server_stream.h b/source/common/quic/envoy_quic_server_stream.h index 3a2ae67c316d..399f6262f1b5 100644 --- a/source/common/quic/envoy_quic_server_stream.h +++ b/source/common/quic/envoy_quic_server_stream.h @@ -1,7 +1,9 @@ #pragma once #include "source/common/quic/envoy_quic_stream.h" +#include "source/common/quic/quic_stats_gatherer.h" +#include "quiche/common/platform/api/quiche_reference_counted.h" #include "quiche/quic/core/http/quic_spdy_server_stream_base.h" namespace Envoy { @@ -18,7 +20,11 @@ class EnvoyQuicServerStream : public quic::QuicSpdyServerStreamBase, envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction headers_with_underscores_action); - void setRequestDecoder(Http::RequestDecoder& decoder) override { request_decoder_ = &decoder; } + void setRequestDecoder(Http::RequestDecoder& decoder) override { + request_decoder_ = &decoder; + stats_gatherer_->setAccessLogHandlers(request_decoder_->accessLogHandlers()); + } + QuicStatsGatherer* statsGatherer() { return stats_gatherer_.get(); } // Http::StreamEncoder void encode1xxHeaders(const Http::ResponseHeaderMap& headers) override; @@ -33,6 +39,23 @@ class EnvoyQuicServerStream : public quic::QuicSpdyServerStreamBase, return http3_options_.override_stream_error_on_invalid_http_message().value(); } + // Accept headers/trailers and stream info from HCM for deferred logging. We pass on the + // header/trailer shared pointers, but copy the non-shared stream info to avoid lifetime issues if + // the stream is destroyed before logging is complete. + void + setDeferredLoggingHeadersAndTrailers(Http::RequestHeaderMapConstSharedPtr request_header_map, + Http::ResponseHeaderMapConstSharedPtr response_header_map, + Http::ResponseTrailerMapConstSharedPtr response_trailer_map, + StreamInfo::StreamInfo& stream_info) override { + std::unique_ptr new_stream_info = + std::make_unique( + filterManagerConnection()->dispatcher().timeSource(), + filterManagerConnection()->connectionInfoProviderSharedPtr()); + new_stream_info->setFrom(stream_info, request_header_map.get()); + stats_gatherer_->setDeferredLoggingHeadersAndTrailers( + request_header_map, response_header_map, response_trailer_map, std::move(new_stream_info)); + }; + // Http::Stream void resetStream(Http::StreamResetReason reason) override; @@ -85,6 +108,8 @@ class EnvoyQuicServerStream : public quic::QuicSpdyServerStreamBase, Http::RequestDecoder* request_decoder_{nullptr}; envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction headers_with_underscores_action_; + + quiche::QuicheReferenceCountedPointer stats_gatherer_; }; } // namespace Quic diff --git a/source/common/quic/quic_filter_manager_connection_impl.h b/source/common/quic/quic_filter_manager_connection_impl.h index b07636f7ac42..c5749e7a85b2 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.h +++ b/source/common/quic/quic_filter_manager_connection_impl.h @@ -53,6 +53,12 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, void enableHalfClose(bool enabled) override; bool isHalfCloseEnabled() override; void close(Network::ConnectionCloseType type) override; + void close(Network::ConnectionCloseType type, absl::string_view details) override { + if (!details.empty()) { + local_close_reason_ = std::string(details); + } + close(type); + } Event::Dispatcher& dispatcher() override { return dispatcher_; } std::string nextProtocol() const override { return EMPTY_STRING; } // No-op. TCP_NODELAY doesn't apply to UDP. diff --git a/source/common/quic/quic_stats_gatherer.cc b/source/common/quic/quic_stats_gatherer.cc new file mode 100644 index 000000000000..be4b83ba70b3 --- /dev/null +++ b/source/common/quic/quic_stats_gatherer.cc @@ -0,0 +1,33 @@ +#include "source/common/quic/quic_stats_gatherer.h" + +#include + +namespace Envoy { +namespace Quic { + +void QuicStatsGatherer::OnPacketAcked(int acked_bytes, + quic::QuicTime::Delta /* delta_largest_observed */) { + bytes_outstanding_ -= acked_bytes; + if (bytes_outstanding_ == 0 && fin_sent_ && !logging_done_) { + maybeDoDeferredLog(); + } +} + +void QuicStatsGatherer::maybeDoDeferredLog(bool record_ack_timing) { + logging_done_ = true; + if (stream_info_ == nullptr) { + return; + } + if (time_source_ != nullptr && record_ack_timing) { + stream_info_->downstreamTiming().onLastDownstreamAckReceived(*time_source_); + } + const Http::RequestHeaderMap* request_headers = request_header_map_.get(); + const Http::ResponseHeaderMap* response_headers = response_header_map_.get(); + const Http::ResponseTrailerMap* response_trailers = response_trailer_map_.get(); + for (const AccessLog::InstanceSharedPtr& log_handler : access_log_handlers_) { + log_handler->log(request_headers, response_headers, response_trailers, *stream_info_); + } +} + +} // namespace Quic +} // namespace Envoy diff --git a/source/common/quic/quic_stats_gatherer.h b/source/common/quic/quic_stats_gatherer.h new file mode 100644 index 000000000000..9708ab07f5cc --- /dev/null +++ b/source/common/quic/quic_stats_gatherer.h @@ -0,0 +1,64 @@ +#pragma once + +#include + +#include "envoy/access_log/access_log.h" +#include "envoy/http/codec.h" +#include "envoy/http/header_map.h" +#include "envoy/stream_info/stream_info.h" + +#include "quiche/quic/core/quic_ack_listener_interface.h" + +namespace Envoy { +namespace Quic { + +// Ack listener that stores access logging information and performs +// logging after the final ack. +class QuicStatsGatherer : public quic::QuicAckListenerInterface { +public: + explicit QuicStatsGatherer(Envoy::TimeSource* time_source) : time_source_(time_source) {} + + // QuicAckListenerInterface + void OnPacketAcked(int acked_bytes, quic::QuicTime::Delta delta_largest_observed) override; + void OnPacketRetransmitted(int /* retransmitted_bytes */) override {} + + // Add bytes sent for this stream, for internal tracking of bytes acked. + void addBytesSent(uint64_t bytes_sent, bool end_stream) { + bytes_outstanding_ += bytes_sent; + fin_sent_ = end_stream; + } + // Log this stream using available stream info and access loggers. + void maybeDoDeferredLog(bool record_ack_timing = true); + // Set list of pointers to access loggers. + void setAccessLogHandlers(std::list handlers) { + access_log_handlers_ = handlers; + } + // Set headers, trailers, and stream info used for deferred logging. + void + setDeferredLoggingHeadersAndTrailers(Http::RequestHeaderMapConstSharedPtr request_header_map, + Http::ResponseHeaderMapConstSharedPtr response_header_map, + Http::ResponseTrailerMapConstSharedPtr response_trailer_map, + std::unique_ptr stream_info) { + request_header_map_ = request_header_map; + response_header_map_ = response_header_map; + response_trailer_map_ = response_trailer_map; + stream_info_ = std::move(stream_info); + } + bool loggingDone() { return logging_done_; } + uint64_t bytesOutstanding() { return bytes_outstanding_; } + +private: + uint64_t bytes_outstanding_ = 0; + bool fin_sent_ = false; + std::list access_log_handlers_{}; + Http::RequestHeaderMapConstSharedPtr request_header_map_; + Http::ResponseHeaderMapConstSharedPtr response_header_map_; + Http::ResponseTrailerMapConstSharedPtr response_trailer_map_; + // nullptr indicates that deferred logging should be skipped. + std::unique_ptr stream_info_; + Envoy::TimeSource* time_source_ = nullptr; + bool logging_done_ = false; +}; + +} // namespace Quic +} // namespace Envoy diff --git a/source/common/router/router.cc b/source/common/router/router.cc index b749de821a46..5d6114f442e8 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -1,5 +1,6 @@ #include "source/common/router/router.h" +#include #include #include #include @@ -282,6 +283,13 @@ Filter::~Filter() { // Upstream resources should already have been cleaned. ASSERT(upstream_requests_.empty()); ASSERT(!retry_state_); + + // Unregister from shadow stream notifications and cancel active streams. + for (auto* shadow_stream : shadow_streams_) { + shadow_stream->removeDestructorCallback(); + shadow_stream->removeWatermarkCallbacks(); + shadow_stream->cancel(); + } } const FilterUtility::StrictHeaderChecker::HeaderCheckResult @@ -698,6 +706,49 @@ Http::FilterHeadersStatus Filter::decodeHeaders(Http::RequestHeaderMap& headers, *this, std::move(generic_conn_pool), can_send_early_data, can_use_http3); LinkedList::moveIntoList(std::move(upstream_request), upstream_requests_); upstream_requests_.front()->acceptHeadersFromRouter(end_stream); + if (streaming_shadows_) { + // start the shadow streams. + for (const auto& shadow_policy_wrapper : active_shadow_policies_) { + const auto& shadow_policy = shadow_policy_wrapper.get(); + const absl::optional shadow_cluster_name = + getShadowCluster(shadow_policy, *downstream_headers_); + if (!shadow_cluster_name.has_value()) { + continue; + } + auto shadow_headers = Http::createHeaderMap(*shadow_headers_); + auto options = + Http::AsyncClient::RequestOptions() + .setTimeout(timeout_.global_timeout_) + .setParentSpan(callbacks_->activeSpan()) + .setChildSpanName("mirror") + .setSampled(shadow_policy.traceSampled()) + .setIsShadow(true) + .setBufferAccount(callbacks_->account()) + // A buffer limit of 1 is set in the case that retry_shadow_buffer_limit_ == 0, + // because a buffer limit of zero on async clients is interpreted as no buffer limit. + .setBufferLimit(1 > retry_shadow_buffer_limit_ ? 1 : retry_shadow_buffer_limit_); + if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.closer_shadow_behavior")) { + options.setFilterConfig(config_); + } + if (end_stream) { + // This is a header-only request, and can be dispatched immediately to the shadow + // without waiting. + Http::RequestMessagePtr request(new Http::RequestMessageImpl( + Http::createHeaderMap(*shadow_headers_))); + config_.shadowWriter().shadow(std::string(shadow_cluster_name.value()), std::move(request), + options); + } else { + Http::AsyncClient::OngoingRequest* shadow_stream = config_.shadowWriter().streamingShadow( + std::string(shadow_cluster_name.value()), std::move(shadow_headers), options); + if (shadow_stream != nullptr) { + shadow_streams_.insert(shadow_stream); + shadow_stream->setDestructorCallback( + [this, shadow_stream]() { shadow_streams_.erase(shadow_stream); }); + shadow_stream->setWatermarkCallbacks(*callbacks_); + } + } + } + } if (end_stream) { onRequestComplete(); } @@ -710,10 +761,10 @@ Filter::createConnPool(Upstream::ThreadLocalCluster& thread_local_cluster) { GenericConnPoolFactory* factory = nullptr; if (cluster_->upstreamConfig().has_value()) { factory = Envoy::Config::Utility::getFactory( - cluster_->upstreamConfig().value()); + cluster_->upstreamConfig().ref()); ENVOY_BUG(factory != nullptr, fmt::format("invalid factory type '{}', failing over to default upstream", - cluster_->upstreamConfig().value().DebugString())); + cluster_->upstreamConfig().ref().DebugString())); } if (!factory) { factory = &config_.router_context_.genericConnPoolFactory(); @@ -750,7 +801,8 @@ Http::FilterDataStatus Filter::decodeData(Buffer::Instance& data, bool end_strea // a backoff timer. ASSERT(upstream_requests_.size() <= 1); - bool buffering = (retry_state_ && retry_state_->enabled()) || !active_shadow_policies_.empty() || + bool buffering = (retry_state_ && retry_state_->enabled()) || + (!active_shadow_policies_.empty() && !streaming_shadows_) || (route_entry_ && route_entry_->internalRedirectPolicy().enabled()); if (buffering && getLength(callbacks_->decodingBuffer()) + data.length() > retry_shadow_buffer_limit_) { @@ -781,19 +833,29 @@ Http::FilterDataStatus Filter::decodeData(Buffer::Instance& data, bool end_strea // already. ASSERT(buffering || !upstream_requests_.empty()); + for (auto* shadow_stream : shadow_streams_) { + if (end_stream) { + shadow_stream->removeDestructorCallback(); + shadow_stream->removeWatermarkCallbacks(); + } + Buffer::OwnedImpl copy(data); + shadow_stream->sendData(copy, end_stream); + } + if (end_stream) { + shadow_streams_.clear(); + } if (buffering) { - // If we are going to buffer for retries or shadowing, we need to make a copy before encoding - // since it's all moves from here on. if (!upstream_requests_.empty()) { Buffer::OwnedImpl copy(data); upstream_requests_.front()->acceptDataFromRouter(copy, end_stream); } - // If we are potentially going to retry or shadow this request we need to buffer. + // If we are potentially going to retry or buffer shadow this request we need to buffer. // This will not cause the connection manager to 413 because before we hit the // buffer limit we give up on retries and buffering. We must buffer using addDecodedData() // so that all buffered data is available by the time we do request complete processing and - // potentially shadow. + // potentially shadow. Additionally, we can't do a copy here because there's a check down + // this stack for whether `data` is the same buffer as already buffered data. callbacks_->addDecodedData(data, true); } else { upstream_requests_.front()->acceptDataFromRouter(data, end_stream); @@ -823,6 +885,14 @@ Http::FilterTrailersStatus Filter::decodeTrailers(Http::RequestTrailerMap& trail if (!upstream_requests_.empty()) { upstream_requests_.front()->acceptTrailersFromRouter(trailers); } + for (auto* shadow_stream : shadow_streams_) { + shadow_stream->removeDestructorCallback(); + shadow_stream->removeWatermarkCallbacks(); + shadow_stream->captureAndSendTrailers( + Http::createHeaderMap(*shadow_trailers_)); + } + shadow_streams_.clear(); + onRequestComplete(); return Http::FilterTrailersStatus::StopIteration; } @@ -881,11 +951,11 @@ void Filter::maybeDoShadowing() { for (const auto& shadow_policy_wrapper : active_shadow_policies_) { const auto& shadow_policy = shadow_policy_wrapper.get(); - const absl::optional cluster_name = + const absl::optional shadow_cluster_name = getShadowCluster(shadow_policy, *downstream_headers_); // The cluster name got from headers is empty. - if (!cluster_name.has_value()) { + if (!shadow_cluster_name.has_value()) { continue; } @@ -907,7 +977,8 @@ void Filter::maybeDoShadowing() { if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.closer_shadow_behavior")) { options.setFilterConfig(config_); } - config_.shadowWriter().shadow(std::string(cluster_name.value()), std::move(request), options); + config_.shadowWriter().shadow(std::string(shadow_cluster_name.value()), std::move(request), + options); } } @@ -922,7 +993,9 @@ void Filter::onRequestComplete() { if (!upstream_requests_.empty()) { // Even if we got an immediate reset, we could still shadow, but that is a riskier change and // seems unnecessary right now. - maybeDoShadowing(); + if (!streaming_shadows_) { + maybeDoShadowing(); + } if (timeout_.global_timeout_.count() > 0) { response_timeout_ = dispatcher.createTimer([this]() -> void { onResponseTimeout(); }); diff --git a/source/common/router/router.h b/source/common/router/router.h index 6a1ba3fe71c5..5d153d670909 100644 --- a/source/common/router/router.h +++ b/source/common/router/router.h @@ -366,7 +366,8 @@ class Filter : Logger::Loggable, Filter(FilterConfig& config, FilterStats& stats) : config_(config), stats_(stats), downstream_1xx_headers_encoded_(false), downstream_response_started_(false), downstream_end_stream_(false), is_retry_(false), - request_buffer_overflowed_(false) {} + request_buffer_overflowed_(false), streaming_shadows_(Runtime::runtimeFeatureEnabled( + "envoy.reloadable_features.streaming_shadow")) {} ~Filter() override; @@ -660,6 +661,9 @@ class Filter : Logger::Loggable, Network::TransportSocketOptionsConstSharedPtr transport_socket_options_; Network::Socket::OptionsSharedPtr upstream_options_; + // Set of ongoing shadow streams which have not yet received end stream. + absl::flat_hash_set shadow_streams_; + const bool streaming_shadows_; }; class ProdFilter : public Filter { diff --git a/source/common/router/shadow_writer_impl.cc b/source/common/router/shadow_writer_impl.cc index aef2ce9ea44a..1e49da9b4cbe 100644 --- a/source/common/router/shadow_writer_impl.cc +++ b/source/common/router/shadow_writer_impl.cc @@ -11,31 +11,57 @@ namespace Envoy { namespace Router { +namespace { + +std::string shadowAppendedHost(absl::string_view host) { + ASSERT(!host.empty()); + // Switch authority to add a shadow postfix. This allows upstream logging to + // make more sense. + auto parts = StringUtil::splitToken(host, ":"); + ASSERT(!parts.empty() && parts.size() <= 2); + return parts.size() == 2 ? absl::StrJoin(parts, "-shadow:") : absl::StrCat(host, "-shadow"); +} + +} // namespace + void ShadowWriterImpl::shadow(const std::string& cluster, Http::RequestMessagePtr&& request, const Http::AsyncClient::RequestOptions& options) { + const auto thread_local_cluster = + getClusterAndPreprocessHeadersAndOptions(cluster, request->headers(), options); + if (thread_local_cluster == nullptr) { + return; + } + // This is basically fire and forget. We don't handle cancelling. + thread_local_cluster->httpAsyncClient().send(std::move(request), *this, options); +} + +Http::AsyncClient::OngoingRequest* +ShadowWriterImpl::streamingShadow(const std::string& cluster, Http::RequestHeaderMapPtr&& headers, + const Http::AsyncClient::RequestOptions& options) { + const auto thread_local_cluster = + getClusterAndPreprocessHeadersAndOptions(cluster, *headers, options); + if (thread_local_cluster == nullptr) { + return nullptr; + } + return thread_local_cluster->httpAsyncClient().startRequest(std::move(headers), *this, options); +} + +Upstream::ThreadLocalCluster* ShadowWriterImpl::getClusterAndPreprocessHeadersAndOptions( + absl::string_view cluster, Http::RequestHeaderMap& headers, + const Http::AsyncClient::RequestOptions& options) { // It's possible that the cluster specified in the route configuration no longer exists due // to a CDS removal. Check that it still exists before shadowing. // TODO(mattklein123): Optimally we would have a stat but for now just fix the crashing issue. const auto thread_local_cluster = cm_.getThreadLocalCluster(cluster); if (thread_local_cluster == nullptr) { ENVOY_LOG(debug, "shadow cluster '{}' does not exist", cluster); - return; + return nullptr; } - ASSERT(!request->headers().getHostValue().empty()); - // Switch authority to add a shadow postfix. This allows upstream logging to make more sense. - auto parts = StringUtil::splitToken(request->headers().getHostValue(), ":"); - ASSERT(!parts.empty() && parts.size() <= 2); - request->headers().setHost(parts.size() == 2 - ? absl::StrJoin(parts, "-shadow:") - : absl::StrCat(request->headers().getHostValue(), "-shadow")); - const auto& shadow_options = options.is_shadow ? options : [options] { - Http::AsyncClient::RequestOptions actual_options(options); - actual_options.setIsShadow(true); - return actual_options; - }(); - // This is basically fire and forget. We don't handle cancelling. - thread_local_cluster->httpAsyncClient().send(std::move(request), *this, shadow_options); + headers.setHost(shadowAppendedHost(headers.getHostValue())); + + const_cast(options).setIsShadow(true); + return thread_local_cluster; } } // namespace Router diff --git a/source/common/router/shadow_writer_impl.h b/source/common/router/shadow_writer_impl.h index c65748e3325e..477bd889a069 100644 --- a/source/common/router/shadow_writer_impl.h +++ b/source/common/router/shadow_writer_impl.h @@ -23,6 +23,10 @@ class ShadowWriterImpl : Logger::Loggable, void shadow(const std::string& cluster, Http::RequestMessagePtr&& request, const Http::AsyncClient::RequestOptions& options) override; + Http::AsyncClient::OngoingRequest* + streamingShadow(const std::string& cluster, Http::RequestHeaderMapPtr&& headers, + const Http::AsyncClient::RequestOptions& options) override; + // Http::AsyncClient::Callbacks void onSuccess(const Http::AsyncClient::Request&, Http::ResponseMessagePtr&&) override {} void onFailure(const Http::AsyncClient::Request&, Http::AsyncClient::FailureReason) override {} @@ -30,6 +34,10 @@ class ShadowWriterImpl : Logger::Loggable, const Http::ResponseHeaderMap*) override {} private: + Upstream::ThreadLocalCluster* + getClusterAndPreprocessHeadersAndOptions(absl::string_view cluster, + Http::RequestHeaderMap& headers, + const Http::AsyncClient::RequestOptions& options); Upstream::ClusterManager& cm_; }; diff --git a/source/common/router/upstream_request.cc b/source/common/router/upstream_request.cc index 08f3edb91f5b..dec3e25ffc8f 100644 --- a/source/common/router/upstream_request.cc +++ b/source/common/router/upstream_request.cc @@ -108,6 +108,7 @@ UpstreamRequest::UpstreamRequest(RouterFilterInterface& parent, } } stream_info_.setUpstreamInfo(std::make_shared()); + stream_info_.route_ = parent.callbacks()->route(); parent_.callbacks()->streamInfo().setUpstreamInfo(stream_info_.upstreamInfo()); stream_info_.healthCheck(parent_.callbacks()->streamInfo().healthCheck()); diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index b55870e15be6..53e965584c08 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -34,7 +34,6 @@ RUNTIME_GUARD(envoy_reloadable_features_allow_upstream_filters); RUNTIME_GUARD(envoy_reloadable_features_append_query_parameters_path_rewriter); RUNTIME_GUARD(envoy_reloadable_features_cares_accept_nodata); RUNTIME_GUARD(envoy_reloadable_features_closer_shadow_behavior); -RUNTIME_GUARD(envoy_reloadable_features_combine_sds_requests); RUNTIME_GUARD(envoy_reloadable_features_conn_pool_delete_when_idle); RUNTIME_GUARD(envoy_reloadable_features_conn_pool_new_stream_with_early_data_and_http3); RUNTIME_GUARD(envoy_reloadable_features_correct_remote_address); @@ -52,14 +51,13 @@ RUNTIME_GUARD(envoy_reloadable_features_http_reject_path_with_fragment); RUNTIME_GUARD(envoy_reloadable_features_http_response_half_close); RUNTIME_GUARD(envoy_reloadable_features_http_skip_adding_content_length_to_upgrade); RUNTIME_GUARD(envoy_reloadable_features_http_strip_fragment_from_path_unsafe_if_disabled); -RUNTIME_GUARD(envoy_reloadable_features_local_ratelimit_match_all_descriptors); RUNTIME_GUARD(envoy_reloadable_features_lua_respond_with_send_local_reply); -RUNTIME_GUARD(envoy_reloadable_features_multiplex_eds); RUNTIME_GUARD(envoy_reloadable_features_no_extension_lookup_by_name); RUNTIME_GUARD(envoy_reloadable_features_no_full_scan_certs_on_sni_mismatch); RUNTIME_GUARD(envoy_reloadable_features_oauth_header_passthrough_fix); RUNTIME_GUARD(envoy_reloadable_features_original_dst_rely_on_idle_timeout); RUNTIME_GUARD(envoy_reloadable_features_postpone_h3_client_connect_to_next_loop); +RUNTIME_GUARD(envoy_reloadable_features_quic_defer_logging_to_ack_listener); RUNTIME_GUARD(envoy_reloadable_features_quic_defer_send_in_response_to_packet); RUNTIME_GUARD(envoy_reloadable_features_reject_require_client_certificate_with_quic); RUNTIME_GUARD(envoy_reloadable_features_skip_dns_lookup_for_proxied_requests); @@ -80,6 +78,8 @@ RUNTIME_GUARD(envoy_restart_features_use_apple_api_for_dns_lookups); // Begin false flags. These should come with a TODO to flip true. // Sentinel and test flag. FALSE_RUNTIME_GUARD(envoy_reloadable_features_test_feature_false); +// TODO(paul-r-gall) Make this enabled by default after additional soak time. +FALSE_RUNTIME_GUARD(envoy_reloadable_features_streaming_shadow); // TODO(adisuissa) reset to true to enable unified mux by default FALSE_RUNTIME_GUARD(envoy_reloadable_features_unified_mux); // TODO(kbaichoo): Make this enabled by default when fairness and chunking diff --git a/source/common/stream_info/BUILD b/source/common/stream_info/BUILD index 1dc253ab6c5f..417a23230b10 100644 --- a/source/common/stream_info/BUILD +++ b/source/common/stream_info/BUILD @@ -13,6 +13,7 @@ envoy_cc_library( hdrs = ["stream_info_impl.h"], deps = [ ":filter_state_lib", + ":stream_id_provider_lib", "//envoy/http:request_id_extension_interface", "//envoy/stream_info:stream_info_interface", "//source/common/common:assert_lib", diff --git a/source/common/stream_info/stream_info_impl.h b/source/common/stream_info/stream_info_impl.h index 9cf98fa38012..b84c575444e4 100644 --- a/source/common/stream_info/stream_info_impl.h +++ b/source/common/stream_info/stream_info_impl.h @@ -17,6 +17,7 @@ #include "source/common/common/utility.h" #include "source/common/network/socket_impl.h" #include "source/common/stream_info/filter_state_impl.h" +#include "source/common/stream_info/stream_id_provider_impl.h" #include "absl/strings/str_replace.h" @@ -336,6 +337,41 @@ struct StreamInfoImpl : public StreamInfo { start_time_monotonic_ = info.startTimeMonotonic(); } + // This function is used to copy over every field exposed in the StreamInfo interface, with a + // couple of exceptions noted below. Note that setFromForRecreateStream is reused here. + // * request_headers_ is a raw pointer; to avoid pointer lifetime issues, a request header pointer + // is required to be passed in here. + // * downstream_connection_info_provider_ is always set in the ctor. + void setFrom(StreamInfo& info, const Http::RequestHeaderMap* request_headers) { + setFromForRecreateStream(info); + route_name_ = info.getRouteName(); + virtual_cluster_name_ = info.virtualClusterName(); + response_code_ = info.responseCode(); + response_code_details_ = info.responseCodeDetails(); + connection_termination_details_ = info.connectionTerminationDetails(); + upstream_info_ = info.upstreamInfo(); + if (info.requestComplete().has_value()) { + // derive final time from other info's complete duration and start time. + final_time_ = info.startTimeMonotonic() + info.requestComplete().value(); + } + response_flags_ = info.responseFlags(); + health_check_request_ = info.healthCheck(); + route_ = info.route(); + metadata_ = info.dynamicMetadata(); + filter_state_ = info.filterState(); + request_headers_ = request_headers; + upstream_cluster_info_ = info.upstreamClusterInfo(); + auto stream_id_provider = info.getStreamIdProvider(); + if (stream_id_provider.has_value() && stream_id_provider->toStringView().has_value()) { + std::string id{stream_id_provider->toStringView().value()}; + stream_id_provider_ = std::make_shared(std::move(id)); + } + trace_reason_ = info.traceReason(); + filter_chain_name_ = info.filterChainName(); + attempt_count_ = info.attemptCount(); + upstream_bytes_meter_ = info.getUpstreamBytesMeter(); + } + void setIsShadow(bool is_shadow) { is_shadow_ = is_shadow; } bool isShadow() const override { return is_shadow_; } diff --git a/source/common/stream_info/utility.cc b/source/common/stream_info/utility.cc index 5b8cebb2d30a..9d25b0e5dd47 100644 --- a/source/common/stream_info/utility.cc +++ b/source/common/stream_info/utility.cc @@ -130,6 +130,14 @@ absl::optional TimingUtility::downstreamHandshakeCompl return duration(timing.value().get().downstreamHandshakeComplete(), stream_info_); } +absl::optional TimingUtility::lastDownstreamAckReceived() { + OptRef timing = stream_info_.downstreamTiming(); + if (!timing) { + return absl::nullopt; + } + return duration(timing.value().get().lastDownstreamAckReceived(), stream_info_); +} + const std::string& Utility::formatDownstreamAddressNoPort(const Network::Address::Instance& address) { if (address.type() == Network::Address::Type::Ip) { diff --git a/source/common/stream_info/utility.h b/source/common/stream_info/utility.h index f4526fbb6fbf..ab6db6c23ff0 100644 --- a/source/common/stream_info/utility.h +++ b/source/common/stream_info/utility.h @@ -98,6 +98,7 @@ class TimingUtility { absl::optional lastDownstreamTxByteSent(); absl::optional lastDownstreamRxByteReceived(); absl::optional downstreamHandshakeComplete(); + absl::optional lastDownstreamAckReceived(); private: const StreamInfo& stream_info_; diff --git a/source/common/tcp_proxy/BUILD b/source/common/tcp_proxy/BUILD index 43e2844854d0..db2c5b1895e4 100644 --- a/source/common/tcp_proxy/BUILD +++ b/source/common/tcp_proxy/BUILD @@ -58,6 +58,7 @@ envoy_cc_library( "//source/common/access_log:access_log_lib", "//source/common/common:assert_lib", "//source/common/common:empty_string", + "//source/common/common:enum_to_int", "//source/common/common:macros", "//source/common/common:minimal_logger_lib", "//source/common/formatter:substitution_format_string_lib", diff --git a/source/common/tcp_proxy/tcp_proxy.cc b/source/common/tcp_proxy/tcp_proxy.cc index 3b4ebfaf2f54..d5f56d65bc07 100644 --- a/source/common/tcp_proxy/tcp_proxy.cc +++ b/source/common/tcp_proxy/tcp_proxy.cc @@ -235,6 +235,13 @@ void Filter::initialize(Network::ReadFilterCallbacks& callbacks, bool set_connec } } +void Filter::onInitFailure(UpstreamFailureReason reason) { + read_callbacks_->connection().close( + Network::ConnectionCloseType::NoFlush, + absl::StrCat(StreamInfo::LocalCloseReasons::get().TcpProxyInitializationFailure, + enumToInt(reason))); +} + void Filter::readDisableUpstream(bool disable) { bool success = false; if (upstream_) { @@ -471,7 +478,7 @@ bool Filter::maybeTunnel(Upstream::ThreadLocalCluster& cluster) { GenericConnPoolFactory* factory = nullptr; if (cluster.info()->upstreamConfig().has_value()) { factory = Envoy::Config::Utility::getFactory( - cluster.info()->upstreamConfig().value()); + cluster.info()->upstreamConfig().ref()); } else { factory = Envoy::Config::Utility::getFactoryByName( "envoy.filters.connection_pools.tcp.generic"); @@ -772,14 +779,17 @@ void Filter::onIdleTimeout() { config_->stats().idle_timeout_.inc(); // This results in also closing the upstream connection. - read_callbacks_->connection().close(Network::ConnectionCloseType::NoFlush); + read_callbacks_->connection().close(Network::ConnectionCloseType::NoFlush, + StreamInfo::LocalCloseReasons::get().TcpSessionIdleTimeout); } void Filter::onMaxDownstreamConnectionDuration() { ENVOY_CONN_LOG(debug, "max connection duration reached", read_callbacks_->connection()); getStreamInfo().setResponseFlag(StreamInfo::ResponseFlag::DurationTimeout); config_->stats().max_downstream_connection_duration_.inc(); - read_callbacks_->connection().close(Network::ConnectionCloseType::NoFlush); + read_callbacks_->connection().close( + Network::ConnectionCloseType::NoFlush, + StreamInfo::LocalCloseReasons::get().MaxConnectionDurationReached); } void Filter::onAccessLogFlushInterval() { diff --git a/source/common/tcp_proxy/tcp_proxy.h b/source/common/tcp_proxy/tcp_proxy.h index 6eda6693082a..0d14f0fc5c24 100644 --- a/source/common/tcp_proxy/tcp_proxy.h +++ b/source/common/tcp_proxy/tcp_proxy.h @@ -459,10 +459,7 @@ class Filter : public Network::ReadFilter, return config_->getRouteFromEntries(read_callbacks_->connection()); } - virtual void onInitFailure(UpstreamFailureReason) { - read_callbacks_->connection().close(Network::ConnectionCloseType::NoFlush); - } - + virtual void onInitFailure(UpstreamFailureReason reason); void initialize(Network::ReadFilterCallbacks& callbacks, bool set_connection_stats); // Create connection to the upstream cluster. This function can be repeatedly called on upstream diff --git a/source/common/tcp_proxy/upstream.cc b/source/common/tcp_proxy/upstream.cc index 585897d90d3a..a2dca26b31f5 100644 --- a/source/common/tcp_proxy/upstream.cc +++ b/source/common/tcp_proxy/upstream.cc @@ -55,10 +55,14 @@ TcpUpstream::onDownstreamEvent(Network::ConnectionEvent event) { // The close call may result in this object being deleted. Latch the // connection locally so it can be returned for potential draining. auto* conn_data = upstream_conn_data_.release(); - conn_data->connection().close(Network::ConnectionCloseType::FlushWrite); + conn_data->connection().close( + Network::ConnectionCloseType::FlushWrite, + StreamInfo::LocalCloseReasons::get().ClosingUpstreamTcpDueToDownstreamRemoteClose); return conn_data; } else if (event == Network::ConnectionEvent::LocalClose) { - upstream_conn_data_->connection().close(Network::ConnectionCloseType::NoFlush); + upstream_conn_data_->connection().close( + Network::ConnectionCloseType::NoFlush, + StreamInfo::LocalCloseReasons::get().ClosingUpstreamTcpDueToDownstreamLocalClose); } return nullptr; } diff --git a/source/common/upstream/BUILD b/source/common/upstream/BUILD index f5282490f6a4..4cdf5fbdfcd0 100644 --- a/source/common/upstream/BUILD +++ b/source/common/upstream/BUILD @@ -63,25 +63,6 @@ envoy_cc_library( ], ) -envoy_cc_library( - name = "multiplexed_subscription_factory_lib", - srcs = ["multiplexed_subscription_factory.cc"], - hdrs = ["multiplexed_subscription_factory.h"], - deps = [ - "//envoy/config:subscription_interface", - "//envoy/event:dispatcher_interface", - "//envoy/local_info:local_info_interface", - "//source/common/common:hash_lib", - "//source/common/config:custom_config_validators_lib", - "//source/common/config:grpc_mux_lib", - "//source/common/config:grpc_subscription_lib", - "//source/common/config:subscription_factory_lib", - "//source/common/config:utility_lib", - "//source/server:transport_socket_config_lib", - "@envoy_api//envoy/config/core/v3:pkg_cc_proto", - ], -) - envoy_cc_library( name = "cluster_discovery_manager_lib", srcs = ["cluster_discovery_manager.cc"], @@ -103,7 +84,6 @@ envoy_cc_library( ":cluster_discovery_manager_lib", ":load_balancer_lib", ":load_stats_reporter_lib", - ":multiplexed_subscription_factory_lib", ":od_cds_api_lib", ":ring_hash_lb_lib", ":subset_lb_lib", diff --git a/source/common/upstream/cds_api_helper.cc b/source/common/upstream/cds_api_helper.cc index b933c77b06aa..6bd1655d4a5e 100644 --- a/source/common/upstream/cds_api_helper.cc +++ b/source/common/upstream/cds_api_helper.cc @@ -21,13 +21,10 @@ CdsApiHelper::onConfigUpdate(const std::vector& adde Config::ScopedResume maybe_resume_eds_leds_sds; if (cm_.adsMux()) { // A cluster update pauses sending EDS and LEDS requests. - std::vector paused_xds_types{ + const std::vector paused_xds_types{ Config::getTypeUrl(), - Config::getTypeUrl()}; - if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.combine_sds_requests")) { - paused_xds_types.push_back( - Config::getTypeUrl()); - } + Config::getTypeUrl(), + Config::getTypeUrl()}; maybe_resume_eds_leds_sds = cm_.adsMux()->pause(paused_xds_types); } diff --git a/source/common/upstream/cluster_manager_impl.cc b/source/common/upstream/cluster_manager_impl.cc index 42cbbc92c891..f20f1e12b2f4 100644 --- a/source/common/upstream/cluster_manager_impl.cc +++ b/source/common/upstream/cluster_manager_impl.cc @@ -199,14 +199,10 @@ void ClusterManagerInitHelper::maybeFinishInitialize() { // avoid double pause ClusterLoadAssignment. Config::ScopedResume maybe_resume_eds_leds_sds; if (cm_.adsMux()) { - - std::vector paused_xds_types{ + const std::vector paused_xds_types{ Config::getTypeUrl(), - Config::getTypeUrl()}; - if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.combine_sds_requests")) { - paused_xds_types.push_back( - Config::getTypeUrl()); - } + Config::getTypeUrl(), + Config::getTypeUrl()}; maybe_resume_eds_leds_sds = cm_.adsMux()->pause(paused_xds_types); } initializeSecondaryClusters(); @@ -349,11 +345,6 @@ ClusterManagerImpl::ClusterManagerImpl( server, makeOptRefFromPtr(xds_resources_delegate_.get()), makeOptRefFromPtr(xds_config_tracker_.get())); - multiplexed_subscription_factory_ = std::make_unique( - local_info, main_thread_dispatcher, *this, validation_context.dynamicValidationVisitor(), api, - server, makeOptRefFromPtr(xds_resources_delegate_.get()), - makeOptRefFromPtr(xds_config_tracker_.get())); - const auto& dyn_resources = bootstrap.dynamic_resources(); // Cluster loading happens in two phases: first all the primary clusters are loaded, and then all @@ -1498,7 +1489,9 @@ void ClusterManagerImpl::ThreadLocalClusterManagerImpl::onHostHealthFailure( break; } TcpConnectionsMap& container = it->second; - container.connections_.begin()->first->close(Network::ConnectionCloseType::NoFlush); + container.connections_.begin()->first->close( + Network::ConnectionCloseType::NoFlush, + StreamInfo::LocalCloseReasons::get().NonPooledTcpConnectionHostHealthFailure); } } else { drainOrCloseConnPools(host, ConnectionPool::DrainBehavior::DrainExistingConnections); diff --git a/source/common/upstream/cluster_manager_impl.h b/source/common/upstream/cluster_manager_impl.h index 0164af14f658..584e08709b33 100644 --- a/source/common/upstream/cluster_manager_impl.h +++ b/source/common/upstream/cluster_manager_impl.h @@ -37,7 +37,6 @@ #include "source/common/upstream/cluster_discovery_manager.h" #include "source/common/upstream/host_utility.h" #include "source/common/upstream/load_stats_reporter.h" -#include "source/common/upstream/multiplexed_subscription_factory.h" #include "source/common/upstream/od_cds_api_impl.h" #include "source/common/upstream/priority_conn_pool_map.h" #include "source/common/upstream/upstream_impl.h" @@ -329,10 +328,6 @@ class ClusterManagerImpl : public ClusterManager, Config::SubscriptionFactory& subscriptionFactory() override { return *subscription_factory_; } - Config::SubscriptionFactory& multiplexedSubscriptionFactory() override { - return *multiplexed_subscription_factory_; - } - void initializeSecondaryClusters(const envoy::config::bootstrap::v3::Bootstrap& bootstrap) override; @@ -817,7 +812,6 @@ class ClusterManagerImpl : public ClusterManager, ClusterTimeoutBudgetStatNames cluster_timeout_budget_stat_names_; std::unique_ptr subscription_factory_; - std::unique_ptr multiplexed_subscription_factory_; ClusterSet primary_clusters_; std::unique_ptr xds_resources_delegate_; diff --git a/source/common/upstream/load_balancer_impl.cc b/source/common/upstream/load_balancer_impl.cc index d0647c87ec59..525cd48274ec 100644 --- a/source/common/upstream/load_balancer_impl.cc +++ b/source/common/upstream/load_balancer_impl.cc @@ -1000,6 +1000,49 @@ double EdfLoadBalancerBase::applySlowStartFactor(double host_weight, const Host& } } +double LeastRequestLoadBalancer::hostWeight(const Host& host) { + // This method is called to calculate the dynamic weight as following when all load balancing + // weights are not equal: + // + // `weight = load_balancing_weight / (active_requests + 1)^active_request_bias` + // + // `active_request_bias` can be configured via runtime and its value is cached in + // `active_request_bias_` to avoid having to do a runtime lookup each time a host weight is + // calculated. + // + // When `active_request_bias == 0.0` we behave like `RoundRobinLoadBalancer` and return the + // host weight without considering the number of active requests at the time we do the pick. + // + // When `active_request_bias > 0.0` we scale the host weight by the number of active + // requests at the time we do the pick. We always add 1 to avoid division by 0. + // + // It might be possible to do better by picking two hosts off of the schedule, and selecting the + // one with fewer active requests at the time of selection. + + double host_weight = static_cast(host.weight()); + + // If the value of active requests is the max value, adding +1 will overflow + // it and cause a divide by zero. This won't happen in normal cases but stops + // failing fuzz tests + const uint64_t active_request_value = + host.stats().rq_active_.value() != std::numeric_limits::max() + ? host.stats().rq_active_.value() + 1 + : host.stats().rq_active_.value(); + + if (active_request_bias_ == 1.0) { + host_weight = static_cast(host.weight()) / active_request_value; + } else if (active_request_bias_ != 0.0) { + host_weight = + static_cast(host.weight()) / std::pow(active_request_value, active_request_bias_); + } + + if (!noHostsAreInSlowStart()) { + return applySlowStartFactor(host_weight, host); + } else { + return host_weight; + } +} + HostConstSharedPtr LeastRequestLoadBalancer::unweightedHostPeek(const HostVector&, const HostsSource&) { // LeastRequestLoadBalancer can not do deterministic preconnecting, because diff --git a/source/common/upstream/load_balancer_impl.h b/source/common/upstream/load_balancer_impl.h index 988f760efb99..72bd19592690 100644 --- a/source/common/upstream/load_balancer_impl.h +++ b/source/common/upstream/load_balancer_impl.h @@ -686,40 +686,7 @@ class LeastRequestLoadBalancer : public EdfLoadBalancerBase { private: void refreshHostSource(const HostsSource&) override {} - double hostWeight(const Host& host) override { - // This method is called to calculate the dynamic weight as following when all load balancing - // weights are not equal: - // - // `weight = load_balancing_weight / (active_requests + 1)^active_request_bias` - // - // `active_request_bias` can be configured via runtime and its value is cached in - // `active_request_bias_` to avoid having to do a runtime lookup each time a host weight is - // calculated. - // - // When `active_request_bias == 0.0` we behave like `RoundRobinLoadBalancer` and return the - // host weight without considering the number of active requests at the time we do the pick. - // - // When `active_request_bias > 0.0` we scale the host weight by the number of active - // requests at the time we do the pick. We always add 1 to avoid division by 0. - // - // It might be possible to do better by picking two hosts off of the schedule, and selecting the - // one with fewer active requests at the time of selection. - - double host_weight = static_cast(host.weight()); - - if (active_request_bias_ == 1.0) { - host_weight = static_cast(host.weight()) / (host.stats().rq_active_.value() + 1); - } else if (active_request_bias_ != 0.0) { - host_weight = static_cast(host.weight()) / - std::pow(host.stats().rq_active_.value() + 1, active_request_bias_); - } - - if (!noHostsAreInSlowStart()) { - return applySlowStartFactor(host_weight, host); - } else { - return host_weight; - } - } + double hostWeight(const Host& host) override; HostConstSharedPtr unweightedHostPeek(const HostVector& hosts_to_use, const HostsSource& source) override; HostConstSharedPtr unweightedHostPick(const HostVector& hosts_to_use, diff --git a/source/common/upstream/multiplexed_subscription_factory.cc b/source/common/upstream/multiplexed_subscription_factory.cc deleted file mode 100644 index 7687c556ed3d..000000000000 --- a/source/common/upstream/multiplexed_subscription_factory.cc +++ /dev/null @@ -1,39 +0,0 @@ -#include "source/common/upstream/multiplexed_subscription_factory.h" - -#include "source/common/common/hash.h" -#include "source/common/config/utility.h" - -namespace Envoy { -namespace Upstream { - -MultiplexedSubscriptionFactory::MultiplexedSubscriptionFactory( - const LocalInfo::LocalInfo& local_info, Event::Dispatcher& dispatcher, - Upstream::ClusterManager& cm, ProtobufMessage::ValidationVisitor& validation_visitor, - Api::Api& api, const Server::Instance& server, - Config::XdsResourcesDelegateOptRef xds_resources_delegate, - Config::XdsConfigTrackerOptRef xds_config_tracker) - : Config::SubscriptionFactoryImpl(local_info, dispatcher, cm, validation_visitor, api, server, - xds_resources_delegate, xds_config_tracker){}; - -Config::GrpcMuxSharedPtr MultiplexedSubscriptionFactory::getOrCreateMux( - const envoy::config::core::v3::ApiConfigSource& config_source, absl::string_view type_url, - Stats::Scope& scope, Config::CustomConfigValidatorsPtr& custom_config_validators) { - if (config_source.api_type() == envoy::config::core::v3::ApiConfigSource::GRPC || - config_source.api_type() == envoy::config::core::v3::ApiConfigSource::DELTA_GRPC) { - const uint64_t xds_server_hash = MessageUtil::hash(config_source.grpc_services(0)); - const uint64_t xds_type_hash = HashUtil::xxHash64(type_url); - const uint64_t mux_key = xds_server_hash ^ xds_type_hash; - if (muxes_.find(mux_key) == muxes_.end()) { - muxes_.emplace( - std::make_pair(mux_key, Config::SubscriptionFactoryImpl::getOrCreateMux( - config_source, type_url, scope, custom_config_validators))); - } - return muxes_.at(mux_key); - } else { - return Config::SubscriptionFactoryImpl::getOrCreateMux(config_source, type_url, scope, - custom_config_validators); - } -} - -} // namespace Upstream -} // namespace Envoy diff --git a/source/common/upstream/multiplexed_subscription_factory.h b/source/common/upstream/multiplexed_subscription_factory.h deleted file mode 100644 index 444fced8b79c..000000000000 --- a/source/common/upstream/multiplexed_subscription_factory.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include "envoy/config/core/v3/base.pb.h" -#include "envoy/config/subscription.h" -#include "envoy/stats/scope.h" - -#include "source/common/common/assert.h" -#include "source/common/config/custom_config_validators_impl.h" -#include "source/common/config/grpc_mux_impl.h" -#include "source/common/config/grpc_subscription_impl.h" -#include "source/common/config/subscription_factory_impl.h" -#include "source/common/config/utility.h" -#include "source/common/protobuf/protobuf.h" -#include "source/server/transport_socket_config_impl.h" - -/** - * MultiplexedSubscriptionFactory is used for instantiation of XDS subscriptions so as to minimize - * the number of open grpc connections used by these subscriptions. This is done by sharing a grpc - * multiplexer between subscriptions handled per subscription server and xds resource type. Please - * see https://github.com/envoyproxy/envoy/issues/2943 for additional information and related - * issues. - * - */ - -namespace Envoy { -namespace Upstream { - -class MultiplexedSubscriptionFactory : public Config::SubscriptionFactoryImpl { -public: - ~MultiplexedSubscriptionFactory() override = default; - - MultiplexedSubscriptionFactory(const LocalInfo::LocalInfo& local_info, - Event::Dispatcher& dispatcher, Upstream::ClusterManager& cm, - ProtobufMessage::ValidationVisitor& validation_visitor, - Api::Api& api, const Server::Instance& server, - Config::XdsResourcesDelegateOptRef xds_resources_delegate, - Config::XdsConfigTrackerOptRef xds_config_tracker); - -protected: - // Config::SubscriptionFactoryImpl - Config::GrpcMuxSharedPtr - getOrCreateMux(const envoy::config::core::v3::ApiConfigSource& api_config_source, - absl::string_view type_url, Stats::Scope& scope, - Config::CustomConfigValidatorsPtr& custom_config_validators) override; - -private: - absl::flat_hash_map muxes_; - friend class MultiplexedSubscriptionFactoryPeer; -}; -} // namespace Upstream -} // namespace Envoy diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index 82c6aade4bdd..7211f24d2e07 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -1040,9 +1040,9 @@ ClusterInfoImpl::ClusterInfoImpl( config.original_dst_lb_config()) : nullptr), upstream_config_(config.has_upstream_config() - ? absl::make_optional( + ? std::make_unique( config.upstream_config()) - : absl::nullopt), + : nullptr), added_via_api_(added_via_api), lb_subset_(LoadBalancerSubsetInfoImpl(config.lb_subset_config())), metadata_(config.metadata()), typed_metadata_(config.metadata()), @@ -1054,11 +1054,10 @@ ClusterInfoImpl::ClusterInfoImpl( common_lb_config_.ignore_new_hosts_until_first_hc()), set_local_interface_name_on_upstream_connections_( config.upstream_connection_options().set_local_interface_name_on_upstream_connections()), - cluster_type_( - config.has_cluster_type() - ? absl::make_optional( - config.cluster_type()) - : absl::nullopt), + cluster_type_(config.has_cluster_type() + ? std::make_unique( + config.cluster_type()) + : nullptr), factory_context_( std::make_unique(*stats_scope_, runtime, factory_context)), upstream_context_(server_context, init_manager, *stats_scope_) { diff --git a/source/common/upstream/upstream_impl.h b/source/common/upstream/upstream_impl.h index 3a9c7d1eab77..e429bdfa26f0 100644 --- a/source/common/upstream/upstream_impl.h +++ b/source/common/upstream/upstream_impl.h @@ -778,9 +778,13 @@ class ClusterInfoImpl : public ClusterInfo, extensionProtocolOptions(const std::string& name) const override; LoadBalancerType lbType() const override { return lb_type_; } envoy::config::cluster::v3::Cluster::DiscoveryType type() const override { return type_; } - const absl::optional& + + OptRef clusterType() const override { - return cluster_type_; + if (cluster_type_ == nullptr) { + return absl::nullopt; + } + return *cluster_type_; } OptRef lbRoundRobinConfig() const override { @@ -817,9 +821,11 @@ class ClusterInfoImpl : public ClusterInfo, } return *lb_original_dst_config_; } - const absl::optional& - upstreamConfig() const override { - return upstream_config_; + OptRef upstreamConfig() const override { + if (upstream_config_ == nullptr) { + return absl::nullopt; + } + return *upstream_config_; } bool maintenanceMode() const override; uint64_t maxRequestsPerConnection() const override { return max_requests_per_connection_; } @@ -974,7 +980,7 @@ class ClusterInfoImpl : public ClusterInfo, lb_maglev_config_; const std::unique_ptr lb_original_dst_config_; - absl::optional upstream_config_; + std::unique_ptr upstream_config_; const bool added_via_api_; LoadBalancerSubsetInfoImpl lb_subset_; const envoy::config::core::v3::Metadata metadata_; @@ -989,7 +995,7 @@ class ClusterInfoImpl : public ClusterInfo, const absl::optional upstream_http_protocol_options_; absl::optional eds_service_name_; - const absl::optional cluster_type_; + std::unique_ptr cluster_type_; const std::unique_ptr factory_context_; std::vector filter_factories_; Http::FilterChainUtility::FilterFactoriesList http_filter_factories_; diff --git a/source/extensions/clusters/eds/BUILD b/source/extensions/clusters/eds/BUILD index 75ecfcac7d2d..cbdbbd402395 100644 --- a/source/extensions/clusters/eds/BUILD +++ b/source/extensions/clusters/eds/BUILD @@ -34,7 +34,6 @@ envoy_cc_extension( "//source/common/network:utility_lib", "//source/common/protobuf:utility_lib", "//source/common/upstream:cluster_factory_lib", - "//source/common/upstream:multiplexed_subscription_factory_lib", "//source/common/upstream:upstream_includes", "@envoy_api//envoy/config/cluster/v3:pkg_cc_proto", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", diff --git a/source/extensions/clusters/eds/eds.cc b/source/extensions/clusters/eds/eds.cc index 16e05c1f3f23..cb44c83bc536 100644 --- a/source/extensions/clusters/eds/eds.cc +++ b/source/extensions/clusters/eds/eds.cc @@ -9,7 +9,6 @@ #include "source/common/common/utility.h" #include "source/common/config/api_version.h" #include "source/common/config/decoded_resource_impl.h" -#include "source/common/runtime/runtime_features.h" namespace Envoy { namespace Upstream { @@ -38,23 +37,10 @@ EdsClusterImpl::EdsClusterImpl( initialize_phase_ = InitializePhase::Secondary; } const auto resource_name = getResourceName(); - if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.multiplex_eds") && - (eds_config.api_config_source().api_type() == - envoy::config::core::v3::ApiConfigSource::GRPC || - eds_config.api_config_source().api_type() == - envoy::config::core::v3::ApiConfigSource::DELTA_GRPC)) { - ENVOY_LOG(trace, "Multiplexing EDS updates over single stream for cluster ", cluster_name_); - subscription_ = - factory_context.clusterManager() - .multiplexedSubscriptionFactory() - .subscriptionFromConfigSource(eds_config, Grpc::Common::typeUrl(resource_name), - info_->statsScope(), *this, resource_decoder_, {}); - } else { - subscription_ = - factory_context.clusterManager().subscriptionFactory().subscriptionFromConfigSource( - eds_config, Grpc::Common::typeUrl(resource_name), info_->statsScope(), *this, - resource_decoder_, {}); - } + subscription_ = + factory_context.clusterManager().subscriptionFactory().subscriptionFromConfigSource( + eds_config, Grpc::Common::typeUrl(resource_name), info_->statsScope(), *this, + resource_decoder_, {}); } void EdsClusterImpl::startPreInit() { subscription_->start({cluster_name_}); } diff --git a/source/extensions/clusters/eds/eds.h b/source/extensions/clusters/eds/eds.h index c9aca23ea9d6..57adb83c7aaa 100644 --- a/source/extensions/clusters/eds/eds.h +++ b/source/extensions/clusters/eds/eds.h @@ -17,7 +17,6 @@ #include "source/common/config/subscription_base.h" #include "source/common/upstream/cluster_factory_impl.h" -#include "source/common/upstream/multiplexed_subscription_factory.h" #include "source/common/upstream/upstream_impl.h" #include "source/extensions/clusters/eds/leds.h" diff --git a/source/extensions/common/proxy_protocol/proxy_protocol_header.cc b/source/extensions/common/proxy_protocol/proxy_protocol_header.cc index 025ced0dd606..0bbe17dd6d9e 100644 --- a/source/extensions/common/proxy_protocol/proxy_protocol_header.cc +++ b/source/extensions/common/proxy_protocol/proxy_protocol_header.cc @@ -43,7 +43,7 @@ void generateV1Header(const Network::Address::Ip& source_address, void generateV2Header(const std::string& src_addr, const std::string& dst_addr, uint32_t src_port, uint32_t dst_port, Network::Address::IpVersion ip_version, - Buffer::Instance& out) { + uint16_t extension_length, Buffer::Instance& out) { out.add(PROXY_PROTO_V2_SIGNATURE, PROXY_PROTO_V2_SIGNATURE_LEN); const uint8_t version_and_command = PROXY_PROTO_V2_VERSION << 4 | PROXY_PROTO_V2_ONBEHALF_OF; @@ -61,11 +61,15 @@ void generateV2Header(const std::string& src_addr, const std::string& dst_addr, address_family_and_protocol |= PROXY_PROTO_V2_TRANSPORT_STREAM; out.add(&address_family_and_protocol, 1); - uint8_t addr_length[2]{0, 0}; + // Number of following bytes part of the header in V2 protocol. + uint16_t addr_length; + uint16_t addr_length_n; // Network byte order + switch (ip_version) { case Network::Address::IpVersion::v4: { - addr_length[1] = PROXY_PROTO_V2_ADDR_LEN_INET; - out.add(addr_length, 2); + addr_length = PROXY_PROTO_V2_ADDR_LEN_INET + extension_length; + addr_length_n = htons(addr_length); + out.add(&addr_length_n, 2); const uint32_t net_src_addr = Network::Address::Ipv4Instance(src_addr, src_port).ip()->ipv4()->address(); const uint32_t net_dst_addr = @@ -75,8 +79,9 @@ void generateV2Header(const std::string& src_addr, const std::string& dst_addr, break; } case Network::Address::IpVersion::v6: { - addr_length[1] = PROXY_PROTO_V2_ADDR_LEN_INET6; - out.add(addr_length, 2); + addr_length = PROXY_PROTO_V2_ADDR_LEN_INET6 + extension_length; + addr_length_n = htons(addr_length); + out.add(&addr_length_n, 2); const absl::uint128 net_src_addr = Network::Address::Ipv6Instance(src_addr, src_port).ip()->ipv6()->address(); const absl::uint128 net_dst_addr = @@ -93,10 +98,51 @@ void generateV2Header(const std::string& src_addr, const std::string& dst_addr, out.add(&net_dst_port, 2); } +void generateV2Header(const std::string& src_addr, const std::string& dst_addr, uint32_t src_port, + uint32_t dst_port, Network::Address::IpVersion ip_version, + Buffer::Instance& out) { + generateV2Header(src_addr, dst_addr, src_port, dst_port, ip_version, 0, out); +} + void generateV2Header(const Network::Address::Ip& source_address, const Network::Address::Ip& dest_address, Buffer::Instance& out) { generateV2Header(source_address.addressAsString(), dest_address.addressAsString(), - source_address.port(), dest_address.port(), source_address.version(), out); + source_address.port(), dest_address.port(), source_address.version(), 0, out); +} + +bool generateV2Header(const Network::ProxyProtocolData& proxy_proto_data, Buffer::Instance& out, + bool pass_all_tlvs, const absl::flat_hash_set& pass_through_tlvs) { + uint64_t extension_length = 0; + for (auto&& tlv : proxy_proto_data.tlv_vector_) { + if (!pass_all_tlvs && !pass_through_tlvs.contains(tlv.type)) { + continue; + } + extension_length += PROXY_PROTO_V2_TLV_TYPE_LENGTH_LEN + tlv.value.size(); + if (extension_length > std::numeric_limits::max()) { + ENVOY_LOG_MISC( + warn, "Generating Proxy Protocol V2 header: TLVs exceed length limit {}, already got {}", + std::numeric_limits::max(), extension_length); + return false; + } + } + + ASSERT(extension_length <= std::numeric_limits::max()); + const auto& src = *proxy_proto_data.src_addr_->ip(); + const auto& dst = *proxy_proto_data.dst_addr_->ip(); + generateV2Header(src.addressAsString(), dst.addressAsString(), src.port(), dst.port(), + src.version(), static_cast(extension_length), out); + + // Generate the TLV vector. + for (auto&& tlv : proxy_proto_data.tlv_vector_) { + if (!pass_all_tlvs && !pass_through_tlvs.contains(tlv.type)) { + continue; + } + out.add(&tlv.type, 1); + uint16_t size = htons(static_cast(tlv.value.size())); + out.add(&size, sizeof(uint16_t)); + out.add(&tlv.value.front(), tlv.value.size()); + } + return true; } void generateProxyProtoHeader(const envoy::config::core::v3::ProxyProtocolConfig& config, diff --git a/source/extensions/common/proxy_protocol/proxy_protocol_header.h b/source/extensions/common/proxy_protocol/proxy_protocol_header.h index 013c842ced20..a4a09f46f98c 100644 --- a/source/extensions/common/proxy_protocol/proxy_protocol_header.h +++ b/source/extensions/common/proxy_protocol/proxy_protocol_header.h @@ -5,6 +5,8 @@ #include "envoy/network/address.h" #include "envoy/network/connection.h" +#include "absl/container/flat_hash_set.h" + namespace Envoy { namespace Extensions { namespace Common { @@ -39,6 +41,8 @@ constexpr uint32_t PROXY_PROTO_V2_ADDR_LEN_INET = 12; constexpr uint32_t PROXY_PROTO_V2_ADDR_LEN_INET6 = 36; constexpr uint32_t PROXY_PROTO_V2_ADDR_LEN_UNIX = 216; +constexpr uint32_t PROXY_PROTO_V2_TLV_TYPE_LENGTH_LEN = 3; + // Generates the v1 PROXY protocol header and adds it to the specified buffer void generateV1Header(const std::string& src_addr, const std::string& dst_addr, uint32_t src_port, uint32_t dst_port, Network::Address::IpVersion ip_version, @@ -48,6 +52,9 @@ void generateV1Header(const Network::Address::Ip& source_address, // Generates the v2 PROXY protocol header and adds it to the specified buffer // TCP is assumed as the transport protocol +void generateV2Header(const std::string& src_addr, const std::string& dst_addr, uint32_t src_port, + uint32_t dst_port, Network::Address::IpVersion ip_version, + uint16_t extension_length, Buffer::Instance& out); void generateV2Header(const std::string& src_addr, const std::string& dst_addr, uint32_t src_port, uint32_t dst_port, Network::Address::IpVersion ip_version, Buffer::Instance& out); @@ -61,6 +68,10 @@ void generateProxyProtoHeader(const envoy::config::core::v3::ProxyProtocolConfig // Generates the v2 PROXY protocol local command header and adds it to the specified buffer void generateV2LocalHeader(Buffer::Instance& out); +// Generates the v2 PROXY protocol header including the TLV vector into the specified buffer. +bool generateV2Header(const Network::ProxyProtocolData& proxy_proto_data, Buffer::Instance& out, + bool pass_all_tlvs, const absl::flat_hash_set& pass_through_tlvs); + } // namespace ProxyProtocol } // namespace Common } // namespace Extensions diff --git a/source/extensions/common/wasm/wasm.cc b/source/extensions/common/wasm/wasm.cc index 3ba2275915b0..cd72be8d1641 100644 --- a/source/extensions/common/wasm/wasm.cc +++ b/source/extensions/common/wasm/wasm.cc @@ -323,6 +323,9 @@ bool createWasm(const PluginSharedPtr& plugin, const Stats::ScopeSharedPtr& scop auto vm_config = config.config().vm_config(); bool fetch = false; if (vm_config.code().has_remote()) { + // TODO(https://github.com/envoyproxy/envoy/issues/25052) Stabilize this feature. + ENVOY_LOG_TO_LOGGER(Envoy::Logger::Registry::getLog(Envoy::Logger::Id::wasm), warn, + "Wasm remote code fetch is unstable and may cause a crash"); auto now = dispatcher.timeSource().monotonicTime() + cache_time_offset_for_testing; source = vm_config.code().remote().http_uri().uri(); std::lock_guard guard(code_cache_mutex); diff --git a/source/extensions/filters/common/local_ratelimit/local_ratelimit_impl.cc b/source/extensions/filters/common/local_ratelimit/local_ratelimit_impl.cc index ced1d51ad5a7..149aaa3080e9 100644 --- a/source/extensions/filters/common/local_ratelimit/local_ratelimit_impl.cc +++ b/source/extensions/filters/common/local_ratelimit/local_ratelimit_impl.cc @@ -167,36 +167,29 @@ OptRef LocalRateLimiterImpl::de bool LocalRateLimiterImpl::requestAllowed( absl::Span request_descriptors) const { - if (Runtime::runtimeFeatureEnabled( - "envoy.reloadable_features.local_ratelimit_match_all_descriptors")) { - - bool allow = requestAllowedHelper(tokens_); - // Global token is not enough. Since global token is not sorted, so we suggest it should be - // larger than other descriptors. - if (!allow) { - return allow; - } - if (!descriptors_.empty() && !request_descriptors.empty()) { - for (const auto& descriptor : sorted_descriptors_) { - for (const auto& request_descriptor : request_descriptors) { - if (descriptor == request_descriptor) { - allow &= requestAllowedHelper(*descriptor.token_state_); - // Descriptor token is not enough. - if (!allow) { - return allow; - } - break; + bool allow = requestAllowedHelper(tokens_); + // Global token is not enough. Since global token is not sorted, so we suggest it should be + // larger than other descriptors. + if (!allow) { + return allow; + } + + if (!descriptors_.empty() && !request_descriptors.empty()) { + for (const auto& descriptor : sorted_descriptors_) { + for (const auto& request_descriptor : request_descriptors) { + if (descriptor == request_descriptor) { + allow &= requestAllowedHelper(*descriptor.token_state_); + // Descriptor token is not enough. + if (!allow) { + return allow; } + break; } } } - return allow; } - auto descriptor = descriptorHelper(request_descriptors); - - return descriptor.has_value() ? requestAllowedHelper(*descriptor.value().get().token_state_) - : requestAllowedHelper(tokens_); + return allow; } int LocalRateLimiterImpl::tokensFillPerSecond(LocalDescriptorImpl& descriptor) { diff --git a/source/extensions/filters/http/dynamic_forward_proxy/proxy_filter.cc b/source/extensions/filters/http/dynamic_forward_proxy/proxy_filter.cc index b79863b9bcf3..cb6adc38fb95 100644 --- a/source/extensions/filters/http/dynamic_forward_proxy/proxy_filter.cc +++ b/source/extensions/filters/http/dynamic_forward_proxy/proxy_filter.cc @@ -90,8 +90,8 @@ Http::FilterHeadersStatus ProxyFilter::decodeHeaders(Http::RequestHeaderMap& hea // We only need to do DNS lookups for hosts in dynamic forward proxy clusters, // since the other cluster types do their own DNS management. - const absl::optional& cluster_type = cluster_info_->clusterType(); - if (!cluster_type) { + OptRef cluster_type = cluster_info_->clusterType(); + if (!cluster_type.has_value()) { return Http::FilterHeadersStatus::Continue; } diff --git a/source/extensions/filters/http/lua/lua_filter.cc b/source/extensions/filters/http/lua/lua_filter.cc index 504210fa67aa..e78caf42ac7c 100644 --- a/source/extensions/filters/http/lua/lua_filter.cc +++ b/source/extensions/filters/http/lua/lua_filter.cc @@ -62,6 +62,12 @@ const OptionHandlers& optionHandlers() { // = } entry. options.return_duplicate_headers_ = lua_toboolean(state, -1); }}, + {"send_xff", + [](lua_State* state, StreamHandleWrapper::HttpCallOptions& options) { + // Handle the case when the table has: {["send_xff"] = + // } entry. + options.request_options_.setSendXff(lua_toboolean(state, -1)); + }}, }); } diff --git a/source/extensions/filters/http/rate_limit_quota/BUILD b/source/extensions/filters/http/rate_limit_quota/BUILD index 6fb2c2743320..4059bd4a2e86 100644 --- a/source/extensions/filters/http/rate_limit_quota/BUILD +++ b/source/extensions/filters/http/rate_limit_quota/BUILD @@ -15,6 +15,7 @@ envoy_cc_library( hdrs = ["filter.h"], deps = [ ":client_lib", + ":matcher_lib", "//envoy/registry", "//source/common/http:headers_lib", "//source/common/http:message_lib", @@ -67,3 +68,21 @@ envoy_cc_library( "@envoy_api//envoy/service/rate_limit_quota/v3:pkg_cc_proto", ], ) + +envoy_cc_library( + name = "matcher_lib", + srcs = ["matcher.cc"], + hdrs = ["matcher.h"], + deps = [ + "//envoy/registry", + "//source/common/http:headers_lib", + "//source/common/http:message_lib", + "//source/common/http:utility_lib", + "//source/common/http/matching:data_impl_lib", + "//source/common/matcher:matcher_lib", + "//source/common/protobuf:utility_lib", + "//source/extensions/filters/http/common:factory_base_lib", + "@envoy_api//envoy/extensions/filters/http/rate_limit_quota/v3:pkg_cc_proto", + "@envoy_api//envoy/service/rate_limit_quota/v3:pkg_cc_proto", + ], +) diff --git a/source/extensions/filters/http/rate_limit_quota/client_impl.h b/source/extensions/filters/http/rate_limit_quota/client_impl.h index 71178ecb5152..123225b4b48b 100644 --- a/source/extensions/filters/http/rate_limit_quota/client_impl.h +++ b/source/extensions/filters/http/rate_limit_quota/client_impl.h @@ -17,6 +17,9 @@ namespace Extensions { namespace HttpFilters { namespace RateLimitQuota { +using ::envoy::service::rate_limit_quota::v3::RateLimitQuotaUsageReports; +using BucketQuotaUsage = + ::envoy::service::rate_limit_quota::v3::RateLimitQuotaUsageReports::BucketQuotaUsage; using GrpcAsyncClient = Grpc::AsyncClient; @@ -51,14 +54,13 @@ class RateLimitClientImpl : public RateLimitClient, private: // Store the client as the bare object since there is no ownership transfer involved. GrpcAsyncClient aync_client_; - Grpc::AsyncStream stream_{}; + Grpc::AsyncStream stream_{}; RateLimitQuotaCallbacks* callbacks_{}; // TODO(tyxia) Further look at the use of this flag later. bool stream_closed_ = false; }; using RateLimitClientPtr = std::unique_ptr; - /** * Create the rate limit client. It is uniquely owned by each worker thread. */ diff --git a/source/extensions/filters/http/rate_limit_quota/config.cc b/source/extensions/filters/http/rate_limit_quota/config.cc index acc300c7ba71..ef9f5d1d067c 100644 --- a/source/extensions/filters/http/rate_limit_quota/config.cc +++ b/source/extensions/filters/http/rate_limit_quota/config.cc @@ -1,5 +1,7 @@ #include "source/extensions/filters/http/rate_limit_quota/config.h" +#include + #include "envoy/registry/registry.h" #include "source/extensions/filters/http/rate_limit_quota/client_impl.h" diff --git a/source/extensions/filters/http/rate_limit_quota/filter.cc b/source/extensions/filters/http/rate_limit_quota/filter.cc index 2b6797bc2725..ef3424bf14c0 100644 --- a/source/extensions/filters/http/rate_limit_quota/filter.cc +++ b/source/extensions/filters/http/rate_limit_quota/filter.cc @@ -2,28 +2,40 @@ #include +#include "source/extensions/filters/http/rate_limit_quota/matcher.h" + namespace Envoy { namespace Extensions { namespace HttpFilters { namespace RateLimitQuota { -void RateLimitQuotaFilter::setDecoderFilterCallbacks( - Http::StreamDecoderFilterCallbacks& callbacks) { - callbacks_ = &callbacks; -} - -// TODO(tyxia) Mostly are example/boilerplate code, polish implementation here. Http::FilterHeadersStatus RateLimitQuotaFilter::decodeHeaders(Http::RequestHeaderMap& headers, bool) { - // TODO(tyxia) Create the rate limit gRPC client and start the stream on the first request. - absl::StatusOr match_result = requestMatching(headers); - - // Request is not matched by any matchers. In this case, requests are ALLOWED by default (i.e., - // fail-open) and will not be reported to RLQS server. + // First, perform the request matching. + absl::StatusOr match_result = requestMatching(headers); if (!match_result.ok()) { + // When the request is not matched by any matchers, it is ALLOWED by default (i.e., fail-open) + // and its quota usage will not be reported to RLQS server. + // TODO(tyxia) Add stats here and other places throughout the filter (if needed). + ENVOY_LOG(debug, + "The request is not matched by any matchers: ", match_result.status().message()); return Envoy::Http::FilterHeadersStatus::Continue; } + // Second, generate the bucket id for this request based on match action when the request matching + // succeeds. + const RateLimitOnMactchAction* match_action = + dynamic_cast(match_result.value().get()); + auto ret = match_action->generateBucketId(*data_ptr_, factory_context_, visitor_); + if (!ret.ok()) { + // When it failed to generate the bucket id for this specific request, the request is ALLOWED by + // default (i.e., fail-open). + ENVOY_LOG(debug, "Unable to generate the bucket id: {}", ret.status().message()); + return Envoy::Http::FilterHeadersStatus::Continue; + } + + // TODO(tyxia) Add impl for quota cache and other actions. + BucketId bucket_id = ret.value(); return Envoy::Http::FilterHeadersStatus::Continue; } @@ -36,7 +48,7 @@ void RateLimitQuotaFilter::createMatcher() { } } -absl::StatusOr +absl::StatusOr RateLimitQuotaFilter::requestMatching(const Http::RequestHeaderMap& headers) { // Initialize the data pointer on first use and reuse it for subsequent requests. // This avoids creating the data object for every request, which is expensive. @@ -50,7 +62,7 @@ RateLimitQuotaFilter::requestMatching(const Http::RequestHeaderMap& headers) { } if (matcher_ == nullptr) { - return absl::InternalError("Matcher has not been initialized yet"); + return absl::InternalError("Matcher tree has not been initialized yet."); } else { data_ptr_->onRequestHeaders(headers); // TODO(tyxia) This function should trigger the CEL expression matching. Here, we need to @@ -60,85 +72,19 @@ RateLimitQuotaFilter::requestMatching(const Http::RequestHeaderMap& headers) { if (match_result.match_state_ == Matcher::MatchState::MatchComplete) { if (match_result.result_) { - // on_match case. - const auto result = match_result.result_(); - const RateLimitOnMactchAction* match_action = - dynamic_cast(result.get()); - // Try to generate the bucket id if the matching succeeded. - return match_action->generateBucketId(*data_ptr_, factory_context_, visitor_); + // Return the matched result for `on_match` case. + return match_result.result_(); } else { - return absl::NotFoundError("The match was completed, no match found"); + return absl::NotFoundError("Matching completed but no match result was found."); } } else { // The returned state from `evaluateMatch` function is `MatchState::UnableToMatch` here. - return absl::InternalError("Unable to match the request"); + return absl::InternalError("Unable to match the request."); } } } -absl::StatusOr -RateLimitOnMactchAction::generateBucketId(const Http::Matching::HttpMatchingDataImpl& data, - Server::Configuration::FactoryContext& factory_context, - RateLimitQuotaValidationVisitor& visitor) const { - BucketId bucket_id; - std::unique_ptr> input_factory_ptr = nullptr; - - if (setting_.has_no_assignment_behavior()) { - // If we reach to this function when request matching was complete but no match was found, it - // means `on_no_match` field is configured. By design,`no_assignment_behavior` is used for this - // field. - // TODO(tyxia) Returns the empty BucketId for now, later parse the `blanket_rule` based on the - // config for fail-open fail-close behavior. - return bucket_id; - } - - // Generate the `BucketId` based on the bucked id builder from the configuration. - for (const auto& id_builder : setting_.bucket_id_builder().bucket_id_builder()) { - std::string bucket_id_key = id_builder.first; - auto builder_method = id_builder.second; - - // Generate the bucket id based on builder method type. - switch (builder_method.value_specifier_case()) { - // Retrieve the string value directly from the config (static method). - case ValueSpecifierCase::kStringValue: { - bucket_id.mutable_bucket()->insert({bucket_id_key, builder_method.string_value()}); - break; - } - // Retrieve the value from the `custom_value` typed extension config (dynamic method). - case ValueSpecifierCase::kCustomValue: { - // Initialize the pointer to input factory on first use. - if (input_factory_ptr == nullptr) { - input_factory_ptr = std::make_unique>( - factory_context.messageValidationVisitor(), visitor); - } - // Create `DataInput` factory callback from the config. - Matcher::DataInputFactoryCb data_input_cb = - input_factory_ptr->createDataInput(builder_method.custom_value()); - auto result = data_input_cb()->get(data); - // If result has data. - if (result.data_) { - if (!result.data_.value().empty()) { - // Build the bucket id from the matched result. - bucket_id.mutable_bucket()->insert({bucket_id_key, result.data_.value()}); - } - } - break; - } - case ValueSpecifierCase::VALUE_SPECIFIER_NOT_SET: { - break; - } - PANIC_DUE_TO_CORRUPT_ENUM; - } - } - - return bucket_id; -} - -/** - * Static registration for the on match action factory. - */ -REGISTER_FACTORY(RateLimitOnMactchActionFactory, - Matcher::ActionFactory); +void RateLimitQuotaFilter::onComplete(const RateLimitQuotaBucketSettings&, RateLimitStatus) {} } // namespace RateLimitQuota } // namespace HttpFilters diff --git a/source/extensions/filters/http/rate_limit_quota/filter.h b/source/extensions/filters/http/rate_limit_quota/filter.h index dabc1923ab12..6077a92e4c51 100644 --- a/source/extensions/filters/http/rate_limit_quota/filter.h +++ b/source/extensions/filters/http/rate_limit_quota/filter.h @@ -15,6 +15,7 @@ #include "source/extensions/filters/http/common/pass_through_filter.h" #include "source/extensions/filters/http/rate_limit_quota/client.h" #include "source/extensions/filters/http/rate_limit_quota/client_impl.h" +#include "source/extensions/filters/http/rate_limit_quota/matcher.h" #include "absl/status/statusor.h" @@ -23,67 +24,24 @@ namespace Extensions { namespace HttpFilters { namespace RateLimitQuota { +using ::envoy::extensions::filters::http::rate_limit_quota::v3::RateLimitQuotaBucketSettings; +using ::envoy::service::rate_limit_quota::v3::BucketId; using FilterConfig = envoy::extensions::filters::http::rate_limit_quota::v3::RateLimitQuotaFilterConfig; using FilterConfigConstSharedPtr = std::shared_ptr; -using ValueSpecifierCase = ::envoy::extensions::filters::http::rate_limit_quota::v3:: - RateLimitQuotaBucketSettings_BucketIdBuilder_ValueBuilder::ValueSpecifierCase; -using BucketId = ::envoy::service::rate_limit_quota::v3::BucketId; using QuotaAssignmentAction = ::envoy::service::rate_limit_quota::v3::RateLimitQuotaResponse:: BucketAction::QuotaAssignmentAction; -class RateLimitQuotaValidationVisitor - : public Matcher::MatchTreeValidationVisitor { -public: - // TODO(tyxia) Add actual validation later once CEL expression is added. - absl::Status performDataInputValidation(const Matcher::DataInputFactory&, - absl::string_view) override { - return absl::OkStatus(); - } -}; - -// Contextual information used to construct the onMatch actions for a match tree. -// Currently it is empty struct. -struct RateLimitOnMactchActionContext {}; - -// This class implements the on_match action behavior. -class RateLimitOnMactchAction : public Matcher::ActionBase, - public Logger::Loggable { -public: - explicit RateLimitOnMactchAction( - envoy::extensions::filters::http::rate_limit_quota::v3::RateLimitQuotaBucketSettings settings) - : setting_(std::move(settings)) {} - - absl::StatusOr generateBucketId(const Http::Matching::HttpMatchingDataImpl& data, - Server::Configuration::FactoryContext& factory_context, - RateLimitQuotaValidationVisitor& visitor) const; - -private: - envoy::extensions::filters::http::rate_limit_quota::v3::RateLimitQuotaBucketSettings setting_; -}; - -class RateLimitOnMactchActionFactory - : public Matcher::ActionFactory { -public: - std::string name() const override { return "rate_limit_quota"; } - - Matcher::ActionFactoryCb - createActionFactoryCb(const Protobuf::Message& config, RateLimitOnMactchActionContext&, - ProtobufMessage::ValidationVisitor& validation_visitor) override { - // Validate and then retrieve the bucket settings from config. - const auto bucket_settings = - MessageUtil::downcastAndValidate(config, - validation_visitor); - return [bucket_settings = std::move(bucket_settings)]() { - return std::make_unique(std::move(bucket_settings)); - }; - } - - ProtobufTypes::MessagePtr createEmptyConfigProto() override { - return std::make_unique< - envoy::extensions::filters::http::rate_limit_quota::v3::RateLimitQuotaBucketSettings>(); - } +/** + * Possible async results for a limit call. + */ +enum class RateLimitStatus { + // The request is not over limit. + OK, + // The request is over limit. + OverLimit, + // The rate limit service could not be queried. + Error, }; class RateLimitQuotaFilter : public Http::PassThroughFilter, @@ -99,15 +57,23 @@ class RateLimitQuotaFilter : public Http::PassThroughFilter, // Http::PassThroughDecoderFilter Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap&, bool) override; void onDestroy() override {} - void setDecoderFilterCallbacks(Http::StreamDecoderFilterCallbacks& callbacks) override; + void setDecoderFilterCallbacks(Http::StreamDecoderFilterCallbacks& callbacks) override { + callbacks_ = &callbacks; + } // RateLimitQuota::RateLimitQuotaCallbacks void onQuotaResponse(envoy::service::rate_limit_quota::v3::RateLimitQuotaResponse&) override {} - // Perform request matching. It returns the generated bucket ids if the matching succeeded and - // returns the error status otherwise. - absl::StatusOr requestMatching(const Http::RequestHeaderMap& headers); + // Perform request matching. It returns the generated bucket ids if the matching succeeded, + // error status otherwise. + absl::StatusOr requestMatching(const Http::RequestHeaderMap& headers); + + Http::Matching::HttpMatchingDataImpl matchingData() { + ASSERT(data_ptr_ != nullptr); + return *data_ptr_; + } + void onComplete(const RateLimitQuotaBucketSettings&, RateLimitStatus); ~RateLimitQuotaFilter() override = default; private: diff --git a/source/extensions/filters/http/rate_limit_quota/matcher.cc b/source/extensions/filters/http/rate_limit_quota/matcher.cc new file mode 100644 index 000000000000..001f43cf3d76 --- /dev/null +++ b/source/extensions/filters/http/rate_limit_quota/matcher.cc @@ -0,0 +1,75 @@ +#include "source/extensions/filters/http/rate_limit_quota/matcher.h" + +#include "envoy/registry/registry.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace RateLimitQuota { + +using ::envoy::type::v3::RateLimitStrategy; +using ValueSpecifierCase = ::envoy::extensions::filters::http::rate_limit_quota::v3:: + RateLimitQuotaBucketSettings_BucketIdBuilder_ValueBuilder::ValueSpecifierCase; + +absl::StatusOr +RateLimitOnMactchAction::generateBucketId(const Http::Matching::HttpMatchingDataImpl& data, + Server::Configuration::FactoryContext& factory_context, + RateLimitQuotaValidationVisitor& visitor) const { + BucketId bucket_id; + std::unique_ptr> input_factory_ptr = nullptr; + // Generate the `BucketId` based on the bucked id builder from the configuration. + for (const auto& id_builder : setting_.bucket_id_builder().bucket_id_builder()) { + std::string bucket_id_key = id_builder.first; + auto builder_method = id_builder.second; + + // Generate the bucket id based on builder method type. + switch (builder_method.value_specifier_case()) { + // Retrieve the string value directly from the config (static method). + case ValueSpecifierCase::kStringValue: + bucket_id.mutable_bucket()->insert({bucket_id_key, builder_method.string_value()}); + break; + // Retrieve the value from the `custom_value` typed extension config (dynamic method). + case ValueSpecifierCase::kCustomValue: { + // Initialize the pointer to input factory on first use. + if (input_factory_ptr == nullptr) { + input_factory_ptr = std::make_unique>( + factory_context.messageValidationVisitor(), visitor); + } + // Create `DataInput` factory callback from the config. + Matcher::DataInputFactoryCb data_input_cb = + input_factory_ptr->createDataInput(builder_method.custom_value()); + auto result = data_input_cb()->get(data); + // If result has data. + if (result.data_) { + if (!result.data_.value().empty()) { + // Build the bucket id from the matched result. + bucket_id.mutable_bucket()->insert({bucket_id_key, result.data_.value()}); + } else { + // TODO(tyxia) Nothing hits this line at this moment. + return absl::InternalError("Empty resulting data from custom value config."); + } + } else { + return absl::InternalError("Failed to generate the id from custom value config."); + } + break; + } + case ValueSpecifierCase::VALUE_SPECIFIER_NOT_SET: { + PANIC_DUE_TO_PROTO_UNSET; + } + PANIC_DUE_TO_CORRUPT_ENUM; + } + } + + return bucket_id; +} + +/** + * Static registration for the on match action factory. + */ +REGISTER_FACTORY(RateLimitOnMactchActionFactory, + Matcher::ActionFactory); + +} // namespace RateLimitQuota +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/http/rate_limit_quota/matcher.h b/source/extensions/filters/http/rate_limit_quota/matcher.h new file mode 100644 index 000000000000..c4ddfeb02086 --- /dev/null +++ b/source/extensions/filters/http/rate_limit_quota/matcher.h @@ -0,0 +1,76 @@ +#pragma once + +#include "envoy/extensions/filters/http/rate_limit_quota/v3/rate_limit_quota.pb.h" +#include "envoy/extensions/filters/http/rate_limit_quota/v3/rate_limit_quota.pb.validate.h" +#include "envoy/service/rate_limit_quota/v3/rlqs.pb.h" +#include "envoy/service/rate_limit_quota/v3/rlqs.pb.validate.h" + +#include "source/common/http/matching/data_impl.h" +#include "source/common/matcher/matcher.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace RateLimitQuota { + +using ::envoy::extensions::filters::http::rate_limit_quota::v3::RateLimitQuotaBucketSettings; +using ::envoy::service::rate_limit_quota::v3::BucketId; + +class RateLimitQuotaValidationVisitor + : public Matcher::MatchTreeValidationVisitor { +public: + // TODO(tyxia) Add actual validation later once CEL expression is added. + absl::Status performDataInputValidation(const Matcher::DataInputFactory&, + absl::string_view) override { + return absl::OkStatus(); + } +}; + +// Contextual information used to construct the onMatch actions for a match tree. +// Currently it is empty struct. +struct RateLimitOnMactchActionContext {}; + +// This class implements the on_match action behavior. +class RateLimitOnMactchAction : public Matcher::ActionBase, + public Logger::Loggable { +public: + explicit RateLimitOnMactchAction(RateLimitQuotaBucketSettings settings) + : setting_(std::move(settings)) {} + + absl::StatusOr generateBucketId(const Http::Matching::HttpMatchingDataImpl& data, + Server::Configuration::FactoryContext& factory_context, + RateLimitQuotaValidationVisitor& visitor) const; + RateLimitQuotaBucketSettings bucketSettings() const { return setting_; } + +private: + RateLimitQuotaBucketSettings setting_; +}; + +class RateLimitOnMactchActionFactory + : public Matcher::ActionFactory { +public: + std::string name() const override { return "rate_limit_quota"; } + + Matcher::ActionFactoryCb + createActionFactoryCb(const Protobuf::Message& config, RateLimitOnMactchActionContext&, + ProtobufMessage::ValidationVisitor& validation_visitor) override { + // Validate and then retrieve the bucket settings from config. + const auto bucket_settings = + MessageUtil::downcastAndValidate(config, + validation_visitor); + return [bucket_settings = std::move(bucket_settings)]() { + return std::make_unique(std::move(bucket_settings)); + }; + } + + ProtobufTypes::MessagePtr createEmptyConfigProto() override { + return std::make_unique< + envoy::extensions::filters::http::rate_limit_quota::v3::RateLimitQuotaBucketSettings>(); + } +}; + +} // namespace RateLimitQuota +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/listener/proxy_protocol/BUILD b/source/extensions/filters/listener/proxy_protocol/BUILD index 7435277bdb7d..7ddeb6e56d9c 100644 --- a/source/extensions/filters/listener/proxy_protocol/BUILD +++ b/source/extensions/filters/listener/proxy_protocol/BUILD @@ -26,12 +26,15 @@ envoy_cc_library( "//source/common/buffer:buffer_lib", "//source/common/common:assert_lib", "//source/common/common:empty_string", + "//source/common/common:hex_lib", "//source/common/common:minimal_logger_lib", "//source/common/common:safe_memcpy_lib", "//source/common/common:utility_lib", "//source/common/network:address_lib", + "//source/common/network:proxy_protocol_filter_state_lib", "//source/common/network:utility_lib", "//source/extensions/common/proxy_protocol:proxy_protocol_header_lib", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/listener/proxy_protocol/v3:pkg_cc_proto", ], ) diff --git a/source/extensions/filters/listener/proxy_protocol/proxy_protocol.cc b/source/extensions/filters/listener/proxy_protocol/proxy_protocol.cc index 22267d1e9c37..ce4df5066bd9 100644 --- a/source/extensions/filters/listener/proxy_protocol/proxy_protocol.cc +++ b/source/extensions/filters/listener/proxy_protocol/proxy_protocol.cc @@ -9,6 +9,7 @@ #include "envoy/common/exception.h" #include "envoy/common/platform.h" +#include "envoy/config/core/v3/proxy_protocol.pb.h" #include "envoy/event/dispatcher.h" #include "envoy/network/listen_socket.h" #include "envoy/stats/scope.h" @@ -17,12 +18,15 @@ #include "source/common/common/assert.h" #include "source/common/common/empty_string.h" #include "source/common/common/fmt.h" +#include "source/common/common/hex.h" #include "source/common/common/safe_memcpy.h" #include "source/common/common/utility.h" #include "source/common/network/address_impl.h" +#include "source/common/network/proxy_protocol_filter_state.h" #include "source/common/network/utility.h" #include "source/extensions/common/proxy_protocol/proxy_protocol_header.h" +using envoy::config::core::v3::ProxyProtocolPassThroughTLVs; using Envoy::Extensions::Common::ProxyProtocol::PROXY_PROTO_V1_SIGNATURE; using Envoy::Extensions::Common::ProxyProtocol::PROXY_PROTO_V1_SIGNATURE_LEN; using Envoy::Extensions::Common::ProxyProtocol::PROXY_PROTO_V2_ADDR_LEN_INET; @@ -47,10 +51,21 @@ Config::Config( Stats::Scope& scope, const envoy::extensions::filters::listener::proxy_protocol::v3::ProxyProtocol& proto_config) : stats_{ALL_PROXY_PROTOCOL_STATS(POOL_COUNTER(scope))}, - allow_requests_without_proxy_protocol_(proto_config.allow_requests_without_proxy_protocol()) { + allow_requests_without_proxy_protocol_(proto_config.allow_requests_without_proxy_protocol()), + pass_all_tlvs_(proto_config.has_pass_through_tlvs() + ? proto_config.pass_through_tlvs().match_type() == + ProxyProtocolPassThroughTLVs::INCLUDE_ALL + : false) { for (const auto& rule : proto_config.rules()) { tlv_types_[0xFF & rule.tlv_type()] = rule.on_tlv_present(); } + + if (proto_config.has_pass_through_tlvs() && + proto_config.pass_through_tlvs().match_type() == ProxyProtocolPassThroughTLVs::INCLUDE) { + for (const auto& tlv_type : proto_config.pass_through_tlvs().tlv_type()) { + pass_through_tlvs_.insert(0xFF & tlv_type); + } + } } const KeyValuePair* Config::isTlvTypeNeeded(uint8_t type) const { @@ -62,6 +77,13 @@ const KeyValuePair* Config::isTlvTypeNeeded(uint8_t type) const { return nullptr; } +bool Config::isPassThroughTlvTypeNeeded(uint8_t tlv_type) const { + if (pass_all_tlvs_) { + return true; + } + return pass_through_tlvs_.contains(tlv_type); +} + size_t Config::numberOfNeededTlvTypes() const { return tlv_types_.size(); } bool Config::allowRequestsWithoutProxyProtocol() const { @@ -119,6 +141,29 @@ ReadOrParseState Filter::parseBuffer(Network::ListenerFilterBuffer& buffer) { } } + if (proxy_protocol_header_.has_value() && + !cb_->filterState().hasData( + Network::ProxyProtocolFilterState::key())) { + if (!proxy_protocol_header_.value().local_command_) { + auto buf = reinterpret_cast(buffer.rawSlice().mem_); + ENVOY_LOG( + trace, + "Parsed proxy protocol header, length: {}, buffer: {}, TLV length: {}, TLV buffer: {}", + proxy_protocol_header_.value().wholeHeaderLength(), + Envoy::Hex::encode(buf, proxy_protocol_header_.value().wholeHeaderLength()), + proxy_protocol_header_.value().extensions_length_, + Envoy::Hex::encode(buf + proxy_protocol_header_.value().headerLengthWithoutExtension(), + proxy_protocol_header_.value().extensions_length_)); + } + + cb_->filterState().setData( + Network::ProxyProtocolFilterState::key(), + std::make_unique(Network::ProxyProtocolData{ + proxy_protocol_header_.value().remote_address_, + proxy_protocol_header_.value().local_address_, parsed_tlvs_}), + StreamInfo::FilterState::StateType::Mutable, StreamInfo::FilterState::LifeSpan::Connection); + } + if (proxy_protocol_header_.has_value() && !proxy_protocol_header_.value().local_command_) { // If this is a local_command, we are not to override address // Error check the source and destination fields. Most errors are caught by the address @@ -360,10 +405,11 @@ bool Filter::parseTlvs(const uint8_t* buf, size_t len) { } // Only save to dynamic metadata if this type of TLV is needed. + absl::string_view tlv_value(reinterpret_cast(buf + idx), tlv_value_length); auto key_value_pair = config_->isTlvTypeNeeded(tlv_type); if (nullptr != key_value_pair) { ProtobufWkt::Value metadata_value; - metadata_value.set_string_value(reinterpret_cast(buf + idx), tlv_value_length); + metadata_value.set_string_value(tlv_value.data(), tlv_value.size()); std::string metadata_key = key_value_pair->metadata_namespace().empty() ? "envoy.filters.listener.proxy_protocol" @@ -374,7 +420,15 @@ bool Filter::parseTlvs(const uint8_t* buf, size_t len) { metadata.mutable_fields()->insert({key_value_pair->key(), metadata_value}); cb_->setDynamicMetadata(metadata_key, metadata); } else { - ENVOY_LOG(trace, "proxy_protocol: Skip TLV of type {} since it's not needed", tlv_type); + ENVOY_LOG(trace, + "proxy_protocol: Skip TLV of type {} since it's not needed for dynamic metadata", + tlv_type); + } + + // Save TLVs to the filter state. + if (config_->isPassThroughTlvTypeNeeded(tlv_type)) { + ENVOY_LOG(trace, "proxy_protocol: Storing parsed TLV of type {} to filter state.", tlv_type); + parsed_tlvs_.push_back({tlv_type, {tlv_value.begin(), tlv_value.end()}}); } idx += tlv_value_length; @@ -390,9 +444,9 @@ ReadOrParseState Filter::readExtensions(Network::ListenerFilterBuffer& buffer) { return ReadOrParseState::TryAgainLater; } - if (proxy_protocol_header_.value().local_command_ || 0 == config_->numberOfNeededTlvTypes()) { - // Ignores the extensions if this is a local command or there's no TLV needs to be saved - // to metadata. Those will drained from the buffer in the end. + if (proxy_protocol_header_.value().local_command_) { + // Ignores the extensions if this is a local command. + // Those will drained from the buffer in the end. return ReadOrParseState::Done; } diff --git a/source/extensions/filters/listener/proxy_protocol/proxy_protocol.h b/source/extensions/filters/listener/proxy_protocol/proxy_protocol.h index 4d594244755f..e7f6974626f1 100644 --- a/source/extensions/filters/listener/proxy_protocol/proxy_protocol.h +++ b/source/extensions/filters/listener/proxy_protocol/proxy_protocol.h @@ -60,6 +60,11 @@ class Config : public Logger::Loggable { */ size_t numberOfNeededTlvTypes() const; + /** + * Return true if the type of TLV is needed for pass-through. + */ + bool isPassThroughTlvTypeNeeded(uint8_t type) const; + /** * Filter configuration that determines if we should pass-through requests without * proxy protocol. Should only be configured to true for trusted downstreams. @@ -69,6 +74,8 @@ class Config : public Logger::Loggable { private: absl::flat_hash_map tlv_types_; const bool allow_requests_without_proxy_protocol_; + const bool pass_all_tlvs_; + absl::flat_hash_set pass_through_tlvs_{}; }; using ConfigSharedPtr = std::shared_ptr; @@ -133,6 +140,9 @@ class Filter : public Network::ListenerFilter, Logger::Loggable proxy_protocol_header_; size_t max_proxy_protocol_len_{MAX_PROXY_PROTO_LEN_V2}; + + // Store the parsed proxy protocol TLVs. + Network::ProxyProtocolTLVVector parsed_tlvs_; }; } // namespace ProxyProtocol diff --git a/source/extensions/filters/network/ext_authz/ext_authz.cc b/source/extensions/filters/network/ext_authz/ext_authz.cc index 0cb7c17c4216..7e1c6a33f057 100644 --- a/source/extensions/filters/network/ext_authz/ext_authz.cc +++ b/source/extensions/filters/network/ext_authz/ext_authz.cc @@ -106,7 +106,7 @@ void Filter::onComplete(Filters::Common::ExtAuthz::ResponsePtr&& response) { (response->status == Filters::Common::ExtAuthz::CheckStatus::Error && !config_->failureModeAllow())) { config_->stats().cx_closed_.inc(); - filter_callbacks_->connection().close(Network::ConnectionCloseType::NoFlush); + filter_callbacks_->connection().close(Network::ConnectionCloseType::NoFlush, "ext_authz_close"); filter_callbacks_->connection().streamInfo().setResponseFlag( StreamInfo::ResponseFlag::UnauthorizedExternalService); filter_callbacks_->connection().streamInfo().setResponseCodeDetails( diff --git a/source/extensions/filters/network/ratelimit/ratelimit.cc b/source/extensions/filters/network/ratelimit/ratelimit.cc index 24da05d83c56..bbc92ab1fef7 100644 --- a/source/extensions/filters/network/ratelimit/ratelimit.cc +++ b/source/extensions/filters/network/ratelimit/ratelimit.cc @@ -131,7 +131,8 @@ void Filter::complete(Filters::Common::RateLimit::LimitStatus status, if (status == Filters::Common::RateLimit::LimitStatus::OverLimit && config_->runtime().snapshot().featureEnabled("ratelimit.tcp_filter_enforcing", 100)) { config_->stats().cx_closed_.inc(); - filter_callbacks_->connection().close(Network::ConnectionCloseType::NoFlush); + filter_callbacks_->connection().close(Network::ConnectionCloseType::NoFlush, + "ratelimit_close_over_limit"); } else if (status == Filters::Common::RateLimit::LimitStatus::Error) { if (config_->failureModeAllow()) { config_->stats().failure_mode_allowed_.inc(); @@ -140,7 +141,8 @@ void Filter::complete(Filters::Common::RateLimit::LimitStatus status, } } else { config_->stats().cx_closed_.inc(); - filter_callbacks_->connection().close(Network::ConnectionCloseType::NoFlush); + filter_callbacks_->connection().close(Network::ConnectionCloseType::NoFlush, + "ratelimit_error_failure_mode_connection_close"); } } else { // We can get completion inline, so only call continue if that isn't happening. diff --git a/source/extensions/filters/network/redis_proxy/conn_pool_impl.cc b/source/extensions/filters/network/redis_proxy/conn_pool_impl.cc index fb04dfbc585e..3ff0ab6c7f6d 100644 --- a/source/extensions/filters/network/redis_proxy/conn_pool_impl.cc +++ b/source/extensions/filters/network/redis_proxy/conn_pool_impl.cc @@ -156,7 +156,8 @@ void InstanceImpl::ThreadLocalPool::onClusterAddOrUpdateNonVirtual( // its members. This is done once to minimize overhead in the data path, makeRequest() in // particular. Upstream::ClusterInfoConstSharedPtr info = cluster_->info(); - const auto& cluster_type = info->clusterType(); + OptRef cluster_type = + info->clusterType(); is_redis_cluster_ = info->lbType() == Upstream::LoadBalancerType::ClusterProvided && cluster_type.has_value() && cluster_type->name() == "envoy.clusters.redis"; } diff --git a/source/extensions/listener_managers/listener_manager/active_stream_listener_base.cc b/source/extensions/listener_managers/listener_manager/active_stream_listener_base.cc index e07eb5d74c7e..be69cc194079 100644 --- a/source/extensions/listener_managers/listener_manager/active_stream_listener_base.cc +++ b/source/extensions/listener_managers/listener_manager/active_stream_listener_base.cc @@ -55,7 +55,7 @@ void ActiveStreamListenerBase::newConnection(Network::ConnectionSocketPtr&& sock if (empty_filter_chain) { ENVOY_CONN_LOG(debug, "closing connection from {}: no filters", *server_conn_ptr, server_conn_ptr->connectionInfoProvider().remoteAddress()->asString()); - server_conn_ptr->close(Network::ConnectionCloseType::NoFlush); + server_conn_ptr->close(Network::ConnectionCloseType::NoFlush, "no_filters"); } newActiveConnection(*filter_chain, std::move(server_conn_ptr), std::move(stream_info)); } @@ -151,7 +151,8 @@ void OwnedActiveStreamListenerBase::removeFilterChain(const Network::FilterChain } else { auto& connections = iter->second->connections_; while (!connections.empty()) { - connections.front()->connection_->close(Network::ConnectionCloseType::NoFlush); + connections.front()->connection_->close(Network::ConnectionCloseType::NoFlush, + "filter_chain_is_being_removed"); } // Since is_deleting_ is on, we need to manually remove the map value and drive the // iterator. Defer delete connection container to avoid race condition in destroying diff --git a/source/extensions/listener_managers/listener_manager/active_tcp_listener.cc b/source/extensions/listener_managers/listener_manager/active_tcp_listener.cc index 6e8a9f4e03a5..7dbad4b4a883 100644 --- a/source/extensions/listener_managers/listener_manager/active_tcp_listener.cc +++ b/source/extensions/listener_managers/listener_manager/active_tcp_listener.cc @@ -54,7 +54,9 @@ ActiveTcpListener::~ActiveTcpListener() { ASSERT(active_connections != nullptr); auto& connections = active_connections->connections_; while (!connections.empty()) { - connections.front()->connection_->close(Network::ConnectionCloseType::NoFlush); + connections.front()->connection_->close( + Network::ConnectionCloseType::NoFlush, + "purging_socket_that_have_not_progressed_to_connections"); } } dispatcher().clearDeferredDeleteList(); @@ -101,6 +103,12 @@ void ActiveTcpListener::onReject(RejectCause cause) { void ActiveTcpListener::onAcceptWorker(Network::ConnectionSocketPtr&& socket, bool hand_off_restored_destination_connections, bool rebalanced) { + // Get Round Trip Time + absl::optional t = socket->lastRoundTripTime(); + if (t.has_value()) { + socket->connectionInfoProvider().setRoundTripTime(t.value()); + } + if (!rebalanced) { Network::BalancedConnectionHandler& target_handler = connection_balancer_.pickTargetHandler(*this); diff --git a/source/extensions/listener_managers/listener_manager/lds_api.cc b/source/extensions/listener_managers/listener_manager/lds_api.cc index e22c69c37044..51f795e36d5b 100644 --- a/source/extensions/listener_managers/listener_manager/lds_api.cc +++ b/source/extensions/listener_managers/listener_manager/lds_api.cc @@ -44,12 +44,9 @@ void LdsApiImpl::onConfigUpdate(const std::vector& a const std::string& system_version_info) { Config::ScopedResume maybe_resume_rds_sds; if (cm_.adsMux()) { - std::vector paused_xds_types{ - Config::getTypeUrl()}; - if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.combine_sds_requests")) { - paused_xds_types.push_back( - Config::getTypeUrl()); - } + const std::vector paused_xds_types{ + Config::getTypeUrl(), + Config::getTypeUrl()}; maybe_resume_rds_sds = cm_.adsMux()->pause(paused_xds_types); } diff --git a/source/extensions/tracers/datadog/BUILD b/source/extensions/tracers/datadog/BUILD index 0c314374f968..f21618260f50 100644 --- a/source/extensions/tracers/datadog/BUILD +++ b/source/extensions/tracers/datadog/BUILD @@ -15,11 +15,24 @@ envoy_cc_library( name = "datadog_tracer_lib", srcs = [ "datadog_tracer_impl.cc", + "dict_util.cc", + "time_util.cc", ], hdrs = [ "datadog_tracer_impl.h", + "dict_util.h", + "time_util.h", + "tracer_stats.h", + ], + copts = [ + # Make sure that headers included from dd_trace_cpp use Abseil + # equivalents of std::string_view and std::optional. + "-DDD_USE_ABSEIL_FOR_ENVOY", + ], + external_deps = [ + "dd_opentracing_cpp", + "dd_trace_cpp", ], - external_deps = ["dd_opentracing_cpp"], deps = [ "//source/common/config:utility_lib", "//source/common/http:async_client_utility_lib", diff --git a/source/extensions/tracers/datadog/dict_util.cc b/source/extensions/tracers/datadog/dict_util.cc new file mode 100644 index 000000000000..79e7f61173e2 --- /dev/null +++ b/source/extensions/tracers/datadog/dict_util.cc @@ -0,0 +1,80 @@ +#include "source/extensions/tracers/datadog/dict_util.h" + +#include + +#include "envoy/http/header_map.h" + +#include "absl/strings/str_join.h" + +namespace Envoy { +namespace Extensions { +namespace Tracers { +namespace Datadog { + +RequestHeaderWriter::RequestHeaderWriter(Http::RequestHeaderMap& headers) : headers_(headers) {} + +void RequestHeaderWriter::set(datadog::tracing::StringView key, + datadog::tracing::StringView value) { + headers_.setCopy(Http::LowerCaseString{key}, value); +} + +ResponseHeaderReader::ResponseHeaderReader(const Http::ResponseHeaderMap& headers) + : headers_(headers) {} + +datadog::tracing::Optional +ResponseHeaderReader::lookup(datadog::tracing::StringView key) const { + auto result = headers_.get(Http::LowerCaseString{key}); + if (result.empty()) { + // `headers_.get` can return multiple header entries. It conveys + // "not found" by returning zero header entries. + return datadog::tracing::nullopt; + } + + if (result.size() == 1) { + return result[0]->value().getStringView(); + } + + // There's more than one matching header entry. + // Per RFC 2616, this is the same as if there were only one entry whose + // value is the comma-separated concatenation of the multiple values. + // I don't expect the Agent to repeat response headers, and we don't even + // examine the Agent's response headers, but here's a solution anyway. + std::vector values; + values.reserve(result.size()); + for (std::size_t i = 0; i < result.size(); ++i) { + values.push_back(result[i]->value().getStringView()); + } + buffer_ = absl::StrJoin(values, ", "); + return buffer_; +} + +void ResponseHeaderReader::visit( + const std::function& + visitor) const { + headers_.iterate([&](const Http::HeaderEntry& entry) { + visitor(entry.key().getStringView(), entry.value().getStringView()); + return Http::ResponseHeaderMap::Iterate::Continue; + }); +} + +TraceContextReader::TraceContextReader(const Tracing::TraceContext& context) : context_(context) {} + +datadog::tracing::Optional +TraceContextReader::lookup(datadog::tracing::StringView key) const { + return context_.getByKey(key); +} + +void TraceContextReader::visit( + const std::function& + visitor) const { + context_.forEach([&](absl::string_view key, absl::string_view value) { + visitor(key, value); + const bool continue_iterating = true; + return continue_iterating; + }); +} + +} // namespace Datadog +} // namespace Tracers +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/tracers/datadog/dict_util.h b/source/extensions/tracers/datadog/dict_util.h new file mode 100644 index 000000000000..a844a5cce1ad --- /dev/null +++ b/source/extensions/tracers/datadog/dict_util.h @@ -0,0 +1,72 @@ +#pragma once + +/* + * This file contains implementations of the datadog::tracing::DictReader and + * datadog::tracing::DictWriter interfaces. + * + * The Datadog core tracing library, dd-trace-cpp, uses these interfaces + * anywhere it needs to read from or write to mappings of string, such as when + * extracting trace context, injecting trace context, reading HTTP response + * headers, or writing HTTP request headers. + */ + +#include +#include + +#include + +namespace Envoy { +namespace Tracing { +class TraceContext; +} // namespace Tracing +namespace Http { +class RequestHeaderMap; +class ResponseHeaderMap; +} // namespace Http +namespace Extensions { +namespace Tracers { +namespace Datadog { + +class RequestHeaderWriter : public datadog::tracing::DictWriter { +public: + explicit RequestHeaderWriter(Http::RequestHeaderMap& headers); + + void set(datadog::tracing::StringView key, datadog::tracing::StringView value) override; + +private: + Http::RequestHeaderMap& headers_; +}; + +class ResponseHeaderReader : public datadog::tracing::DictReader { +public: + explicit ResponseHeaderReader(const Http::ResponseHeaderMap& headers); + + datadog::tracing::Optional + lookup(datadog::tracing::StringView key) const override; + + void visit(const std::function& visitor) const override; + +private: + const Http::ResponseHeaderMap& headers_; + mutable std::string buffer_; +}; + +class TraceContextReader : public datadog::tracing::DictReader { +public: + explicit TraceContextReader(const Tracing::TraceContext& context); + + datadog::tracing::Optional + lookup(datadog::tracing::StringView key) const override; + + void visit(const std::function& visitor) const override; + +private: + const Tracing::TraceContext& context_; +}; + +} // namespace Datadog +} // namespace Tracers +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/tracers/datadog/time_util.cc b/source/extensions/tracers/datadog/time_util.cc new file mode 100644 index 000000000000..201452aa3abc --- /dev/null +++ b/source/extensions/tracers/datadog/time_util.cc @@ -0,0 +1,26 @@ +#include "source/extensions/tracers/datadog/time_util.h" + +#include + +namespace Envoy { +namespace Extensions { +namespace Tracers { +namespace Datadog { + +datadog::tracing::TimePoint estimateTime(SystemTime wall) { + return estimateTime(wall, datadog::tracing::default_clock); +} + +datadog::tracing::TimePoint estimateTime(SystemTime wall, const datadog::tracing::Clock& clock) { + datadog::tracing::TimePoint point = clock(); + if (point.wall > wall) { + point.tick -= point.wall - wall; + } + point.wall = wall; + return point; +} + +} // namespace Datadog +} // namespace Tracers +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/tracers/datadog/time_util.h b/source/extensions/tracers/datadog/time_util.h new file mode 100644 index 000000000000..723c4ded82d2 --- /dev/null +++ b/source/extensions/tracers/datadog/time_util.h @@ -0,0 +1,45 @@ +#pragma once + +/** + * This file contains functions related to time points and durations. + * + * Envoy has a time type that contains both a system time point and a steady + * ("monotonic") time point. However, only the system time is exposed to the + * tracing subsystem. This may be remedied in the future, but for now we work + * with the system time. + * + * This is problematic for the Datadog core tracing library (dd-trace-cpp), + * because it uses the steady time to calculate the duration of a span + * (end.tick - begin.tick). So, we need to get a steady clock time from a given system + * clock time. The scheme is to measure the current system/steady time, + * compare the system part with the given system time, and then adjust the + * measured steady time accordingly. This is correct if the system clock has not + * been adjusted since the given system time was measured. It's incorrect + * otherwise, hence only an estimate. This conversion is performed by the + * estimateTime function. + */ + +#include + +#include "envoy/common/time.h" + +namespace Envoy { +namespace Extensions { +namespace Tracers { +namespace Datadog { + +/** + * Convert a specified system \p time to a datadog time point, estimating the + steady portion of the result by examining the current time as measured by + the optionally specified \p clock and comparing it with the given \p time. + * @param time system time to convert from + * @param clock datadog clock used to measure the current time (default clock if omitted) + * @return datadog time point whose steady portion is estimated from the given \p time. + */ +datadog::tracing::TimePoint estimateTime(SystemTime time); +datadog::tracing::TimePoint estimateTime(SystemTime time, const datadog::tracing::Clock& clock); + +} // namespace Datadog +} // namespace Tracers +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/tracers/datadog/tracer_stats.h b/source/extensions/tracers/datadog/tracer_stats.h new file mode 100644 index 000000000000..543507d95b95 --- /dev/null +++ b/source/extensions/tracers/datadog/tracer_stats.h @@ -0,0 +1,28 @@ +#include "envoy/stats/scope.h" +#include "envoy/stats/stats_macros.h" + +namespace Envoy { +namespace Extensions { +namespace Tracers { +namespace Datadog { + +#define DATADOG_TRACER_STATS(COUNTER) \ + COUNTER(reports_skipped_no_cluster) \ + COUNTER(reports_sent) \ + COUNTER(reports_dropped) \ + COUNTER(reports_failed) + +struct TracerStats { + DATADOG_TRACER_STATS(GENERATE_COUNTER_STRUCT) +}; + +inline TracerStats makeTracerStats(Stats::Scope& scope) { + return TracerStats{DATADOG_TRACER_STATS(POOL_COUNTER_PREFIX(scope, "tracing.datadog."))}; +} + +#undef DATADOG_TRACER_STATS + +} // namespace Datadog +} // namespace Tracers +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/transport_sockets/alts/tsi_socket.cc b/source/extensions/transport_sockets/alts/tsi_socket.cc index f5822ac0eaf5..c882c9a96cef 100644 --- a/source/extensions/transport_sockets/alts/tsi_socket.cc +++ b/source/extensions/transport_sockets/alts/tsi_socket.cc @@ -63,7 +63,8 @@ void TsiSocket::doHandshakeNext() { callbacks_->connection().connectionInfoProvider().remoteAddress()); if (!handshaker_) { ENVOY_CONN_LOG(warn, "TSI: failed to create handshaker", callbacks_->connection()); - callbacks_->connection().close(Network::ConnectionCloseType::NoFlush); + callbacks_->connection().close(Network::ConnectionCloseType::NoFlush, + "failed_creating_handshaker"); return; } @@ -373,7 +374,7 @@ void TsiSocket::onNextDone(NextResultPtr&& result) { Network::PostIoAction action = doHandshakeNextDone(std::move(result)); if (action == Network::PostIoAction::Close) { - callbacks_->connection().close(Network::ConnectionCloseType::NoFlush); + callbacks_->connection().close(Network::ConnectionCloseType::NoFlush, "tsi_handshake_failed"); } } diff --git a/source/extensions/transport_sockets/proxy_protocol/BUILD b/source/extensions/transport_sockets/proxy_protocol/BUILD index 2917ee384df4..4e91f1ad363b 100644 --- a/source/extensions/transport_sockets/proxy_protocol/BUILD +++ b/source/extensions/transport_sockets/proxy_protocol/BUILD @@ -31,6 +31,7 @@ envoy_cc_library( "//envoy/network:connection_interface", "//envoy/network:transport_socket_interface", "//source/common/buffer:buffer_lib", + "//source/common/common:hex_lib", "//source/common/common:scalar_to_byte_vector_lib", "//source/common/common:utility_lib", "//source/common/network:address_lib", diff --git a/source/extensions/transport_sockets/proxy_protocol/config.cc b/source/extensions/transport_sockets/proxy_protocol/config.cc index 9e62bc11fb1d..cf8f1e08b387 100644 --- a/source/extensions/transport_sockets/proxy_protocol/config.cc +++ b/source/extensions/transport_sockets/proxy_protocol/config.cc @@ -26,8 +26,8 @@ UpstreamProxyProtocolSocketConfigFactory::createTransportSocketFactory( outer_config.transport_socket(), context.messageValidationVisitor(), inner_config_factory); auto inner_transport_factory = inner_config_factory.createTransportSocketFactory(*inner_factory_config, context); - return std::make_unique(std::move(inner_transport_factory), - outer_config.config()); + return std::make_unique( + std::move(inner_transport_factory), outer_config.config(), context.scope()); } ProtobufTypes::MessagePtr UpstreamProxyProtocolSocketConfigFactory::createEmptyConfigProto() { diff --git a/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.cc b/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.cc index 73d9c2272a45..537483186b21 100644 --- a/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.cc +++ b/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.cc @@ -6,6 +6,7 @@ #include "envoy/network/transport_socket.h" #include "source/common/buffer/buffer_impl.h" +#include "source/common/common/hex.h" #include "source/common/common/scalar_to_byte_vector.h" #include "source/common/common/utility.h" #include "source/common/network/address_impl.h" @@ -13,16 +14,34 @@ using envoy::config::core::v3::ProxyProtocolConfig; using envoy::config::core::v3::ProxyProtocolConfig_Version; +using envoy::config::core::v3::ProxyProtocolPassThroughTLVs; namespace Envoy { namespace Extensions { namespace TransportSockets { namespace ProxyProtocol { +UpstreamProxyProtocolStats GenerateUpstreamProxyProtocolStats(Stats::Scope& stats_scope) { + const char prefix[]{"upstream.proxyprotocol."}; + return {ALL_PROXY_PROTOCOL_TRANSPORT_SOCKET_STATS(POOL_COUNTER_PREFIX(stats_scope, prefix))}; +} + UpstreamProxyProtocolSocket::UpstreamProxyProtocolSocket( Network::TransportSocketPtr&& transport_socket, - Network::TransportSocketOptionsConstSharedPtr options, ProxyProtocolConfig_Version version) - : PassthroughSocket(std::move(transport_socket)), options_(options), version_(version) {} + Network::TransportSocketOptionsConstSharedPtr options, ProxyProtocolConfig config, + Stats::Scope& scope) + : PassthroughSocket(std::move(transport_socket)), options_(options), version_(config.version()), + stats_(GenerateUpstreamProxyProtocolStats(scope)), + pass_all_tlvs_(config.has_pass_through_tlvs() ? config.pass_through_tlvs().match_type() == + ProxyProtocolPassThroughTLVs::INCLUDE_ALL + : false) { + if (config.has_pass_through_tlvs() && + config.pass_through_tlvs().match_type() == ProxyProtocolPassThroughTLVs::INCLUDE) { + for (const auto& tlv_type : config.pass_through_tlvs().tlv_type()) { + pass_through_tlvs_.insert(0xFF & tlv_type); + } + } +} void UpstreamProxyProtocolSocket::setTransportSocketCallbacks( Network::TransportSocketCallbacks& callbacks) { @@ -66,13 +85,26 @@ void UpstreamProxyProtocolSocket::generateHeaderV1() { Common::ProxyProtocol::generateV1Header(*src_addr->ip(), *dst_addr->ip(), header_buffer_); } +namespace { +std::string toHex(const Buffer::Instance& buffer) { + std::string bufferStr = buffer.toString(); + return Hex::encode(reinterpret_cast(bufferStr.data()), bufferStr.length()); +} +} // namespace + void UpstreamProxyProtocolSocket::generateHeaderV2() { if (!options_ || !options_->proxyProtocolOptions().has_value()) { Common::ProxyProtocol::generateV2LocalHeader(header_buffer_); } else { const auto options = options_->proxyProtocolOptions().value(); - Common::ProxyProtocol::generateV2Header(*options.src_addr_->ip(), *options.dst_addr_->ip(), - header_buffer_); + if (!Common::ProxyProtocol::generateV2Header(options, header_buffer_, pass_all_tlvs_, + pass_through_tlvs_)) { + // There is a warn log in generateV2Header method. + stats_.v2_tlvs_exceed_max_length_.inc(); + } + + ENVOY_LOG(trace, "generated proxy protocol v2 header, length: {}, buffer: {}", + header_buffer_.length(), toHex(header_buffer_)); } } @@ -108,8 +140,9 @@ void UpstreamProxyProtocolSocket::onConnected() { } UpstreamProxyProtocolSocketFactory::UpstreamProxyProtocolSocketFactory( - Network::UpstreamTransportSocketFactoryPtr transport_socket_factory, ProxyProtocolConfig config) - : PassthroughFactory(std::move(transport_socket_factory)), config_(config) {} + Network::UpstreamTransportSocketFactoryPtr transport_socket_factory, ProxyProtocolConfig config, + Stats::Scope& scope) + : PassthroughFactory(std::move(transport_socket_factory)), config_(config), scope_(scope) {} Network::TransportSocketPtr UpstreamProxyProtocolSocketFactory::createTransportSocket( Network::TransportSocketOptionsConstSharedPtr options, @@ -118,8 +151,8 @@ Network::TransportSocketPtr UpstreamProxyProtocolSocketFactory::createTransportS if (inner_socket == nullptr) { return nullptr; } - return std::make_unique(std::move(inner_socket), options, - config_.version()); + return std::make_unique(std::move(inner_socket), options, config_, + scope_); } void UpstreamProxyProtocolSocketFactory::hashKey( diff --git a/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.h b/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.h index 583f79bca273..1a92423c9a1c 100644 --- a/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.h +++ b/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.h @@ -3,6 +3,7 @@ #include "envoy/config/core/v3/proxy_protocol.pb.h" #include "envoy/network/connection.h" #include "envoy/network/transport_socket.h" +#include "envoy/stats/stats.h" #include "source/common/buffer/buffer_impl.h" #include "source/common/common/logger.h" @@ -16,12 +17,23 @@ namespace Extensions { namespace TransportSockets { namespace ProxyProtocol { +#define ALL_PROXY_PROTOCOL_TRANSPORT_SOCKET_STATS(COUNTER) \ + /* Upstream events counter. */ \ + COUNTER(v2_tlvs_exceed_max_length) + +/** + * Wrapper struct for upstream ProxyProtocol stats. @see stats_macros.h + */ +struct UpstreamProxyProtocolStats { + ALL_PROXY_PROTOCOL_TRANSPORT_SOCKET_STATS(GENERATE_COUNTER_STRUCT) +}; + class UpstreamProxyProtocolSocket : public TransportSockets::PassthroughSocket, public Logger::Loggable { public: UpstreamProxyProtocolSocket(Network::TransportSocketPtr&& transport_socket, Network::TransportSocketOptionsConstSharedPtr options, - ProxyProtocolConfig_Version version); + ProxyProtocolConfig config, Stats::Scope& scope); void setTransportSocketCallbacks(Network::TransportSocketCallbacks& callbacks) override; Network::IoResult doWrite(Buffer::Instance& buffer, bool end_stream) override; @@ -37,13 +49,16 @@ class UpstreamProxyProtocolSocket : public TransportSockets::PassthroughSocket, Network::TransportSocketCallbacks* callbacks_{}; Buffer::OwnedImpl header_buffer_{}; ProxyProtocolConfig_Version version_{ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1}; + UpstreamProxyProtocolStats stats_; + const bool pass_all_tlvs_; + absl::flat_hash_set pass_through_tlvs_{}; }; class UpstreamProxyProtocolSocketFactory : public PassthroughFactory { public: UpstreamProxyProtocolSocketFactory( Network::UpstreamTransportSocketFactoryPtr transport_socket_factory, - ProxyProtocolConfig config); + ProxyProtocolConfig config, Stats::Scope& scope); // Network::UpstreamTransportSocketFactory Network::TransportSocketPtr @@ -54,6 +69,7 @@ class UpstreamProxyProtocolSocketFactory : public PassthroughFactory { private: ProxyProtocolConfig config_; + Stats::Scope& scope_; }; } // namespace ProxyProtocol diff --git a/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index 60a50e4724c8..d5a46c7f739a 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.cc +++ b/source/extensions/transport_sockets/tls/ssl_socket.cc @@ -175,7 +175,8 @@ void SslSocket::resumeHandshake() { PostIoAction action = doHandshake(); if (action == PostIoAction::Close) { ENVOY_CONN_LOG(debug, "async handshake completion error", callbacks_->connection()); - callbacks_->connection().close(Network::ConnectionCloseType::FlushWrite); + callbacks_->connection().close(Network::ConnectionCloseType::FlushWrite, + "failed_resuming_async_handshake"); } } diff --git a/source/extensions/upstreams/http/tcp/upstream_request.cc b/source/extensions/upstreams/http/tcp/upstream_request.cc index c00d4a7db700..aa96aa176d8d 100644 --- a/source/extensions/upstreams/http/tcp/upstream_request.cc +++ b/source/extensions/upstreams/http/tcp/upstream_request.cc @@ -89,7 +89,8 @@ void TcpUpstream::readDisable(bool disable) { void TcpUpstream::resetStream() { upstream_request_ = nullptr; - upstream_conn_data_->connection().close(Network::ConnectionCloseType::NoFlush); + upstream_conn_data_->connection().close(Network::ConnectionCloseType::NoFlush, + "tcp_upstream_reset_stream"); } void TcpUpstream::onUpstreamData(Buffer::Instance& data, bool end_stream) { diff --git a/source/server/api_listener_impl.h b/source/server/api_listener_impl.h index 79fcdc811929..98fffeb32e9b 100644 --- a/source/server/api_listener_impl.h +++ b/source/server/api_listener_impl.h @@ -120,6 +120,7 @@ class ApiListenerImplBase : public ApiListener, return false; } void close(Network::ConnectionCloseType) override {} + void close(Network::ConnectionCloseType, absl::string_view) override {} Event::Dispatcher& dispatcher() override { return parent_.parent_.factory_context_.mainThreadDispatcher(); } @@ -161,6 +162,7 @@ class ApiListenerImplBase : public ApiListener, const StreamInfo::StreamInfo& streamInfo() const override { return stream_info_; } void setDelayedCloseTimeout(std::chrono::milliseconds) override {} absl::string_view transportFailureReason() const override { return EMPTY_STRING; } + absl::string_view localCloseReason() const override { return EMPTY_STRING; } bool startSecureTransport() override { IS_ENVOY_BUG("Unexpected function call"); return false; diff --git a/source/server/options_impl.h b/source/server/options_impl.h index c5cc3269066f..6d7f8b7294a7 100644 --- a/source/server/options_impl.h +++ b/source/server/options_impl.h @@ -64,7 +64,10 @@ class OptionsImpl : public Server::Options, protected Logger::Loggable&& config_proto) { + config_proto_ = std::move(config_proto); } void setConfigYaml(const std::string& config_yaml) { config_yaml_ = config_yaml; } void setAdminAddressPath(const std::string& admin_address_path) { @@ -123,7 +126,7 @@ class OptionsImpl : public Server::Options, protected Logger::Loggable config_proto_{ + new envoy::config::bootstrap::v3::Bootstrap()}; std::string config_yaml_; bool allow_unknown_static_fields_{false}; bool reject_unknown_dynamic_fields_{false}; diff --git a/source/server/server.cc b/source/server/server.cc index 4dbf349b49f5..1a8594b3cbd0 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -377,7 +377,7 @@ void InstanceUtil::loadBootstrapConfig(envoy::config::bootstrap::v3::Bootstrap& const std::string& config_yaml = options.configYaml(); const envoy::config::bootstrap::v3::Bootstrap& config_proto = options.configProto(); - // Exactly one of config_path and config_yaml should be specified. + // One of config_path and config_yaml or bootstrap should be specified. if (config_path.empty() && config_yaml.empty() && config_proto.ByteSizeLong() == 0) { throw EnvoyException("At least one of --config-path or --config-yaml or Options::configProto() " "should be non-empty"); diff --git a/test/common/api/os_sys_calls_test.cc b/test/common/api/os_sys_calls_test.cc index 432a43b737e0..29f371af823e 100644 --- a/test/common/api/os_sys_calls_test.cc +++ b/test/common/api/os_sys_calls_test.cc @@ -68,31 +68,4 @@ TEST(OsSyscallsTest, OpenPwritePreadFstatCloseStatUnlink) { TestEnvironment::removePath(path); } -TEST(OsSyscallsTest, SetAlternateGetifaddrs) { - auto& os_syscalls = Api::OsSysCallsSingleton::get(); - const bool pre_alternate_support = os_syscalls.supportsGetifaddrs(); - Api::InterfaceAddressVector interfaces{}; -#if defined(WIN32) || (defined(__ANDROID_API__) && __ANDROID_API__ < 24) - EXPECT_FALSE(pre_alternate_support); - EXPECT_DEATH(os_syscalls.getifaddrs(interfaces), "not implemented"); -#else - EXPECT_TRUE(pre_alternate_support); - const auto pre_alternate_rc = os_syscalls.getifaddrs(interfaces); - EXPECT_EQ(0, pre_alternate_rc.return_value_); - EXPECT_FALSE(interfaces.empty()); -#endif - - os_syscalls.setAlternateGetifaddrs( - [](Api::InterfaceAddressVector& interfaces) -> Api::SysCallIntResult { - interfaces.emplace_back("made_up_if", 0, nullptr); - return {0, 0}; - }); - interfaces.clear(); - - const bool post_alternate_support = os_syscalls.supportsGetifaddrs(); - EXPECT_TRUE(post_alternate_support); - EXPECT_EQ(0, os_syscalls.getifaddrs(interfaces).return_value_); - EXPECT_EQ(1, interfaces.size()); - EXPECT_EQ("made_up_if", interfaces.front().interface_name_); -} } // namespace Envoy diff --git a/test/common/buffer/buffer_test.cc b/test/common/buffer/buffer_test.cc index 132e747ed18a..f83f50a1ebe0 100644 --- a/test/common/buffer/buffer_test.cc +++ b/test/common/buffer/buffer_test.cc @@ -1103,7 +1103,6 @@ TEST(BufferHelperTest, AddFragments) { } } } - } // namespace } // namespace Buffer } // namespace Envoy diff --git a/test/common/config/subscription_factory_impl_test.cc b/test/common/config/subscription_factory_impl_test.cc index 5ed43a60b8ff..c8adc47d37c6 100644 --- a/test/common/config/subscription_factory_impl_test.cc +++ b/test/common/config/subscription_factory_impl_test.cc @@ -108,28 +108,6 @@ TEST_F(SubscriptionFactoryTest, NoConfigSpecifier) { "Missing config source specifier in envoy::config::core::v3::ConfigSource"); } -TEST_F(SubscriptionFactoryTest, UnsupportedConfigSourceAggregatedGrpc) { - envoy::config::core::v3::ConfigSource config; - Upstream::ClusterManager::ClusterSet primary_clusters; - config.mutable_api_config_source()->set_api_type( - envoy::config::core::v3::ApiConfigSource::AGGREGATED_GRPC); - config.mutable_api_config_source()->set_transport_api_version(envoy::config::core::v3::V3); - EXPECT_CALL(cm_, primaryClusters()).WillOnce(ReturnRef(primary_clusters)); - EXPECT_THROW_WITH_MESSAGE(subscriptionFromConfigSource(config), EnvoyException, - "Unsupported config source AGGREGATED_GRPC"); -} - -TEST_F(SubscriptionFactoryTest, UnsupportedConfigSourceAggregatedDeltaGrpc) { - envoy::config::core::v3::ConfigSource config; - Upstream::ClusterManager::ClusterSet primary_clusters; - config.mutable_api_config_source()->set_api_type( - envoy::config::core::v3::ApiConfigSource::AGGREGATED_DELTA_GRPC); - config.mutable_api_config_source()->set_transport_api_version(envoy::config::core::v3::V3); - EXPECT_CALL(cm_, primaryClusters()).WillOnce(ReturnRef(primary_clusters)); - EXPECT_THROW_WITH_MESSAGE(subscriptionFromConfigSource(config), EnvoyException, - "Unsupported config source AGGREGATED_DELTA_GRPC"); -} - TEST_F(SubscriptionFactoryTest, RestClusterEmpty) { envoy::config::core::v3::ConfigSource config; Upstream::ClusterManager::ClusterSet primary_clusters; @@ -415,11 +393,14 @@ TEST_P(SubscriptionFactoryTestUnifiedOrLegacyMux, GrpcCollectionAggregatedSubscr Upstream::ClusterManager::ClusterSet primary_clusters; primary_clusters.insert("static_cluster"); EXPECT_CALL(cm_, primaryClusters()).WillOnce(ReturnRef(primary_clusters)); - GrpcMuxSharedPtr ads_mux = std::make_shared>(); + auto ads_mux = std::make_shared>(); EXPECT_CALL(cm_, adsMux()).WillOnce(Return(ads_mux)); EXPECT_CALL(dispatcher_, createTimer_(_)); // onConfigUpdateFailed() should not be called for gRPC stream connection failure EXPECT_CALL(callbacks_, onConfigUpdateFailed(_, _)).Times(0); + // Since this is ADS, the mux's start() should not be called (which attempts to create a gRPC + // stream). + EXPECT_CALL(*ads_mux, start()).Times(0); collectionSubscriptionFromUrl("xdstp://foo/envoy.config.endpoint.v3.ClusterLoadAssignment/bar", config) ->start({}); diff --git a/test/common/event/scaled_range_timer_manager_impl_test.cc b/test/common/event/scaled_range_timer_manager_impl_test.cc index dbd7ca897a12..40a5fd02c8f2 100644 --- a/test/common/event/scaled_range_timer_manager_impl_test.cc +++ b/test/common/event/scaled_range_timer_manager_impl_test.cc @@ -9,6 +9,7 @@ #include "test/mocks/common.h" #include "test/mocks/event/wrapped_dispatcher.h" #include "test/test_common/simulated_time_system.h" +#include "test/test_common/utility.h" #include "gtest/gtest.h" diff --git a/test/common/formatter/substitution_formatter_fuzz_test.dict b/test/common/formatter/substitution_formatter_fuzz_test.dict index 4886e898c93f..637faeacff65 100644 --- a/test/common/formatter/substitution_formatter_fuzz_test.dict +++ b/test/common/formatter/substitution_formatter_fuzz_test.dict @@ -11,6 +11,7 @@ "%RESPONSE_DURATION%" "%RESPONSE_FLAGS%" "%RESPONSE_TX_DURATION%" +"%ROUNDTRIP_DURATION%" "%DOWNSTREAM_HANDSHAKE_DURATION%" "%ROUTE_NAME%" "%UPSTREAM_HOST%" diff --git a/test/common/formatter/substitution_formatter_test.cc b/test/common/formatter/substitution_formatter_test.cc index 69d2bd7cab22..0202af5b5ae0 100644 --- a/test/common/formatter/substitution_formatter_test.cc +++ b/test/common/formatter/substitution_formatter_test.cc @@ -326,6 +326,31 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::numberValue(25.0))); } + { + StreamInfoFormatter roundtrip_duration_format("ROUNDTRIP_DURATION"); + + EXPECT_EQ(absl::nullopt, + roundtrip_duration_format.format(request_headers, response_headers, response_trailers, + stream_info, body)); + EXPECT_THAT(roundtrip_duration_format.formatValue(request_headers, response_headers, + response_trailers, stream_info, body), + ProtoEq(ValueUtil::nullValue())); + } + + { + StreamInfoFormatter roundtrip_duration_format("ROUNDTRIP_DURATION"); + + EXPECT_CALL(time_system, monotonicTime) + .WillOnce(Return(MonotonicTime(std::chrono::nanoseconds(25000000)))); + stream_info.downstream_timing_.onLastDownstreamAckReceived(time_system); + + EXPECT_EQ("25", roundtrip_duration_format.format(request_headers, response_headers, + response_trailers, stream_info, body)); + EXPECT_THAT(roundtrip_duration_format.formatValue(request_headers, response_headers, + response_trailers, stream_info, body), + ProtoEq(ValueUtil::numberValue(25.0))); + } + { StreamInfoFormatter bytes_received_format("BYTES_RECEIVED"); EXPECT_CALL(stream_info, bytesReceived()).WillRepeatedly(Return(1)); diff --git a/test/common/grpc/grpc_client_integration.h b/test/common/grpc/grpc_client_integration.h index f22a30ee4145..84abeda51cf3 100644 --- a/test/common/grpc/grpc_client_integration.h +++ b/test/common/grpc/grpc_client_integration.h @@ -18,8 +18,6 @@ enum class SotwOrDelta { Sotw, Delta, UnifiedSotw, UnifiedDelta }; // Unified or Legacy grpc mux implementation enum class LegacyOrUnified { Legacy, Unified }; -enum class EdsUpdateMode { Multiplexed, StreamPerCluster }; - class BaseGrpcClientIntegrationParamTest { public: virtual ~BaseGrpcClientIntegrationParamTest() = default; @@ -123,27 +121,6 @@ class DeltaSotwIntegrationParamTest SotwOrDelta sotwOrDelta() const { return std::get<2>(GetParam()); } }; -class MultiplexedDeltaSotwIntegrationParamTest - : public BaseGrpcClientIntegrationParamTest, - public testing::TestWithParam< - std::tuple> { -public: - ~MultiplexedDeltaSotwIntegrationParamTest() override = default; - static std::string protocolTestParamsToString( - const ::testing::TestParamInfo< - std::tuple>& p) { - return fmt::format( - "{}_{}_{}_{}", std::get<0>(p.param) == Network::Address::IpVersion::v4 ? "IPv4" : "IPv6", - std::get<1>(p.param) == ClientType::GoogleGrpc ? "GoogleGrpc" : "EnvoyGrpc", - std::get<2>(p.param) == SotwOrDelta::Delta ? "Delta" : "StateOfTheWorld", - std::get<3>(p.param) == EdsUpdateMode::Multiplexed ? "Multiplexed" : "StreamPerCluster"); - } - Network::Address::IpVersion ipVersion() const override { return std::get<0>(GetParam()); } - ClientType clientType() const override { return std::get<1>(GetParam()); } - SotwOrDelta sotwOrDelta() const { return std::get<2>(GetParam()); } - EdsUpdateMode edsUpdateMode() const { return std::get<3>(GetParam()); } -}; - // Skip tests based on gRPC client type. #define SKIP_IF_GRPC_CLIENT(client_type) \ if (clientType() == (client_type)) { \ @@ -169,12 +146,6 @@ class MultiplexedDeltaSotwIntegrationParamTest testing::Combine(testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), \ testing::ValuesIn(TestEnvironment::getsGrpcVersionsForTest()), \ testing::Values(Grpc::LegacyOrUnified::Legacy, Grpc::LegacyOrUnified::Unified)) -#define EDS_MODE_DELTA_SOTW_GRPC_CLIENT_INTEGRATION_PARAMS \ - testing::Combine( \ - testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), \ - testing::ValuesIn(TestEnvironment::getsGrpcVersionsForTest()), \ - testing::Values(Grpc::SotwOrDelta::Sotw, Grpc::SotwOrDelta::Delta), \ - testing::Values(Grpc::EdsUpdateMode::Multiplexed, Grpc::EdsUpdateMode::StreamPerCluster)) #define DELTA_SOTW_UNIFIED_GRPC_CLIENT_INTEGRATION_PARAMS \ testing::Combine(testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), \ testing::ValuesIn(TestEnvironment::getsGrpcVersionsForTest()), \ diff --git a/test/common/http/async_client_impl_test.cc b/test/common/http/async_client_impl_test.cc index 822b4cbe4422..ddbd9a4e1cde 100644 --- a/test/common/http/async_client_impl_test.cc +++ b/test/common/http/async_client_impl_test.cc @@ -36,6 +36,7 @@ using testing::Invoke; using testing::NiceMock; using testing::Return; using testing::ReturnRef; +using testing::StrictMock; namespace Envoy { namespace Http { @@ -81,8 +82,8 @@ class AsyncClientImplTest : public testing::Test { TestRequestHeaderMapImpl headers_{}; RequestMessagePtr message_{new RequestMessageImpl()}; Stats::MockIsolatedStatsStore stats_store_; - MockAsyncClientCallbacks callbacks_; - MockAsyncClientStreamCallbacks stream_callbacks_; + NiceMock callbacks_; + NiceMock stream_callbacks_; NiceMock cm_; NiceMock stream_encoder_; ResponseDecoder* response_decoder_{}; @@ -203,6 +204,157 @@ TEST_F(AsyncClientImplTest, Basic) { .value()); } +TEST_F(AsyncClientImplTest, BasicOngoingRequest) { + auto headers = std::make_unique(); + HttpTestUtility::addDefaultHeaders(*headers); + TestRequestHeaderMapImpl headers_copy = *headers; + + Buffer::OwnedImpl data("test data"); + const Buffer::OwnedImpl data_copy(data.toString()); + + auto trailers = std::make_unique(); + trailers->addCopy("some", "trailer"); + const TestRequestTrailerMapImpl trailers_copy = *trailers; + + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _, _)) + .WillOnce(Invoke( + [&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks, + const ConnectionPool::Instance::StreamOptions&) -> ConnectionPool::Cancellable* { + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); + response_decoder_ = &decoder; + return nullptr; + })); + + headers_copy.addCopy("x-envoy-internal", "true"); + headers_copy.addCopy("x-forwarded-for", "127.0.0.1"); + + EXPECT_CALL(stream_encoder_, encodeHeaders(HeaderMapEqualRef(&headers_copy), false)); + EXPECT_CALL(stream_encoder_, encodeData(BufferEqual(&data_copy), false)); + EXPECT_CALL(stream_encoder_, encodeTrailers(HeaderMapEqualRef(&trailers_copy))); + + AsyncClient::OngoingRequest* request = + client_.startRequest(std::move(headers), callbacks_, AsyncClient::RequestOptions()); + EXPECT_NE(request, nullptr); + + request->sendData(data, false); + request->captureAndSendTrailers(std::move(trailers)); + + expectSuccess(request, 200); + + ResponseHeaderMapPtr response_headers(new TestResponseHeaderMapImpl{{":status", "200"}}); + response_decoder_->decodeHeaders(std::move(response_headers), false); + Buffer::OwnedImpl response_data("test data"); + response_decoder_->decodeData(response_data, true); + + EXPECT_EQ( + 1UL, + cm_.thread_local_cluster_.cluster_.info_->stats_store_.counter("upstream_rq_200").value()); + EXPECT_EQ(1UL, cm_.thread_local_cluster_.cluster_.info_->stats_store_ + .counter("internal.upstream_rq_200") + .value()); +} + +TEST_F(AsyncClientImplTest, OngoingRequestWithWatermarking) { + auto headers = std::make_unique(); + HttpTestUtility::addDefaultHeaders(*headers); + TestRequestHeaderMapImpl headers_copy = *headers; + headers_copy.addCopy("x-envoy-internal", "true"); + headers_copy.addCopy("x-forwarded-for", "127.0.0.1"); + + Buffer::OwnedImpl data("test data"); + const Buffer::OwnedImpl data_copy(data.toString()); + + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _, _)) + .WillOnce(Invoke( + [&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks, + const ConnectionPool::Instance::StreamOptions&) -> ConnectionPool::Cancellable* { + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); + // Pretend like the connection is already backed up. + dynamic_cast(stream_encoder_.getStream()).runHighWatermarkCallbacks(); + response_decoder_ = &decoder; + return nullptr; + })); + + EXPECT_CALL(stream_encoder_, encodeHeaders(HeaderMapEqualRef(&headers_copy), false)); + + auto* request = + client_.startRequest(std::move(headers), callbacks_, AsyncClient::RequestOptions()); + EXPECT_NE(request, nullptr); + StrictMock watermark_callbacks; + // Registering a new watermark callback should note that the high watermark has already been hit. + EXPECT_CALL(watermark_callbacks, onDecoderFilterAboveWriteBufferHighWatermark()); + request->setWatermarkCallbacks(watermark_callbacks); + + // Upstream gets unblocked. + EXPECT_CALL(watermark_callbacks, onDecoderFilterBelowWriteBufferLowWatermark()); + dynamic_cast(stream_encoder_.getStream()).runLowWatermarkCallbacks(); + + EXPECT_CALL(stream_encoder_, encodeData(BufferEqual(&data_copy), false)); + request->sendData(data, false); + // Blocked again. + EXPECT_CALL(watermark_callbacks, onDecoderFilterAboveWriteBufferHighWatermark()); + dynamic_cast(stream_encoder_.getStream()).runHighWatermarkCallbacks(); + + // Clear the callback, which calls the low watermark function. + EXPECT_CALL(watermark_callbacks, onDecoderFilterBelowWriteBufferLowWatermark()); + request->removeWatermarkCallbacks(); + // Add the callback back. + EXPECT_CALL(watermark_callbacks, onDecoderFilterAboveWriteBufferHighWatermark()); + request->setWatermarkCallbacks(watermark_callbacks); + + EXPECT_CALL(stream_encoder_, encodeData(BufferStringEqual(""), true)); + Buffer::OwnedImpl empty; + request->sendData(empty, true); + + ResponseHeaderMapPtr response_headers(new TestResponseHeaderMapImpl{{":status", "200"}}); + // On request end, we expect to run the low watermark callbacks. + EXPECT_CALL(watermark_callbacks, onDecoderFilterBelowWriteBufferLowWatermark()); + response_decoder_->decodeHeaders(std::move(response_headers), true); +} + +TEST_F(AsyncClientImplTest, OngoingRequestWithWatermarkingAndReset) { + auto headers = std::make_unique(); + HttpTestUtility::addDefaultHeaders(*headers); + TestRequestHeaderMapImpl headers_copy = *headers; + headers_copy.addCopy("x-envoy-internal", "true"); + headers_copy.addCopy("x-forwarded-for", "127.0.0.1"); + + Buffer::OwnedImpl data("test data"); + const Buffer::OwnedImpl data_copy(data.toString()); + + EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _, _)) + .WillOnce(Invoke( + [&](ResponseDecoder& decoder, ConnectionPool::Callbacks& callbacks, + const ConnectionPool::Instance::StreamOptions&) -> ConnectionPool::Cancellable* { + callbacks.onPoolReady(stream_encoder_, cm_.thread_local_cluster_.conn_pool_.host_, + stream_info_, {}); + response_decoder_ = &decoder; + return nullptr; + })); + + EXPECT_CALL(stream_encoder_, encodeHeaders(HeaderMapEqualRef(&headers_copy), false)); + + auto* request = + client_.startRequest(std::move(headers), callbacks_, AsyncClient::RequestOptions()); + EXPECT_NE(request, nullptr); + + StrictMock watermark_callbacks; + request->setWatermarkCallbacks(watermark_callbacks); + + EXPECT_CALL(stream_encoder_, encodeData(BufferEqual(&data_copy), false)); + request->sendData(data, false); + // Upstream is blocked. + EXPECT_CALL(watermark_callbacks, onDecoderFilterAboveWriteBufferHighWatermark()); + dynamic_cast(stream_encoder_.getStream()).runHighWatermarkCallbacks(); + + // Reset the stream, which will call the low watermark callbacks. + EXPECT_CALL(watermark_callbacks, onDecoderFilterBelowWriteBufferLowWatermark()); + expectSuccess(request, 503); + stream_encoder_.getStream().resetStream(StreamResetReason::RemoteReset); +} + TEST_F(AsyncClientImplTracingTest, Basic) { Tracing::MockSpan* child_span{new Tracing::MockSpan()}; message_->body().add("test body"); @@ -1120,7 +1272,7 @@ TEST_F(AsyncClientImplTest, ResetInOnHeaders) { stream->sendData(*body, false); Http::StreamDecoderFilterCallbacks* filter_callbacks = - static_cast(stream); + dynamic_cast(stream); filter_callbacks->encodeHeaders( ResponseHeaderMapPtr(new TestResponseHeaderMapImpl{{":status", "200"}}), false, "details"); dispatcher_.clearDeferredDeleteList(); @@ -1661,7 +1813,7 @@ TEST_F(AsyncClientImplTest, WatermarkCallbacks) { AsyncClient::Stream* stream = client_.start(stream_callbacks_, AsyncClient::StreamOptions()); stream->sendHeaders(headers_, false); Http::StreamDecoderFilterCallbacks* filter_callbacks = - static_cast(stream); + dynamic_cast(stream); filter_callbacks->onDecoderFilterAboveWriteBufferHighWatermark(); EXPECT_TRUE(stream->isAboveWriteBufferHighWatermark()); filter_callbacks->onDecoderFilterAboveWriteBufferHighWatermark(); @@ -1677,7 +1829,7 @@ TEST_F(AsyncClientImplTest, RdsGettersTest) { AsyncClient::Stream* stream = client_.start(stream_callbacks_, AsyncClient::StreamOptions()); stream->sendHeaders(headers_, false); Http::StreamDecoderFilterCallbacks* filter_callbacks = - static_cast(stream); + dynamic_cast(stream); auto route = filter_callbacks->route(); ASSERT_NE(nullptr, route); auto route_entry = route->routeEntry(); @@ -1698,7 +1850,7 @@ TEST_F(AsyncClientImplTest, RdsGettersTest) { TEST_F(AsyncClientImplTest, DumpState) { AsyncClient::Stream* stream = client_.start(stream_callbacks_, AsyncClient::StreamOptions()); Http::StreamDecoderFilterCallbacks* filter_callbacks = - static_cast(stream); + dynamic_cast(stream); std::stringstream out; filter_callbacks->scope().dumpState(out); diff --git a/test/common/http/codec_impl_corpus/oss-fuzz-47120-codec_impl_fuzz_test-6205436203761664 b/test/common/http/codec_impl_corpus/oss-fuzz-47120-codec_impl_fuzz_test-6205436203761664 new file mode 100644 index 000000000000..2e6f60ce2d09 --- /dev/null +++ b/test/common/http/codec_impl_corpus/oss-fuzz-47120-codec_impl_fuzz_test-6205436203761664 @@ -0,0 +1 @@ +actions { new_stream { request_headers { headers { key: ":method" value: "CONNECT" } } } } actions { new_stream { request_headers { headers { key: ":scheme" e: " " } headers { key: ":path" value: "/" } } end_stream: true } } \ No newline at end of file diff --git a/test/common/http/conn_manager_impl_fuzz_test.cc b/test/common/http/conn_manager_impl_fuzz_test.cc index 413dae7b7bd0..5f1059040cc7 100644 --- a/test/common/http/conn_manager_impl_fuzz_test.cc +++ b/test/common/http/conn_manager_impl_fuzz_test.cc @@ -599,6 +599,8 @@ DEFINE_PROTO_FUZZER(const test::common::http::ConnManagerImplTestCase& input) { ON_CALL(Const(filter_callbacks.connection_), ssl()).WillByDefault(Return(ssl_connection)); ON_CALL(filter_callbacks.connection_, close(_)) .WillByDefault(InvokeWithoutArgs([&connection_alive] { connection_alive = false; })); + ON_CALL(filter_callbacks.connection_, close(_, _)) + .WillByDefault(InvokeWithoutArgs([&connection_alive] { connection_alive = false; })); filter_callbacks.connection_.stream_info_.downstream_connection_info_provider_->setLocalAddress( std::make_shared("127.0.0.1")); filter_callbacks.connection_.stream_info_.downstream_connection_info_provider_->setRemoteAddress( diff --git a/test/common/http/conn_manager_impl_test.cc b/test/common/http/conn_manager_impl_test.cc index 5d1f246e1cc1..85968b95ec44 100644 --- a/test/common/http/conn_manager_impl_test.cc +++ b/test/common/http/conn_manager_impl_test.cc @@ -2218,7 +2218,7 @@ TEST_F(HttpConnectionManagerImplTest, TestFilterCanEnrichAccessLogs) { conn_manager_->onData(fake_input, false); } -TEST_F(HttpConnectionManagerImplTest, TestDownstreamDisconnectAccessLog) { +TEST_F(HttpConnectionManagerImplTest, TestRemoteDownstreamDisconnectAccessLog) { setup(false, ""); std::shared_ptr filter(new NiceMock()); @@ -2262,6 +2262,48 @@ TEST_F(HttpConnectionManagerImplTest, TestDownstreamDisconnectAccessLog) { filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); } +TEST_F(HttpConnectionManagerImplTest, TestLocalDownstreamDisconnectAccessLog) { + setup(false, ""); + + std::shared_ptr filter(new NiceMock()); + std::shared_ptr handler(new NiceMock()); + + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .WillOnce(Invoke([&](FilterChainManager& manager) -> bool { + FilterFactoryCb filter_factory = createDecoderFilterFactoryCb(filter); + FilterFactoryCb handler_factory = createLogHandlerFactoryCb(handler); + + manager.applyFilterFactoryCb({}, filter_factory); + manager.applyFilterFactoryCb({}, handler_factory); + return true; + })); + + EXPECT_CALL(*handler, log(_, _, _, _)) + .WillOnce(Invoke([](const HeaderMap*, const HeaderMap*, const HeaderMap*, + const StreamInfo::StreamInfo& stream_info) { + EXPECT_EQ("downstream_local_disconnect(reason_for_local_close)", + stream_info.responseCodeDetails().value()); + })); + + EXPECT_CALL(*codec_, dispatch(_)) + .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ + {":method", "GET"}, {":authority", "host"}, {":path", "/"}}}; + decoder_->decodeHeaders(std::move(headers), true); + + data.drain(4); + return Http::okStatus(); + })); + + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); + + filter_callbacks_.connection_.close(Network::ConnectionCloseType::NoFlush, + "reason for local close"); +} + TEST_F(HttpConnectionManagerImplTest, TestAccessLogWithTrailers) { setup(false, ""); @@ -3225,6 +3267,38 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutAfterBidiData) { EXPECT_EQ("world", response_body); } +TEST_F(HttpConnectionManagerImplTest, RoundTripTimeHasValue) { + setup(false, ""); + + // Set up the codec. + Buffer::OwnedImpl fake_input("input"); + conn_manager_->createCodec(fake_input); + + startRequest(true); + + EXPECT_CALL(filter_callbacks_.connection_, lastRoundTripTime()) + .WillOnce(Return(absl::optional(300))); + EXPECT_CALL(filter_callbacks_.connection_, connectionInfoSetter()); + + filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); +} + +TEST_F(HttpConnectionManagerImplTest, RoundTripTimeHasNoValue) { + setup(false, ""); + + // Set up the codec. + Buffer::OwnedImpl fake_input("input"); + conn_manager_->createCodec(fake_input); + + startRequest(true); + + EXPECT_CALL(filter_callbacks_.connection_, lastRoundTripTime()) + .WillOnce(Return(absl::optional())); + EXPECT_CALL(filter_callbacks_.connection_, connectionInfoSetter()).Times(0); + + filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); +} + TEST_F(HttpConnectionManagerImplTest, RequestTimeoutDisabledByDefault) { setup(false, ""); @@ -3885,7 +3959,7 @@ TEST_F(HttpConnectionManagerImplTest, DrainCloseRaceWithClose) { EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Return(codecProtocolError("protocol error"))); EXPECT_CALL(*drain_timer, disableTimer()); EXPECT_CALL(filter_callbacks_.connection_, - close(Network::ConnectionCloseType::FlushWriteAndDelay)) + close(Network::ConnectionCloseType::FlushWriteAndDelay, _)) .WillOnce(Return()); conn_manager_->onData(fake_input, false); @@ -3983,7 +4057,7 @@ TEST_F(HttpConnectionManagerImplTest, DrainClose) { EXPECT_CALL(*codec_, goAway()); EXPECT_CALL(filter_callbacks_.connection_, - close(Network::ConnectionCloseType::FlushWriteAndDelay)); + close(Network::ConnectionCloseType::FlushWriteAndDelay, _)); EXPECT_CALL(*drain_timer, disableTimer()); drain_timer->invokeCallback(); diff --git a/test/common/http/conn_manager_impl_test_2.cc b/test/common/http/conn_manager_impl_test_2.cc index de8f1e0782fa..16b7ab7d4d2e 100644 --- a/test/common/http/conn_manager_impl_test_2.cc +++ b/test/common/http/conn_manager_impl_test_2.cc @@ -34,7 +34,7 @@ TEST_F(HttpConnectionManagerImplTest, ResponseBeforeRequestComplete) { EXPECT_CALL(*decoder_filters_[0], onStreamComplete()); EXPECT_CALL(*decoder_filters_[0], onDestroy()); EXPECT_CALL(filter_callbacks_.connection_, - close(Network::ConnectionCloseType::FlushWriteAndDelay)); + close(Network::ConnectionCloseType::FlushWriteAndDelay, _)); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; decoder_filters_[0]->callbacks_->streamInfo().setResponseCodeDetails(""); @@ -60,7 +60,7 @@ TEST_F(HttpConnectionManagerImplTest, DisconnectOnProxyConnectionDisconnect) { EXPECT_CALL(*decoder_filters_[0], onStreamComplete()); EXPECT_CALL(*decoder_filters_[0], onDestroy()); EXPECT_CALL(filter_callbacks_.connection_, - close(Network::ConnectionCloseType::FlushWriteAndDelay)); + close(Network::ConnectionCloseType::FlushWriteAndDelay, _)); ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; decoder_filters_[0]->callbacks_->streamInfo().setResponseCodeDetails(""); @@ -119,7 +119,7 @@ TEST_F(HttpConnectionManagerImplTest, ResponseStartBeforeRequestComplete) { // connection since we already sent a connection: close header. We won't "reset" the stream // however. EXPECT_CALL(filter_callbacks_.connection_, - close(Network::ConnectionCloseType::FlushWriteAndDelay)); + close(Network::ConnectionCloseType::FlushWriteAndDelay, _)); Buffer::OwnedImpl fake_response("world"); filter->callbacks_->encodeData(fake_response, true); } @@ -160,7 +160,7 @@ TEST_F(HttpConnectionManagerImplTest, DownstreamProtocolError) { // depending on whether the downstream client closes the connection prior to the delayed close // timer firing. EXPECT_CALL(filter_callbacks_.connection_, - close(Network::ConnectionCloseType::FlushWriteAndDelay)); + close(Network::ConnectionCloseType::FlushWriteAndDelay, _)); // Kick off the incoming data. Buffer::OwnedImpl fake_input("1234"); @@ -244,7 +244,7 @@ TEST_F(HttpConnectionManagerImplTest, FrameFloodError) { // FrameFloodException should result in reset of the streams followed by abortive close. EXPECT_CALL(filter_callbacks_.connection_, - close(Network::ConnectionCloseType::FlushWriteAndDelay)); + close(Network::ConnectionCloseType::FlushWriteAndDelay, _)); EXPECT_CALL(*log_handler, log(_, _, _, _)) .WillOnce(Invoke([](const HeaderMap*, const HeaderMap*, const HeaderMap*, @@ -271,7 +271,7 @@ TEST_F(HttpConnectionManagerImplTest, IdleTimeoutNoCodec) { EXPECT_CALL(*idle_timer, enableTimer(_, _)); setup(false, ""); - EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::FlushWrite)); + EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::FlushWrite, _)); EXPECT_CALL(*idle_timer, disableTimer()); idle_timer->invokeCallback(); @@ -311,7 +311,7 @@ TEST_F(HttpConnectionManagerImplTest, IdleTimeout) { EXPECT_CALL(*codec_, goAway()); EXPECT_CALL(filter_callbacks_.connection_, - close(Network::ConnectionCloseType::FlushWriteAndDelay)); + close(Network::ConnectionCloseType::FlushWriteAndDelay, _)); EXPECT_CALL(*idle_timer, disableTimer()); EXPECT_CALL(*drain_timer, disableTimer()); drain_timer->invokeCallback(); @@ -328,7 +328,7 @@ TEST_F(HttpConnectionManagerImplTest, ConnectionDurationResponseFlag) { EXPECT_CALL(*connection_duration_timer, enableTimer(_, _)); setup(false, ""); - EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::FlushWrite)); + EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::FlushWrite, _)); filter_callbacks_.connection_.streamInfo().setResponseFlag( StreamInfo::ResponseFlag::DurationTimeout); EXPECT_CALL(*connection_duration_timer, disableTimer()); @@ -350,7 +350,7 @@ TEST_F(HttpConnectionManagerImplTest, ConnectionDurationNoCodec) { EXPECT_CALL(*connection_duration_timer, enableTimer(_, _)); setup(false, ""); - EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::FlushWrite)); + EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::FlushWrite, _)); EXPECT_CALL(*connection_duration_timer, disableTimer()); connection_duration_timer->invokeCallback(); @@ -419,7 +419,7 @@ TEST_F(HttpConnectionManagerImplTest, ConnectionDuration) { EXPECT_CALL(*codec_, goAway()); EXPECT_CALL(filter_callbacks_.connection_, - close(Network::ConnectionCloseType::FlushWriteAndDelay)); + close(Network::ConnectionCloseType::FlushWriteAndDelay, _)); EXPECT_CALL(*connection_duration_timer, disableTimer()); EXPECT_CALL(*drain_timer, disableTimer()); drain_timer->invokeCallback(); diff --git a/test/common/http/http1/codec_impl_test.cc b/test/common/http/http1/codec_impl_test.cc index 8749feff7f7e..34dc65c19940 100644 --- a/test/common/http/http1/codec_impl_test.cc +++ b/test/common/http/http1/codec_impl_test.cc @@ -74,6 +74,13 @@ Buffer::OwnedImpl createBufferWithNByteSlices(absl::string_view input, size_t ma ASSERT(buffer.getRawSlices().size() == (input.size() + max_slice_size - 1) / max_slice_size); return buffer; } + +struct HTTPStringTestCase { + const absl::string_view http_version_; + const absl::optional balsa_parser_expected_error_; + const absl::optional http_parser_expected_error_; +}; + } // namespace class Http1CodecTestBase : public testing::TestWithParam { @@ -376,11 +383,12 @@ void Http1ServerConnectionImplTest::testServerAllowChunkedContentLength(uint32_t #ifdef ENVOY_ENABLE_UHV // Header validation is done by the HCM when header map is fully parsed. EXPECT_CALL(decoder, decodeHeaders_(_, _)) - .WillOnce(Invoke([this, &expected_headers](RequestHeaderMapPtr& headers, bool) -> void { - auto result = header_validator_->validateRequestHeaderMap(*headers); - EXPECT_THAT(headers, HeaderMapEqualIgnoreOrder(&expected_headers)); - ASSERT_TRUE(result.ok()); - })); + .WillOnce( + Invoke([this, &expected_headers](RequestHeaderMapSharedPtr& headers, bool) -> void { + auto result = header_validator_->validateRequestHeaderMap(*headers); + EXPECT_THAT(headers, HeaderMapEqualIgnoreOrder(&expected_headers)); + ASSERT_TRUE(result.ok()); + })); #else EXPECT_CALL(decoder, decodeHeaders_(HeaderMapEqual(&expected_headers), false)); #endif @@ -393,16 +401,17 @@ void Http1ServerConnectionImplTest::testServerAllowChunkedContentLength(uint32_t // sendLocalReply and closes network connection (based on the // stream_error_on_invalid_http_message flag, which in this test is assumed to equal false). EXPECT_CALL(decoder, decodeHeaders_(_, _)) - .WillOnce(Invoke([this, &response_encoder](RequestHeaderMapPtr& headers, bool) -> void { - auto result = header_validator_->validateRequestHeaderMap(*headers); - ASSERT_FALSE(result.ok()); - response_encoder->encodeHeaders(TestResponseHeaderMapImpl{{":status", "400"}, - {"connection", "close"}, - {"content-length", "0"}}, - true); - response_encoder->getStream().resetStream(StreamResetReason::LocalReset); - connection_.state_ = Network::Connection::State::Closing; - })); + .WillOnce( + Invoke([this, &response_encoder](RequestHeaderMapSharedPtr& headers, bool) -> void { + auto result = header_validator_->validateRequestHeaderMap(*headers); + ASSERT_FALSE(result.ok()); + response_encoder->encodeHeaders(TestResponseHeaderMapImpl{{":status", "400"}, + {"connection", "close"}, + {"content-length", "0"}}, + true); + response_encoder->getStream().resetStream(StreamResetReason::LocalReset); + connection_.state_ = Network::Connection::State::Closing; + })); ON_CALL(connection_, write(_, _)).WillByDefault(AddBufferToString(&response)); #else EXPECT_CALL(decoder, decodeHeaders_(_, _)).Times(0); @@ -544,7 +553,7 @@ TEST_P(Http1ServerConnectionImplTest, ContentLengthAllBitsSet) { TestRequestHeaderMapImpl expected_headers{ {"content-length", "3"}, {":path", "/"}, {":method", "POST"}}; EXPECT_CALL(decoder, decodeHeaders_(HeaderMapEqual(&expected_headers), false)) - .WillOnce(Invoke([&](Http::RequestHeaderMapPtr&, bool) -> void { + .WillOnce(Invoke([&](Http::RequestHeaderMapSharedPtr&, bool) -> void { connection_.state_ = Network::Connection::State::Closing; })); EXPECT_CALL(decoder, decodeData(_, _)).Times(0); @@ -908,6 +917,66 @@ TEST_P(Http1ServerConnectionImplTest, Http10MultipleResponses) { } } +TEST_P(Http1ServerConnectionImplTest, HttpVersion) { + // SPELLCHECKER(off) + HTTPStringTestCase kRequestHTTPStringTestCases[] = { + {"", {}, {}}, // HTTP/0.9 has no HTTP-version. + {"HTTP/1.0", {}, {}}, + {"HTTP/1.1", {}, {}}, + {"HTTP/9.1", {}, {}}, + {"aHTTP/1.1", "HPE_INVALID_VERSION", "HPE_INVALID_CONSTANT"}, +#ifdef ENVOY_ENABLE_UHV + {"HHTTP/1.1", "HPE_INVALID_VERSION", "HPE_INVALID_VERSION"}, + {"HTTPS/1.1", "HPE_INVALID_VERSION", "HPE_INVALID_VERSION"}, +#else + {"HHTTP/1.1", "HPE_INVALID_VERSION", "HPE_STRICT"}, + {"HTTPS/1.1", "HPE_INVALID_VERSION", "HPE_STRICT"}, +#endif + {"FTP/1.1", "HPE_INVALID_VERSION", "HPE_INVALID_CONSTANT"}, + {"HTTP/1.01", "HPE_INVALID_VERSION", "HPE_INVALID_VERSION"}, + {"HTTP/A.0", "HPE_INVALID_VERSION", "HPE_INVALID_VERSION"}}; + // SPELLCHECKER(on) + + for (const auto& test_case : kRequestHTTPStringTestCases) { + // BalsaParser signals an error if and only if http-parser signals an error, + // even though they may give different error codes. + ASSERT_EQ(test_case.balsa_parser_expected_error_.has_value(), + test_case.http_parser_expected_error_.has_value()); + + absl::optional expected_error; + if (parser_impl_ == Http1ParserImpl::BalsaParser) { + expected_error = test_case.balsa_parser_expected_error_; + } else { + expected_error = test_case.http_parser_expected_error_; + } + + initialize(); + + InSequence sequence; + + MockRequestDecoder decoder; + EXPECT_CALL(callbacks_, newStream(_, _)).WillOnce(ReturnRef(decoder)); + if (expected_error.has_value()) { + EXPECT_CALL(decoder, + sendLocalReply(Http::Code::BadRequest, "Bad Request", _, _, "http1.codec_error")); + } else { + EXPECT_CALL(decoder, decodeHeaders_(_, true)); + } + + Buffer::OwnedImpl buffer(absl::StrCat("GET /", test_case.http_version_.empty() ? "" : " ", + test_case.http_version_, "\r\n\r\n")); + auto status = codec_->dispatch(buffer); + + if (expected_error.has_value()) { + EXPECT_TRUE(isCodecProtocolError(status)) << test_case.http_version_; + EXPECT_EQ(status.message(), absl::StrCat("http/1.1 protocol error: ", expected_error.value())) + << test_case.http_version_; + } else { + EXPECT_TRUE(status.ok()) << test_case.http_version_; + } + } +} + TEST_P(Http1ServerConnectionImplTest, Http11AbsolutePath1) { initialize(); @@ -1280,7 +1349,7 @@ TEST_P(Http1ServerConnectionImplTest, HeaderNameWithUnderscoreAllowed) { #ifdef ENVOY_ENABLE_UHV // Header validation is done by the HCM when header map is fully parsed. EXPECT_CALL(decoder, decodeHeaders_(_, _)) - .WillOnce(Invoke([this, &expected_headers](RequestHeaderMapPtr& headers, bool) -> void { + .WillOnce(Invoke([this, &expected_headers](RequestHeaderMapSharedPtr& headers, bool) -> void { auto result = header_validator_->validateRequestHeaderMap(*headers); EXPECT_THAT(headers, HeaderMapEqualIgnoreOrder(&expected_headers)); ASSERT_TRUE(result.ok()); @@ -1313,7 +1382,7 @@ TEST_P(Http1ServerConnectionImplTest, HeaderNameWithUnderscoreAreDropped) { #ifdef ENVOY_ENABLE_UHV // Header validation is done by the HCM when header map is fully parsed. EXPECT_CALL(decoder, decodeHeaders_(_, _)) - .WillOnce(Invoke([this, &expected_headers](RequestHeaderMapPtr& headers, bool) -> void { + .WillOnce(Invoke([this, &expected_headers](RequestHeaderMapSharedPtr& headers, bool) -> void { auto result = header_validator_->validateRequestHeaderMap(*headers); EXPECT_THAT(headers, HeaderMapEqualIgnoreOrder(&expected_headers)); ASSERT_TRUE(result.ok()); @@ -1350,7 +1419,7 @@ TEST_P(Http1ServerConnectionImplTest, HeaderNameWithUnderscoreCauseRequestReject // sendLocalReply and closes network connection (based on the // stream_error_on_invalid_http_message flag, which in this test is assumed to equal false). EXPECT_CALL(decoder, decodeHeaders_(_, _)) - .WillOnce(Invoke([this, &response_encoder](RequestHeaderMapPtr& headers, bool) -> void { + .WillOnce(Invoke([this, &response_encoder](RequestHeaderMapSharedPtr& headers, bool) -> void { auto result = header_validator_->validateRequestHeaderMap(*headers); ASSERT_FALSE(result.ok()); response_encoder->encodeHeaders(TestResponseHeaderMapImpl{{":status", "400"}, @@ -1489,7 +1558,7 @@ TEST_P(Http1ServerConnectionImplTest, CloseDuringHeadersComplete) { TestRequestHeaderMapImpl expected_headers{ {"content-length", "5"}, {":path", "/"}, {":method", "POST"}}; EXPECT_CALL(decoder, decodeHeaders_(HeaderMapEqual(&expected_headers), false)) - .WillOnce(Invoke([&](Http::RequestHeaderMapPtr&, bool) -> void { + .WillOnce(Invoke([&](Http::RequestHeaderMapSharedPtr&, bool) -> void { connection_.state_ = Network::Connection::State::Closing; })); EXPECT_CALL(decoder, decodeData(_, _)).Times(0); @@ -3781,6 +3850,61 @@ TEST_P(Http1ClientConnectionImplTest, ResponseHttpVersion) { } } +TEST_P(Http1ClientConnectionImplTest, HttpVersion) { + // SPELLCHECKER(off) + HTTPStringTestCase kResponseHTTPStringTestCases[] = { + {"HTTP/1.0", {}, {}}, + {"HTTP/1.1", {}, {}}, + {"HTTP/9.1", {}, {}}, + {"aHTTP/1.1", "HPE_INVALID_CONSTANT", "HPE_INVALID_CONSTANT"}, +#ifdef ENVOY_ENABLE_UHV + {"HHTTP/1.1", "HPE_INVALID_VERSION", "HPE_INVALID_VERSION"}, + {"HTTPS/1.1", "HPE_INVALID_VERSION", "HPE_INVALID_VERSION"}, +#else + {"HHTTP/1.1", "HPE_INVALID_VERSION", "HPE_STRICT"}, + {"HTTPS/1.1", "HPE_INVALID_VERSION", "HPE_STRICT"}, +#endif + {"FTP/1.1", "HPE_INVALID_CONSTANT", "HPE_INVALID_CONSTANT"}, + {"HTTP/1.01", "HPE_INVALID_VERSION", "HPE_INVALID_VERSION"}, + {"HTTP/A.0", "HPE_INVALID_VERSION", "HPE_INVALID_VERSION"}}; + // SPELLCHECKER(on) + + for (const auto& test_case : kResponseHTTPStringTestCases) { + // BalsaParser signals an error if and only if http-parser signals an error, + // even though they may give different error codes. + ASSERT_EQ(test_case.balsa_parser_expected_error_.has_value(), + test_case.http_parser_expected_error_.has_value()); + + absl::optional expected_error; + if (parser_impl_ == Http1ParserImpl::BalsaParser) { + expected_error = test_case.balsa_parser_expected_error_; + } else { + expected_error = test_case.http_parser_expected_error_; + } + + initialize(); + + StrictMock response_decoder; + Http::RequestEncoder& request_encoder = codec_->newStream(response_decoder); + TestRequestHeaderMapImpl headers{{":method", "GET"}, {":path", "/"}, {":authority", "host"}}; + EXPECT_TRUE(request_encoder.encodeHeaders(headers, true).ok()); + + if (!expected_error.has_value()) { + EXPECT_CALL(response_decoder, decodeHeaders_(_, true)); + } + Buffer::OwnedImpl response( + absl::StrCat(test_case.http_version_, " 200 OK\r\nContent-Length: 0\r\n\r\n")); + auto status = codec_->dispatch(response); + if (expected_error.has_value()) { + EXPECT_TRUE(isCodecProtocolError(status)) << test_case.http_version_; + EXPECT_EQ(status.message(), absl::StrCat("http/1.1 protocol error: ", expected_error.value())) + << test_case.http_version_; + } else { + EXPECT_TRUE(status.ok()) << test_case.http_version_; + } + } +} + // 304 responses must not have a body. TEST_P(Http1ClientConnectionImplTest, 304WithBody) { initialize(); diff --git a/test/common/http/http1/http1_codec_impl_fuzz_test.cc b/test/common/http/http1/http1_codec_impl_fuzz_test.cc index 8a96df064659..18a141f83e04 100644 --- a/test/common/http/http1/http1_codec_impl_fuzz_test.cc +++ b/test/common/http/http1/http1_codec_impl_fuzz_test.cc @@ -42,13 +42,13 @@ class Http1Harness { void fuzz_response(Buffer::Instance& payload) { client_ = std::make_unique( - mock_client_connection, Http1::CodecStats::atomicGet(http1_stats, stats_store), + mock_client_connection, Http1::CodecStats::atomicGet(http1_stats, *stats_store.rootScope()), mock_client_callbacks, client_settings, Http::DEFAULT_MAX_HEADERS_COUNT); Status status = client_->dispatch(payload); } void fuzz_request(Buffer::Instance& payload) { server_ = std::make_unique( - mock_server_connection, Http1::CodecStats::atomicGet(http1_stats, stats_store), + mock_server_connection, Http1::CodecStats::atomicGet(http1_stats, *stats_store.rootScope()), mock_server_callbacks, server_settings, Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, envoy::config::core::v3::HttpProtocolOptions::ALLOW); diff --git a/test/common/http/http2/BUILD b/test/common/http/http2/BUILD index 6013e1c75ff4..8ec8281c5448 100644 --- a/test/common/http/http2/BUILD +++ b/test/common/http/http2/BUILD @@ -130,30 +130,12 @@ envoy_cc_test( ], ) -envoy_cc_test( - name = "metadata_encoder_decoder_test", - srcs = ["metadata_encoder_decoder_test.cc"], - external_deps = [ - "nghttp2", - ], - deps = [ - "//source/common/buffer:buffer_lib", - "//source/common/common:random_generator_lib", - "//source/common/http/http2:metadata_decoder_lib", - "//source/common/http/http2:metadata_encoder_lib", - "//source/common/runtime:runtime_lib", - "//test/common/http/http2:http2_frame", - "//test/test_common:logging_lib", - "//test/test_common:utility_lib", - ], -) - envoy_cc_test( name = "metadata_encoder_test", srcs = ["metadata_encoder_test.cc"], external_deps = [ - "nghttp2", "quiche_http2_adapter", + "quiche_http2_test_tools", ], deps = [ "//source/common/buffer:buffer_lib", @@ -225,3 +207,27 @@ envoy_cc_fuzz_test( "//test/test_common:utility_lib", ], ) + +envoy_proto_library( + name = "http2_connection_proto", + srcs = ["http2_connection.proto"], + deps = ["//test/fuzz:common_proto"], +) + +envoy_cc_fuzz_test( + name = "http2_connection_fuzz_test", + srcs = ["http2_connection_fuzz_test.cc"], + corpus = "http2_connection_corpus", + external_deps = [ + "nghttp2", + ], + deps = [ + ":http2_connection_proto_cc_proto", + "//source/common/http/http2:codec_lib", + "//test/common/http/http2:http2_frame", + "//test/mocks/http:http_mocks", + "//test/mocks/network:network_mocks", + "//test/test_common:test_runtime_lib", + "//test/test_common:utility_lib", + ], +) diff --git a/test/common/http/http2/codec_impl_test.cc b/test/common/http/http2/codec_impl_test.cc index 361b297783ad..7f251d9e0575 100644 --- a/test/common/http/http2/codec_impl_test.cc +++ b/test/common/http/http2/codec_impl_test.cc @@ -1180,7 +1180,7 @@ TEST_P(Http2CodecImplTest, ConnectionKeepalive) { EXPECT_CALL(*timeout_timer, disableTimer()).Times(0); // This indicates that no ACK was received. send_timer->invokeCallback(); driveClient(); - EXPECT_CALL(client_connection_, close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(client_connection_, close(Network::ConnectionCloseType::NoFlush, _)); timeout_timer->invokeCallback(); } @@ -1364,7 +1364,7 @@ TEST_P(Http2CodecImplTest, ShouldDumpActiveStreamsWithoutAllocatingMemory) { EXPECT_THAT(ostream.contents(), HasSubstr("local_end_stream_: 1")); EXPECT_THAT(ostream.contents(), HasSubstr("pending_trailers_to_encode_: null\n" - " absl::get(headers_or_trailers_): \n" + " absl::get(headers_or_trailers_): \n" " ':scheme', 'http'\n" " ':method', 'GET'\n" " ':authority', 'host'\n" @@ -2247,7 +2247,7 @@ TEST_P(Http2CodecImplFlowControlTest, WindowUpdateOnReadResumingFlood) { if (defer_processing_backedup_streams_) { EXPECT_TRUE(process_buffered_data_callback->enabled_); } - EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush, _)); violation_callback->invokeCallback(); EXPECT_EQ(frame_count, CommonUtility::OptionsLimits::DEFAULT_MAX_OUTBOUND_FRAMES + 1); @@ -2304,7 +2304,7 @@ TEST_P(Http2CodecImplFlowControlTest, RstStreamOnPendingFlushTimeoutFlood) { driveToCompletion(); EXPECT_TRUE(violation_callback->enabled_); - EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush, _)); violation_callback->invokeCallback(); EXPECT_EQ(1, server_stats_store_.counter("http2.tx_flush_timeout").value()); @@ -2685,7 +2685,7 @@ TEST_P(Http2CodecImplTest, HeaderNameWithUnderscoreAreDropped) { #ifdef ENVOY_ENABLE_UHV // Header validation is done by the HCM when header map is fully parsed. EXPECT_CALL(request_decoder_, decodeHeaders_(_, _)) - .WillOnce(Invoke([this, &expected_headers](RequestHeaderMapPtr& headers, bool) -> void { + .WillOnce(Invoke([this, &expected_headers](RequestHeaderMapSharedPtr& headers, bool) -> void { auto result = header_validator_->validateRequestHeaderMap(*headers); EXPECT_THAT(headers, HeaderMapEqualIgnoreOrder(&expected_headers)); ASSERT_TRUE(result.ok()); @@ -2714,7 +2714,7 @@ TEST_P(Http2CodecImplTest, HeaderNameWithUnderscoreAreRejected) { // sendLocalReply and closes network connection (based on the // stream_error_on_invalid_http_message flag, which in this test is assumed to equal false). EXPECT_CALL(request_decoder_, decodeHeaders_(_, _)) - .WillOnce(Invoke([this](RequestHeaderMapPtr& headers, bool) -> void { + .WillOnce(Invoke([this](RequestHeaderMapSharedPtr& headers, bool) -> void { auto result = header_validator_->validateRequestHeaderMap(*headers); ASSERT_FALSE(result.ok()); response_encoder_->encodeHeaders(TestResponseHeaderMapImpl{{":status", "400"}, @@ -2748,7 +2748,7 @@ TEST_P(Http2CodecImplTest, HeaderNameWithUnderscoreAllowed) { #ifdef ENVOY_ENABLE_UHV // Header validation is done by the HCM when header map is fully parsed. EXPECT_CALL(request_decoder_, decodeHeaders_(_, _)) - .WillOnce(Invoke([this, &expected_headers](RequestHeaderMapPtr& headers, bool) -> void { + .WillOnce(Invoke([this, &expected_headers](RequestHeaderMapSharedPtr& headers, bool) -> void { auto result = header_validator_->validateRequestHeaderMap(*headers); EXPECT_THAT(headers, HeaderMapEqualIgnoreOrder(&expected_headers)); ASSERT_TRUE(result.ok()); @@ -3098,7 +3098,7 @@ TEST_P(Http2CodecImplTest, ResponseHeadersFlood) { EXPECT_NO_THROW(driveToCompletion()); EXPECT_TRUE(violation_callback->enabled_); - EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush, _)); violation_callback->invokeCallback(); EXPECT_EQ(frame_count, CommonUtility::OptionsLimits::DEFAULT_MAX_OUTBOUND_FRAMES + 1); @@ -3136,7 +3136,7 @@ TEST_P(Http2CodecImplTest, ResponseDataFlood) { EXPECT_NO_THROW(driveToCompletion()); EXPECT_TRUE(violation_callback->enabled_); - EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush, _)); violation_callback->invokeCallback(); EXPECT_EQ(frame_count, CommonUtility::OptionsLimits::DEFAULT_MAX_OUTBOUND_FRAMES + 1); @@ -3217,7 +3217,7 @@ TEST_P(Http2CodecImplTest, ResponseDataFloodCounterReset) { EXPECT_NO_THROW(driveToCompletion()); EXPECT_TRUE(violation_callback->enabled_); - EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush, _)); violation_callback->invokeCallback(); } @@ -3293,7 +3293,7 @@ TEST_P(Http2CodecImplTest, ResponseTrailersFlood) { EXPECT_NO_THROW(driveToCompletion()); EXPECT_TRUE(violation_callback->enabled_); - EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush, _)); violation_callback->invokeCallback(); EXPECT_EQ(frame_count, CommonUtility::OptionsLimits::DEFAULT_MAX_OUTBOUND_FRAMES + 1); @@ -3345,7 +3345,7 @@ TEST_P(Http2CodecImplTest, MetadataFlood) { driveToCompletion(); EXPECT_TRUE(violation_callback->enabled_); - EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush, _)); violation_callback->invokeCallback(); EXPECT_EQ(frame_count, CommonUtility::OptionsLimits::DEFAULT_MAX_OUTBOUND_FRAMES + 1); @@ -3447,7 +3447,7 @@ TEST_P(Http2CodecImplTest, GoAwayCausesOutboundFlood) { driveToCompletion(); EXPECT_TRUE(violation_callback->enabled_); - EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush, _)); violation_callback->invokeCallback(); EXPECT_EQ(frame_count, CommonUtility::OptionsLimits::DEFAULT_MAX_OUTBOUND_FRAMES + 1); @@ -3455,7 +3455,7 @@ TEST_P(Http2CodecImplTest, GoAwayCausesOutboundFlood) { } // Verify that codec detects flood of outbound frames caused by shutdownNotice() method -TEST_P(Http2CodecImplTest, ShudowNoticeCausesOutboundFlood) { +TEST_P(Http2CodecImplTest, ShutdownNoticeCausesOutboundFlood) { initialize(); TestRequestHeaderMapImpl request_headers; @@ -3490,7 +3490,7 @@ TEST_P(Http2CodecImplTest, ShudowNoticeCausesOutboundFlood) { driveToCompletion(); EXPECT_TRUE(violation_callback->enabled_); - EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush, _)); violation_callback->invokeCallback(); EXPECT_EQ(frame_count, CommonUtility::OptionsLimits::DEFAULT_MAX_OUTBOUND_FRAMES + 1); @@ -3547,7 +3547,7 @@ TEST_P(Http2CodecImplTest, KeepAliveCausesOutboundFlood) { send_timer->invokeCallback(); EXPECT_TRUE(violation_callback->enabled_); - EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush, _)); violation_callback->invokeCallback(); EXPECT_EQ(frame_count, CommonUtility::OptionsLimits::DEFAULT_MAX_OUTBOUND_FRAMES + 1); @@ -3590,7 +3590,7 @@ TEST_P(Http2CodecImplTest, ResetStreamCausesOutboundFlood) { server_->getStream(1)->resetStream(StreamResetReason::RemoteReset); EXPECT_TRUE(violation_callback->enabled_); - EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(server_connection_, close(Envoy::Network::ConnectionCloseType::NoFlush, _)); violation_callback->invokeCallback(); EXPECT_EQ(frame_count, CommonUtility::OptionsLimits::DEFAULT_MAX_OUTBOUND_FRAMES + 1); diff --git a/test/common/http/http2/http2_connection.proto b/test/common/http/http2/http2_connection.proto new file mode 100644 index 000000000000..d11860eac664 --- /dev/null +++ b/test/common/http/http2/http2_connection.proto @@ -0,0 +1,93 @@ +syntax = "proto3"; + +package test.common.http.http2; +import "test/fuzz/common.proto"; + +message Http2DataFrame { + bytes data = 1; +} + +message Http2HeaderFrame { + uint32 stream_dependency = 1; + uint32 weight = 2; + test.fuzz.Headers headers = 3; + bytes padding = 4; +} + +message Http2PriorityFrame { + uint32 stream_dependency = 1; + uint32 weight = 2; +} + +message Http2RstFrame { + uint32 error_code = 1; +} + +message Http2Setting { + // Uses only lower 2 bytes + uint32 identifier = 1; + uint32 value = 2; +} + +message Http2SettingsFrame { + repeated Http2Setting settings = 1; +} + +message Http2PushPromiseFrame { + uint32 stream_id = 1; + test.fuzz.Headers headers = 2; + bytes padding = 3; +} + +message Http2PingFrame { + uint64 data = 1; +} + +message Http2GoAwayFrame { + uint32 last_stream_id = 1; + uint32 error_code = 2; + bytes debug_data = 3; +} + +message Http2WindowUpdateFrame { + uint32 window_size_increment = 1; +} + +message Http2ContinuationFrame { + test.fuzz.Headers headers = 1; +} + +message Http2Frame { + uint32 streamid = 1; + // Uses only least significant byte + uint32 flags = 2; + oneof frame { + Http2DataFrame data = 3; + Http2HeaderFrame headers = 4; + Http2PriorityFrame priority = 5; + Http2RstFrame rst = 6; + Http2SettingsFrame settings = 7; + Http2PushPromiseFrame push_promise = 8; + Http2PingFrame ping = 9; + Http2GoAwayFrame go_away = 10; + Http2WindowUpdateFrame window_update = 11; + Http2ContinuationFrame continuation = 12; + } +} + +// All junk frame +message JunkFrame { + bytes data = 1; +} + +message Http2FrameOrJunk { + oneof frame { + Http2Frame h2frame = 1; + JunkFrame junk = 2; + } +} + +message Http2ConnectionFuzzCase { + Http2SettingsFrame settings = 1; + repeated Http2FrameOrJunk frames = 2; +} diff --git a/test/common/http/http2/http2_connection_corpus/CONTINUATION b/test/common/http/http2/http2_connection_corpus/CONTINUATION new file mode 100644 index 000000000000..4fed7de37d74 --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/CONTINUATION @@ -0,0 +1,5 @@ +frames { + junk { + data: "\000\000\012\011\000\000\000\000\001\101\101\101\101\101\101\101\101\101\101" + } +} diff --git a/test/common/http/http2/http2_connection_corpus/DATA b/test/common/http/http2/http2_connection_corpus/DATA new file mode 100644 index 000000000000..6e066c50ef5d --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/DATA @@ -0,0 +1,5 @@ +frames { + junk { + data: "\000\000\012\000\000\000\000\000\001\101\101\101\101\101\101\101\101\101\101" + } +} diff --git a/test/common/http/http2/http2_connection_corpus/GOAWAY b/test/common/http/http2/http2_connection_corpus/GOAWAY new file mode 100644 index 000000000000..b765069de8fa --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/GOAWAY @@ -0,0 +1,5 @@ +frames { + junk { + data: "\000\000\012\007\000\000\000\000\001\101\101\101\101\101\101\101\101\101\101" + } +} diff --git a/test/common/http/http2/http2_connection_corpus/HDRS000 b/test/common/http/http2/http2_connection_corpus/HDRS000 new file mode 100644 index 000000000000..3894764d81e5 --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/HDRS000 @@ -0,0 +1,197 @@ +frames { + h2frame { + headers { + key: ":scheme" + value: "http" + } + headers { + key: ":path" + value: "/" + } + headers { + key: ":method" + value: "get" + } + headers { + key: "content-type" + value: "text/plain; charset=UTF-8" + } + headers { + key: "access-control-request-private-network" + value: "empty" + } + headers { + key: "grpc-status" + value: "empty" + } + headers { + key: "x-forwarded-host" + value: "empty" + } + headers { + key: "-original-path" + value: "empty" + } + headers { + key: "proxy-authenticate" + value: "empty" + } + headers { + key: "expect" + value: "100-continue" + } + headers { + key: "-retriable-header-names" + value: "empty" + } + headers { + key: "-internal" + value: "true" + } + headers { + key: "accept-encoding" + value: "identity" + } + headers { + key: "date" + value: "empty" + } + headers { + key: "transfer-encoding" + value: "zstd" + } + headers { + key: "vary" + value: "Accept-Encoding" + } + headers { + key: "-retry-on" + value: "retriable-headers" + } + headers { + key: "connection" + value: "http2-settings" + } + headers { + key: "-overloaded" + value: "true" + } + headers { + key: ":protocol" + value: "bytestream" + } + headers { + key: "-degraded" + value: "empty" + } + headers { + key: "-ratelimited" + value: "true" + } + headers { + key: "grpc-timeout" + value: "empty" + } + headers { + key: "x-content-type-options" + value: "empty" + } + headers { + key: "x-ot-span-context" + value: "empty" + } + headers { + key: "x-client-trace-id" + value: "empty" + } + headers { + key: "if-match" + value: "empty" + } + headers { + key: "-upstream-stream-duration-ms" + value: "empty" + } + headers { + key: "-upstream-service-time" + value: "empty" + } + headers { + key: "-immediate-health-check-fail" + value: "true" + } + headers { + key: "content-range" + value: "empty" + } + headers { + key: "-upstream-canary" + value: "empty" + } + headers { + key: "x-squash-debug" + value: "empty" + } + headers { + key: "grpc-accept-encoding" + value: "identity" + } + headers { + key: "access-control-allow-methods" + value: "empty" + } + headers { + key: "cdn-loop" + value: "empty" + } + headers { + key: "user-agent" + value: "Envoy/HC" + } + headers { + key: "-upstream-hostname" + value: "empty" + } + headers { + key: "access-control-allow-origin" + value: "empty" + } + headers { + key: "access-control-request-headers" + value: "empty" + } + headers { + key: "set-cookie" + value: "empty" + } + headers { + key: "-upstream-rq-per-try-timeout-ms" + value: "empty" + } + headers { + key: "-decorator-operation" + value: "empty" + } + headers { + key: "grpc-status-details-bin" + value: "empty" + } + headers { + key: "grpc-message-type" + value: "empty" + } + headers { + key: "proxy-status" + value: "empty" + } + headers { + key: "upgrade" + value: "h2c" + } + headers { + key: "location" + value: "empty" + } + } + } +} diff --git a/test/common/http/http2/http2_connection_corpus/HDRS001 b/test/common/http/http2/http2_connection_corpus/HDRS001 new file mode 100644 index 000000000000..3da5642f1000 --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/HDRS001 @@ -0,0 +1,197 @@ +frames { + h2frame { + headers { + key: ":scheme" + value: "https" + } + headers { + key: ":path" + value: "/" + } + headers { + key: ":method" + value: "get" + } + headers { + key: "content-type" + value: "text/plain; charset=UTF-8" + } + headers { + key: "access-control-request-private-network" + value: "empty" + } + headers { + key: "grpc-status" + value: "empty" + } + headers { + key: "x-forwarded-host" + value: "empty" + } + headers { + key: "-original-path" + value: "empty" + } + headers { + key: "proxy-authenticate" + value: "empty" + } + headers { + key: "expect" + value: "100-continue" + } + headers { + key: "-retriable-header-names" + value: "empty" + } + headers { + key: "-internal" + value: "true" + } + headers { + key: "accept-encoding" + value: "identity" + } + headers { + key: "date" + value: "empty" + } + headers { + key: "transfer-encoding" + value: "zstd" + } + headers { + key: "vary" + value: "Accept-Encoding" + } + headers { + key: "-retry-on" + value: "retriable-headers" + } + headers { + key: "connection" + value: "http2-settings" + } + headers { + key: "-overloaded" + value: "true" + } + headers { + key: ":protocol" + value: "bytestream" + } + headers { + key: "-degraded" + value: "empty" + } + headers { + key: "-ratelimited" + value: "true" + } + headers { + key: "grpc-timeout" + value: "empty" + } + headers { + key: "x-content-type-options" + value: "empty" + } + headers { + key: "x-ot-span-context" + value: "empty" + } + headers { + key: "x-client-trace-id" + value: "empty" + } + headers { + key: "if-match" + value: "empty" + } + headers { + key: "-upstream-stream-duration-ms" + value: "empty" + } + headers { + key: "-upstream-service-time" + value: "empty" + } + headers { + key: "-immediate-health-check-fail" + value: "true" + } + headers { + key: "content-range" + value: "empty" + } + headers { + key: "-upstream-canary" + value: "empty" + } + headers { + key: "x-squash-debug" + value: "empty" + } + headers { + key: "grpc-accept-encoding" + value: "identity" + } + headers { + key: "access-control-allow-methods" + value: "empty" + } + headers { + key: "cdn-loop" + value: "empty" + } + headers { + key: "user-agent" + value: "Envoy/HC" + } + headers { + key: "-upstream-hostname" + value: "empty" + } + headers { + key: "access-control-allow-origin" + value: "empty" + } + headers { + key: "access-control-request-headers" + value: "empty" + } + headers { + key: "set-cookie" + value: "empty" + } + headers { + key: "-upstream-rq-per-try-timeout-ms" + value: "empty" + } + headers { + key: "-decorator-operation" + value: "empty" + } + headers { + key: "grpc-status-details-bin" + value: "empty" + } + headers { + key: "grpc-message-type" + value: "empty" + } + headers { + key: "proxy-status" + value: "empty" + } + headers { + key: "upgrade" + value: "h2c" + } + headers { + key: "location" + value: "empty" + } + } + } +} diff --git a/test/common/http/http2/http2_connection_corpus/HDRS002 b/test/common/http/http2/http2_connection_corpus/HDRS002 new file mode 100644 index 000000000000..ed04593f0217 --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/HDRS002 @@ -0,0 +1,197 @@ +frames { + h2frame { + headers { + key: ":scheme" + value: "http" + } + headers { + key: ":path" + value: "/" + } + headers { + key: ":method" + value: "head" + } + headers { + key: "content-type" + value: "text/plain; charset=UTF-8" + } + headers { + key: "access-control-request-private-network" + value: "empty" + } + headers { + key: "grpc-status" + value: "empty" + } + headers { + key: "x-forwarded-host" + value: "empty" + } + headers { + key: "-original-path" + value: "empty" + } + headers { + key: "proxy-authenticate" + value: "empty" + } + headers { + key: "expect" + value: "100-continue" + } + headers { + key: "-retriable-header-names" + value: "empty" + } + headers { + key: "-internal" + value: "true" + } + headers { + key: "accept-encoding" + value: "identity" + } + headers { + key: "date" + value: "empty" + } + headers { + key: "transfer-encoding" + value: "zstd" + } + headers { + key: "vary" + value: "Accept-Encoding" + } + headers { + key: "-retry-on" + value: "retriable-headers" + } + headers { + key: "connection" + value: "http2-settings" + } + headers { + key: "-overloaded" + value: "true" + } + headers { + key: ":protocol" + value: "bytestream" + } + headers { + key: "-degraded" + value: "empty" + } + headers { + key: "-ratelimited" + value: "true" + } + headers { + key: "grpc-timeout" + value: "empty" + } + headers { + key: "x-content-type-options" + value: "empty" + } + headers { + key: "x-ot-span-context" + value: "empty" + } + headers { + key: "x-client-trace-id" + value: "empty" + } + headers { + key: "if-match" + value: "empty" + } + headers { + key: "-upstream-stream-duration-ms" + value: "empty" + } + headers { + key: "-upstream-service-time" + value: "empty" + } + headers { + key: "-immediate-health-check-fail" + value: "true" + } + headers { + key: "content-range" + value: "empty" + } + headers { + key: "-upstream-canary" + value: "empty" + } + headers { + key: "x-squash-debug" + value: "empty" + } + headers { + key: "grpc-accept-encoding" + value: "identity" + } + headers { + key: "access-control-allow-methods" + value: "empty" + } + headers { + key: "cdn-loop" + value: "empty" + } + headers { + key: "user-agent" + value: "Envoy/HC" + } + headers { + key: "-upstream-hostname" + value: "empty" + } + headers { + key: "access-control-allow-origin" + value: "empty" + } + headers { + key: "access-control-request-headers" + value: "empty" + } + headers { + key: "set-cookie" + value: "empty" + } + headers { + key: "-upstream-rq-per-try-timeout-ms" + value: "empty" + } + headers { + key: "-decorator-operation" + value: "empty" + } + headers { + key: "grpc-status-details-bin" + value: "empty" + } + headers { + key: "grpc-message-type" + value: "empty" + } + headers { + key: "proxy-status" + value: "empty" + } + headers { + key: "upgrade" + value: "h2c" + } + headers { + key: "location" + value: "empty" + } + } + } +} diff --git a/test/common/http/http2/http2_connection_corpus/HDRS003 b/test/common/http/http2/http2_connection_corpus/HDRS003 new file mode 100644 index 000000000000..1842ad58dc42 --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/HDRS003 @@ -0,0 +1,197 @@ +frames { + h2frame { + headers { + key: ":scheme" + value: "https" + } + headers { + key: ":path" + value: "/" + } + headers { + key: ":method" + value: "head" + } + headers { + key: "content-type" + value: "text/plain; charset=UTF-8" + } + headers { + key: "access-control-request-private-network" + value: "empty" + } + headers { + key: "grpc-status" + value: "empty" + } + headers { + key: "x-forwarded-host" + value: "empty" + } + headers { + key: "-original-path" + value: "empty" + } + headers { + key: "proxy-authenticate" + value: "empty" + } + headers { + key: "expect" + value: "100-continue" + } + headers { + key: "-retriable-header-names" + value: "empty" + } + headers { + key: "-internal" + value: "true" + } + headers { + key: "accept-encoding" + value: "identity" + } + headers { + key: "date" + value: "empty" + } + headers { + key: "transfer-encoding" + value: "zstd" + } + headers { + key: "vary" + value: "Accept-Encoding" + } + headers { + key: "-retry-on" + value: "retriable-headers" + } + headers { + key: "connection" + value: "http2-settings" + } + headers { + key: "-overloaded" + value: "true" + } + headers { + key: ":protocol" + value: "bytestream" + } + headers { + key: "-degraded" + value: "empty" + } + headers { + key: "-ratelimited" + value: "true" + } + headers { + key: "grpc-timeout" + value: "empty" + } + headers { + key: "x-content-type-options" + value: "empty" + } + headers { + key: "x-ot-span-context" + value: "empty" + } + headers { + key: "x-client-trace-id" + value: "empty" + } + headers { + key: "if-match" + value: "empty" + } + headers { + key: "-upstream-stream-duration-ms" + value: "empty" + } + headers { + key: "-upstream-service-time" + value: "empty" + } + headers { + key: "-immediate-health-check-fail" + value: "true" + } + headers { + key: "content-range" + value: "empty" + } + headers { + key: "-upstream-canary" + value: "empty" + } + headers { + key: "x-squash-debug" + value: "empty" + } + headers { + key: "grpc-accept-encoding" + value: "identity" + } + headers { + key: "access-control-allow-methods" + value: "empty" + } + headers { + key: "cdn-loop" + value: "empty" + } + headers { + key: "user-agent" + value: "Envoy/HC" + } + headers { + key: "-upstream-hostname" + value: "empty" + } + headers { + key: "access-control-allow-origin" + value: "empty" + } + headers { + key: "access-control-request-headers" + value: "empty" + } + headers { + key: "set-cookie" + value: "empty" + } + headers { + key: "-upstream-rq-per-try-timeout-ms" + value: "empty" + } + headers { + key: "-decorator-operation" + value: "empty" + } + headers { + key: "grpc-status-details-bin" + value: "empty" + } + headers { + key: "grpc-message-type" + value: "empty" + } + headers { + key: "proxy-status" + value: "empty" + } + headers { + key: "upgrade" + value: "h2c" + } + headers { + key: "location" + value: "empty" + } + } + } +} diff --git a/test/common/http/http2/http2_connection_corpus/HDRS004 b/test/common/http/http2/http2_connection_corpus/HDRS004 new file mode 100644 index 000000000000..59a593923349 --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/HDRS004 @@ -0,0 +1,197 @@ +frames { + h2frame { + headers { + key: ":scheme" + value: "http" + } + headers { + key: ":path" + value: "/" + } + headers { + key: ":method" + value: "post" + } + headers { + key: "content-type" + value: "text/plain; charset=UTF-8" + } + headers { + key: "access-control-request-private-network" + value: "empty" + } + headers { + key: "grpc-status" + value: "empty" + } + headers { + key: "x-forwarded-host" + value: "empty" + } + headers { + key: "-original-path" + value: "empty" + } + headers { + key: "proxy-authenticate" + value: "empty" + } + headers { + key: "expect" + value: "100-continue" + } + headers { + key: "-retriable-header-names" + value: "empty" + } + headers { + key: "-internal" + value: "true" + } + headers { + key: "accept-encoding" + value: "identity" + } + headers { + key: "date" + value: "empty" + } + headers { + key: "transfer-encoding" + value: "zstd" + } + headers { + key: "vary" + value: "Accept-Encoding" + } + headers { + key: "-retry-on" + value: "retriable-headers" + } + headers { + key: "connection" + value: "http2-settings" + } + headers { + key: "-overloaded" + value: "true" + } + headers { + key: ":protocol" + value: "bytestream" + } + headers { + key: "-degraded" + value: "empty" + } + headers { + key: "-ratelimited" + value: "true" + } + headers { + key: "grpc-timeout" + value: "empty" + } + headers { + key: "x-content-type-options" + value: "empty" + } + headers { + key: "x-ot-span-context" + value: "empty" + } + headers { + key: "x-client-trace-id" + value: "empty" + } + headers { + key: "if-match" + value: "empty" + } + headers { + key: "-upstream-stream-duration-ms" + value: "empty" + } + headers { + key: "-upstream-service-time" + value: "empty" + } + headers { + key: "-immediate-health-check-fail" + value: "true" + } + headers { + key: "content-range" + value: "empty" + } + headers { + key: "-upstream-canary" + value: "empty" + } + headers { + key: "x-squash-debug" + value: "empty" + } + headers { + key: "grpc-accept-encoding" + value: "identity" + } + headers { + key: "access-control-allow-methods" + value: "empty" + } + headers { + key: "cdn-loop" + value: "empty" + } + headers { + key: "user-agent" + value: "Envoy/HC" + } + headers { + key: "-upstream-hostname" + value: "empty" + } + headers { + key: "access-control-allow-origin" + value: "empty" + } + headers { + key: "access-control-request-headers" + value: "empty" + } + headers { + key: "set-cookie" + value: "empty" + } + headers { + key: "-upstream-rq-per-try-timeout-ms" + value: "empty" + } + headers { + key: "-decorator-operation" + value: "empty" + } + headers { + key: "grpc-status-details-bin" + value: "empty" + } + headers { + key: "grpc-message-type" + value: "empty" + } + headers { + key: "proxy-status" + value: "empty" + } + headers { + key: "upgrade" + value: "h2c" + } + headers { + key: "location" + value: "empty" + } + } + } +} diff --git a/test/common/http/http2/http2_connection_corpus/HDRS005 b/test/common/http/http2/http2_connection_corpus/HDRS005 new file mode 100644 index 000000000000..7bfcdd5dfd6a --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/HDRS005 @@ -0,0 +1,197 @@ +frames { + h2frame { + headers { + key: ":scheme" + value: "https" + } + headers { + key: ":path" + value: "/" + } + headers { + key: ":method" + value: "post" + } + headers { + key: "content-type" + value: "text/plain; charset=UTF-8" + } + headers { + key: "access-control-request-private-network" + value: "empty" + } + headers { + key: "grpc-status" + value: "empty" + } + headers { + key: "x-forwarded-host" + value: "empty" + } + headers { + key: "-original-path" + value: "empty" + } + headers { + key: "proxy-authenticate" + value: "empty" + } + headers { + key: "expect" + value: "100-continue" + } + headers { + key: "-retriable-header-names" + value: "empty" + } + headers { + key: "-internal" + value: "true" + } + headers { + key: "accept-encoding" + value: "identity" + } + headers { + key: "date" + value: "empty" + } + headers { + key: "transfer-encoding" + value: "zstd" + } + headers { + key: "vary" + value: "Accept-Encoding" + } + headers { + key: "-retry-on" + value: "retriable-headers" + } + headers { + key: "connection" + value: "http2-settings" + } + headers { + key: "-overloaded" + value: "true" + } + headers { + key: ":protocol" + value: "bytestream" + } + headers { + key: "-degraded" + value: "empty" + } + headers { + key: "-ratelimited" + value: "true" + } + headers { + key: "grpc-timeout" + value: "empty" + } + headers { + key: "x-content-type-options" + value: "empty" + } + headers { + key: "x-ot-span-context" + value: "empty" + } + headers { + key: "x-client-trace-id" + value: "empty" + } + headers { + key: "if-match" + value: "empty" + } + headers { + key: "-upstream-stream-duration-ms" + value: "empty" + } + headers { + key: "-upstream-service-time" + value: "empty" + } + headers { + key: "-immediate-health-check-fail" + value: "true" + } + headers { + key: "content-range" + value: "empty" + } + headers { + key: "-upstream-canary" + value: "empty" + } + headers { + key: "x-squash-debug" + value: "empty" + } + headers { + key: "grpc-accept-encoding" + value: "identity" + } + headers { + key: "access-control-allow-methods" + value: "empty" + } + headers { + key: "cdn-loop" + value: "empty" + } + headers { + key: "user-agent" + value: "Envoy/HC" + } + headers { + key: "-upstream-hostname" + value: "empty" + } + headers { + key: "access-control-allow-origin" + value: "empty" + } + headers { + key: "access-control-request-headers" + value: "empty" + } + headers { + key: "set-cookie" + value: "empty" + } + headers { + key: "-upstream-rq-per-try-timeout-ms" + value: "empty" + } + headers { + key: "-decorator-operation" + value: "empty" + } + headers { + key: "grpc-status-details-bin" + value: "empty" + } + headers { + key: "grpc-message-type" + value: "empty" + } + headers { + key: "proxy-status" + value: "empty" + } + headers { + key: "upgrade" + value: "h2c" + } + headers { + key: "location" + value: "empty" + } + } + } +} diff --git a/test/common/http/http2/http2_connection_corpus/HDRS006 b/test/common/http/http2/http2_connection_corpus/HDRS006 new file mode 100644 index 000000000000..2d0bdff076de --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/HDRS006 @@ -0,0 +1,197 @@ +frames { + h2frame { + headers { + key: ":scheme" + value: "http" + } + headers { + key: ":path" + value: "/" + } + headers { + key: ":method" + value: "put" + } + headers { + key: "content-type" + value: "text/plain; charset=UTF-8" + } + headers { + key: "access-control-request-private-network" + value: "empty" + } + headers { + key: "grpc-status" + value: "empty" + } + headers { + key: "x-forwarded-host" + value: "empty" + } + headers { + key: "-original-path" + value: "empty" + } + headers { + key: "proxy-authenticate" + value: "empty" + } + headers { + key: "expect" + value: "100-continue" + } + headers { + key: "-retriable-header-names" + value: "empty" + } + headers { + key: "-internal" + value: "true" + } + headers { + key: "accept-encoding" + value: "identity" + } + headers { + key: "date" + value: "empty" + } + headers { + key: "transfer-encoding" + value: "zstd" + } + headers { + key: "vary" + value: "Accept-Encoding" + } + headers { + key: "-retry-on" + value: "retriable-headers" + } + headers { + key: "connection" + value: "http2-settings" + } + headers { + key: "-overloaded" + value: "true" + } + headers { + key: ":protocol" + value: "bytestream" + } + headers { + key: "-degraded" + value: "empty" + } + headers { + key: "-ratelimited" + value: "true" + } + headers { + key: "grpc-timeout" + value: "empty" + } + headers { + key: "x-content-type-options" + value: "empty" + } + headers { + key: "x-ot-span-context" + value: "empty" + } + headers { + key: "x-client-trace-id" + value: "empty" + } + headers { + key: "if-match" + value: "empty" + } + headers { + key: "-upstream-stream-duration-ms" + value: "empty" + } + headers { + key: "-upstream-service-time" + value: "empty" + } + headers { + key: "-immediate-health-check-fail" + value: "true" + } + headers { + key: "content-range" + value: "empty" + } + headers { + key: "-upstream-canary" + value: "empty" + } + headers { + key: "x-squash-debug" + value: "empty" + } + headers { + key: "grpc-accept-encoding" + value: "identity" + } + headers { + key: "access-control-allow-methods" + value: "empty" + } + headers { + key: "cdn-loop" + value: "empty" + } + headers { + key: "user-agent" + value: "Envoy/HC" + } + headers { + key: "-upstream-hostname" + value: "empty" + } + headers { + key: "access-control-allow-origin" + value: "empty" + } + headers { + key: "access-control-request-headers" + value: "empty" + } + headers { + key: "set-cookie" + value: "empty" + } + headers { + key: "-upstream-rq-per-try-timeout-ms" + value: "empty" + } + headers { + key: "-decorator-operation" + value: "empty" + } + headers { + key: "grpc-status-details-bin" + value: "empty" + } + headers { + key: "grpc-message-type" + value: "empty" + } + headers { + key: "proxy-status" + value: "empty" + } + headers { + key: "upgrade" + value: "h2c" + } + headers { + key: "location" + value: "empty" + } + } + } +} diff --git a/test/common/http/http2/http2_connection_corpus/HDRS007 b/test/common/http/http2/http2_connection_corpus/HDRS007 new file mode 100644 index 000000000000..9c4359fa84aa --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/HDRS007 @@ -0,0 +1,197 @@ +frames { + h2frame { + headers { + key: ":scheme" + value: "https" + } + headers { + key: ":path" + value: "/" + } + headers { + key: ":method" + value: "put" + } + headers { + key: "content-type" + value: "text/plain; charset=UTF-8" + } + headers { + key: "access-control-request-private-network" + value: "empty" + } + headers { + key: "grpc-status" + value: "empty" + } + headers { + key: "x-forwarded-host" + value: "empty" + } + headers { + key: "-original-path" + value: "empty" + } + headers { + key: "proxy-authenticate" + value: "empty" + } + headers { + key: "expect" + value: "100-continue" + } + headers { + key: "-retriable-header-names" + value: "empty" + } + headers { + key: "-internal" + value: "true" + } + headers { + key: "accept-encoding" + value: "identity" + } + headers { + key: "date" + value: "empty" + } + headers { + key: "transfer-encoding" + value: "zstd" + } + headers { + key: "vary" + value: "Accept-Encoding" + } + headers { + key: "-retry-on" + value: "retriable-headers" + } + headers { + key: "connection" + value: "http2-settings" + } + headers { + key: "-overloaded" + value: "true" + } + headers { + key: ":protocol" + value: "bytestream" + } + headers { + key: "-degraded" + value: "empty" + } + headers { + key: "-ratelimited" + value: "true" + } + headers { + key: "grpc-timeout" + value: "empty" + } + headers { + key: "x-content-type-options" + value: "empty" + } + headers { + key: "x-ot-span-context" + value: "empty" + } + headers { + key: "x-client-trace-id" + value: "empty" + } + headers { + key: "if-match" + value: "empty" + } + headers { + key: "-upstream-stream-duration-ms" + value: "empty" + } + headers { + key: "-upstream-service-time" + value: "empty" + } + headers { + key: "-immediate-health-check-fail" + value: "true" + } + headers { + key: "content-range" + value: "empty" + } + headers { + key: "-upstream-canary" + value: "empty" + } + headers { + key: "x-squash-debug" + value: "empty" + } + headers { + key: "grpc-accept-encoding" + value: "identity" + } + headers { + key: "access-control-allow-methods" + value: "empty" + } + headers { + key: "cdn-loop" + value: "empty" + } + headers { + key: "user-agent" + value: "Envoy/HC" + } + headers { + key: "-upstream-hostname" + value: "empty" + } + headers { + key: "access-control-allow-origin" + value: "empty" + } + headers { + key: "access-control-request-headers" + value: "empty" + } + headers { + key: "set-cookie" + value: "empty" + } + headers { + key: "-upstream-rq-per-try-timeout-ms" + value: "empty" + } + headers { + key: "-decorator-operation" + value: "empty" + } + headers { + key: "grpc-status-details-bin" + value: "empty" + } + headers { + key: "grpc-message-type" + value: "empty" + } + headers { + key: "proxy-status" + value: "empty" + } + headers { + key: "upgrade" + value: "h2c" + } + headers { + key: "location" + value: "empty" + } + } + } +} diff --git a/test/common/http/http2/http2_connection_corpus/HDRS008 b/test/common/http/http2/http2_connection_corpus/HDRS008 new file mode 100644 index 000000000000..b51fa50d2cc2 --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/HDRS008 @@ -0,0 +1,197 @@ +frames { + h2frame { + headers { + key: ":scheme" + value: "http" + } + headers { + key: ":path" + value: "/" + } + headers { + key: ":method" + value: "delete" + } + headers { + key: "content-type" + value: "text/plain; charset=UTF-8" + } + headers { + key: "access-control-request-private-network" + value: "empty" + } + headers { + key: "grpc-status" + value: "empty" + } + headers { + key: "x-forwarded-host" + value: "empty" + } + headers { + key: "-original-path" + value: "empty" + } + headers { + key: "proxy-authenticate" + value: "empty" + } + headers { + key: "expect" + value: "100-continue" + } + headers { + key: "-retriable-header-names" + value: "empty" + } + headers { + key: "-internal" + value: "true" + } + headers { + key: "accept-encoding" + value: "identity" + } + headers { + key: "date" + value: "empty" + } + headers { + key: "transfer-encoding" + value: "zstd" + } + headers { + key: "vary" + value: "Accept-Encoding" + } + headers { + key: "-retry-on" + value: "retriable-headers" + } + headers { + key: "connection" + value: "http2-settings" + } + headers { + key: "-overloaded" + value: "true" + } + headers { + key: ":protocol" + value: "bytestream" + } + headers { + key: "-degraded" + value: "empty" + } + headers { + key: "-ratelimited" + value: "true" + } + headers { + key: "grpc-timeout" + value: "empty" + } + headers { + key: "x-content-type-options" + value: "empty" + } + headers { + key: "x-ot-span-context" + value: "empty" + } + headers { + key: "x-client-trace-id" + value: "empty" + } + headers { + key: "if-match" + value: "empty" + } + headers { + key: "-upstream-stream-duration-ms" + value: "empty" + } + headers { + key: "-upstream-service-time" + value: "empty" + } + headers { + key: "-immediate-health-check-fail" + value: "true" + } + headers { + key: "content-range" + value: "empty" + } + headers { + key: "-upstream-canary" + value: "empty" + } + headers { + key: "x-squash-debug" + value: "empty" + } + headers { + key: "grpc-accept-encoding" + value: "identity" + } + headers { + key: "access-control-allow-methods" + value: "empty" + } + headers { + key: "cdn-loop" + value: "empty" + } + headers { + key: "user-agent" + value: "Envoy/HC" + } + headers { + key: "-upstream-hostname" + value: "empty" + } + headers { + key: "access-control-allow-origin" + value: "empty" + } + headers { + key: "access-control-request-headers" + value: "empty" + } + headers { + key: "set-cookie" + value: "empty" + } + headers { + key: "-upstream-rq-per-try-timeout-ms" + value: "empty" + } + headers { + key: "-decorator-operation" + value: "empty" + } + headers { + key: "grpc-status-details-bin" + value: "empty" + } + headers { + key: "grpc-message-type" + value: "empty" + } + headers { + key: "proxy-status" + value: "empty" + } + headers { + key: "upgrade" + value: "h2c" + } + headers { + key: "location" + value: "empty" + } + } + } +} diff --git a/test/common/http/http2/http2_connection_corpus/HDRS009 b/test/common/http/http2/http2_connection_corpus/HDRS009 new file mode 100644 index 000000000000..49595f6ca3b4 --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/HDRS009 @@ -0,0 +1,197 @@ +frames { + h2frame { + headers { + key: ":scheme" + value: "https" + } + headers { + key: ":path" + value: "/" + } + headers { + key: ":method" + value: "delete" + } + headers { + key: "content-type" + value: "text/plain; charset=UTF-8" + } + headers { + key: "access-control-request-private-network" + value: "empty" + } + headers { + key: "grpc-status" + value: "empty" + } + headers { + key: "x-forwarded-host" + value: "empty" + } + headers { + key: "-original-path" + value: "empty" + } + headers { + key: "proxy-authenticate" + value: "empty" + } + headers { + key: "expect" + value: "100-continue" + } + headers { + key: "-retriable-header-names" + value: "empty" + } + headers { + key: "-internal" + value: "true" + } + headers { + key: "accept-encoding" + value: "identity" + } + headers { + key: "date" + value: "empty" + } + headers { + key: "transfer-encoding" + value: "zstd" + } + headers { + key: "vary" + value: "Accept-Encoding" + } + headers { + key: "-retry-on" + value: "retriable-headers" + } + headers { + key: "connection" + value: "http2-settings" + } + headers { + key: "-overloaded" + value: "true" + } + headers { + key: ":protocol" + value: "bytestream" + } + headers { + key: "-degraded" + value: "empty" + } + headers { + key: "-ratelimited" + value: "true" + } + headers { + key: "grpc-timeout" + value: "empty" + } + headers { + key: "x-content-type-options" + value: "empty" + } + headers { + key: "x-ot-span-context" + value: "empty" + } + headers { + key: "x-client-trace-id" + value: "empty" + } + headers { + key: "if-match" + value: "empty" + } + headers { + key: "-upstream-stream-duration-ms" + value: "empty" + } + headers { + key: "-upstream-service-time" + value: "empty" + } + headers { + key: "-immediate-health-check-fail" + value: "true" + } + headers { + key: "content-range" + value: "empty" + } + headers { + key: "-upstream-canary" + value: "empty" + } + headers { + key: "x-squash-debug" + value: "empty" + } + headers { + key: "grpc-accept-encoding" + value: "identity" + } + headers { + key: "access-control-allow-methods" + value: "empty" + } + headers { + key: "cdn-loop" + value: "empty" + } + headers { + key: "user-agent" + value: "Envoy/HC" + } + headers { + key: "-upstream-hostname" + value: "empty" + } + headers { + key: "access-control-allow-origin" + value: "empty" + } + headers { + key: "access-control-request-headers" + value: "empty" + } + headers { + key: "set-cookie" + value: "empty" + } + headers { + key: "-upstream-rq-per-try-timeout-ms" + value: "empty" + } + headers { + key: "-decorator-operation" + value: "empty" + } + headers { + key: "grpc-status-details-bin" + value: "empty" + } + headers { + key: "grpc-message-type" + value: "empty" + } + headers { + key: "proxy-status" + value: "empty" + } + headers { + key: "upgrade" + value: "h2c" + } + headers { + key: "location" + value: "empty" + } + } + } +} diff --git a/test/common/http/http2/http2_connection_corpus/HDRS010 b/test/common/http/http2/http2_connection_corpus/HDRS010 new file mode 100644 index 000000000000..de3caa48bbd0 --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/HDRS010 @@ -0,0 +1,197 @@ +frames { + h2frame { + headers { + key: ":scheme" + value: "http" + } + headers { + key: ":path" + value: "/" + } + headers { + key: ":method" + value: "options" + } + headers { + key: "content-type" + value: "text/plain; charset=UTF-8" + } + headers { + key: "access-control-request-private-network" + value: "empty" + } + headers { + key: "grpc-status" + value: "empty" + } + headers { + key: "x-forwarded-host" + value: "empty" + } + headers { + key: "-original-path" + value: "empty" + } + headers { + key: "proxy-authenticate" + value: "empty" + } + headers { + key: "expect" + value: "100-continue" + } + headers { + key: "-retriable-header-names" + value: "empty" + } + headers { + key: "-internal" + value: "true" + } + headers { + key: "accept-encoding" + value: "identity" + } + headers { + key: "date" + value: "empty" + } + headers { + key: "transfer-encoding" + value: "zstd" + } + headers { + key: "vary" + value: "Accept-Encoding" + } + headers { + key: "-retry-on" + value: "retriable-headers" + } + headers { + key: "connection" + value: "http2-settings" + } + headers { + key: "-overloaded" + value: "true" + } + headers { + key: ":protocol" + value: "bytestream" + } + headers { + key: "-degraded" + value: "empty" + } + headers { + key: "-ratelimited" + value: "true" + } + headers { + key: "grpc-timeout" + value: "empty" + } + headers { + key: "x-content-type-options" + value: "empty" + } + headers { + key: "x-ot-span-context" + value: "empty" + } + headers { + key: "x-client-trace-id" + value: "empty" + } + headers { + key: "if-match" + value: "empty" + } + headers { + key: "-upstream-stream-duration-ms" + value: "empty" + } + headers { + key: "-upstream-service-time" + value: "empty" + } + headers { + key: "-immediate-health-check-fail" + value: "true" + } + headers { + key: "content-range" + value: "empty" + } + headers { + key: "-upstream-canary" + value: "empty" + } + headers { + key: "x-squash-debug" + value: "empty" + } + headers { + key: "grpc-accept-encoding" + value: "identity" + } + headers { + key: "access-control-allow-methods" + value: "empty" + } + headers { + key: "cdn-loop" + value: "empty" + } + headers { + key: "user-agent" + value: "Envoy/HC" + } + headers { + key: "-upstream-hostname" + value: "empty" + } + headers { + key: "access-control-allow-origin" + value: "empty" + } + headers { + key: "access-control-request-headers" + value: "empty" + } + headers { + key: "set-cookie" + value: "empty" + } + headers { + key: "-upstream-rq-per-try-timeout-ms" + value: "empty" + } + headers { + key: "-decorator-operation" + value: "empty" + } + headers { + key: "grpc-status-details-bin" + value: "empty" + } + headers { + key: "grpc-message-type" + value: "empty" + } + headers { + key: "proxy-status" + value: "empty" + } + headers { + key: "upgrade" + value: "h2c" + } + headers { + key: "location" + value: "empty" + } + } + } +} diff --git a/test/common/http/http2/http2_connection_corpus/HDRS011 b/test/common/http/http2/http2_connection_corpus/HDRS011 new file mode 100644 index 000000000000..34601c377459 --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/HDRS011 @@ -0,0 +1,197 @@ +frames { + h2frame { + headers { + key: ":scheme" + value: "https" + } + headers { + key: ":path" + value: "/" + } + headers { + key: ":method" + value: "options" + } + headers { + key: "content-type" + value: "text/plain; charset=UTF-8" + } + headers { + key: "access-control-request-private-network" + value: "empty" + } + headers { + key: "grpc-status" + value: "empty" + } + headers { + key: "x-forwarded-host" + value: "empty" + } + headers { + key: "-original-path" + value: "empty" + } + headers { + key: "proxy-authenticate" + value: "empty" + } + headers { + key: "expect" + value: "100-continue" + } + headers { + key: "-retriable-header-names" + value: "empty" + } + headers { + key: "-internal" + value: "true" + } + headers { + key: "accept-encoding" + value: "identity" + } + headers { + key: "date" + value: "empty" + } + headers { + key: "transfer-encoding" + value: "zstd" + } + headers { + key: "vary" + value: "Accept-Encoding" + } + headers { + key: "-retry-on" + value: "retriable-headers" + } + headers { + key: "connection" + value: "http2-settings" + } + headers { + key: "-overloaded" + value: "true" + } + headers { + key: ":protocol" + value: "bytestream" + } + headers { + key: "-degraded" + value: "empty" + } + headers { + key: "-ratelimited" + value: "true" + } + headers { + key: "grpc-timeout" + value: "empty" + } + headers { + key: "x-content-type-options" + value: "empty" + } + headers { + key: "x-ot-span-context" + value: "empty" + } + headers { + key: "x-client-trace-id" + value: "empty" + } + headers { + key: "if-match" + value: "empty" + } + headers { + key: "-upstream-stream-duration-ms" + value: "empty" + } + headers { + key: "-upstream-service-time" + value: "empty" + } + headers { + key: "-immediate-health-check-fail" + value: "true" + } + headers { + key: "content-range" + value: "empty" + } + headers { + key: "-upstream-canary" + value: "empty" + } + headers { + key: "x-squash-debug" + value: "empty" + } + headers { + key: "grpc-accept-encoding" + value: "identity" + } + headers { + key: "access-control-allow-methods" + value: "empty" + } + headers { + key: "cdn-loop" + value: "empty" + } + headers { + key: "user-agent" + value: "Envoy/HC" + } + headers { + key: "-upstream-hostname" + value: "empty" + } + headers { + key: "access-control-allow-origin" + value: "empty" + } + headers { + key: "access-control-request-headers" + value: "empty" + } + headers { + key: "set-cookie" + value: "empty" + } + headers { + key: "-upstream-rq-per-try-timeout-ms" + value: "empty" + } + headers { + key: "-decorator-operation" + value: "empty" + } + headers { + key: "grpc-status-details-bin" + value: "empty" + } + headers { + key: "grpc-message-type" + value: "empty" + } + headers { + key: "proxy-status" + value: "empty" + } + headers { + key: "upgrade" + value: "h2c" + } + headers { + key: "location" + value: "empty" + } + } + } +} diff --git a/test/common/http/http2/http2_connection_corpus/HDRS012 b/test/common/http/http2/http2_connection_corpus/HDRS012 new file mode 100644 index 000000000000..33e4e01beebe --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/HDRS012 @@ -0,0 +1,197 @@ +frames { + h2frame { + headers { + key: ":scheme" + value: "http" + } + headers { + key: ":path" + value: "/" + } + headers { + key: ":method" + value: "trace" + } + headers { + key: "content-type" + value: "text/plain; charset=UTF-8" + } + headers { + key: "access-control-request-private-network" + value: "empty" + } + headers { + key: "grpc-status" + value: "empty" + } + headers { + key: "x-forwarded-host" + value: "empty" + } + headers { + key: "-original-path" + value: "empty" + } + headers { + key: "proxy-authenticate" + value: "empty" + } + headers { + key: "expect" + value: "100-continue" + } + headers { + key: "-retriable-header-names" + value: "empty" + } + headers { + key: "-internal" + value: "true" + } + headers { + key: "accept-encoding" + value: "identity" + } + headers { + key: "date" + value: "empty" + } + headers { + key: "transfer-encoding" + value: "zstd" + } + headers { + key: "vary" + value: "Accept-Encoding" + } + headers { + key: "-retry-on" + value: "retriable-headers" + } + headers { + key: "connection" + value: "http2-settings" + } + headers { + key: "-overloaded" + value: "true" + } + headers { + key: ":protocol" + value: "bytestream" + } + headers { + key: "-degraded" + value: "empty" + } + headers { + key: "-ratelimited" + value: "true" + } + headers { + key: "grpc-timeout" + value: "empty" + } + headers { + key: "x-content-type-options" + value: "empty" + } + headers { + key: "x-ot-span-context" + value: "empty" + } + headers { + key: "x-client-trace-id" + value: "empty" + } + headers { + key: "if-match" + value: "empty" + } + headers { + key: "-upstream-stream-duration-ms" + value: "empty" + } + headers { + key: "-upstream-service-time" + value: "empty" + } + headers { + key: "-immediate-health-check-fail" + value: "true" + } + headers { + key: "content-range" + value: "empty" + } + headers { + key: "-upstream-canary" + value: "empty" + } + headers { + key: "x-squash-debug" + value: "empty" + } + headers { + key: "grpc-accept-encoding" + value: "identity" + } + headers { + key: "access-control-allow-methods" + value: "empty" + } + headers { + key: "cdn-loop" + value: "empty" + } + headers { + key: "user-agent" + value: "Envoy/HC" + } + headers { + key: "-upstream-hostname" + value: "empty" + } + headers { + key: "access-control-allow-origin" + value: "empty" + } + headers { + key: "access-control-request-headers" + value: "empty" + } + headers { + key: "set-cookie" + value: "empty" + } + headers { + key: "-upstream-rq-per-try-timeout-ms" + value: "empty" + } + headers { + key: "-decorator-operation" + value: "empty" + } + headers { + key: "grpc-status-details-bin" + value: "empty" + } + headers { + key: "grpc-message-type" + value: "empty" + } + headers { + key: "proxy-status" + value: "empty" + } + headers { + key: "upgrade" + value: "h2c" + } + headers { + key: "location" + value: "empty" + } + } + } +} diff --git a/test/common/http/http2/http2_connection_corpus/HDRS013 b/test/common/http/http2/http2_connection_corpus/HDRS013 new file mode 100644 index 000000000000..b519ad384548 --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/HDRS013 @@ -0,0 +1,197 @@ +frames { + h2frame { + headers { + key: ":scheme" + value: "https" + } + headers { + key: ":path" + value: "/" + } + headers { + key: ":method" + value: "trace" + } + headers { + key: "content-type" + value: "text/plain; charset=UTF-8" + } + headers { + key: "access-control-request-private-network" + value: "empty" + } + headers { + key: "grpc-status" + value: "empty" + } + headers { + key: "x-forwarded-host" + value: "empty" + } + headers { + key: "-original-path" + value: "empty" + } + headers { + key: "proxy-authenticate" + value: "empty" + } + headers { + key: "expect" + value: "100-continue" + } + headers { + key: "-retriable-header-names" + value: "empty" + } + headers { + key: "-internal" + value: "true" + } + headers { + key: "accept-encoding" + value: "identity" + } + headers { + key: "date" + value: "empty" + } + headers { + key: "transfer-encoding" + value: "zstd" + } + headers { + key: "vary" + value: "Accept-Encoding" + } + headers { + key: "-retry-on" + value: "retriable-headers" + } + headers { + key: "connection" + value: "http2-settings" + } + headers { + key: "-overloaded" + value: "true" + } + headers { + key: ":protocol" + value: "bytestream" + } + headers { + key: "-degraded" + value: "empty" + } + headers { + key: "-ratelimited" + value: "true" + } + headers { + key: "grpc-timeout" + value: "empty" + } + headers { + key: "x-content-type-options" + value: "empty" + } + headers { + key: "x-ot-span-context" + value: "empty" + } + headers { + key: "x-client-trace-id" + value: "empty" + } + headers { + key: "if-match" + value: "empty" + } + headers { + key: "-upstream-stream-duration-ms" + value: "empty" + } + headers { + key: "-upstream-service-time" + value: "empty" + } + headers { + key: "-immediate-health-check-fail" + value: "true" + } + headers { + key: "content-range" + value: "empty" + } + headers { + key: "-upstream-canary" + value: "empty" + } + headers { + key: "x-squash-debug" + value: "empty" + } + headers { + key: "grpc-accept-encoding" + value: "identity" + } + headers { + key: "access-control-allow-methods" + value: "empty" + } + headers { + key: "cdn-loop" + value: "empty" + } + headers { + key: "user-agent" + value: "Envoy/HC" + } + headers { + key: "-upstream-hostname" + value: "empty" + } + headers { + key: "access-control-allow-origin" + value: "empty" + } + headers { + key: "access-control-request-headers" + value: "empty" + } + headers { + key: "set-cookie" + value: "empty" + } + headers { + key: "-upstream-rq-per-try-timeout-ms" + value: "empty" + } + headers { + key: "-decorator-operation" + value: "empty" + } + headers { + key: "grpc-status-details-bin" + value: "empty" + } + headers { + key: "grpc-message-type" + value: "empty" + } + headers { + key: "proxy-status" + value: "empty" + } + headers { + key: "upgrade" + value: "h2c" + } + headers { + key: "location" + value: "empty" + } + } + } +} diff --git a/test/common/http/http2/http2_connection_corpus/HDRS014 b/test/common/http/http2/http2_connection_corpus/HDRS014 new file mode 100644 index 000000000000..0e6b3b20ccdc --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/HDRS014 @@ -0,0 +1,197 @@ +frames { + h2frame { + headers { + key: ":scheme" + value: "http" + } + headers { + key: ":path" + value: "/" + } + headers { + key: ":method" + value: "patch" + } + headers { + key: "content-type" + value: "text/plain; charset=UTF-8" + } + headers { + key: "access-control-request-private-network" + value: "empty" + } + headers { + key: "grpc-status" + value: "empty" + } + headers { + key: "x-forwarded-host" + value: "empty" + } + headers { + key: "-original-path" + value: "empty" + } + headers { + key: "proxy-authenticate" + value: "empty" + } + headers { + key: "expect" + value: "100-continue" + } + headers { + key: "-retriable-header-names" + value: "empty" + } + headers { + key: "-internal" + value: "true" + } + headers { + key: "accept-encoding" + value: "identity" + } + headers { + key: "date" + value: "empty" + } + headers { + key: "transfer-encoding" + value: "zstd" + } + headers { + key: "vary" + value: "Accept-Encoding" + } + headers { + key: "-retry-on" + value: "retriable-headers" + } + headers { + key: "connection" + value: "http2-settings" + } + headers { + key: "-overloaded" + value: "true" + } + headers { + key: ":protocol" + value: "bytestream" + } + headers { + key: "-degraded" + value: "empty" + } + headers { + key: "-ratelimited" + value: "true" + } + headers { + key: "grpc-timeout" + value: "empty" + } + headers { + key: "x-content-type-options" + value: "empty" + } + headers { + key: "x-ot-span-context" + value: "empty" + } + headers { + key: "x-client-trace-id" + value: "empty" + } + headers { + key: "if-match" + value: "empty" + } + headers { + key: "-upstream-stream-duration-ms" + value: "empty" + } + headers { + key: "-upstream-service-time" + value: "empty" + } + headers { + key: "-immediate-health-check-fail" + value: "true" + } + headers { + key: "content-range" + value: "empty" + } + headers { + key: "-upstream-canary" + value: "empty" + } + headers { + key: "x-squash-debug" + value: "empty" + } + headers { + key: "grpc-accept-encoding" + value: "identity" + } + headers { + key: "access-control-allow-methods" + value: "empty" + } + headers { + key: "cdn-loop" + value: "empty" + } + headers { + key: "user-agent" + value: "Envoy/HC" + } + headers { + key: "-upstream-hostname" + value: "empty" + } + headers { + key: "access-control-allow-origin" + value: "empty" + } + headers { + key: "access-control-request-headers" + value: "empty" + } + headers { + key: "set-cookie" + value: "empty" + } + headers { + key: "-upstream-rq-per-try-timeout-ms" + value: "empty" + } + headers { + key: "-decorator-operation" + value: "empty" + } + headers { + key: "grpc-status-details-bin" + value: "empty" + } + headers { + key: "grpc-message-type" + value: "empty" + } + headers { + key: "proxy-status" + value: "empty" + } + headers { + key: "upgrade" + value: "h2c" + } + headers { + key: "location" + value: "empty" + } + } + } +} diff --git a/test/common/http/http2/http2_connection_corpus/HDRS015 b/test/common/http/http2/http2_connection_corpus/HDRS015 new file mode 100644 index 000000000000..f3a4ec4649f3 --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/HDRS015 @@ -0,0 +1,197 @@ +frames { + h2frame { + headers { + key: ":scheme" + value: "https" + } + headers { + key: ":path" + value: "/" + } + headers { + key: ":method" + value: "patch" + } + headers { + key: "content-type" + value: "text/plain; charset=UTF-8" + } + headers { + key: "access-control-request-private-network" + value: "empty" + } + headers { + key: "grpc-status" + value: "empty" + } + headers { + key: "x-forwarded-host" + value: "empty" + } + headers { + key: "-original-path" + value: "empty" + } + headers { + key: "proxy-authenticate" + value: "empty" + } + headers { + key: "expect" + value: "100-continue" + } + headers { + key: "-retriable-header-names" + value: "empty" + } + headers { + key: "-internal" + value: "true" + } + headers { + key: "accept-encoding" + value: "identity" + } + headers { + key: "date" + value: "empty" + } + headers { + key: "transfer-encoding" + value: "zstd" + } + headers { + key: "vary" + value: "Accept-Encoding" + } + headers { + key: "-retry-on" + value: "retriable-headers" + } + headers { + key: "connection" + value: "http2-settings" + } + headers { + key: "-overloaded" + value: "true" + } + headers { + key: ":protocol" + value: "bytestream" + } + headers { + key: "-degraded" + value: "empty" + } + headers { + key: "-ratelimited" + value: "true" + } + headers { + key: "grpc-timeout" + value: "empty" + } + headers { + key: "x-content-type-options" + value: "empty" + } + headers { + key: "x-ot-span-context" + value: "empty" + } + headers { + key: "x-client-trace-id" + value: "empty" + } + headers { + key: "if-match" + value: "empty" + } + headers { + key: "-upstream-stream-duration-ms" + value: "empty" + } + headers { + key: "-upstream-service-time" + value: "empty" + } + headers { + key: "-immediate-health-check-fail" + value: "true" + } + headers { + key: "content-range" + value: "empty" + } + headers { + key: "-upstream-canary" + value: "empty" + } + headers { + key: "x-squash-debug" + value: "empty" + } + headers { + key: "grpc-accept-encoding" + value: "identity" + } + headers { + key: "access-control-allow-methods" + value: "empty" + } + headers { + key: "cdn-loop" + value: "empty" + } + headers { + key: "user-agent" + value: "Envoy/HC" + } + headers { + key: "-upstream-hostname" + value: "empty" + } + headers { + key: "access-control-allow-origin" + value: "empty" + } + headers { + key: "access-control-request-headers" + value: "empty" + } + headers { + key: "set-cookie" + value: "empty" + } + headers { + key: "-upstream-rq-per-try-timeout-ms" + value: "empty" + } + headers { + key: "-decorator-operation" + value: "empty" + } + headers { + key: "grpc-status-details-bin" + value: "empty" + } + headers { + key: "grpc-message-type" + value: "empty" + } + headers { + key: "proxy-status" + value: "empty" + } + headers { + key: "upgrade" + value: "h2c" + } + headers { + key: "location" + value: "empty" + } + } + } +} diff --git a/test/common/http/http2/http2_connection_corpus/HDRS016 b/test/common/http/http2/http2_connection_corpus/HDRS016 new file mode 100644 index 000000000000..41f0025bfbbe --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/HDRS016 @@ -0,0 +1,197 @@ +frames { + h2frame { + headers { + key: ":scheme" + value: "http" + } + headers { + key: ":path" + value: "/" + } + headers { + key: ":method" + value: "connect" + } + headers { + key: "content-type" + value: "text/plain; charset=UTF-8" + } + headers { + key: "access-control-request-private-network" + value: "empty" + } + headers { + key: "grpc-status" + value: "empty" + } + headers { + key: "x-forwarded-host" + value: "empty" + } + headers { + key: "-original-path" + value: "empty" + } + headers { + key: "proxy-authenticate" + value: "empty" + } + headers { + key: "expect" + value: "100-continue" + } + headers { + key: "-retriable-header-names" + value: "empty" + } + headers { + key: "-internal" + value: "true" + } + headers { + key: "accept-encoding" + value: "identity" + } + headers { + key: "date" + value: "empty" + } + headers { + key: "transfer-encoding" + value: "zstd" + } + headers { + key: "vary" + value: "Accept-Encoding" + } + headers { + key: "-retry-on" + value: "retriable-headers" + } + headers { + key: "connection" + value: "http2-settings" + } + headers { + key: "-overloaded" + value: "true" + } + headers { + key: ":protocol" + value: "bytestream" + } + headers { + key: "-degraded" + value: "empty" + } + headers { + key: "-ratelimited" + value: "true" + } + headers { + key: "grpc-timeout" + value: "empty" + } + headers { + key: "x-content-type-options" + value: "empty" + } + headers { + key: "x-ot-span-context" + value: "empty" + } + headers { + key: "x-client-trace-id" + value: "empty" + } + headers { + key: "if-match" + value: "empty" + } + headers { + key: "-upstream-stream-duration-ms" + value: "empty" + } + headers { + key: "-upstream-service-time" + value: "empty" + } + headers { + key: "-immediate-health-check-fail" + value: "true" + } + headers { + key: "content-range" + value: "empty" + } + headers { + key: "-upstream-canary" + value: "empty" + } + headers { + key: "x-squash-debug" + value: "empty" + } + headers { + key: "grpc-accept-encoding" + value: "identity" + } + headers { + key: "access-control-allow-methods" + value: "empty" + } + headers { + key: "cdn-loop" + value: "empty" + } + headers { + key: "user-agent" + value: "Envoy/HC" + } + headers { + key: "-upstream-hostname" + value: "empty" + } + headers { + key: "access-control-allow-origin" + value: "empty" + } + headers { + key: "access-control-request-headers" + value: "empty" + } + headers { + key: "set-cookie" + value: "empty" + } + headers { + key: "-upstream-rq-per-try-timeout-ms" + value: "empty" + } + headers { + key: "-decorator-operation" + value: "empty" + } + headers { + key: "grpc-status-details-bin" + value: "empty" + } + headers { + key: "grpc-message-type" + value: "empty" + } + headers { + key: "proxy-status" + value: "empty" + } + headers { + key: "upgrade" + value: "h2c" + } + headers { + key: "location" + value: "empty" + } + } + } +} diff --git a/test/common/http/http2/http2_connection_corpus/HDRS017 b/test/common/http/http2/http2_connection_corpus/HDRS017 new file mode 100644 index 000000000000..1223b85419c7 --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/HDRS017 @@ -0,0 +1,197 @@ +frames { + h2frame { + headers { + key: ":scheme" + value: "https" + } + headers { + key: ":path" + value: "/" + } + headers { + key: ":method" + value: "connect" + } + headers { + key: "content-type" + value: "text/plain; charset=UTF-8" + } + headers { + key: "access-control-request-private-network" + value: "empty" + } + headers { + key: "grpc-status" + value: "empty" + } + headers { + key: "x-forwarded-host" + value: "empty" + } + headers { + key: "-original-path" + value: "empty" + } + headers { + key: "proxy-authenticate" + value: "empty" + } + headers { + key: "expect" + value: "100-continue" + } + headers { + key: "-retriable-header-names" + value: "empty" + } + headers { + key: "-internal" + value: "true" + } + headers { + key: "accept-encoding" + value: "identity" + } + headers { + key: "date" + value: "empty" + } + headers { + key: "transfer-encoding" + value: "zstd" + } + headers { + key: "vary" + value: "Accept-Encoding" + } + headers { + key: "-retry-on" + value: "retriable-headers" + } + headers { + key: "connection" + value: "http2-settings" + } + headers { + key: "-overloaded" + value: "true" + } + headers { + key: ":protocol" + value: "bytestream" + } + headers { + key: "-degraded" + value: "empty" + } + headers { + key: "-ratelimited" + value: "true" + } + headers { + key: "grpc-timeout" + value: "empty" + } + headers { + key: "x-content-type-options" + value: "empty" + } + headers { + key: "x-ot-span-context" + value: "empty" + } + headers { + key: "x-client-trace-id" + value: "empty" + } + headers { + key: "if-match" + value: "empty" + } + headers { + key: "-upstream-stream-duration-ms" + value: "empty" + } + headers { + key: "-upstream-service-time" + value: "empty" + } + headers { + key: "-immediate-health-check-fail" + value: "true" + } + headers { + key: "content-range" + value: "empty" + } + headers { + key: "-upstream-canary" + value: "empty" + } + headers { + key: "x-squash-debug" + value: "empty" + } + headers { + key: "grpc-accept-encoding" + value: "identity" + } + headers { + key: "access-control-allow-methods" + value: "empty" + } + headers { + key: "cdn-loop" + value: "empty" + } + headers { + key: "user-agent" + value: "Envoy/HC" + } + headers { + key: "-upstream-hostname" + value: "empty" + } + headers { + key: "access-control-allow-origin" + value: "empty" + } + headers { + key: "access-control-request-headers" + value: "empty" + } + headers { + key: "set-cookie" + value: "empty" + } + headers { + key: "-upstream-rq-per-try-timeout-ms" + value: "empty" + } + headers { + key: "-decorator-operation" + value: "empty" + } + headers { + key: "grpc-status-details-bin" + value: "empty" + } + headers { + key: "grpc-message-type" + value: "empty" + } + headers { + key: "proxy-status" + value: "empty" + } + headers { + key: "upgrade" + value: "h2c" + } + headers { + key: "location" + value: "empty" + } + } + } +} diff --git a/test/common/http/http2/http2_connection_corpus/HEADERS b/test/common/http/http2/http2_connection_corpus/HEADERS new file mode 100644 index 000000000000..ade3dbf6efe9 --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/HEADERS @@ -0,0 +1,5 @@ +frames { + junk { + data: "\000\000\012\001\000\000\000\000\001\101\101\101\101\101\101\101\101\101\101" + } +} diff --git a/test/common/http/http2/http2_connection_corpus/PING b/test/common/http/http2/http2_connection_corpus/PING new file mode 100644 index 000000000000..b63d83fb4c9d --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/PING @@ -0,0 +1,5 @@ +frames { + junk { + data: "\000\000\012\006\000\000\000\000\001\101\101\101\101\101\101\101\101\101\101" + } +} diff --git a/test/common/http/http2/http2_connection_corpus/PRIORITY b/test/common/http/http2/http2_connection_corpus/PRIORITY new file mode 100644 index 000000000000..fd644ec77528 --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/PRIORITY @@ -0,0 +1,5 @@ +frames { + junk { + data: "\000\000\012\002\000\000\000\000\001\101\101\101\101\101\101\101\101\101\101" + } +} diff --git a/test/common/http/http2/http2_connection_corpus/PUSH_PROMISE b/test/common/http/http2/http2_connection_corpus/PUSH_PROMISE new file mode 100644 index 000000000000..7b053189e942 --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/PUSH_PROMISE @@ -0,0 +1,5 @@ +frames { + junk { + data: "\000\000\012\005\000\000\000\000\001\101\101\101\101\101\101\101\101\101\101" + } +} diff --git a/test/common/http/http2/http2_connection_corpus/RST_STREAM b/test/common/http/http2/http2_connection_corpus/RST_STREAM new file mode 100644 index 000000000000..7cc327c68899 --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/RST_STREAM @@ -0,0 +1,5 @@ +frames { + junk { + data: "\000\000\012\003\000\000\000\000\001\101\101\101\101\101\101\101\101\101\101" + } +} diff --git a/test/common/http/http2/http2_connection_corpus/SETTINGS b/test/common/http/http2/http2_connection_corpus/SETTINGS new file mode 100644 index 000000000000..53cf81a95cb6 --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/SETTINGS @@ -0,0 +1,5 @@ +frames { + junk { + data: "\000\000\012\004\000\000\000\000\001\101\101\101\101\101\101\101\101\101\101" + } +} diff --git a/test/common/http/http2/http2_connection_corpus/WINDOW_UPDATE b/test/common/http/http2/http2_connection_corpus/WINDOW_UPDATE new file mode 100644 index 000000000000..6d30bea8222f --- /dev/null +++ b/test/common/http/http2/http2_connection_corpus/WINDOW_UPDATE @@ -0,0 +1,5 @@ +frames { + junk { + data: "\000\000\012\010\000\000\000\000\001\101\101\101\101\101\101\101\101\101\101" + } +} diff --git a/test/common/http/http2/http2_connection_fuzz_test.cc b/test/common/http/http2/http2_connection_fuzz_test.cc new file mode 100644 index 000000000000..29107b7cd1e8 --- /dev/null +++ b/test/common/http/http2/http2_connection_fuzz_test.cc @@ -0,0 +1,251 @@ +#include + +#include +#include + +#include "source/common/http/http2/codec_impl.h" + +#include "test/common/http/http2/http2_connection.pb.h" +#include "test/common/http/http2/http2_frame.h" +#include "test/fuzz/fuzz_runner.h" +#include "test/mocks/http/mocks.h" +#include "test/mocks/network/mocks.h" +#include "test/test_common/test_runtime.h" + +#include "absl/strings/string_view.h" +#include "nghttp2/nghttp2.h" + +namespace Envoy { +namespace Http { +namespace Http2 { +namespace { + +static const uint32_t MAX_HEADERS = 32; +static const size_t MAX_HD_TABLE_BUF_SIZE = 4096; + +void deflate_headers(nghttp2_hd_deflater* deflater, const test::fuzz::Headers& fragment, + std::string& payload) { +#define MAX_HDRS 32 + nghttp2_nv h2hdrs[MAX_HEADERS]; + uint8_t buf[MAX_HD_TABLE_BUF_SIZE]; + + unsigned n_headers = fragment.headers().size(); + if (n_headers > MAX_HEADERS) + n_headers = MAX_HEADERS; + auto headers = fragment.headers(); + for (unsigned i = 0; i < n_headers; i++) { + auto hv = headers.at(i); + size_t keylen = hv.key().size(); + uint8_t* key = reinterpret_cast(alloca(keylen)); + std::memcpy(key, hv.key().data(), keylen); + + size_t valuelen = hv.value().size(); + uint8_t* value = reinterpret_cast(alloca(valuelen)); + std::memcpy(value, hv.value().data(), valuelen); + + h2hdrs[i].name = key; + h2hdrs[i].namelen = keylen; + h2hdrs[i].value = value; + h2hdrs[i].valuelen = valuelen; + h2hdrs[i].flags = 0; + } + ssize_t len = nghttp2_hd_deflate_hd(deflater, buf, sizeof(buf), h2hdrs, n_headers); + if (len > 0) + payload.append(reinterpret_cast(buf), len); +} + +Http2Frame pb_to_h2_frame(nghttp2_hd_deflater* deflater, + const test::common::http::http2::Http2FrameOrJunk& pb_frame) { + if (pb_frame.has_junk()) { + // Junk frame + auto junk = pb_frame.junk(); + absl::string_view contents = junk.data(); + return Http2Frame::makeGenericFrame(contents); + } else if (pb_frame.has_h2frame()) { + // Need to encode headers + auto h2frame = pb_frame.h2frame(); + uint32_t streamid = Http2Frame::makeClientStreamId(h2frame.streamid()); + uint8_t flags = static_cast(h2frame.flags() & 0xff); + bool use_padding = flags & static_cast(Http2Frame::HeadersFlags::Padded); + Http2Frame::Type type; + std::string payload; + if (h2frame.has_data()) { + type = Http2Frame::Type::Data; + auto f = h2frame.data(); + payload.append(f.data()); + } else if (h2frame.has_headers()) { + type = Http2Frame::Type::Headers; + auto f = h2frame.headers(); + uint8_t padding_len = static_cast(f.padding().size() & 0xff); + if (use_padding) { + payload.push_back(static_cast(padding_len)); + } + + uint32_t stream_dependency = htonl(f.stream_dependency()); + payload.append(reinterpret_cast(&stream_dependency), sizeof(stream_dependency)); + + if (f.weight() > 0 && f.weight() <= 256) { + uint8_t weight = static_cast((f.weight() - 1) & 0xff); + payload.append(reinterpret_cast(&weight), sizeof(weight)); + } + + deflate_headers(deflater, f.headers(), payload); + if (use_padding) { + payload.append(f.padding().data(), padding_len); + } + } else if (h2frame.has_priority()) { + type = Http2Frame::Type::Priority; + auto f = h2frame.priority(); + uint32_t stream_dependency = htonl(f.stream_dependency()); + payload.append(reinterpret_cast(&stream_dependency), sizeof(stream_dependency)); + payload.push_back(static_cast(f.weight() & 0xff)); + } else if (h2frame.has_rst()) { + type = Http2Frame::Type::RstStream; + auto f = h2frame.rst(); + uint32_t error_code = htonl(f.error_code()); + payload.append(reinterpret_cast(&error_code), sizeof(error_code)); + } else if (h2frame.has_settings()) { + type = Http2Frame::Type::Settings; + auto f = h2frame.settings(); + for (auto& setting : f.settings()) { + uint16_t id = static_cast(setting.identifier() & 0xffff); + uint32_t value = htonl(setting.value()); + payload.append(reinterpret_cast(&id), sizeof(id)); + payload.append(reinterpret_cast(&value), sizeof(value)); + } + } else if (h2frame.has_push_promise()) { + type = Http2Frame::Type::PushPromise; + auto f = h2frame.push_promise(); + uint8_t padding_len = static_cast(f.padding().size() & 0xff); + if (use_padding) { + payload.push_back(static_cast(padding_len)); + } + uint32_t stream_id = htonl(f.stream_id()); + payload.append(reinterpret_cast(&stream_id), sizeof(stream_id)); + deflate_headers(deflater, f.headers(), payload); + if (use_padding) { + payload.append(f.padding().data(), padding_len); + } + } else if (h2frame.has_ping()) { + type = Http2Frame::Type::Ping; + auto f = h2frame.ping(); + uint64_t pdata = f.data(); + payload.append(reinterpret_cast(&pdata), sizeof(pdata)); + } else if (h2frame.has_go_away()) { + type = Http2Frame::Type::GoAway; + auto f = h2frame.go_away(); + uint32_t last_stream_id = htonl(f.last_stream_id()); + payload.append(reinterpret_cast(&last_stream_id), sizeof(last_stream_id)); + uint32_t error_code = htonl(f.error_code()); + payload.append(reinterpret_cast(&error_code), sizeof(error_code)); + payload.append(f.debug_data()); + } else if (h2frame.has_window_update()) { + type = Http2Frame::Type::WindowUpdate; + auto f = h2frame.window_update(); + uint32_t wsi = htonl(f.window_size_increment()); + payload.append(reinterpret_cast(&wsi), sizeof(wsi)); + } else if (h2frame.has_continuation()) { + type = Http2Frame::Type::Continuation; + auto f = h2frame.continuation(); + deflate_headers(deflater, f.headers(), payload); + } else { + // Empty frame + return Http2Frame(); + } + return Http2Frame::makeRawFrame(type, flags, streamid, payload); + } else { + // Empty frame + return Http2Frame(); + } +} + +envoy::config::core::v3::Http2ProtocolOptions http2Settings() { + envoy::config::core::v3::Http2ProtocolOptions options( + ::Envoy::Http2::Utility::initializeAndValidateOptions( + envoy::config::core::v3::Http2ProtocolOptions())); + + options.mutable_hpack_table_size()->set_value(MAX_HD_TABLE_BUF_SIZE); + options.set_allow_metadata(true); + return options; +} + +class Http2Harness { +public: + Http2Harness() : server_settings(http2Settings()) { + ON_CALL(mock_server_callbacks, newStream(_, _)) + .WillByDefault(Invoke( + [&](ResponseEncoder&, bool) -> RequestDecoder& { return orphan_request_decoder; })); + } + + void fuzz_request(std::vector& frames, bool use_oghttp2) { + TestScopedRuntime scoped_runtime; + if (use_oghttp2) { + scoped_runtime.mergeValues({{"envoy.reloadable_features.http2_use_oghttp2", "true"}}); + } else { + scoped_runtime.mergeValues({{"envoy.reloadable_features.http2_use_oghttp2", "false"}}); + } + Stats::Scope& scope = *stats_store.rootScope(); + server_ = std::make_unique( + mock_server_connection, mock_server_callbacks, + Http2::CodecStats::atomicGet(http2_stats, scope), random, server_settings, + Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, + envoy::config::core::v3::HttpProtocolOptions::ALLOW); + + Buffer::OwnedImpl payload; + payload.add(Http2Frame::Preamble, 24); + static Http2Frame emptySettingsFrame = Http2Frame::makeEmptySettingsFrame(); + payload.add(emptySettingsFrame.data(), emptySettingsFrame.size()); + for (auto frame : frames) { + payload.add(frame.data(), frame.size()); + } + Status status = server_->dispatch(payload); + } + +private: + const envoy::config::core::v3::Http2ProtocolOptions server_settings; + Stats::IsolatedStoreImpl stats_store; + Http2::CodecStats::AtomicPtr http2_stats; + + NiceMock orphan_request_decoder; + NiceMock mock_server_connection; + NiceMock mock_server_callbacks; + NiceMock random; + + ServerConnectionPtr server_; +}; + +static std::unique_ptr harness; +static void reset_harness() { harness = nullptr; } + +// HTTP/2 fuzzer +// +// Uses a mix of structured HTTP/2 frames and junk frames for coverage + +DEFINE_PROTO_FUZZER(const test::common::http::http2::Http2ConnectionFuzzCase& input) { + if (harness == nullptr) { + harness = std::make_unique(); + atexit(reset_harness); + } + + // Convert input to Http2Frame wire format + size_t n_frames = input.frames_size(); + if (n_frames == 0) + return; + std::vector frames; + nghttp2_hd_deflater* deflater = NULL; + if (nghttp2_hd_deflate_new(&deflater, MAX_HD_TABLE_BUF_SIZE) != 0) + return; + for (auto& pbframe : input.frames()) { + Http2Frame frame = pb_to_h2_frame(deflater, pbframe); + frames.push_back(frame); + } + nghttp2_hd_deflate_del(deflater); + + harness->fuzz_request(frames, true /* use oghttp2 rather than nghttp2 */); + harness->fuzz_request(frames, false); +} + +} // namespace +} // namespace Http2 +} // namespace Http +} // namespace Envoy diff --git a/test/common/http/http2/http2_frame.cc b/test/common/http/http2/http2_frame.cc index 59a71d378a2a..676a2a826065 100644 --- a/test/common/http/http2/http2_frame.cc +++ b/test/common/http/http2/http2_frame.cc @@ -114,6 +114,15 @@ void Http2Frame::appendEmptyHeader() { data_.push_back(0x00); } +Http2Frame Http2Frame::makeRawFrame(Type type, uint8_t flags, uint32_t stream_id, + absl::string_view payload) { + Http2Frame frame; + frame.buildHeader(type, 0, flags, makeNetworkOrderStreamId(stream_id)); + frame.appendData(payload); + frame.adjustPayloadSize(); + return frame; +} + Http2Frame Http2Frame::makePingFrame(absl::string_view data) { static constexpr size_t kPingPayloadSize = 8; Http2Frame frame; diff --git a/test/common/http/http2/http2_frame.h b/test/common/http/http2/http2_frame.h index e74dfe1c93ad..c6f61c08634b 100644 --- a/test/common/http/http2/http2_frame.h +++ b/test/common/http/http2/http2_frame.h @@ -63,6 +63,7 @@ class Http2Frame { None = 0, EndStream = 1, EndHeaders = 4, + Padded = 8, }; enum class DataFlags : uint8_t { @@ -127,6 +128,8 @@ class Http2Frame { static uint32_t makeClientStreamId(uint32_t stream_id) { return (stream_id << 1) | 1; } // Methods for creating HTTP2 frames + static Http2Frame makeRawFrame(Type type, uint8_t flags, uint32_t stream_id, + absl::string_view payload); static Http2Frame makePingFrame(absl::string_view data = {}); static Http2Frame makeEmptySettingsFrame(SettingsFlags flags = SettingsFlags::None); static Http2Frame makeSettingsFrame(SettingsFlags flags, diff --git a/test/common/http/http2/metadata_encoder_decoder_test.cc b/test/common/http/http2/metadata_encoder_decoder_test.cc deleted file mode 100644 index 9816836e04c7..000000000000 --- a/test/common/http/http2/metadata_encoder_decoder_test.cc +++ /dev/null @@ -1,398 +0,0 @@ -#include "envoy/http/metadata_interface.h" - -#include "source/common/buffer/buffer_impl.h" -#include "source/common/common/logger.h" -#include "source/common/common/random_generator.h" -#include "source/common/http/http2/metadata_decoder.h" -#include "source/common/http/http2/metadata_encoder.h" - -#include "test/test_common/logging.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "http2_frame.h" -#include "nghttp2/nghttp2.h" - -// A global variable in nghttp2 to disable preface and initial settings for tests. -// TODO(soya3129): Remove after issue https://github.com/nghttp2/nghttp2/issues/1246 is fixed. -extern "C" { -extern int nghttp2_enable_strict_preface; -} - -namespace Envoy { -namespace Http { -namespace Http2 { -namespace { - -static const uint64_t STREAM_ID = 1; - -// The buffer stores data sent by encoder and received by decoder. -struct TestBuffer { - uint8_t buf[1024 * 1024] = {0}; - size_t length = 0; -}; - -// The application data structure passes to nghttp2 session. -struct UserData { - MetadataEncoder* encoder; - MetadataDecoder* decoder; - // Stores data sent by encoder and received by the decoder. - TestBuffer* output_buffer; -}; - -// Nghttp2 callback function for sending extension frame. -static ssize_t pack_extension_callback(nghttp2_session* session, uint8_t* buf, size_t len, - const nghttp2_frame*, void* user_data) { - EXPECT_NE(nullptr, session); - - MetadataEncoder* encoder = reinterpret_cast(user_data)->encoder; - const uint64_t size_copied = encoder->packNextFramePayload(buf, len); - - return static_cast(size_copied); -} - -// Nghttp2 callback function for receiving extension frame. -static int on_extension_chunk_recv_callback(nghttp2_session* session, const nghttp2_frame_hd* hd, - const uint8_t* data, size_t len, void* user_data) { - EXPECT_NE(nullptr, session); - EXPECT_GE(hd->length, len); - - MetadataDecoder* decoder = reinterpret_cast(user_data)->decoder; - bool success = decoder->receiveMetadata(data, len); - return success ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE; -} - -// Nghttp2 callback function for unpack extension frames. -static int unpack_extension_callback(nghttp2_session* session, void** payload, - const nghttp2_frame_hd* hd, void* user_data) { - EXPECT_NE(nullptr, session); - EXPECT_NE(nullptr, hd); - EXPECT_NE(nullptr, payload); - - MetadataDecoder* decoder = reinterpret_cast(user_data)->decoder; - bool result = decoder->onMetadataFrameComplete((hd->flags == END_METADATA_FLAG) ? true : false); - return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE; -} - -// Nghttp2 callback function for sending data to peer. -static ssize_t send_callback(nghttp2_session* session, const uint8_t* buf, size_t len, int flags, - void* user_data) { - EXPECT_NE(nullptr, session); - EXPECT_LE(0, flags); - - TestBuffer* buffer = (reinterpret_cast(user_data))->output_buffer; - memcpy(buffer->buf + buffer->length, buf, len); - buffer->length += len; - return len; -} - -} // namespace - -class MetadataEncoderDecoderTest : public testing::Test { -public: - void initialize(MetadataCallback cb) { - decoder_ = std::make_unique(cb); - - // Enables extension frame. - nghttp2_option_new(&option_); - nghttp2_option_set_user_recv_extension_type(option_, METADATA_FRAME_TYPE); - - // Sets callback functions. - nghttp2_session_callbacks_new(&callbacks_); - nghttp2_session_callbacks_set_pack_extension_callback(callbacks_, pack_extension_callback); - nghttp2_session_callbacks_set_send_callback(callbacks_, send_callback); - nghttp2_session_callbacks_set_on_extension_chunk_recv_callback( - callbacks_, on_extension_chunk_recv_callback); - nghttp2_session_callbacks_set_unpack_extension_callback(callbacks_, unpack_extension_callback); - - // Sets application data to pass to nghttp2 session. - user_data_.encoder = &encoder_; - user_data_.decoder = decoder_.get(); - user_data_.output_buffer = &output_buffer_; - - // Creates new nghttp2 session. - nghttp2_enable_strict_preface = 0; - nghttp2_session_client_new2(&session_, callbacks_, &user_data_, option_); - nghttp2_enable_strict_preface = 1; - } - - void cleanUp() { - nghttp2_session_del(session_); - nghttp2_session_callbacks_del(callbacks_); - nghttp2_option_del(option_); - } - - void verifyMetadataMapVector(MetadataMapVector& expect, MetadataMapPtr&& metadata_map_ptr) { - ASSERT_EQ(expect.front()->size(), metadata_map_ptr->size()); - for (const auto& metadata : *metadata_map_ptr) { - EXPECT_EQ(expect.front()->find(metadata.first)->second, metadata.second); - } - expect.erase(expect.begin()); - } - - void submitMetadata(const MetadataMapVector& metadata_map_vector) { - // Creates metadata payload. - encoder_.createPayload(metadata_map_vector); - for (uint8_t flags : encoder_.payloadFrameFlagBytes()) { - int result = - nghttp2_submit_extension(session_, METADATA_FRAME_TYPE, flags, STREAM_ID, nullptr); - EXPECT_EQ(0, result); - } - // Triggers nghttp2 to populate the payloads of the METADATA frames. - int result = nghttp2_session_send(session_); - EXPECT_EQ(0, result); - } - - nghttp2_session* session_ = nullptr; - nghttp2_session_callbacks* callbacks_; - MetadataEncoder encoder_; - std::unique_ptr decoder_; - nghttp2_option* option_; - int count_ = 0; - - // Stores data received by peer. - TestBuffer output_buffer_; - - // Application data passed to nghttp2. - UserData user_data_; - - Random::RandomGeneratorImpl random_generator_; -}; - -TEST_F(MetadataEncoderDecoderTest, TestMetadataSizeLimit) { - MetadataMap metadata_map = { - {"header_key1", std::string(1024 * 1024 + 1, 'a')}, - }; - MetadataMapPtr metadata_map_ptr = std::make_unique(metadata_map); - MetadataMapVector metadata_map_vector; - metadata_map_vector.push_back(std::move(metadata_map_ptr)); - - // Verifies the encoding/decoding result in decoder's callback functions. - initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void { - this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr)); - }); - - // metadata_map exceeds size limit. - EXPECT_LOG_CONTAINS("error", "exceeds the max bound.", - EXPECT_FALSE(encoder_.createPayload(metadata_map_vector))); - - std::string payload = std::string(1024 * 1024 + 1, 'a'); - EXPECT_FALSE( - decoder_->receiveMetadata(reinterpret_cast(payload.data()), payload.size())); - - cleanUp(); -} - -TEST_F(MetadataEncoderDecoderTest, TestTotalPayloadSize) { - initialize([](MetadataMapPtr&&) {}); - - const std::string payload = std::string(1024, 'a'); - EXPECT_EQ(0, decoder_->totalPayloadSize()); - EXPECT_TRUE( - decoder_->receiveMetadata(reinterpret_cast(payload.data()), payload.size())); - EXPECT_EQ(payload.size(), decoder_->totalPayloadSize()); - EXPECT_TRUE( - decoder_->receiveMetadata(reinterpret_cast(payload.data()), payload.size())); - EXPECT_EQ(2 * payload.size(), decoder_->totalPayloadSize()); - cleanUp(); -} - -TEST_F(MetadataEncoderDecoderTest, TestDecodeBadData) { - MetadataMap metadata_map = { - {"header_key1", "header_value1"}, - }; - MetadataMapPtr metadata_map_ptr = std::make_unique(metadata_map); - MetadataMapVector metadata_map_vector; - metadata_map_vector.push_back(std::move(metadata_map_ptr)); - - // Verifies the encoding/decoding result in decoder's callback functions. - initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void { - this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr)); - }); - submitMetadata(metadata_map_vector); - - // Messes up with the encoded payload, and passes it to the decoder. - output_buffer_.buf[10] |= 0xff; - decoder_->receiveMetadata(output_buffer_.buf, output_buffer_.length); - EXPECT_FALSE(decoder_->onMetadataFrameComplete(true)); - - cleanUp(); -} - -// Checks if accumulated metadata size reaches size limit, returns failure. -TEST_F(MetadataEncoderDecoderTest, VerifyEncoderDecoderMultipleMetadataReachSizeLimit) { - MetadataMap metadata_map_empty = {}; - MetadataCallback cb = [](std::unique_ptr) -> void {}; - initialize(cb); - - ssize_t result = 0; - - for (int i = 0; i < 100; i++) { - // Cleans up the output buffer. - memset(output_buffer_.buf, 0, output_buffer_.length); - output_buffer_.length = 0; - - MetadataMap metadata_map = { - {"header_key1", std::string(10000, 'a')}, - {"header_key2", std::string(10000, 'b')}, - }; - MetadataMapPtr metadata_map_ptr = std::make_unique(metadata_map); - MetadataMapVector metadata_map_vector; - metadata_map_vector.push_back(std::move(metadata_map_ptr)); - - // Encode and decode the second MetadataMap. - decoder_->callback_ = [this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void { - this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr)); - }; - submitMetadata(metadata_map_vector); - - result = nghttp2_session_mem_recv(session_, output_buffer_.buf, output_buffer_.length); - if (result < 0) { - break; - } - } - // Verifies max metadata limit reached. - EXPECT_LT(result, 0); - EXPECT_LE(decoder_->max_payload_size_bound_, decoder_->total_payload_size_); - - cleanUp(); -} - -// Tests encoding/decoding small metadata map vectors. -TEST_F(MetadataEncoderDecoderTest, EncodeMetadataMapVectorSmall) { - MetadataMap metadata_map = { - {"header_key1", std::string(5, 'a')}, - {"header_key2", std::string(5, 'b')}, - }; - MetadataMapPtr metadata_map_ptr = std::make_unique(metadata_map); - MetadataMap metadata_map_2 = { - {"header_key3", std::string(5, 'a')}, - {"header_key4", std::string(5, 'b')}, - }; - MetadataMapPtr metadata_map_ptr_2 = std::make_unique(metadata_map); - MetadataMap metadata_map_3 = { - {"header_key1", std::string(1, 'a')}, - {"header_key2", std::string(1, 'b')}, - }; - MetadataMapPtr metadata_map_ptr_3 = std::make_unique(metadata_map); - - MetadataMapVector metadata_map_vector; - metadata_map_vector.push_back(std::move(metadata_map_ptr)); - metadata_map_vector.push_back(std::move(metadata_map_ptr_2)); - metadata_map_vector.push_back(std::move(metadata_map_ptr_3)); - - // Verifies the encoding/decoding result in decoder's callback functions. - initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void { - this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr)); - }); - submitMetadata(metadata_map_vector); - - // Verifies flag and payload are encoded correctly. - const uint64_t consume_size = random_generator_.random() % output_buffer_.length; - nghttp2_session_mem_recv(session_, output_buffer_.buf, consume_size); - nghttp2_session_mem_recv(session_, output_buffer_.buf + consume_size, - output_buffer_.length - consume_size); - - cleanUp(); -} - -// Tests encoding/decoding large metadata map vectors. -TEST_F(MetadataEncoderDecoderTest, EncodeMetadataMapVectorLarge) { - MetadataMapVector metadata_map_vector; - for (int i = 0; i < 10; i++) { - MetadataMap metadata_map = { - {"header_key1", std::string(50000, 'a')}, - {"header_key2", std::string(50000, 'b')}, - }; - MetadataMapPtr metadata_map_ptr = std::make_unique(metadata_map); - metadata_map_vector.push_back(std::move(metadata_map_ptr)); - } - // Verifies the encoding/decoding result in decoder's callback functions. - initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void { - this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr)); - }); - submitMetadata(metadata_map_vector); - // Verifies flag and payload are encoded correctly. - const uint64_t consume_size = random_generator_.random() % output_buffer_.length; - nghttp2_session_mem_recv(session_, output_buffer_.buf, consume_size); - nghttp2_session_mem_recv(session_, output_buffer_.buf + consume_size, - output_buffer_.length - consume_size); - cleanUp(); -} - -// Tests encoding/decoding with fuzzed metadata size. -TEST_F(MetadataEncoderDecoderTest, EncodeFuzzedMetadata) { - MetadataMapVector metadata_map_vector; - for (int i = 0; i < 10; i++) { - Random::RandomGeneratorImpl random; - int value_size_1 = random.random() % (2 * Http::METADATA_MAX_PAYLOAD_SIZE) + 1; - int value_size_2 = random.random() % (2 * Http::METADATA_MAX_PAYLOAD_SIZE) + 1; - MetadataMap metadata_map = { - {"header_key1", std::string(value_size_1, 'a')}, - {"header_key2", std::string(value_size_2, 'a')}, - }; - MetadataMapPtr metadata_map_ptr = std::make_unique(metadata_map); - metadata_map_vector.push_back(std::move(metadata_map_ptr)); - } - - // Verifies the encoding/decoding result in decoder's callback functions. - initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void { - this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr)); - }); - submitMetadata(metadata_map_vector); - - // Verifies flag and payload are encoded correctly. - nghttp2_session_mem_recv(session_, output_buffer_.buf, output_buffer_.length); - - cleanUp(); -} - -TEST_F(MetadataEncoderDecoderTest, EncodeDecodeFrameTest) { - MetadataMap metadataMap = { - {"Connections", "15"}, - {"Timeout Seconds", "10"}, - }; - MetadataMapPtr metadataMapPtr = std::make_unique(metadataMap); - MetadataMapVector metadata_map_vector; - metadata_map_vector.push_back(std::move(metadataMapPtr)); - Http2Frame http2FrameFromUltility = Http2Frame::makeMetadataFrameFromMetadataMap( - 1, metadataMap, Http2Frame::MetadataFlags::EndMetadata); - MetadataDecoder decoder([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void { - this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr)); - }); - decoder.receiveMetadata(http2FrameFromUltility.data() + 9, http2FrameFromUltility.size() - 9); - decoder.onMetadataFrameComplete(true); -} - -using MetadataEncoderDecoderDeathTest = MetadataEncoderDecoderTest; - -// Crash if a caller tries to pack more frames than the encoder has data for. -TEST_F(MetadataEncoderDecoderDeathTest, PackTooManyFrames) { - MetadataMap metadata_map = { - {"header_key1", std::string(5, 'a')}, - {"header_key2", std::string(5, 'b')}, - }; - MetadataMapPtr metadata_map_ptr = std::make_unique(metadata_map); - MetadataMapVector metadata_map_vector; - metadata_map_vector.push_back(std::move(metadata_map_ptr)); - - initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void { - this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr)); - }); - submitMetadata(metadata_map_vector); - - // Try to send an extra METADATA frame. Submitting the frame to nghttp2 should succeed, but - // pack_extension_callback should fail, and that failure will propagate through - // nghttp2_session_send. How to handle the failure is up to the HTTP/2 codec (in practice, it will - // throw a CodecProtocolException). - int result = nghttp2_submit_extension(session_, METADATA_FRAME_TYPE, 0, STREAM_ID, nullptr); - EXPECT_EQ(0, result); - EXPECT_DEATH(nghttp2_session_send(session_), - "No payload remaining to pack into a METADATA frame."); - - cleanUp(); -} - -} // namespace Http2 -} // namespace Http -} // namespace Envoy diff --git a/test/common/http/http2/metadata_encoder_test.cc b/test/common/http/http2/metadata_encoder_test.cc index 1d073b80a7be..4e9d114cf462 100644 --- a/test/common/http/http2/metadata_encoder_test.cc +++ b/test/common/http/http2/metadata_encoder_test.cc @@ -11,9 +11,9 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include "http2_frame.h" -#include "nghttp2/nghttp2.h" #include "quiche/http2/adapter/callback_visitor.h" #include "quiche/http2/adapter/data_source.h" +#include "quiche/http2/adapter/mock_http2_visitor.h" #include "quiche/http2/adapter/nghttp2_adapter.h" // A global variable in nghttp2 to disable preface and initial settings for tests. @@ -39,45 +39,41 @@ struct TestBuffer { size_t length = 0; }; -// The application data structure passes to nghttp2 session. -struct UserData { - NewMetadataEncoder* encoder; - MetadataDecoder* decoder; - // Stores data sent by encoder and received by the decoder. - TestBuffer* output_buffer; -}; - -// Nghttp2 callback function for receiving extension frame. -static int onExtensionChunkRecvCallback(nghttp2_session* /*session*/, const nghttp2_frame_hd* hd, - const uint8_t* data, size_t len, void* user_data) { - EXPECT_GE(hd->length, len); +class MetadataUnpackingVisitor : public http2::adapter::test::MockHttp2Visitor { +public: + MetadataUnpackingVisitor(MetadataDecoder* decoder, TestBuffer* output_buffer) + : decoder_(decoder), buffer_(output_buffer) { + // These calls are not important to intercept for these tests. + EXPECT_CALL(*this, OnBeforeFrameSent).Times(testing::AnyNumber()); + EXPECT_CALL(*this, OnFrameSent).Times(testing::AnyNumber()); + EXPECT_CALL(*this, OnFrameHeader).Times(testing::AnyNumber()); + } - MetadataDecoder* decoder = reinterpret_cast(user_data)->decoder; - bool success = decoder->receiveMetadata(data, len); - return success ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE; -} + void OnBeginMetadataForStream(http2::adapter::Http2StreamId /*stream_id*/, + size_t payload_length) override { + remaining_payload_ = payload_length; + } -// Nghttp2 callback function for unpack extension frames. -static int unpackExtensionCallback(nghttp2_session* /*session*/, void** payload, - const nghttp2_frame_hd* hd, void* user_data) { - EXPECT_NE(nullptr, hd); - EXPECT_NE(nullptr, payload); + bool OnMetadataForStream(http2::adapter::Http2StreamId /*stream_id*/, + absl::string_view metadata) override { + return decoder_->receiveMetadata(reinterpret_cast(metadata.data()), + metadata.size()); + } - MetadataDecoder* decoder = reinterpret_cast(user_data)->decoder; - bool result = decoder->onMetadataFrameComplete((hd->flags == END_METADATA_FLAG) ? true : false); - return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE; -} + bool OnMetadataEndForStream(http2::adapter::Http2StreamId /*stream_id*/) override { + return decoder_->onMetadataFrameComplete(true); + } -// Nghttp2 callback function for sending data to peer. -static ssize_t sendCallback(nghttp2_session* /*session*/, const uint8_t* buf, size_t len, int flags, - void* user_data) { - EXPECT_LE(0, flags); + int64_t OnReadyToSend(absl::string_view serialized) override { + memcpy(buffer_->buf + buffer_->length, serialized.data(), serialized.size()); + buffer_->length += serialized.size(); + return serialized.size(); + } - TestBuffer* buffer = (reinterpret_cast(user_data))->output_buffer; - memcpy(buffer->buf + buffer->length, buf, len); - buffer->length += len; - return len; -} + MetadataDecoder* decoder_; + TestBuffer* buffer_; + size_t remaining_payload_ = 0; +}; } // namespace @@ -91,27 +87,13 @@ class MetadataEncoderTest : public testing::Test { nghttp2_option_new(&option); nghttp2_option_set_user_recv_extension_type(option, METADATA_FRAME_TYPE); - // Sets callback functions. - nghttp2_session_callbacks* callbacks; - nghttp2_session_callbacks_new(&callbacks); - nghttp2_session_callbacks_set_send_callback(callbacks, sendCallback); - nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(callbacks, - onExtensionChunkRecvCallback); - nghttp2_session_callbacks_set_unpack_extension_callback(callbacks, unpackExtensionCallback); - - // Sets application data to pass to nghttp2 session. - user_data_.encoder = &encoder_; - user_data_.decoder = decoder_.get(); - user_data_.output_buffer = &output_buffer_; - // Creates new nghttp2 session. nghttp2_enable_strict_preface = 0; - visitor_ = std::make_unique( - http2::adapter::Perspective::kClient, *callbacks, &user_data_); + visitor_ = std::make_unique(decoder_.get(), &output_buffer_); + session_ = http2::adapter::NgHttp2Adapter::CreateClientAdapter(*visitor_, option); nghttp2_enable_strict_preface = 1; nghttp2_option_del(option); - nghttp2_session_callbacks_del(callbacks); } void verifyMetadataMapVector(MetadataMapVector& expect, MetadataMapPtr&& metadata_map_ptr) { @@ -133,7 +115,7 @@ class MetadataEncoderTest : public testing::Test { } std::unique_ptr session_; - std::unique_ptr visitor_; + std::unique_ptr visitor_; NewMetadataEncoder encoder_; std::unique_ptr decoder_; int count_ = 0; @@ -141,9 +123,6 @@ class MetadataEncoderTest : public testing::Test { // Stores data received by peer. TestBuffer output_buffer_; - // Application data passed to nghttp2. - UserData user_data_; - Random::RandomGeneratorImpl random_generator_; }; @@ -188,6 +167,7 @@ TEST_F(MetadataEncoderTest, VerifyEncoderDecoderMultipleMetadataReachSizeLimit) ssize_t result = 0; + EXPECT_CALL(*visitor_, OnConnectionError); for (int i = 0; i < 100; i++) { // Cleans up the output buffer. memset(output_buffer_.buf, 0, output_buffer_.length); diff --git a/test/common/http/http2/response_header_corpus/clusterfuzz-testcase-minimized-response_header_fuzz_test-4950422035234816 b/test/common/http/http2/response_header_corpus/clusterfuzz-testcase-minimized-response_header_fuzz_test-4950422035234816 new file mode 100644 index 000000000000..0e77d1e82221 Binary files /dev/null and b/test/common/http/http2/response_header_corpus/clusterfuzz-testcase-minimized-response_header_fuzz_test-4950422035234816 differ diff --git a/test/common/http/http2/response_header_fuzz_test.cc b/test/common/http/http2/response_header_fuzz_test.cc index b0ecae2e22e8..09f1995c2362 100644 --- a/test/common/http/http2/response_header_fuzz_test.cc +++ b/test/common/http/http2/response_header_fuzz_test.cc @@ -36,7 +36,7 @@ void replay(const Frame& frame, ClientCodecFrameInjector& codec) { } DEFINE_FUZZER(const uint8_t* buf, size_t len) { - static ClientCodecFrameInjector codec; + ClientCodecFrameInjector codec; Frame frame; frame.assign(buf, buf + len); // Replay with the fuzzer bytes. diff --git a/test/common/network/multi_connection_base_impl_test.cc b/test/common/network/multi_connection_base_impl_test.cc index 034a40fbc07c..59be1155f469 100644 --- a/test/common/network/multi_connection_base_impl_test.cc +++ b/test/common/network/multi_connection_base_impl_test.cc @@ -375,8 +375,8 @@ TEST_F(MultiConnectionBaseImplTest, CloseDuringAttempt) { EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*createdConnections()[0], removeConnectionCallbacks(_)); EXPECT_CALL(*createdConnections()[1], removeConnectionCallbacks(_)); - EXPECT_CALL(*createdConnections()[0], close(ConnectionCloseType::FlushWrite)); - EXPECT_CALL(*createdConnections()[1], close(ConnectionCloseType::NoFlush)); + EXPECT_CALL(*createdConnections()[0], close(ConnectionCloseType::FlushWrite, _)); + EXPECT_CALL(*createdConnections()[1], close(ConnectionCloseType::NoFlush, _)); impl_->close(ConnectionCloseType::FlushWrite); } @@ -393,11 +393,11 @@ TEST_F(MultiConnectionBaseImplTest, CloseDuringAttemptWithCallbacks) { EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*createdConnections()[0], removeConnectionCallbacks(_)); EXPECT_CALL(*createdConnections()[1], removeConnectionCallbacks(_)); - EXPECT_CALL(*createdConnections()[1], close(ConnectionCloseType::NoFlush)); + EXPECT_CALL(*createdConnections()[1], close(ConnectionCloseType::NoFlush, _)); // addConnectionCallbacks() should be applied to the now final connection. EXPECT_CALL(*createdConnections()[0], addConnectionCallbacks(_)) .WillOnce(Invoke([&](ConnectionCallbacks& c) -> void { EXPECT_EQ(&c, &callbacks); })); - EXPECT_CALL(*createdConnections()[0], close(ConnectionCloseType::FlushWrite)); + EXPECT_CALL(*createdConnections()[0], close(ConnectionCloseType::FlushWrite, _)); impl_->close(ConnectionCloseType::FlushWrite); } @@ -406,7 +406,7 @@ TEST_F(MultiConnectionBaseImplTest, CloseAfterAttemptComplete) { connectFirstAttempt(); - EXPECT_CALL(*createdConnections()[0], close(ConnectionCloseType::FlushWrite)); + EXPECT_CALL(*createdConnections()[0], close(ConnectionCloseType::FlushWrite, _)); impl_->close(ConnectionCloseType::FlushWrite); } diff --git a/test/common/quic/envoy_quic_server_session_test.cc b/test/common/quic/envoy_quic_server_session_test.cc index e77c2ce5146b..266ed03c6920 100644 --- a/test/common/quic/envoy_quic_server_session_test.cc +++ b/test/common/quic/envoy_quic_server_session_test.cc @@ -294,6 +294,7 @@ TEST_F(EnvoyQuicServerSessionTest, NewStream) { Http::MockRequestDecoder request_decoder; EXPECT_CALL(http_connection_callbacks_, newStream(_, false)) .WillOnce(testing::ReturnRef(request_decoder)); + EXPECT_CALL(request_decoder, accessLogHandlers()); quic::QuicStreamId stream_id = 4u; auto stream = reinterpret_cast(envoy_quic_session_.GetOrCreateStream(stream_id)); @@ -311,7 +312,7 @@ TEST_F(EnvoyQuicServerSessionTest, NewStream) { headers.OnHeaderBlockEnd(/*uncompressed_header_bytes=*/0, /*compressed_header_bytes=*/0); // Request headers should be propagated to decoder. EXPECT_CALL(request_decoder, decodeHeaders_(_, /*end_stream=*/true)) - .WillOnce(Invoke([&host](const Http::RequestHeaderMapPtr& decoded_headers, bool) { + .WillOnce(Invoke([&host](const Http::RequestHeaderMapSharedPtr& decoded_headers, bool) { EXPECT_EQ(host, decoded_headers->getHostValue()); EXPECT_EQ("/", decoded_headers->getPathValue()); EXPECT_EQ(Http::Headers::get().MethodValues.Get, decoded_headers->getMethodValue()); @@ -355,6 +356,7 @@ TEST_F(EnvoyQuicServerSessionTest, OnResetFrameIetfQuic) { Http::MockRequestDecoder request_decoder; Http::MockStreamCallbacks stream_callbacks; + EXPECT_CALL(request_decoder, accessLogHandlers()).Times(3); auto stream1 = dynamic_cast(createNewStream(request_decoder, stream_callbacks)); // Receiving RESET_STREAM alone should only close read side. @@ -438,6 +440,7 @@ TEST_F(EnvoyQuicServerSessionTest, ConnectionCloseWithActiveStream) { Http::MockRequestDecoder request_decoder; Http::MockStreamCallbacks stream_callbacks; + EXPECT_CALL(request_decoder, accessLogHandlers()); quic::QuicStream* stream = createNewStream(request_decoder, stream_callbacks); EXPECT_CALL(*quic_connection_, SendConnectionClosePacket(quic::QUIC_NO_ERROR, _, "Closed by application")); @@ -453,6 +456,7 @@ TEST_F(EnvoyQuicServerSessionTest, RemoteConnectionCloseWithActiveStream) { Http::MockRequestDecoder request_decoder; Http::MockStreamCallbacks stream_callbacks; + EXPECT_CALL(request_decoder, accessLogHandlers()); quic::QuicStream* stream = createNewStream(request_decoder, stream_callbacks); EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::RemoteClose)); EXPECT_CALL(stream_callbacks, onResetStream(Http::StreamResetReason::ConnectionFailure, _)); @@ -470,6 +474,7 @@ TEST_F(EnvoyQuicServerSessionTest, NoFlushWithDataToWrite) { Http::MockRequestDecoder request_decoder; Http::MockStreamCallbacks stream_callbacks; + EXPECT_CALL(request_decoder, accessLogHandlers()); quic::QuicStream* stream = createNewStream(request_decoder, stream_callbacks); envoy_quic_session_.MarkConnectionLevelWriteBlocked(stream->id()); EXPECT_CALL(*quic_connection_, @@ -487,6 +492,7 @@ TEST_F(EnvoyQuicServerSessionTest, FlushCloseWithDataToWrite) { installReadFilter(); Http::MockRequestDecoder request_decoder; Http::MockStreamCallbacks stream_callbacks; + EXPECT_CALL(request_decoder, accessLogHandlers()); quic::QuicStream* stream = createNewStream(request_decoder, stream_callbacks); envoy_quic_session_.MarkConnectionLevelWriteBlocked(stream->id()); @@ -526,6 +532,7 @@ TEST_F(EnvoyQuicServerSessionTest, WriteUpdatesDelayCloseTimer) { envoy_quic_session_.setDelayedCloseTimeout(std::chrono::milliseconds(100)); Http::MockRequestDecoder request_decoder; Http::MockStreamCallbacks stream_callbacks; + EXPECT_CALL(request_decoder, accessLogHandlers()); // Create a stream and write enough data to make it blocked. auto stream = dynamic_cast(createNewStream(request_decoder, stream_callbacks)); @@ -540,7 +547,7 @@ TEST_F(EnvoyQuicServerSessionTest, WriteUpdatesDelayCloseTimer) { request_headers.OnHeaderBlockEnd(/*uncompressed_header_bytes=*/0, /*compressed_header_bytes=*/0); // Request headers should be propagated to decoder. EXPECT_CALL(request_decoder, decodeHeaders_(_, /*end_stream=*/true)) - .WillOnce(Invoke([&host](const Http::RequestHeaderMapPtr& decoded_headers, bool) { + .WillOnce(Invoke([&host](const Http::RequestHeaderMapSharedPtr& decoded_headers, bool) { EXPECT_EQ(host, decoded_headers->getHostValue()); EXPECT_EQ("/", decoded_headers->getPathValue()); EXPECT_EQ(Http::Headers::get().MethodValues.Get, decoded_headers->getMethodValue()); @@ -625,6 +632,7 @@ TEST_F(EnvoyQuicServerSessionTest, FlushCloseNoTimeout) { Http::MockRequestDecoder request_decoder; Http::MockStreamCallbacks stream_callbacks; + EXPECT_CALL(request_decoder, accessLogHandlers()); // Create a stream and write enough data to make it blocked. auto stream = dynamic_cast(createNewStream(request_decoder, stream_callbacks)); @@ -639,7 +647,7 @@ TEST_F(EnvoyQuicServerSessionTest, FlushCloseNoTimeout) { request_headers.OnHeaderBlockEnd(/*uncompressed_header_bytes=*/0, /*compressed_header_bytes=*/0); // Request headers should be propagated to decoder. EXPECT_CALL(request_decoder, decodeHeaders_(_, /*end_stream=*/true)) - .WillOnce(Invoke([&host](const Http::RequestHeaderMapPtr& decoded_headers, bool) { + .WillOnce(Invoke([&host](const Http::RequestHeaderMapSharedPtr& decoded_headers, bool) { EXPECT_EQ(host, decoded_headers->getHostValue()); EXPECT_EQ("/", decoded_headers->getPathValue()); EXPECT_EQ(Http::Headers::get().MethodValues.Get, decoded_headers->getMethodValue()); @@ -687,6 +695,7 @@ TEST_F(EnvoyQuicServerSessionTest, FlushCloseWithTimeout) { envoy_quic_session_.setDelayedCloseTimeout(std::chrono::milliseconds(100)); Http::MockRequestDecoder request_decoder; Http::MockStreamCallbacks stream_callbacks; + EXPECT_CALL(request_decoder, accessLogHandlers()); quic::QuicStream* stream = createNewStream(request_decoder, stream_callbacks); envoy_quic_session_.MarkConnectionLevelWriteBlocked(stream->id()); @@ -718,6 +727,7 @@ TEST_F(EnvoyQuicServerSessionTest, FlushAndWaitForCloseWithTimeout) { envoy_quic_session_.setDelayedCloseTimeout(std::chrono::milliseconds(100)); Http::MockRequestDecoder request_decoder; Http::MockStreamCallbacks stream_callbacks; + EXPECT_CALL(request_decoder, accessLogHandlers()); quic::QuicStream* stream = createNewStream(request_decoder, stream_callbacks); envoy_quic_session_.MarkConnectionLevelWriteBlocked(stream->id()); @@ -748,6 +758,7 @@ TEST_F(EnvoyQuicServerSessionTest, FlusWriteTransitToFlushWriteWithDelay) { envoy_quic_session_.setDelayedCloseTimeout(std::chrono::milliseconds(100)); Http::MockRequestDecoder request_decoder; Http::MockStreamCallbacks stream_callbacks; + EXPECT_CALL(request_decoder, accessLogHandlers()); quic::QuicStream* stream = createNewStream(request_decoder, stream_callbacks); envoy_quic_session_.MarkConnectionLevelWriteBlocked(stream->id()); @@ -864,6 +875,7 @@ TEST_F(EnvoyQuicServerSessionTest, SendBufferWatermark) { installReadFilter(); Http::MockRequestDecoder request_decoder; Http::MockStreamCallbacks stream_callbacks; + EXPECT_CALL(request_decoder, accessLogHandlers()); EXPECT_CALL(http_connection_callbacks_, newStream(_, false)) .WillOnce(Invoke([&request_decoder, &stream_callbacks](Http::ResponseEncoder& encoder, bool) -> Http::RequestDecoder& { @@ -884,7 +896,7 @@ TEST_F(EnvoyQuicServerSessionTest, SendBufferWatermark) { request_headers.OnHeaderBlockEnd(/*uncompressed_header_bytes=*/0, /*compressed_header_bytes=*/0); // Request headers should be propagated to decoder. EXPECT_CALL(request_decoder, decodeHeaders_(_, /*end_stream=*/true)) - .WillOnce(Invoke([&host](const Http::RequestHeaderMapPtr& decoded_headers, bool) { + .WillOnce(Invoke([&host](const Http::RequestHeaderMapSharedPtr& decoded_headers, bool) { EXPECT_EQ(host, decoded_headers->getHostValue()); EXPECT_EQ("/", decoded_headers->getPathValue()); EXPECT_EQ(Http::Headers::get().MethodValues.Get, decoded_headers->getMethodValue()); @@ -907,6 +919,7 @@ TEST_F(EnvoyQuicServerSessionTest, SendBufferWatermark) { // send buffer watermark. Http::MockRequestDecoder request_decoder2; Http::MockStreamCallbacks stream_callbacks2; + EXPECT_CALL(request_decoder2, accessLogHandlers()); EXPECT_CALL(http_connection_callbacks_, newStream(_, false)) .WillOnce(Invoke([&request_decoder2, &stream_callbacks2](Http::ResponseEncoder& encoder, bool) -> Http::RequestDecoder& { @@ -916,7 +929,7 @@ TEST_F(EnvoyQuicServerSessionTest, SendBufferWatermark) { auto stream2 = dynamic_cast(envoy_quic_session_.GetOrCreateStream(stream_id + 4)); EXPECT_CALL(request_decoder2, decodeHeaders_(_, /*end_stream=*/true)) - .WillOnce(Invoke([&host](const Http::RequestHeaderMapPtr& decoded_headers, bool) { + .WillOnce(Invoke([&host](const Http::RequestHeaderMapSharedPtr& decoded_headers, bool) { EXPECT_EQ(host, decoded_headers->getHostValue()); EXPECT_EQ("/", decoded_headers->getPathValue()); EXPECT_EQ(Http::Headers::get().MethodValues.Get, decoded_headers->getMethodValue()); @@ -937,6 +950,7 @@ TEST_F(EnvoyQuicServerSessionTest, SendBufferWatermark) { // high watermark reached upon creation. Http::MockRequestDecoder request_decoder3; Http::MockStreamCallbacks stream_callbacks3; + EXPECT_CALL(request_decoder3, accessLogHandlers()); EXPECT_CALL(http_connection_callbacks_, newStream(_, false)) .WillOnce(Invoke([&request_decoder3, &stream_callbacks3](Http::ResponseEncoder& encoder, bool) -> Http::RequestDecoder& { @@ -947,7 +961,7 @@ TEST_F(EnvoyQuicServerSessionTest, SendBufferWatermark) { auto stream3 = dynamic_cast(envoy_quic_session_.GetOrCreateStream(stream_id + 8)); EXPECT_CALL(request_decoder3, decodeHeaders_(_, /*end_stream=*/true)) - .WillOnce(Invoke([&host](const Http::RequestHeaderMapPtr& decoded_headers, bool) { + .WillOnce(Invoke([&host](const Http::RequestHeaderMapSharedPtr& decoded_headers, bool) { EXPECT_EQ(host, decoded_headers->getHostValue()); EXPECT_EQ("/", decoded_headers->getPathValue()); EXPECT_EQ(Http::Headers::get().MethodValues.Get, decoded_headers->getMethodValue()); diff --git a/test/common/quic/envoy_quic_server_stream_test.cc b/test/common/quic/envoy_quic_server_stream_test.cc index 32ae20d03947..99cdfbec9232 100644 --- a/test/common/quic/envoy_quic_server_stream_test.cc +++ b/test/common/quic/envoy_quic_server_stream_test.cc @@ -11,9 +11,11 @@ #include "source/server/active_listener_base.h" #include "test/common/quic/test_utils.h" +#include "test/mocks/access_log/mocks.h" #include "test/mocks/http/mocks.h" #include "test/mocks/http/stream_decoder.h" #include "test/mocks/network/mocks.h" +#include "test/test_common/test_time.h" #include "test/test_common/utility.h" #include "gmock/gmock.h" @@ -52,6 +54,7 @@ class EnvoyQuicServerStreamTest : public testing::Test { envoy::config::core::v3::HttpProtocolOptions::ALLOW)), response_headers_{{":status", "200"}, {"response-key", "response-value"}}, response_trailers_{{"trailer-key", "trailer-value"}} { + EXPECT_CALL(stream_decoder_, accessLogHandlers()); quic_stream_->setRequestDecoder(stream_decoder_); quic_stream_->addCallbacks(stream_callbacks_); quic_stream_->getStream().setFlushTimeout(std::chrono::milliseconds(30000)); @@ -102,7 +105,7 @@ class EnvoyQuicServerStreamTest : public testing::Test { size_t receiveRequest(const std::string& payload, bool fin, size_t decoder_buffer_high_watermark) { EXPECT_CALL(stream_decoder_, decodeHeaders_(_, /*end_stream=*/false)) - .WillOnce(Invoke([this](const Http::RequestHeaderMapPtr& headers, bool) { + .WillOnce(Invoke([this](const Http::RequestHeaderMapSharedPtr& headers, bool) { EXPECT_EQ(host_, headers->getHostValue()); EXPECT_EQ("/", headers->getPathValue()); EXPECT_EQ(Http::Headers::get().MethodValues.Post, headers->getMethodValue()); @@ -126,7 +129,7 @@ class EnvoyQuicServerStreamTest : public testing::Test { size_t receiveRequestHeaders(bool end_stream) { EXPECT_CALL(stream_decoder_, decodeHeaders_(_, end_stream)) - .WillOnce(Invoke([this](const Http::RequestHeaderMapPtr& headers, bool) { + .WillOnce(Invoke([this](const Http::RequestHeaderMapSharedPtr& headers, bool) { EXPECT_EQ(host_, headers->getHostValue()); EXPECT_EQ("/", headers->getPathValue()); EXPECT_EQ(Http::Headers::get().MethodValues.Post, headers->getMethodValue()); @@ -193,7 +196,7 @@ class EnvoyQuicServerStreamTest : public testing::Test { TEST_F(EnvoyQuicServerStreamTest, GetRequestAndResponse) { EXPECT_CALL(stream_decoder_, decodeHeaders_(_, /*end_stream=*/false)) - .WillOnce(Invoke([this](const Http::RequestHeaderMapPtr& headers, bool) { + .WillOnce(Invoke([this](const Http::RequestHeaderMapSharedPtr& headers, bool) { EXPECT_EQ(host_, headers->getHostValue()); EXPECT_EQ("/", headers->getPathValue()); EXPECT_EQ(Http::Headers::get().MethodValues.Get, headers->getMethodValue()); @@ -454,8 +457,9 @@ TEST_F(EnvoyQuicServerStreamTest, ReadDisableAndReEnableImmediately) { TEST_F(EnvoyQuicServerStreamTest, ReadDisableUponHeaders) { std::string payload(1024, 'a'); EXPECT_CALL(stream_decoder_, decodeHeaders_(_, /*end_stream=*/false)) - .WillOnce(Invoke( - [this](const Http::RequestHeaderMapPtr&, bool) { quic_stream_->readDisable(true); })); + .WillOnce(Invoke([this](const Http::RequestHeaderMapSharedPtr&, bool) { + quic_stream_->readDisable(true); + })); EXPECT_CALL(stream_decoder_, decodeData(_, _)); std::string data = absl::StrCat(spdyHeaderToHttp3StreamPayload(spdy_request_headers_), bodyToHttp3StreamPayload(payload)); @@ -750,6 +754,30 @@ TEST_F(EnvoyQuicServerStreamTest, ConnectionCloseAfterEndStreamEncoded) { quic_stream_->encodeHeaders(response_headers_, /*end_stream=*/true); } +// Tests that when stream is cleaned up, QuicStatsGatherer executes any pending logs. +TEST_F(EnvoyQuicServerStreamTest, StatsGathererLogsOnStreamDestruction) { + + // Set up QuicStatsGatherer with required access logger, stream info, headers and trailers. + std::shared_ptr mock_logger(new NiceMock()); + std::list loggers = {mock_logger}; + Event::GlobalTimeSystem test_time_; + Envoy::StreamInfo::StreamInfoImpl stream_info{Http::Protocol::Http2, test_time_.timeSystem(), + nullptr}; + quic_stream_->statsGatherer()->setAccessLogHandlers(loggers); + quic_stream_->setDeferredLoggingHeadersAndTrailers(nullptr, nullptr, nullptr, stream_info); + + receiveRequest(request_body_, false, request_body_.size() * 2); + quic_stream_->encodeHeaders(response_headers_, /*end_stream=*/true); + EXPECT_CALL(quic_session_, MaybeSendStopSendingFrame(_, _)); + EXPECT_CALL(stream_callbacks_, onResetStream(Http::StreamResetReason::LocalReset, _)); + // The stats gatherer has outstanding bytes that have not been acked. + EXPECT_GT(quic_stream_->statsGatherer()->bytesOutstanding(), 0); + // Close the stream; incoming acks will no longer invoke the stats gatherer but + // the stats gatherer should log on stream close despite not receiving final downstream ack. + EXPECT_CALL(*mock_logger, log(_, _, _, _)); + quic_stream_->resetStream(Http::StreamResetReason::LocalRefusedStreamReset); +} + TEST_F(EnvoyQuicServerStreamTest, MetadataNotSupported) { Http::MetadataMap metadata_map = {{"key", "value"}}; Http::MetadataMapPtr metadata_map_ptr = std::make_unique(metadata_map); diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index c2a12ea15a61..39b874047a9f 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -9722,6 +9722,41 @@ TEST_F(RouteMatcherTest, PatternMatchInvalidVariableName) { EnvoyException, "path_match_policy.path_template /rest/{on==e}/{two} is invalid"); } +TEST_F(RouteMatcherTest, PatternMatchWildcardMiddleThreePartVariableNamed) { + const std::string yaml = R"EOF( +virtual_hosts: + - name: path_pattern + domains: ["*"] + routes: + - match: + path_match_policy: + name: envoy.path.match.uri_template.uri_template_matcher + typed_config: + "@type": type.googleapis.com/envoy.extensions.path.match.uri_template.v3.UriTemplateMatchConfig + path_template: "/rest/{one}/{middle=*/videos/*}/end" + case_sensitive: false + route: + cluster: "path-pattern-cluster-one" + path_rewrite_policy: + name: envoy.path.rewrite.uri_template.uri_template_rewriter + typed_config: + "@type": type.googleapis.com/envoy.extensions.path.rewrite.uri_template.v3.UriTemplateRewriteConfig + path_template_rewrite: "/{middle}" + )EOF"; + NiceMock stream_info; + factory_context_.cluster_manager_.initializeClusters({"path-pattern-cluster-one"}, {}); + + TestConfigImpl config(parseRouteConfigurationFromYaml(yaml), factory_context_, true); + + Http::TestRequestHeaderMapImpl headers = + genHeaders("path.prefix.com", "/rest/one/previous/videos/three/end", "GET"); + const RouteEntry* route = config.route(headers, 0)->routeEntry(); + EXPECT_EQ("/previous/videos/three", route->currentUrlPathAfterRewrite(headers)); + route->finalizeRequestHeaders(headers, stream_info, true); + EXPECT_EQ("/previous/videos/three", headers.get_(Http::Headers::get().Path)); + EXPECT_EQ("path.prefix.com", headers.get_(Http::Headers::get().Host)); +} + TEST_F(RouteMatcherTest, PatternMatchInvalidPlacedWildcard) { const std::string yaml = R"EOF( virtual_hosts: diff --git a/test/common/router/router_2_test.cc b/test/common/router/router_2_test.cc index cdc52a614d03..a591bc150e28 100644 --- a/test/common/router/router_2_test.cc +++ b/test/common/router/router_2_test.cc @@ -30,12 +30,12 @@ TEST_F(RouterTestSuppressEnvoyHeaders, Http1Upstream) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); EXPECT_CALL(callbacks_.route_->route_entry_, finalizeRequestHeaders(_, _, false)); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_FALSE(headers.has("x-envoy-expected-rq-timeout-ms")); // When the router filter gets reset we should cancel the pool request. EXPECT_CALL(cancellable_, cancel(_)); - router_.onDestroy(); + router_->onDestroy(); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -54,7 +54,7 @@ TEST_F(RouterTestSuppressEnvoyHeaders, MaintenanceMode) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); } // Validate that x-envoy-upstream-service-time is not added when Envoy header @@ -70,7 +70,7 @@ TEST_F(RouterTestSuppressEnvoyHeaders, EnvoyUpstreamServiceTime) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -102,7 +102,7 @@ TEST_F(RouterTestSuppressEnvoyHeaders, EnvoyAttemptCountInResponseNotPresent) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); } class WatermarkTest : public RouterTestBase { @@ -135,7 +135,7 @@ class WatermarkTest : public RouterTestBase { return nullptr; })); HttpTestUtility::addDefaultHeaders(headers_); - router_.decodeHeaders(headers_, header_only_request); + router_->decodeHeaders(headers_, header_only_request); if (pool_ready) { EXPECT_EQ( 1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -200,13 +200,13 @@ TEST_F(WatermarkTest, UpstreamWatermarks) { TEST_F(WatermarkTest, FilterWatermarks) { EXPECT_CALL(callbacks_, decoderBufferLimit()).Times(AtLeast(3)).WillRepeatedly(Return(10)); - router_.setDecoderFilterCallbacks(callbacks_); + router_->setDecoderFilterCallbacks(callbacks_); // Send the headers sans-fin, and don't flag the pool as ready. sendRequest(false, false); // Send 10 bytes of body to fill the 10 byte buffer. Buffer::OwnedImpl data("1234567890"); - router_.decodeData(data, false); + router_->decodeData(data, false); EXPECT_EQ(0u, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_flow_control_backed_up_total") .value()); @@ -214,7 +214,7 @@ TEST_F(WatermarkTest, FilterWatermarks) { // Send one extra byte. This should cause the buffer to go over the limit and pause downstream // data. Buffer::OwnedImpl last_byte("!"); - router_.decodeData(last_byte, true); + router_->decodeData(last_byte, true); EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_flow_control_backed_up_total") .value()); @@ -238,13 +238,13 @@ TEST_F(WatermarkTest, FilterWatermarks) { TEST_F(WatermarkTest, FilterWatermarksUnwound) { num_add_callbacks_ = 0; EXPECT_CALL(callbacks_, decoderBufferLimit()).Times(AtLeast(3)).WillRepeatedly(Return(10)); - router_.setDecoderFilterCallbacks(callbacks_); + router_->setDecoderFilterCallbacks(callbacks_); // Send the headers sans-fin, and don't flag the pool as ready. sendRequest(false, false); // Send 11 bytes of body to fill the 10 byte buffer. Buffer::OwnedImpl data("1234567890!"); - router_.decodeData(data, false); + router_->decodeData(data, false); EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_flow_control_backed_up_total") .value()); @@ -261,7 +261,7 @@ TEST_F(WatermarkTest, FilterWatermarksUnwound) { // limit, no retry will occur. TEST_F(WatermarkTest, RetryRequestNotComplete) { EXPECT_CALL(callbacks_, decoderBufferLimit()).Times(AtLeast(2)).WillRepeatedly(Return(10)); - router_.setDecoderFilterCallbacks(callbacks_); + router_->setDecoderFilterCallbacks(callbacks_); NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; expectNewStreamWithImmediateEncoder(encoder1, &response_decoder, Http::Protocol::Http10); @@ -271,13 +271,13 @@ TEST_F(WatermarkTest, RetryRequestNotComplete) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); Buffer::OwnedImpl data("1234567890123"); - EXPECT_CALL(*router_.retry_state_, enabled()).WillOnce(Return(true)); - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).Times(0); - EXPECT_CALL(*router_.retry_state_, shouldRetryReset(_, _, _)).Times(0); + EXPECT_CALL(*router_->retry_state_, enabled()).WillOnce(Return(true)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)).Times(0); + EXPECT_CALL(*router_->retry_state_, shouldRetryReset(_, _, _)).Times(0); // This will result in retry_state_ being deleted. - router_.decodeData(data, false); + router_->decodeData(data, false); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -321,7 +321,7 @@ TEST_F(RouterTestChildSpan, BasicFlow) { EXPECT_CALL(callbacks_.active_span_, spawnChild_(_, "router observability_name egress", _)) .WillOnce(Return(child_span)); EXPECT_CALL(callbacks_, tracingConfig()).Times(2); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -370,7 +370,7 @@ TEST_F(RouterTestChildSpan, ResetFlow) { EXPECT_CALL(callbacks_.active_span_, spawnChild_(_, "router observability_name egress", _)) .WillOnce(Return(child_span)); EXPECT_CALL(callbacks_, tracingConfig()).Times(2); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -423,7 +423,7 @@ TEST_F(RouterTestChildSpan, CancelFlow) { EXPECT_CALL(callbacks_.active_span_, spawnChild_(_, "router observability_name egress", _)) .WillOnce(Return(child_span)); EXPECT_CALL(callbacks_, tracingConfig()).Times(2); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -443,7 +443,7 @@ TEST_F(RouterTestChildSpan, CancelFlow) { EXPECT_CALL(*child_span, setTag(Eq(Tracing::Tags::get().Canceled), Eq(Tracing::Tags::get().True))); EXPECT_CALL(*child_span, finishSpan()); - router_.onDestroy(); + router_->onDestroy(); } // Make sure child spans start/inject/finish with retry flow. @@ -472,7 +472,7 @@ TEST_F(RouterTestChildSpan, ResetRetryFlow) { EXPECT_CALL(callbacks_.active_span_, spawnChild_(_, "router observability_name egress", _)) .WillOnce(Return(child_span_1)); EXPECT_CALL(callbacks_, tracingConfig()).Times(2); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -492,7 +492,7 @@ TEST_F(RouterTestChildSpan, ResetRetryFlow) { EXPECT_CALL(*child_span_1, setTag(Eq(Tracing::Tags::get().ErrorReason), Eq("remote reset"))); EXPECT_CALL(*child_span_1, finishSpan()); - router_.retry_state_->expectResetRetry(); + router_->retry_state_->expectResetRetry(); encoder1.stream_.resetStream(Http::StreamResetReason::RemoteReset); // We expect this reset to kick off a new request. @@ -505,7 +505,7 @@ TEST_F(RouterTestChildSpan, ResetRetryFlow) { -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; EXPECT_CALL(*child_span_2, injectContext(_, _)); - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)); callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, upstream_stream_info_, Http::Protocol::Http10); return nullptr; @@ -516,7 +516,7 @@ TEST_F(RouterTestChildSpan, ResetRetryFlow) { EXPECT_CALL(callbacks_, tracingConfig()).Times(2); EXPECT_CALL(*child_span_2, setTag(Eq(Tracing::Tags::get().RetryCount), Eq("1"))); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -565,7 +565,7 @@ TEST_F(RouterTestNoChildSpan, BasicFlow) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -632,7 +632,7 @@ TEST_P(RouterTestStrictCheckOneHeader, SingleInvalidHeader) { EXPECT_TRUE(end_stream); })); - EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, router_.decodeHeaders(req_headers, true)); + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, router_->decodeHeaders(req_headers, true)); EXPECT_EQ(callbacks_.details(), fmt::format("request_headers_failed_strict_check{{{}}}", checked_header)); } @@ -662,8 +662,8 @@ TEST_P(RouterTestStrictCheckSomeHeaders, IgnoreOmittedHeaders) { HttpTestUtility::addDefaultHeaders(headers); expectResponseTimerCreate(); - EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, router_.decodeHeaders(headers, true)); - router_.onDestroy(); + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, router_->decodeHeaders(headers, true)); + router_->onDestroy(); } const std::vector SUPPORTED_STRICT_CHECKED_HEADERS = { @@ -701,10 +701,10 @@ TEST_P(RouterTestStrictCheckAllHeaders, MultipleInvalidHeaders) { EXPECT_FALSE(end_stream); })); - EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, router_.decodeHeaders(headers, true)); + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, router_->decodeHeaders(headers, true)); EXPECT_THAT(callbacks_.details(), StartsWith(fmt::format("request_headers_failed_strict_check{{"))); - router_.onDestroy(); + router_->onDestroy(); } // Request has headers with invalid values, but headers are *excluded* from the @@ -769,9 +769,9 @@ TEST_F(RouterTestSupressGRPCStatsEnabled, ExcludeTimeoutHttpStats) { Http::TestRequestHeaderMapImpl headers{ {"x-envoy-internal", "true"}, {"content-type", "application/grpc"}, {"grpc-timeout", "20S"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); Buffer::OwnedImpl data; - router_.decodeData(data, true); + router_->decodeData(data, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -782,7 +782,7 @@ TEST_F(RouterTestSupressGRPCStatsEnabled, ExcludeTimeoutHttpStats) { {":status", "504"}, {"content-length", "24"}, {"content-type", "text/plain"}}; EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), false)); EXPECT_CALL(callbacks_, encodeData(_, true)); - EXPECT_CALL(*router_.retry_state_, shouldRetryReset(_, _, _)).Times(0); + EXPECT_CALL(*router_->retry_state_, shouldRetryReset(_, _, _)).Times(0); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, _)); response_timeout_->invokeCallback(); @@ -822,9 +822,9 @@ TEST_F(RouterTestSupressGRPCStatsDisabled, IncludeHttpTimeoutStats) { Http::TestRequestHeaderMapImpl headers{ {"x-envoy-internal", "true"}, {"content-type", "application/grpc"}, {"grpc-timeout", "20S"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); Buffer::OwnedImpl data; - router_.decodeData(data, true); + router_->decodeData(data, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -835,7 +835,7 @@ TEST_F(RouterTestSupressGRPCStatsDisabled, IncludeHttpTimeoutStats) { {":status", "504"}, {"content-length", "24"}, {"content-type", "text/plain"}}; EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), false)); EXPECT_CALL(callbacks_, encodeData(_, true)); - EXPECT_CALL(*router_.retry_state_, shouldRetryReset(_, _, _)).Times(0); + EXPECT_CALL(*router_->retry_state_, shouldRetryReset(_, _, _)).Times(0); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, _)); response_timeout_->invokeCallback(); diff --git a/test/common/router/router_test.cc b/test/common/router/router_test.cc index 64453e284eb6..b132121218a8 100644 --- a/test/common/router/router_test.cc +++ b/test/common/router/router_test.cc @@ -105,11 +105,10 @@ class RouterTest : public RouterTestBase { })); cm_.thread_local_cluster_.cluster_.info_->upstream_config_ = - absl::make_optional(); + std::make_unique(); envoy::extensions::upstreams::http::generic::v3::GenericConnectionPoolProto generic_config; - cm_.thread_local_cluster_.cluster_.info_->upstream_config_.value() - .mutable_typed_config() - ->PackFrom(generic_config); + cm_.thread_local_cluster_.cluster_.info_->upstream_config_->mutable_typed_config()->PackFrom( + generic_config); callbacks_.route_->route_entry_.connect_config_ = absl::make_optional(); @@ -122,7 +121,7 @@ class RouterTest : public RouterTestBase { EXPECT_CALL( cm_.thread_local_cluster_.cluster_.info_->request_response_size_stats_store_, deliverHistogramToSinks(Property(&Stats::Metric::name, "upstream_rq_headers_size"), 74ull)); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); EXPECT_CALL(callbacks_.dispatcher_, createTimer_); EXPECT_CALL( @@ -130,11 +129,11 @@ class RouterTest : public RouterTestBase { deliverHistogramToSinks(Property(&Stats::Metric::name, "upstream_rq_body_size"), 5ull)); Buffer::InstancePtr body_data(new Buffer::OwnedImpl("hello")); EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, - router_.decodeData(*body_data, !with_trailers)); + router_->decodeData(*body_data, !with_trailers)); if (with_trailers) { Http::TestRequestTrailerMapImpl trailers{{"some", "trailer"}}; - router_.decodeTrailers(trailers); + router_->decodeTrailers(trailers); } EXPECT_CALL( @@ -159,7 +158,7 @@ class RouterTest : public RouterTestBase { response_decoder->decodeTrailers(std::move(response_trailers)); } - router_.onDestroy(); + router_->onDestroy(); } void testAutoSniOptions( @@ -179,7 +178,7 @@ class RouterTest : public RouterTestBase { expectResponseTimerCreate(); HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(server_name, stream_info.filterState() ->getDataReadOnly(Network::UpstreamServerName::key()) @@ -191,7 +190,7 @@ class RouterTest : public RouterTestBase { ->value()[0]); } EXPECT_CALL(cancellable_, cancel(_)); - router_.onDestroy(); + router_->onDestroy(); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -294,7 +293,7 @@ TEST_F(RouterTest, RouteNotFound) { HttpTestUtility::addDefaultHeaders(headers); EXPECT_CALL(callbacks_, route()).WillOnce(Return(nullptr)); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1UL, stats_store_.counter("test.no_route").value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); EXPECT_EQ(0U, @@ -322,8 +321,8 @@ TEST_F(RouterTest, MissingRequiredHeaders) { testing::Eq("missing required header: :method"), _, _, "filter_removed_required_request_headers{missing_required_header:_:method}")) .WillOnce(InvokeWithoutArgs([] {})); - router_.decodeHeaders(headers, true); - router_.onDestroy(); + router_->decodeHeaders(headers, true); + router_->onDestroy(); } TEST_F(RouterTest, ClusterNotFound) { @@ -332,7 +331,7 @@ TEST_F(RouterTest, ClusterNotFound) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); ON_CALL(cm_, getThreadLocalCluster(_)).WillByDefault(Return(nullptr)); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1UL, stats_store_.counter("test.no_cluster").value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); EXPECT_EQ(0U, @@ -344,7 +343,7 @@ TEST_F(RouterTest, PoolFailureWithPriority) { ON_CALL(callbacks_.route_->route_entry_, priority()) .WillByDefault(Return(Upstream::ResourcePriority::High)); EXPECT_CALL(cm_.thread_local_cluster_, - httpConnPool(Upstream::ResourcePriority::High, _, &router_)); + httpConnPool(Upstream::ResourcePriority::High, _, router_.get())); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _, _)) .WillOnce(Invoke([&](Http::StreamDecoder&, Http::ConnectionPool::Callbacks& callbacks, const Http::ConnectionPool::Instance::StreamOptions&) @@ -363,7 +362,7 @@ TEST_F(RouterTest, PoolFailureWithPriority) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); // Pool failure, so upstream request was not initiated. EXPECT_EQ(0U, @@ -376,7 +375,7 @@ TEST_F(RouterTest, PoolFailureDueToConnectTimeout) { ON_CALL(callbacks_.route_->route_entry_, priority()) .WillByDefault(Return(Upstream::ResourcePriority::High)); EXPECT_CALL(cm_.thread_local_cluster_, - httpConnPool(Upstream::ResourcePriority::High, _, &router_)); + httpConnPool(Upstream::ResourcePriority::High, _, router_.get())); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _, _)) .WillOnce(Invoke([&](Http::StreamDecoder&, Http::ConnectionPool::Callbacks& callbacks, const Http::ConnectionPool::Instance::StreamOptions&) @@ -395,7 +394,7 @@ TEST_F(RouterTest, PoolFailureDueToConnectTimeout) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); // Pool failure, so upstream request was not initiated. EXPECT_EQ(0U, @@ -414,12 +413,12 @@ TEST_F(RouterTest, Http1Upstream) { HttpTestUtility::addDefaultHeaders(headers); EXPECT_CALL(callbacks_.route_->route_entry_, finalizeRequestHeaders(_, _, true)); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ("10", headers.get_("x-envoy-expected-rq-timeout-ms")); // When the router filter gets reset we should cancel the pool request. EXPECT_CALL(cancellable_, cancel(_)); - router_.onDestroy(); + router_->onDestroy(); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -434,11 +433,11 @@ TEST_F(RouterTest, Http2Upstream) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); // When the router filter gets reset we should cancel the pool request. EXPECT_CALL(cancellable_, cancel(_)); - router_.onDestroy(); + router_->onDestroy(); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -461,11 +460,11 @@ TEST_F(RouterTest, HashPolicy) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); // When the router filter gets reset we should cancel the pool request. EXPECT_CALL(cancellable_, cancel(_)); - router_.onDestroy(); + router_->onDestroy(); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -476,7 +475,7 @@ TEST_F(RouterTest, HashPolicyNoHash) { .WillByDefault(Return(&callbacks_.route_->route_entry_.hash_policy_)); EXPECT_CALL(callbacks_.route_->route_entry_.hash_policy_, generateHash(_, _, _, _)) .WillOnce(Return(absl::optional())); - EXPECT_CALL(cm_.thread_local_cluster_, httpConnPool(_, _, &router_)) + EXPECT_CALL(cm_.thread_local_cluster_, httpConnPool(_, _, router_.get())) .WillOnce(Invoke([&](Upstream::ResourcePriority, absl::optional, Upstream::LoadBalancerContext* context) { EXPECT_FALSE(context->computeHashKey()); @@ -488,11 +487,11 @@ TEST_F(RouterTest, HashPolicyNoHash) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); // When the router filter gets reset we should cancel the pool request. EXPECT_CALL(cancellable_, cancel(_)); - router_.onDestroy(); + router_->onDestroy(); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -500,7 +499,7 @@ TEST_F(RouterTest, HashPolicyNoHash) { TEST_F(RouterTest, HashKeyNoHashPolicy) { ON_CALL(callbacks_.route_->route_entry_, hashPolicy()).WillByDefault(Return(nullptr)); - EXPECT_FALSE(router_.computeHashKey().has_value()); + EXPECT_FALSE(router_->computeHashKey().has_value()); } TEST_F(RouterTest, AddCookie) { @@ -536,14 +535,14 @@ TEST_F(RouterTest, AddCookie) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); response_decoder->decodeHeaders(std::move(response_headers), true); EXPECT_EQ(callbacks_.details(), "via_upstream"); // When the router filter gets reset we should cancel the pool request. - router_.onDestroy(); + router_->onDestroy(); } TEST_F(RouterTest, AddCookieNoDuplicate) { @@ -579,13 +578,13 @@ TEST_F(RouterTest, AddCookieNoDuplicate) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}, {"set-cookie", "foo=baz"}}); response_decoder->decodeHeaders(std::move(response_headers), true); // When the router filter gets reset we should cancel the pool request. - router_.onDestroy(); + router_->onDestroy(); } TEST_F(RouterTest, AddMultipleCookies) { @@ -629,15 +628,15 @@ TEST_F(RouterTest, AddMultipleCookies) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); response_decoder->decodeHeaders(std::move(response_headers), true); - router_.onDestroy(); + router_->onDestroy(); } -TEST_F(RouterTest, MetadataNoOp) { EXPECT_EQ(nullptr, router_.metadataMatchCriteria()); } +TEST_F(RouterTest, MetadataNoOp) { EXPECT_EQ(nullptr, router_->metadataMatchCriteria()); } TEST_F(RouterTest, MetadataMatchCriteria) { ON_CALL(callbacks_.route_->route_entry_, metadataMatchCriteria()) @@ -655,11 +654,11 @@ TEST_F(RouterTest, MetadataMatchCriteria) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); // When the router filter gets reset we should cancel the pool request. EXPECT_CALL(cancellable_, cancel(_)); - router_.onDestroy(); + router_->onDestroy(); } TEST_F(RouterTest, MetadataMatchCriteriaFromRequest) { @@ -684,11 +683,11 @@ TEST_F(RouterTest, NoMetadataMatchCriteria) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); // When the router filter gets reset we should cancel the pool request. EXPECT_CALL(cancellable_, cancel(_)); - router_.onDestroy(); + router_->onDestroy(); } TEST_F(RouterTest, CancelBeforeBoundToPool) { @@ -698,11 +697,11 @@ TEST_F(RouterTest, CancelBeforeBoundToPool) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); // When the router filter gets reset we should cancel the pool request. EXPECT_CALL(cancellable_, cancel(_)); - router_.onDestroy(); + router_->onDestroy(); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -720,7 +719,7 @@ TEST_F(RouterTest, NoHost) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(0U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_rq_maintenance_mode") .value()); @@ -743,7 +742,7 @@ TEST_F(RouterTest, MaintenanceMode) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_rq_maintenance_mode") .value()); @@ -764,7 +763,7 @@ TEST_F(RouterTest, ResponseCodeDetailsSetByUpstream) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); @@ -782,7 +781,7 @@ TEST_F(RouterTest, EnvoyUpstreamServiceTime) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); @@ -843,7 +842,7 @@ TEST_F(RouterTest, EnvoyAttemptCountInRequestUpdatedInRetries) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -851,7 +850,7 @@ TEST_F(RouterTest, EnvoyAttemptCountInRequestUpdatedInRetries) { EXPECT_EQ(1, atoi(std::string(headers.getEnvoyAttemptCountValue()).c_str())); // 5xx response. - router_.retry_state_->expectHeadersRetry(); + router_->retry_state_->expectHeadersRetry(); Upstream::RetryOptionsPredicate::UpdateOptionsReturn update_options_return{ std::make_shared()}; EXPECT_CALL(*retry_options_predicate, updateOptions(_)).WillOnce(Return(update_options_return)); @@ -865,13 +864,13 @@ TEST_F(RouterTest, EnvoyAttemptCountInRequestUpdatedInRetries) { // Verify retry options predicate return values have been updated. EXPECT_EQ(update_options_return.new_upstream_socket_options_.value(), - router_.upstreamSocketOptions()); + router_->upstreamSocketOptions()); // We expect the 5xx response to kick off a new request. EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); NiceMock encoder2; expectNewStreamWithImmediateEncoder(encoder2, &response_decoder, Http::Protocol::Http10); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -879,7 +878,8 @@ TEST_F(RouterTest, EnvoyAttemptCountInRequestUpdatedInRetries) { EXPECT_EQ(2, atoi(std::string(headers.getEnvoyAttemptCountValue()).c_str())); // Normal response. - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->health_checker_, setUnhealthy(_)) .Times(0); Http::ResponseHeaderMapPtr response_headers2( @@ -942,7 +942,7 @@ TEST_F(RouterTest, EnvoyAttemptCountInResponsePresentWithLocalReply) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); // Pool failure, so upstream request was never initiated. EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -963,13 +963,13 @@ TEST_F(RouterTest, EnvoyAttemptCountInResponseWithRetries) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_EQ(1U, callbacks_.stream_info_.attemptCount().value()); // 5xx response. - router_.retry_state_->expectHeadersRetry(); + router_->retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, @@ -981,13 +981,14 @@ TEST_F(RouterTest, EnvoyAttemptCountInResponseWithRetries) { EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); NiceMock encoder2; expectNewStreamWithImmediateEncoder(encoder2, &response_decoder, Http::Protocol::Http10); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_EQ(2U, callbacks_.stream_info_.attemptCount().value()); // Normal response. - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->health_checker_, setUnhealthy(_)) .Times(0); Http::ResponseHeaderMapPtr response_headers2( @@ -1066,7 +1067,7 @@ TEST_F(RouterTest, AllDebugConfig) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); @@ -1080,12 +1081,12 @@ TEST_F(RouterTest, NoRetriesOverflow) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // 5xx response. - router_.retry_state_->expectHeadersRetry(); + router_->retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, @@ -1097,13 +1098,13 @@ TEST_F(RouterTest, NoRetriesOverflow) { EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); NiceMock encoder2; expectNewStreamWithImmediateEncoder(encoder2, &response_decoder, Http::Protocol::Http10); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // RetryOverflow kicks in. EXPECT_CALL(callbacks_.stream_info_, setResponseFlag(StreamInfo::ResponseFlag::UpstreamOverflow)); - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)) + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) .WillOnce(Return(RetryStatus::NoOverflow)); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->health_checker_, setUnhealthy(_)) .Times(0); @@ -1140,10 +1141,10 @@ TEST_F(RouterTest, ResetDuringEncodeHeaders) { putResult(Upstream::Outlier::Result::LocalOriginConnectFailed, _)) .Times(!upstream_filters); // The reset will be converted into a local reply. - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); - router_.onDestroy(); + router_->onDestroy(); } TEST_F(RouterTest, UpstreamTimeoutAllStatsEmission) { @@ -1155,9 +1156,9 @@ TEST_F(RouterTest, UpstreamTimeoutAllStatsEmission) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); Buffer::OwnedImpl data; - router_.decodeData(data, true); + router_->decodeData(data, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -1187,9 +1188,9 @@ TEST_F(RouterTest, UpstreamTimeout) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); Buffer::OwnedImpl data; - router_.decodeData(data, true); + router_->decodeData(data, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -1200,7 +1201,7 @@ TEST_F(RouterTest, UpstreamTimeout) { {":status", "504"}, {"content-length", "24"}, {"content-type", "text/plain"}}; EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), false)); EXPECT_CALL(callbacks_, encodeData(_, true)); - EXPECT_CALL(*router_.retry_state_, shouldRetryReset(_, _, _)).Times(0); + EXPECT_CALL(*router_->retry_state_, shouldRetryReset(_, _, _)).Times(0); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, _)); response_timeout_->invokeCallback(); @@ -1227,9 +1228,9 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStat) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-rq-timeout-ms", "400"}, {"x-envoy-upstream-rq-per-try-timeout-ms", "200"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); Buffer::OwnedImpl data; - router_.decodeData(data, true); + router_->decodeData(data, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -1263,9 +1264,9 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatFailure) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-rq-timeout-ms", "400"}, {"x-envoy-upstream-rq-per-try-timeout-ms", "200"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); Buffer::OwnedImpl data; - router_.decodeData(data, true); + router_->decodeData(data, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -1297,9 +1298,9 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatOnlyGlobal) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-rq-timeout-ms", "200"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); Buffer::OwnedImpl data; - router_.decodeData(data, true); + router_->decodeData(data, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -1333,9 +1334,9 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatDuringRetries) { {"x-envoy-upstream-rq-timeout-ms", "400"}, {"x-envoy-upstream-rq-per-try-timeout-ms", "100"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); Buffer::OwnedImpl data; - router_.decodeData(data, true); + router_->decodeData(data, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -1352,7 +1353,7 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatDuringRetries) { // Per-try timeout. test_time_.advanceTimeWait(std::chrono::milliseconds(100)); - router_.retry_state_->expectHeadersRetry(); + router_->retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "504"}}); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, @@ -1367,7 +1368,7 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatDuringRetries) { expectNewStreamWithImmediateEncoder(encoder2, &response_decoder2, Http::Protocol::Http10); expectPerTryTimerCreate(); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -1391,7 +1392,7 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatDuringRetries) { test_time_.advanceTimeWait(std::chrono::milliseconds(100)); EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), false)); EXPECT_CALL(callbacks_, encodeData(_, true)); - EXPECT_CALL(*router_.retry_state_, shouldRetryReset(_, _, _)); + EXPECT_CALL(*router_->retry_state_, shouldRetryReset(_, _, _)); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, _)); per_try_timeout_->invokeCallback(); @@ -1416,9 +1417,9 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatDuringGlobalTimeout) { {"x-envoy-upstream-rq-timeout-ms", "400"}, {"x-envoy-upstream-rq-per-try-timeout-ms", "320"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); Buffer::OwnedImpl data; - router_.decodeData(data, true); + router_->decodeData(data, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -1434,7 +1435,7 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatDuringGlobalTimeout) { .Times(0); // 5xx response. - router_.retry_state_->expectHeadersRetry(); + router_->retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, @@ -1449,7 +1450,7 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatDuringGlobalTimeout) { Http::ResponseDecoder* response_decoder2 = nullptr; expectNewStreamWithImmediateEncoder(encoder2, &response_decoder2, Http::Protocol::Http10); expectPerTryTimerCreate(); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -1474,7 +1475,7 @@ TEST_F(RouterTest, TimeoutBudgetHistogramStatDuringGlobalTimeout) { test_time_.advanceTimeWait(std::chrono::milliseconds(240)); EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), false)); EXPECT_CALL(callbacks_, encodeData(_, true)); - EXPECT_CALL(*router_.retry_state_, shouldRetryReset(_, _, _)).Times(0); + EXPECT_CALL(*router_->retry_state_, shouldRetryReset(_, _, _)).Times(0); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, _)); response_timeout_->invokeCallback(); @@ -1497,7 +1498,7 @@ TEST_F(RouterTest, GrpcOkTrailersOnly) { Http::TestRequestHeaderMapImpl headers{{"content-type", "application/grpc"}, {"grpc-timeout", "20S"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -1519,7 +1520,7 @@ TEST_F(RouterTest, GrpcAlreadyExistsTrailersOnly) { Http::TestRequestHeaderMapImpl headers{{"content-type", "application/grpc"}, {"grpc-timeout", "20S"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -1541,7 +1542,7 @@ TEST_F(RouterTest, GrpcOutlierDetectionUnavailableStatusCode) { Http::TestRequestHeaderMapImpl headers{{"content-type", "application/grpc"}, {"grpc-timeout", "20S"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -1564,7 +1565,7 @@ TEST_F(RouterTest, GrpcInternalTrailersOnly) { Http::TestRequestHeaderMapImpl headers{{"content-type", "application/grpc"}, {"grpc-timeout", "20S"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -1587,7 +1588,7 @@ TEST_F(RouterTest, GrpcDataEndStream) { Http::TestRequestHeaderMapImpl headers{{"content-type", "application/grpc"}, {"grpc-timeout", "20S"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -1613,7 +1614,7 @@ TEST_F(RouterTest, GrpcReset) { Http::TestRequestHeaderMapImpl headers{{"content-type", "application/grpc"}, {"grpc-timeout", "20S"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -1640,7 +1641,7 @@ TEST_F(RouterTest, GrpcOk) { Http::TestRequestHeaderMapImpl headers{{"content-type", "application/grpc"}, {"grpc-timeout", "20S"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -1671,7 +1672,7 @@ TEST_F(RouterTest, GrpcInternal) { Http::TestRequestHeaderMapImpl headers{{"content-type", "application/grpc"}, {"grpc-timeout", "20S"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -1696,9 +1697,9 @@ TEST_F(RouterTest, UpstreamTimeoutWithAltResponse) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-rq-timeout-alt-response", "204"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); Buffer::OwnedImpl data; - router_.decodeData(data, true); + router_->decodeData(data, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -1707,7 +1708,7 @@ TEST_F(RouterTest, UpstreamTimeoutWithAltResponse) { EXPECT_CALL(encoder.stream_, resetStream(Http::StreamResetReason::LocalReset)); Http::TestResponseHeaderMapImpl response_headers{{":status", "204"}}; EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), true)); - EXPECT_CALL(*router_.retry_state_, shouldRetryReset(_, _, _)).Times(0); + EXPECT_CALL(*router_->retry_state_, shouldRetryReset(_, _, _)).Times(0); EXPECT_CALL( cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(204))); @@ -1729,7 +1730,7 @@ TEST_F(RouterTest, UpstreamPerTryIdleTimeout) { // This pattern helps ensure that we're actually invoking the callback. bool filter_state_verified = false; - router_.config().upstream_logs_.push_back( + router_->config().upstream_logs_.push_back( std::make_shared([&](const auto& stream_info) { filter_state_verified = stream_info.hasResponseFlag(StreamInfo::ResponseFlag::StreamIdleTimeout); @@ -1751,13 +1752,13 @@ TEST_F(RouterTest, UpstreamPerTryIdleTimeout) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); response_timeout_ = new Event::MockTimer(&callbacks_.dispatcher_); EXPECT_CALL(*response_timeout_, enableTimer(_, _)); Buffer::OwnedImpl data; - router_.decodeData(data, true); + router_->decodeData(data, true); per_try_idle_timeout_ = new Event::MockTimer(&callbacks_.dispatcher_); EXPECT_CALL(*per_try_idle_timeout_, enableTimer(std::chrono::milliseconds(3000), _)); @@ -1814,13 +1815,13 @@ TEST_F(RouterTest, UpstreamPerTryIdleTimeoutSuccess) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); response_timeout_ = new Event::MockTimer(&callbacks_.dispatcher_); EXPECT_CALL(*response_timeout_, enableTimer(_, _)); Buffer::OwnedImpl data; - router_.decodeData(data, true); + router_->decodeData(data, true); per_try_idle_timeout_ = new Event::MockTimer(&callbacks_.dispatcher_); EXPECT_CALL(*per_try_idle_timeout_, enableTimer(std::chrono::milliseconds(3000), _)); @@ -1854,7 +1855,7 @@ TEST_F(RouterTest, UpstreamPerTryTimeout) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-internal", "true"}, {"x-envoy-upstream-rq-per-try-timeout-ms", "5"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); // We verify that both timeouts are started after decodeData(_, true) is called. This // verifies that we are not starting the initial per try timeout on the first onPoolReady. @@ -1862,7 +1863,7 @@ TEST_F(RouterTest, UpstreamPerTryTimeout) { expectResponseTimerCreate(); Buffer::OwnedImpl data; - router_.decodeData(data, true); + router_->decodeData(data, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -1904,12 +1905,12 @@ TEST_F(RouterTest, UpstreamPerTryTimeoutDelayedPoolReady) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-internal", "true"}, {"x-envoy-upstream-rq-per-try-timeout-ms", "5"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); // Global timeout starts when decodeData(_, true) is called. expectResponseTimerCreate(); Buffer::OwnedImpl data; - router_.decodeData(data, true); + router_->decodeData(data, true); // Per try timeout starts when onPoolReady is called. expectPerTryTimerCreate(); @@ -1961,9 +1962,9 @@ TEST_F(RouterTest, UpstreamPerTryTimeoutExcludesNewStream) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-internal", "true"}, {"x-envoy-upstream-rq-per-try-timeout-ms", "5"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); Buffer::OwnedImpl data; - router_.decodeData(data, true); + router_->decodeData(data, true); per_try_timeout_ = new Event::MockTimer(&callbacks_.dispatcher_); EXPECT_CALL(*per_try_timeout_, enableTimer(_, _)); @@ -2013,7 +2014,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutFirstRequestSucceeds) { const Http::ConnectionPool::Instance::StreamOptions&) -> Http::ConnectionPool::Cancellable* { response_decoder1 = &decoder; - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)); callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, upstream_stream_info_, Http::Protocol::Http10); return nullptr; @@ -2024,12 +2025,12 @@ TEST_F(RouterTest, HedgedPerTryTimeoutFirstRequestSucceeds) { .Times(2); expectPerTryTimerCreate(); expectResponseTimerCreate(); - EXPECT_EQ(0U, router_.upstreamRequests().size()); - EXPECT_TRUE(router_.finalUpstreamRequest() == nullptr); + EXPECT_EQ(0U, router_->upstreamRequests().size()); + EXPECT_TRUE(router_->finalUpstreamRequest() == nullptr); Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-rq-per-try-timeout-ms", "5"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -2039,7 +2040,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutFirstRequestSucceeds) { EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); NiceMock encoder2; Http::ResponseDecoder* response_decoder2 = nullptr; - router_.retry_state_->expectHedgedPerTryTimeoutRetry(); + router_->retry_state_->expectHedgedPerTryTimeoutRetry(); EXPECT_CALL(*retry_options_predicate, updateOptions(_)); per_try_timeout_->invokeCallback(); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _, _)) @@ -2048,18 +2049,18 @@ TEST_F(RouterTest, HedgedPerTryTimeoutFirstRequestSucceeds) { const Http::ConnectionPool::Instance::StreamOptions&) -> Http::ConnectionPool::Cancellable* { response_decoder2 = &decoder; - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)); callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); - EXPECT_EQ(2U, router_.upstreamRequests().size()); - EXPECT_TRUE(router_.finalUpstreamRequest() == nullptr); + EXPECT_EQ(2U, router_->upstreamRequests().size()); + EXPECT_TRUE(router_->finalUpstreamRequest() == nullptr); // We should not have updated any stats yet because no requests have been // canceled @@ -2069,7 +2070,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutFirstRequestSucceeds) { // incremented properly. Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(*router_.retry_state_, wouldRetryFromHeaders(_, _, _)) + EXPECT_CALL(*router_->retry_state_, wouldRetryFromHeaders(_, _, _)) .WillOnce(Return(RetryState::RetryDecision::NoRetry)); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); @@ -2084,8 +2085,8 @@ TEST_F(RouterTest, HedgedPerTryTimeoutFirstRequestSucceeds) { ASSERT(response_decoder1); response_decoder1->decodeHeaders(std::move(response_headers), true); EXPECT_TRUE(verifyHostUpstreamStats(1, 0)); - EXPECT_EQ(0U, router_.upstreamRequests().size()); - EXPECT_FALSE(router_.finalUpstreamRequest() == nullptr); + EXPECT_EQ(0U, router_->upstreamRequests().size()); + EXPECT_FALSE(router_->finalUpstreamRequest() == nullptr); // TODO: Verify hedge stats here once they are implemented. } @@ -2108,7 +2109,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutResetsOnBadHeaders) { const Http::ConnectionPool::Instance::StreamOptions&) -> Http::ConnectionPool::Cancellable* { response_decoder1 = &decoder; - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)); callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, upstream_stream_info_, Http::Protocol::Http10); return nullptr; @@ -2122,7 +2123,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutResetsOnBadHeaders) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-rq-per-try-timeout-ms", "5"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -2132,7 +2133,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutResetsOnBadHeaders) { EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); NiceMock encoder2; Http::ResponseDecoder* response_decoder2 = nullptr; - router_.retry_state_->expectHedgedPerTryTimeoutRetry(); + router_->retry_state_->expectHedgedPerTryTimeoutRetry(); per_try_timeout_->invokeCallback(); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _, _)) @@ -2141,13 +2142,13 @@ TEST_F(RouterTest, HedgedPerTryTimeoutResetsOnBadHeaders) { const Http::ConnectionPool::Instance::StreamOptions&) -> Http::ConnectionPool::Cancellable* { response_decoder2 = &decoder; - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)); callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -2163,7 +2164,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutResetsOnBadHeaders) { putHttpResponseCode(500)); EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); EXPECT_CALL(encoder2.stream_, resetStream(_)); - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)) + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) .WillOnce(Return(RetryStatus::NoOverflow)); // Not end_stream, otherwise we wouldn't need to reset. ASSERT(response_decoder2); @@ -2219,7 +2220,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutThirdRequestSucceeds) { const Http::ConnectionPool::Instance::StreamOptions&) -> Http::ConnectionPool::Cancellable* { response_decoder1 = &decoder; - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)); upstream_stream_info_.downstream_connection_info_provider_->setConnectionID(111); callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, upstream_stream_info_, Http::Protocol::Http10); @@ -2230,7 +2231,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutThirdRequestSucceeds) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-rq-per-try-timeout-ms", "5"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -2247,7 +2248,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutThirdRequestSucceeds) { putHttpResponseCode(500)); EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); EXPECT_CALL(callbacks_, encodeHeaders_(_, _)).Times(0); - router_.retry_state_->expectHeadersRetry(); + router_->retry_state_->expectHeadersRetry(); // NOLINTNEXTLINE: Silence null pointer access warning response_decoder1->decodeHeaders(std::move(response_headers1), true); @@ -2259,21 +2260,21 @@ TEST_F(RouterTest, HedgedPerTryTimeoutThirdRequestSucceeds) { const Http::ConnectionPool::Instance::StreamOptions&) -> Http::ConnectionPool::Cancellable* { response_decoder2 = &decoder; - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)); upstream_stream_info_.downstream_connection_info_provider_->setConnectionID(222); callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); // Now trigger a per try timeout on the 2nd request, expect a 3rd - router_.retry_state_->expectHedgedPerTryTimeoutRetry(); + router_->retry_state_->expectHedgedPerTryTimeoutRetry(); EXPECT_CALL( cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(504))); @@ -2285,7 +2286,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutThirdRequestSucceeds) { const Http::ConnectionPool::Instance::StreamOptions&) -> Http::ConnectionPool::Cancellable* { response_decoder3 = &decoder; - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)); upstream_stream_info_.downstream_connection_info_provider_->setConnectionID(333); callbacks.onPoolReady(encoder3, cm_.thread_local_cluster_.conn_pool_.host_, upstream_stream_info_, Http::Protocol::Http10); @@ -2295,7 +2296,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutThirdRequestSucceeds) { EXPECT_CALL(callbacks_, encodeHeaders_(_, _)).Times(0); per_try_timeout_->invokeCallback(); expectPerTryTimerCreate(); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(3U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); @@ -2315,7 +2316,8 @@ TEST_F(RouterTest, HedgedPerTryTimeoutThirdRequestSucceeds) { EXPECT_EQ(headers.Status()->value(), "200"); EXPECT_TRUE(end_stream); })); - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); // NOLINTNEXTLINE: Silence null pointer access warning response_decoder3->decodeHeaders(std::move(response_headers2), true); EXPECT_TRUE(verifyHostUpstreamStats(1, 1)); @@ -2338,7 +2340,7 @@ TEST_F(RouterTest, RetryOnlyOnceForSameUpstreamRequest) { const Http::ConnectionPool::Instance::StreamOptions&) -> Http::ConnectionPool::Cancellable* { response_decoder1 = &decoder; - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)); callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, upstream_stream_info_, Http::Protocol::Http10); return nullptr; @@ -2352,14 +2354,14 @@ TEST_F(RouterTest, RetryOnlyOnceForSameUpstreamRequest) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-rq-per-try-timeout-ms", "5"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); EXPECT_CALL( cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(504))); - router_.retry_state_->expectHedgedPerTryTimeoutRetry(); + router_->retry_state_->expectHedgedPerTryTimeoutRetry(); per_try_timeout_->invokeCallback(); NiceMock encoder2; @@ -2370,22 +2372,22 @@ TEST_F(RouterTest, RetryOnlyOnceForSameUpstreamRequest) { const Http::ConnectionPool::Instance::StreamOptions&) -> Http::ConnectionPool::Cancellable* { response_decoder2 = &decoder; - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)); callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); // Now send a 5xx back and make sure we don't ask whether we should retry it. Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "500"}}); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(500)); - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).Times(0); - EXPECT_CALL(*router_.retry_state_, wouldRetryFromHeaders(_, _, _)) + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)).Times(0); + EXPECT_CALL(*router_->retry_state_, wouldRetryFromHeaders(_, _, _)) .WillOnce(Return(RetryState::RetryDecision::RetryWithBackoff)); ASSERT(response_decoder1); response_decoder1->decodeHeaders(std::move(response_headers1), true); @@ -2412,7 +2414,7 @@ TEST_F(RouterTest, BadHeadersDroppedIfPreviousRetryScheduled) { const Http::ConnectionPool::Instance::StreamOptions&) -> Http::ConnectionPool::Cancellable* { response_decoder1 = &decoder; - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)); callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, upstream_stream_info_, Http::Protocol::Http10); return nullptr; @@ -2426,14 +2428,14 @@ TEST_F(RouterTest, BadHeadersDroppedIfPreviousRetryScheduled) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-rq-per-try-timeout-ms", "5"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); EXPECT_CALL( cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(504))); - router_.retry_state_->expectHedgedPerTryTimeoutRetry(); + router_->retry_state_->expectHedgedPerTryTimeoutRetry(); per_try_timeout_->invokeCallback(); expectPerTryTimerCreate(); @@ -2444,8 +2446,8 @@ TEST_F(RouterTest, BadHeadersDroppedIfPreviousRetryScheduled) { new Http::TestResponseHeaderMapImpl{{":status", "500"}}); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(500)); - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).Times(0); - EXPECT_CALL(*router_.retry_state_, wouldRetryFromHeaders(_, _, _)) + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)).Times(0); + EXPECT_CALL(*router_->retry_state_, wouldRetryFromHeaders(_, _, _)) .WillOnce(Return(RetryState::RetryDecision::RetryWithBackoff)); EXPECT_CALL(callbacks_, encodeHeaders_(_, _)).Times(0); ASSERT(response_decoder1); @@ -2460,16 +2462,17 @@ TEST_F(RouterTest, BadHeadersDroppedIfPreviousRetryScheduled) { const Http::ConnectionPool::Instance::StreamOptions&) -> Http::ConnectionPool::Cancellable* { response_decoder2 = &decoder; - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)); callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); Http::ResponseHeaderMapPtr response_headers2( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); EXPECT_CALL(callbacks_, encodeHeaders_(_, _)) .WillOnce(Invoke([&](Http::ResponseHeaderMap& headers, bool end_stream) -> void { EXPECT_EQ(headers.Status()->value(), "200"); @@ -2495,16 +2498,16 @@ TEST_F(RouterTest, RetryRequestBeforeBody) { Http::TestRequestHeaderMapImpl headers{ {"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}, {"myheader", "present"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); - router_.retry_state_->expectResetRetry(); + router_->retry_state_->expectResetRetry(); EXPECT_CALL(*retry_options_predicate, updateOptions(_)); encoder1.stream_.resetStream(Http::StreamResetReason::RemoteReset); NiceMock encoder2; expectNewStreamWithImmediateEncoder(encoder2, &response_decoder, Http::Protocol::Http10); EXPECT_CALL(encoder2, encodeHeaders(HeaderHasValueRef("myheader", "present"), false)); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); @@ -2513,7 +2516,7 @@ TEST_F(RouterTest, RetryRequestBeforeBody) { const std::string body("body"); EXPECT_CALL(encoder2, encodeData(BufferStringEqual(body), true)); Buffer::OwnedImpl buf(body); - router_.decodeData(buf, true); + router_->decodeData(buf, true); // Send successful response, verify success. Http::ResponseHeaderMapPtr response_headers( @@ -2544,13 +2547,13 @@ TEST_F(RouterTest, RetryRequestDuringBody) { Http::TestRequestHeaderMapImpl headers{ {"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}, {"myheader", "present"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); const std::string body1("body1"); Buffer::OwnedImpl buf1(body1); - EXPECT_CALL(*router_.retry_state_, enabled()).WillOnce(Return(true)); - router_.decodeData(buf1, false); + EXPECT_CALL(*router_->retry_state_, enabled()).WillOnce(Return(true)); + router_->decodeData(buf1, false); - router_.retry_state_->expectResetRetry(); + router_->retry_state_->expectResetRetry(); encoder1.stream_.resetStream(Http::StreamResetReason::RemoteReset); NiceMock encoder2; @@ -2558,7 +2561,7 @@ TEST_F(RouterTest, RetryRequestDuringBody) { EXPECT_CALL(encoder2, encodeHeaders(HeaderHasValueRef("myheader", "present"), false)); EXPECT_CALL(encoder2, encodeData(BufferStringEqual(body1), false)); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); @@ -2567,8 +2570,8 @@ TEST_F(RouterTest, RetryRequestDuringBody) { const std::string body2("body2"); EXPECT_CALL(encoder2, encodeData(BufferStringEqual(body2), true)); Buffer::OwnedImpl buf2(body2); - EXPECT_CALL(*router_.retry_state_, enabled()).WillOnce(Return(true)); - router_.decodeData(buf2, true); + EXPECT_CALL(*router_->retry_state_, enabled()).WillOnce(Return(true)); + router_->decodeData(buf2, true); // Send successful response, verify success. Http::ResponseHeaderMapPtr response_headers( @@ -2598,25 +2601,25 @@ TEST_F(RouterTest, RetryRequestDuringBodyDataBetweenAttemptsNotEndStream) { Http::TestRequestHeaderMapImpl headers{ {"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}, {"myheader", "present"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); const std::string body1("body1"); Buffer::OwnedImpl buf1(body1); - EXPECT_CALL(*router_.retry_state_, enabled()).Times(3).WillRepeatedly(Return(true)); - router_.decodeData(buf1, false); + EXPECT_CALL(*router_->retry_state_, enabled()).Times(3).WillRepeatedly(Return(true)); + router_->decodeData(buf1, false); - router_.retry_state_->expectResetRetry(); + router_->retry_state_->expectResetRetry(); encoder1.stream_.resetStream(Http::StreamResetReason::RemoteReset); const std::string body2("body2"); Buffer::OwnedImpl buf2(body2); - router_.decodeData(buf2, false); + router_->decodeData(buf2, false); NiceMock encoder2; expectNewStreamWithImmediateEncoder(encoder2, &response_decoder, Http::Protocol::Http10); EXPECT_CALL(encoder2, encodeHeaders(HeaderHasValueRef("myheader", "present"), false)); EXPECT_CALL(encoder2, encodeData(BufferStringEqual(body1 + body2), false)); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); @@ -2625,7 +2628,7 @@ TEST_F(RouterTest, RetryRequestDuringBodyDataBetweenAttemptsNotEndStream) { const std::string body3("body3"); EXPECT_CALL(encoder2, encodeData(BufferStringEqual(body3), true)); Buffer::OwnedImpl buf3(body3); - router_.decodeData(buf3, true); + router_->decodeData(buf3, true); // Send successful response, verify success. Http::ResponseHeaderMapPtr response_headers( @@ -2654,26 +2657,26 @@ TEST_F(RouterTest, RetryRequestDuringBodyCompleteBetweenAttempts) { Http::TestRequestHeaderMapImpl headers{ {"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}, {"myheader", "present"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); const std::string body1("body1"); Buffer::OwnedImpl buf1(body1); - EXPECT_CALL(*router_.retry_state_, enabled()).Times(2).WillRepeatedly(Return(true)); - router_.decodeData(buf1, false); + EXPECT_CALL(*router_->retry_state_, enabled()).Times(2).WillRepeatedly(Return(true)); + router_->decodeData(buf1, false); - router_.retry_state_->expectResetRetry(); + router_->retry_state_->expectResetRetry(); encoder1.stream_.resetStream(Http::StreamResetReason::RemoteReset); // Complete request while there is no upstream request. const std::string body2("body2"); Buffer::OwnedImpl buf2(body2); - router_.decodeData(buf2, true); + router_->decodeData(buf2, true); NiceMock encoder2; expectNewStreamWithImmediateEncoder(encoder2, &response_decoder, Http::Protocol::Http10); EXPECT_CALL(encoder2, encodeHeaders(HeaderHasValueRef("myheader", "present"), false)); EXPECT_CALL(encoder2, encodeData(BufferStringEqual(body1 + body2), true)); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); @@ -2705,18 +2708,18 @@ TEST_F(RouterTest, RetryRequestDuringBodyTrailerBetweenAttempts) { Http::TestRequestHeaderMapImpl headers{ {"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}, {"myheader", "present"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); const std::string body1("body1"); Buffer::OwnedImpl buf1(body1); - EXPECT_CALL(*router_.retry_state_, enabled()).WillOnce(Return(true)); - router_.decodeData(buf1, false); + EXPECT_CALL(*router_->retry_state_, enabled()).WillOnce(Return(true)); + router_->decodeData(buf1, false); - router_.retry_state_->expectResetRetry(); + router_->retry_state_->expectResetRetry(); encoder1.stream_.resetStream(Http::StreamResetReason::RemoteReset); // Complete request while there is no upstream request. Http::TestRequestTrailerMapImpl trailers{{"some", "trailer"}}; - router_.decodeTrailers(trailers); + router_->decodeTrailers(trailers); NiceMock encoder2; expectNewStreamWithImmediateEncoder(encoder2, &response_decoder, Http::Protocol::Http10); @@ -2724,7 +2727,7 @@ TEST_F(RouterTest, RetryRequestDuringBodyTrailerBetweenAttempts) { EXPECT_CALL(encoder2, encodeHeaders(HeaderHasValueRef("myheader", "present"), false)); EXPECT_CALL(encoder2, encodeData(BufferStringEqual(body1), false)); EXPECT_CALL(encoder2, encodeTrailers(HeaderMapEqualRef(&trailers))); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); @@ -2757,19 +2760,19 @@ TEST_F(RouterTest, RetryRequestDuringBodyBufferLimitExceeded) { Http::TestRequestHeaderMapImpl headers{ {"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}, {"myheader", "present"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); const std::string body1("body1"); Buffer::OwnedImpl buf1(body1); - EXPECT_CALL(*router_.retry_state_, enabled()).Times(2).WillRepeatedly(Return(true)); - router_.decodeData(buf1, false); + EXPECT_CALL(*router_->retry_state_, enabled()).Times(2).WillRepeatedly(Return(true)); + router_->decodeData(buf1, false); - router_.retry_state_->expectResetRetry(); + router_->retry_state_->expectResetRetry(); encoder1.stream_.resetStream(Http::StreamResetReason::RemoteReset); // Complete request while there is no upstream request. const std::string body2(50, 'a'); Buffer::OwnedImpl buf2(body2); - router_.decodeData(buf2, false); + router_->decodeData(buf2, false); EXPECT_EQ(callbacks_.details(), "request_payload_exceeded_retry_buffer_limit"); EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ @@ -2791,7 +2794,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutGlobalTimeout) { const Http::ConnectionPool::Instance::StreamOptions&) -> Http::ConnectionPool::Cancellable* { response_decoder1 = &decoder; - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)); callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, upstream_stream_info_, Http::Protocol::Http10); return nullptr; @@ -2805,7 +2808,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutGlobalTimeout) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-rq-per-try-timeout-ms", "5"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -2814,7 +2817,7 @@ TEST_F(RouterTest, HedgedPerTryTimeoutGlobalTimeout) { putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(504))); EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); EXPECT_CALL(callbacks_, encodeHeaders_(_, _)).Times(0); - router_.retry_state_->expectHedgedPerTryTimeoutRetry(); + router_->retry_state_->expectHedgedPerTryTimeoutRetry(); per_try_timeout_->invokeCallback(); NiceMock encoder2; @@ -2825,13 +2828,13 @@ TEST_F(RouterTest, HedgedPerTryTimeoutGlobalTimeout) { const Http::ConnectionPool::Instance::StreamOptions&) -> Http::ConnectionPool::Cancellable* { response_decoder2 = &decoder; - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)); callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); expectPerTryTimerCreate(); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -2867,7 +2870,7 @@ TEST_F(RouterTest, HedgingRetriesExhaustedBadResponse) { const Http::ConnectionPool::Instance::StreamOptions&) -> Http::ConnectionPool::Cancellable* { response_decoder1 = &decoder; - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)); callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, upstream_stream_info_, Http::Protocol::Http10); return nullptr; @@ -2880,7 +2883,7 @@ TEST_F(RouterTest, HedgingRetriesExhaustedBadResponse) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-rq-per-try-timeout-ms", "5"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -2889,7 +2892,7 @@ TEST_F(RouterTest, HedgingRetriesExhaustedBadResponse) { putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(504))); EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); EXPECT_CALL(callbacks_, encodeHeaders_(_, _)).Times(0); - router_.retry_state_->expectHedgedPerTryTimeoutRetry(); + router_->retry_state_->expectHedgedPerTryTimeoutRetry(); per_try_timeout_->invokeCallback(); NiceMock encoder2; @@ -2900,7 +2903,7 @@ TEST_F(RouterTest, HedgingRetriesExhaustedBadResponse) { const Http::ConnectionPool::Instance::StreamOptions&) -> Http::ConnectionPool::Cancellable* { response_decoder2 = &decoder; - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)); callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, upstream_stream_info_, Http::Protocol::Http10); return nullptr; @@ -2909,7 +2912,7 @@ TEST_F(RouterTest, HedgingRetriesExhaustedBadResponse) { putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess, absl::optional(absl::nullopt))); expectPerTryTimerCreate(); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -2921,7 +2924,7 @@ TEST_F(RouterTest, HedgingRetriesExhaustedBadResponse) { EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(503)); - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)) + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) .WillOnce(Return(RetryStatus::NoRetryLimitExceeded)); ASSERT(response_decoder2); response_decoder2->decodeHeaders(std::move(bad_response_headers1), true); @@ -2936,7 +2939,7 @@ TEST_F(RouterTest, HedgingRetriesExhaustedBadResponse) { // We should not call shouldRetryHeaders() because you never retry the same // request twice. - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).Times(0); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)).Times(0); EXPECT_CALL(callbacks_, encodeHeaders_(_, _)) .WillOnce(Invoke([&](Http::ResponseHeaderMap& headers, bool) -> void { @@ -2968,7 +2971,7 @@ TEST_F(RouterTest, HedgingRetriesProceedAfterReset) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-rq-per-try-timeout-ms", "5"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -2977,7 +2980,7 @@ TEST_F(RouterTest, HedgingRetriesProceedAfterReset) { putResult(Upstream::Outlier::Result::LocalOriginTimeout, absl::optional(504))); EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); EXPECT_CALL(callbacks_, encodeHeaders_(_, _)).Times(0); - router_.retry_state_->expectHedgedPerTryTimeoutRetry(); + router_->retry_state_->expectHedgedPerTryTimeoutRetry(); per_try_timeout_->invokeCallback(); NiceMock encoder2; @@ -2985,7 +2988,7 @@ TEST_F(RouterTest, HedgingRetriesProceedAfterReset) { expectNewStreamWithImmediateEncoder(encoder2, &response_decoder2, Http::Protocol::Http10); expectPerTryTimerCreate(); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -2999,13 +3002,14 @@ TEST_F(RouterTest, HedgingRetriesProceedAfterReset) { // We should not call shouldRetryReset() because you never retry the same // request twice. - EXPECT_CALL(*router_.retry_state_, shouldRetryReset(_, _, _)).Times(0); + EXPECT_CALL(*router_->retry_state_, shouldRetryReset(_, _, _)).Times(0); // Now trigger a 200 in response to the second request. Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); EXPECT_CALL(callbacks_, encodeHeaders_(_, _)) .WillOnce(Invoke([&](Http::ResponseHeaderMap& headers, bool) -> void { EXPECT_EQ(headers.Status()->value(), "200"); @@ -3026,7 +3030,7 @@ TEST_F(RouterTest, HedgingRetryImmediatelyReset) { NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; - router_.retry_425_response_ = true; + router_->retry_425_response_ = true; expectNewStreamWithImmediateEncoder(encoder, &response_decoder, Http::Protocol::Http10); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, @@ -3035,15 +3039,15 @@ TEST_F(RouterTest, HedgingRetryImmediatelyReset) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-rq-per-try-timeout-ms", "5"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); expectPerTryTimerCreate(); expectResponseTimerCreate(); Buffer::OwnedImpl body("test body"); EXPECT_CALL(encoder, encodeData(_, _)); Buffer::InstancePtr body_data(new Buffer::OwnedImpl("hello")); - router_.retry_state_->expectHedgedPerTryTimeoutRetry(); - EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_.decodeData(*body_data, true)); + router_->retry_state_->expectHedgedPerTryTimeoutRetry(); + EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_->decodeData(*body_data, true)); EXPECT_CALL( cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, @@ -3059,18 +3063,18 @@ TEST_F(RouterTest, HedgingRetryImmediatelyReset) { const Http::ConnectionPool::Instance::StreamOptions& options) -> Http::ConnectionPool::Cancellable* { EXPECT_FALSE(options.can_send_early_data_); - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectFailed, _)); callbacks.onPoolFailure(ConnectionPool::PoolFailureReason::RemoteConnectionFailure, absl::string_view(), cm_.thread_local_cluster_.conn_pool_.host_); return nullptr; })); - EXPECT_CALL(*router_.retry_state_, + EXPECT_CALL(*router_->retry_state_, shouldRetryReset(_, /*http3_used=*/RetryState::Http3Used::Unknown, _)) .WillOnce(Return(RetryStatus::NoRetryLimitExceeded)); ON_CALL(callbacks_, decodingBuffer()).WillByDefault(Return(body_data.get())); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); @@ -3080,7 +3084,7 @@ TEST_F(RouterTest, HedgingRetryImmediatelyReset) { // The request was already retried when the per try timeout occurred so it // should't even consult the retry state. - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).Times(0); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)).Times(0); EXPECT_CALL(callbacks_, encodeHeaders_(_, _)) .WillOnce(Invoke([&](Http::ResponseHeaderMap& headers, bool) -> void { EXPECT_EQ(headers.Status()->value(), "200"); @@ -3104,9 +3108,9 @@ TEST_F(RouterTest, RetryNoneHealthy) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); - router_.retry_state_->expectResetRetry(); + router_->retry_state_->expectResetRetry(); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectFailed, _)); encoder1.stream_.resetStream(Http::StreamResetReason::LocalReset); @@ -3118,7 +3122,7 @@ TEST_F(RouterTest, RetryNoneHealthy) { EXPECT_CALL(callbacks_, encodeData(_, true)); EXPECT_CALL(callbacks_.stream_info_, setResponseFlag(StreamInfo::ResponseFlag::NoHealthyUpstream)); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); // Pool failure for the first try, so only 1 upstream request was made. EXPECT_EQ(1U, @@ -3134,19 +3138,19 @@ TEST_F(RouterTest, RetryUpstreamReset) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); - EXPECT_CALL(*router_.retry_state_, enabled()).WillOnce(Return(true)); + router_->decodeHeaders(headers, false); + EXPECT_CALL(*router_->retry_state_, enabled()).WillOnce(Return(true)); EXPECT_CALL(callbacks_, addDecodedData(_, _)); Buffer::OwnedImpl body("test body"); - router_.decodeData(body, true); + router_->decodeData(body, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); - EXPECT_CALL(*router_.retry_state_, shouldRetryReset(Http::StreamResetReason::RemoteReset, _, _)) + EXPECT_CALL(*router_->retry_state_, shouldRetryReset(Http::StreamResetReason::RemoteReset, _, _)) .WillOnce(Invoke([this](const Http::StreamResetReason, RetryState::Http3Used http3_used, RetryState::DoRetryResetCallback callback) { EXPECT_EQ(RetryState::Http3Used::No, http3_used); - router_.retry_state_->callback_ = [callback]() { callback(/*disable_http3=*/false); }; + router_->retry_state_->callback_ = [callback]() { callback(/*disable_http3=*/false); }; return RetryStatus::Yes; })); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, @@ -3170,13 +3174,14 @@ TEST_F(RouterTest, RetryUpstreamReset) { return nullptr; })); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); // Normal response. - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, @@ -3188,24 +3193,24 @@ TEST_F(RouterTest, RetryUpstreamReset) { TEST_F(RouterTest, RetryHttp3UpstreamReset) { NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; - router_.retry_425_response_ = true; + router_->retry_425_response_ = true; expectNewStreamWithImmediateEncoder(encoder1, &response_decoder, Http::Protocol::Http3); expectResponseTimerCreate(); Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); - EXPECT_CALL(*router_.retry_state_, enabled()).WillOnce(Return(true)); + router_->decodeHeaders(headers, false); + EXPECT_CALL(*router_->retry_state_, enabled()).WillOnce(Return(true)); EXPECT_CALL(callbacks_, addDecodedData(_, _)); Buffer::OwnedImpl body("test body"); - router_.decodeData(body, true); + router_->decodeData(body, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); - EXPECT_CALL(*router_.retry_state_, shouldRetryReset(Http::StreamResetReason::RemoteReset, _, _)) + EXPECT_CALL(*router_->retry_state_, shouldRetryReset(Http::StreamResetReason::RemoteReset, _, _)) .WillOnce(Invoke([this](const Http::StreamResetReason, RetryState::Http3Used http3_used, RetryState::DoRetryResetCallback callback) { EXPECT_EQ(RetryState::Http3Used::Yes, http3_used); - router_.retry_state_->callback_ = [callback]() { callback(/*disable_http3=*/true); }; + router_->retry_state_->callback_ = [callback]() { callback(/*disable_http3=*/true); }; return RetryStatus::Yes; })); @@ -3231,13 +3236,14 @@ TEST_F(RouterTest, RetryHttp3UpstreamReset) { return nullptr; })); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 1)); // Normal response. - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, @@ -3255,12 +3261,12 @@ TEST_F(RouterTest, NoRetryWithBodyLimit) { EXPECT_CALL(callbacks_.route_->route_entry_, retryShadowBufferLimit()).WillOnce(Return(0)); Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); // Unlike RetryUpstreamReset above the data won't be buffered as the body exceeds the buffer limit - EXPECT_CALL(*router_.retry_state_, enabled()).WillOnce(Return(true)); + EXPECT_CALL(*router_->retry_state_, enabled()).WillOnce(Return(true)); EXPECT_CALL(callbacks_, addDecodedData(_, _)).Times(0); Buffer::OwnedImpl body("t"); - router_.decodeData(body, false); + router_->decodeData(body, false); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -3281,7 +3287,7 @@ TEST_F(RouterTest, RetryUpstreamPerTryTimeout) { const Http::ConnectionPool::Instance::StreamOptions&) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)); callbacks.onPoolReady(encoder1, cm_.thread_local_cluster_.conn_pool_.host_, upstream_stream_info_, Http::Protocol::Http10); return nullptr; @@ -3294,11 +3300,11 @@ TEST_F(RouterTest, RetryUpstreamPerTryTimeout) { {"x-envoy-internal", "true"}, {"x-envoy-upstream-rq-per-try-timeout-ms", "5"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); - router_.retry_state_->expectResetRetry(); + router_->retry_state_->expectResetRetry(); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginTimeout, _)); per_try_timeout_->invokeCallback(); @@ -3306,7 +3312,7 @@ TEST_F(RouterTest, RetryUpstreamPerTryTimeout) { // We expect this reset to kick off a new request. NiceMock encoder2; - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _, _)) .WillOnce( Invoke([&](Http::ResponseDecoder& decoder, Http::ConnectionPool::Callbacks& callbacks, @@ -3322,12 +3328,13 @@ TEST_F(RouterTest, RetryUpstreamPerTryTimeout) { })); expectPerTryTimerCreate(); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // Normal response. - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, @@ -3352,11 +3359,11 @@ TEST_F(RouterTest, RetryUpstreamConnectionFailure) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)).Times(0); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)).Times(0); - router_.retry_state_->expectResetRetry(); + router_->retry_state_->expectResetRetry(); conn_pool_callbacks->onPoolFailure(ConnectionPool::PoolFailureReason::RemoteConnectionFailure, absl::string_view(), nullptr); @@ -3373,18 +3380,19 @@ TEST_F(RouterTest, RetryUpstreamConnectionFailure) { const Http::ConnectionPool::Instance::StreamOptions&) -> Http::ConnectionPool::Cancellable* { response_decoder = &decoder; - EXPECT_CALL(*router_.retry_state_, onHostAttempted(_)); + EXPECT_CALL(*router_->retry_state_, onHostAttempted(_)); callbacks.onPoolReady(encoder2, cm_.thread_local_cluster_.conn_pool_.host_, upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // Normal response. - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, @@ -3404,12 +3412,13 @@ TEST_F(RouterTest, DontResetStartedResponseOnUpstreamPerTryTimeout) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-internal", "true"}, {"x-envoy-upstream-rq-per-try-timeout-ms", "5"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // Since the response is already started we don't retry. - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); EXPECT_CALL(callbacks_, encodeHeaders_(_, false)); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); @@ -3437,12 +3446,13 @@ TEST_F(RouterTest, RetryUpstreamResetResponseStarted) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // Since the response is already started we don't retry. - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); EXPECT_CALL(callbacks_, encodeHeaders_(_, false)); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); @@ -3475,7 +3485,7 @@ TEST_F(RouterTest, Coalesce1xxHeaders) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -3528,14 +3538,14 @@ TEST_F(RouterTest, RetryUpstreamReset1xxResponseStarted) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // The 100-continue will result in resetting retry_state_, so when the stream // is reset we won't even check shouldRetryReset() (or shouldRetryHeaders()). - EXPECT_CALL(*router_.retry_state_, shouldRetryReset(_, _, _)).Times(0); - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).Times(0); + EXPECT_CALL(*router_->retry_state_, shouldRetryReset(_, _, _)).Times(0); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)).Times(0); EXPECT_CALL(callbacks_, encode1xxHeaders_(_)); Http::ResponseHeaderMapPtr continue_headers( new Http::TestResponseHeaderMapImpl{{":status", "100"}}); @@ -3560,12 +3570,12 @@ TEST_F(RouterTest, RetryUpstream5xx) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // 5xx response. - router_.retry_state_->expectHeadersRetry(); + router_->retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, @@ -3578,12 +3588,13 @@ TEST_F(RouterTest, RetryUpstream5xx) { NiceMock encoder2; expectNewStreamWithImmediateEncoder(encoder2, &response_decoder, Http::Protocol::Http10); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // Normal response. - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->health_checker_, setUnhealthy(_)) .Times(0); Http::ResponseHeaderMapPtr response_headers2( @@ -3603,12 +3614,12 @@ TEST_F(RouterTest, RetryTimeoutDuringRetryDelay) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // 5xx response. - router_.retry_state_->expectHeadersRetry(); + router_->retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, @@ -3640,10 +3651,10 @@ TEST_F(RouterTest, MaxStreamDurationValidlyConfiguredWithoutRetryPolicy) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); max_stream_duration_timer_->invokeCallback(); - router_.onDestroy(); + router_->onDestroy(); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); } @@ -3658,9 +3669,9 @@ TEST_F(RouterTest, MaxStreamDurationDisabledIfSetToZero) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); - router_.onDestroy(); + router_->onDestroy(); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); } @@ -3674,9 +3685,9 @@ TEST_F(RouterTest, MaxStreamDurationCallbackNotCalled) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); - router_.onDestroy(); + router_->onDestroy(); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); } @@ -3690,13 +3701,13 @@ TEST_F(RouterTest, MaxStreamDurationWhenDownstreamAlreadyStartedWithoutRetryPoli Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); response_decoder->decodeHeaders(std::move(response_headers), false); max_stream_duration_timer_->invokeCallback(); - router_.onDestroy(); + router_->onDestroy(); EXPECT_TRUE(verifyHostUpstreamStats(1, 0)); } @@ -3712,9 +3723,9 @@ TEST_F(RouterTest, MaxStreamDurationWithRetryPolicy) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "reset"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); - router_.retry_state_->expectResetRetry(); + router_->retry_state_->expectResetRetry(); max_stream_duration_timer_->invokeCallback(); // Second upstream request @@ -3723,9 +3734,10 @@ TEST_F(RouterTest, MaxStreamDurationWithRetryPolicy) { expectNewStreamWithImmediateEncoder(encoder2, &response_decoder, Http::Protocol::Http10); expectMaxStreamDurationTimerCreate(std::chrono::milliseconds(500)); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); response_decoder->decodeHeaders(std::move(response_headers), true); @@ -3740,12 +3752,12 @@ TEST_F(RouterTest, RetryTimeoutDuringRetryDelayWithUpstreamRequestNoHost) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // 5xx response. - router_.retry_state_->expectHeadersRetry(); + router_->retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, @@ -3761,7 +3773,7 @@ TEST_F(RouterTest, RetryTimeoutDuringRetryDelayWithUpstreamRequestNoHost) { response_decoder = &decoder; return &cancellable; })); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); // Fire timeout. EXPECT_CALL(cancellable, cancel(_)); @@ -3793,12 +3805,12 @@ TEST_F(RouterTest, RetryTimeoutDuringRetryDelayWithUpstreamRequestNoHostAltRespo {"x-envoy-internal", "true"}, {"x-envoy-upstream-rq-timeout-alt-response", "204"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // 5xx response. - router_.retry_state_->expectHeadersRetry(); + router_->retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, @@ -3814,7 +3826,7 @@ TEST_F(RouterTest, RetryTimeoutDuringRetryDelayWithUpstreamRequestNoHostAltRespo response_decoder = &decoder; return &cancellable; })); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); // Fire timeout. EXPECT_CALL(cancellable, cancel(_)); @@ -3840,20 +3852,20 @@ TEST_F(RouterTest, RetryUpstream5xxNotComplete) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); Buffer::InstancePtr body_data(new Buffer::OwnedImpl("hello")); - EXPECT_CALL(*router_.retry_state_, enabled()).WillOnce(Return(true)); + EXPECT_CALL(*router_->retry_state_, enabled()).WillOnce(Return(true)); EXPECT_CALL(callbacks_, addDecodedData(_, true)); - EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_.decodeData(*body_data, false)); + EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_->decodeData(*body_data, false)); Http::TestRequestTrailerMapImpl trailers{{"some", "trailer"}}; - router_.decodeTrailers(trailers); + router_->decodeTrailers(trailers); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // 5xx response. - router_.retry_state_->expectHeadersRetry(); + router_->retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); EXPECT_CALL(encoder1.stream_, resetStream(Http::StreamResetReason::LocalReset)); @@ -3870,12 +3882,13 @@ TEST_F(RouterTest, RetryUpstream5xxNotComplete) { EXPECT_CALL(encoder2, encodeHeaders(_, false)); EXPECT_CALL(encoder2, encodeData(_, false)); EXPECT_CALL(encoder2, encodeTrailers(_)); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // Normal response. - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putHttpResponseCode(200)); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, putResponseTime(_)); @@ -3914,12 +3927,12 @@ TEST_F(RouterTest, RetryUpstreamGrpcCancelled) { {"content-type", "application/grpc"}, {"grpc-timeout", "20S"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // gRPC with status "cancelled" (1) - router_.retry_state_->expectHeadersRetry(); + router_->retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "200"}, {"grpc-status", "1"}}); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, @@ -3932,12 +3945,13 @@ TEST_F(RouterTest, RetryUpstreamGrpcCancelled) { NiceMock encoder2; expectNewStreamWithImmediateEncoder(encoder2, &response_decoder, Http::Protocol::Http10); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // Normal response. - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}, {"grpc-status", "0"}}); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->outlier_detector_, @@ -3949,7 +3963,7 @@ TEST_F(RouterTest, RetryUpstreamGrpcCancelled) { // Verifies that the initial host is select with max host count of one, but during retries // RetryPolicy will be consulted. TEST_F(RouterTest, RetryRespectsMaxHostSelectionCount) { - router_.reject_all_hosts_ = true; + router_->reject_all_hosts_ = true; NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; @@ -3959,24 +3973,24 @@ TEST_F(RouterTest, RetryRespectsMaxHostSelectionCount) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); - ON_CALL(*router_.retry_state_, hostSelectionMaxAttempts()).WillByDefault(Return(3)); + ON_CALL(*router_->retry_state_, hostSelectionMaxAttempts()).WillByDefault(Return(3)); // The router should accept any host at this point, since we're not in a retry. - EXPECT_EQ(1, router_.hostSelectionRetryCount()); + EXPECT_EQ(1, router_->hostSelectionRetryCount()); Buffer::InstancePtr body_data(new Buffer::OwnedImpl("hello")); - EXPECT_CALL(*router_.retry_state_, enabled()).WillOnce(Return(true)); + EXPECT_CALL(*router_->retry_state_, enabled()).WillOnce(Return(true)); EXPECT_CALL(callbacks_, addDecodedData(_, true)); - EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_.decodeData(*body_data, false)); + EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_->decodeData(*body_data, false)); Http::TestRequestTrailerMapImpl trailers{{"some", "trailer"}}; - router_.decodeTrailers(trailers); + router_->decodeTrailers(trailers); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // 5xx response. - router_.retry_state_->expectHeadersRetry(); + router_->retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); EXPECT_CALL(encoder1.stream_, resetStream(Http::StreamResetReason::LocalReset)); @@ -3993,15 +4007,16 @@ TEST_F(RouterTest, RetryRespectsMaxHostSelectionCount) { EXPECT_CALL(encoder2, encodeHeaders(_, false)); EXPECT_CALL(encoder2, encodeData(_, false)); EXPECT_CALL(encoder2, encodeTrailers(_)); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // Now that we're triggered a retry, we should see the configured number of host selections. - EXPECT_EQ(3, router_.hostSelectionRetryCount()); + EXPECT_EQ(3, router_->hostSelectionRetryCount()); // Normal response. - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->health_checker_, setUnhealthy(_)) .Times(0); Http::ResponseHeaderMapPtr response_headers2( @@ -4015,7 +4030,7 @@ TEST_F(RouterTest, RetryRespectsMaxHostSelectionCount) { // Verifies that the initial request accepts any host, but during retries // RetryPolicy will be consulted. TEST_F(RouterTest, RetryRespectsRetryHostPredicate) { - router_.reject_all_hosts_ = true; + router_->reject_all_hosts_ = true; NiceMock encoder1; Http::ResponseDecoder* response_decoder = nullptr; @@ -4025,24 +4040,24 @@ TEST_F(RouterTest, RetryRespectsRetryHostPredicate) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); NiceMock host; // The router should accept any host at this point, since we're not in a retry. - EXPECT_FALSE(router_.shouldSelectAnotherHost(host)); + EXPECT_FALSE(router_->shouldSelectAnotherHost(host)); Buffer::InstancePtr body_data(new Buffer::OwnedImpl("hello")); - EXPECT_CALL(*router_.retry_state_, enabled()).WillOnce(Return(true)); + EXPECT_CALL(*router_->retry_state_, enabled()).WillOnce(Return(true)); EXPECT_CALL(callbacks_, addDecodedData(_, true)); - EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_.decodeData(*body_data, false)); + EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_->decodeData(*body_data, false)); Http::TestRequestTrailerMapImpl trailers{{"some", "trailer"}}; - router_.decodeTrailers(trailers); + router_->decodeTrailers(trailers); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // 5xx response. - router_.retry_state_->expectHeadersRetry(); + router_->retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); EXPECT_CALL(encoder1.stream_, resetStream(Http::StreamResetReason::LocalReset)); @@ -4059,15 +4074,16 @@ TEST_F(RouterTest, RetryRespectsRetryHostPredicate) { EXPECT_CALL(encoder2, encodeHeaders(_, false)); EXPECT_CALL(encoder2, encodeData(_, false)); EXPECT_CALL(encoder2, encodeTrailers(_)); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); // Now that we're triggered a retry, we should see the router reject hosts. - EXPECT_TRUE(router_.shouldSelectAnotherHost(host)); + EXPECT_TRUE(router_->shouldSelectAnotherHost(host)); // Normal response. - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->health_checker_, setUnhealthy(_)) .Times(0); Http::ResponseHeaderMapPtr response_headers2( @@ -4171,7 +4187,7 @@ TEST_F(RouterTest, InternalRedirectAcceptedWithRequestBody) { Buffer::InstancePtr body_data(new Buffer::OwnedImpl("random_fake_data")); EXPECT_CALL(callbacks_, addDecodedData(_, true)); - EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_.decodeData(*body_data, true)); + EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_->decodeData(*body_data, true)); EXPECT_CALL(callbacks_.downstream_callbacks_, clearRouteCache()); EXPECT_CALL(callbacks_, recreateStream(_)).WillOnce(Return(true)); @@ -4187,7 +4203,7 @@ TEST_F(RouterTest, InternalRedirectAcceptedWithRequestBody) { .value()); // In production, the HCM recreateStream would have called this. - router_.onDestroy(); + router_->onDestroy(); EXPECT_EQ(1, callbacks_.streamInfo() .filterState() ->getDataMutable("num_internal_redirects") @@ -4250,7 +4266,7 @@ TEST_F(RouterTest, HttpInternalRedirectSucceeded) { .value()); // In production, the HCM recreateStream would have called this. - router_.onDestroy(); + router_->onDestroy(); EXPECT_EQ(3, callbacks_.streamInfo() .filterState() ->getDataMutable("num_internal_redirects") @@ -4279,7 +4295,7 @@ TEST_F(RouterTest, HttpInternalRedirectMatchedToDirectResponseSucceeded) { .value()); // In production, the HCM recreateStream would have called this. - router_.onDestroy(); + router_->onDestroy(); EXPECT_EQ(1, callbacks_.streamInfo() .filterState() ->getDataMutable("num_internal_redirects") @@ -4301,7 +4317,7 @@ TEST_F(RouterTest, InternalRedirectStripsFragment) { .value()); // In production, the HCM recreateStream would have called this. - router_.onDestroy(); + router_->onDestroy(); EXPECT_EQ("/", default_request_headers_.getPathValue()); } @@ -4323,7 +4339,7 @@ TEST_F(RouterTest, InternalRedirectKeepsFragmentWithOveride) { .value()); // In production, the HCM recreateStream would have called this. - router_.onDestroy(); + router_->onDestroy(); EXPECT_EQ("/#fragment", default_request_headers_.getPathValue()); } @@ -4345,7 +4361,7 @@ TEST_F(RouterTest, HttpsInternalRedirectSucceeded) { .value()); // In production, the HCM recreateStream would have called this. - router_.onDestroy(); + router_->onDestroy(); } TEST_F(RouterTest, CrossSchemeRedirectAllowedByPolicy) { @@ -4368,7 +4384,7 @@ TEST_F(RouterTest, CrossSchemeRedirectAllowedByPolicy) { .value()); // In production, the HCM recreateStream would have called this. - router_.onDestroy(); + router_->onDestroy(); } namespace { @@ -4394,7 +4410,31 @@ makeShadowPolicy(std::string cluster = "", std::string cluster_header = "", } // namespace -TEST_F(RouterTest, ShadowWithClusterHeader) { +class RouterShadowingTest : public RouterTest, public testing::WithParamInterface { +public: + RouterShadowingTest() : streaming_shadow_(GetParam()) { + scoped_runtime_.mergeValues( + {{"envoy.reloadable_features.streaming_shadow", streaming_shadow_ ? "true" : "false"}}); + // Recreate router filter so it latches the correct value of streaming shadow. + router_ = std::make_unique(config_, config_.default_stats_); + router_->setDecoderFilterCallbacks(callbacks_); + router_->downstream_connection_.stream_info_.downstream_connection_info_provider_ + ->setLocalAddress(host_address_); + router_->downstream_connection_.stream_info_.downstream_connection_info_provider_ + ->setRemoteAddress(Network::Utility::parseInternetAddressAndPort("1.2.3.4:80")); + } + +protected: + bool streaming_shadow_; + TestScopedRuntime scoped_runtime_; +}; + +INSTANTIATE_TEST_SUITE_P(StreamingShadow, RouterShadowingTest, testing::Bool()); + +TEST_P(RouterShadowingTest, BufferingShadowWithClusterHeader) { + if (streaming_shadow_) { + GTEST_SKIP(); + } ShadowPolicyPtr policy = makeShadowPolicy("", "some_header", "bar"); callbacks_.route_->route_entry_.shadow_policies_.push_back(policy); ON_CALL(callbacks_, streamId()).WillByDefault(Return(43)); @@ -4414,17 +4454,18 @@ TEST_F(RouterTest, ShadowWithClusterHeader) { HttpTestUtility::addDefaultHeaders(headers); headers.addCopy("some_header", "some_cluster"); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); Buffer::InstancePtr body_data(new Buffer::OwnedImpl("hello")); + EXPECT_CALL(callbacks_, addDecodedData(_, true)); - EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_.decodeData(*body_data, false)); + + EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_->decodeData(*body_data, false)); Http::TestRequestTrailerMapImpl trailers{{"some", "trailer"}}; EXPECT_CALL(callbacks_, decodingBuffer()) .Times(AtLeast(2)) .WillRepeatedly(Return(body_data.get())); - EXPECT_CALL(*shadow_writer_, shadow_("some_cluster", _, _)) .WillOnce(Invoke([](const std::string&, Http::RequestMessagePtr& request, const Http::AsyncClient::RequestOptions& options) -> void { @@ -4433,7 +4474,8 @@ TEST_F(RouterTest, ShadowWithClusterHeader) { EXPECT_EQ(absl::optional(10), options.timeout); EXPECT_TRUE(options.sampled_.value()); })); - router_.decodeTrailers(trailers); + + router_->decodeTrailers(trailers); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -4443,7 +4485,7 @@ TEST_F(RouterTest, ShadowWithClusterHeader) { EXPECT_TRUE(verifyHostUpstreamStats(1, 0)); } -TEST_F(RouterTest, ShadowNoClusterHeaderInHeader) { +TEST_P(RouterShadowingTest, ShadowNoClusterHeaderInHeader) { ShadowPolicyPtr policy = makeShadowPolicy("", "some_header", "bar"); callbacks_.route_->route_entry_.shadow_policies_.push_back(policy); ON_CALL(callbacks_, streamId()).WillByDefault(Return(43)); @@ -4461,14 +4503,17 @@ TEST_F(RouterTest, ShadowNoClusterHeaderInHeader) { expectResponseTimerCreate(); Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + EXPECT_CALL(*shadow_writer_, streamingShadow_(_, _, _)).Times(0); + router_->decodeHeaders(headers, false); Buffer::InstancePtr body_data(new Buffer::OwnedImpl("hello")); - EXPECT_CALL(callbacks_, addDecodedData(_, true)); - EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_.decodeData(*body_data, false)); + if (!streaming_shadow_) { + EXPECT_CALL(callbacks_, addDecodedData(_, true)); + } + EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_->decodeData(*body_data, false)); Http::TestRequestTrailerMapImpl trailers{{"some", "trailer"}}; - router_.decodeTrailers(trailers); + router_->decodeTrailers(trailers); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -4480,7 +4525,7 @@ TEST_F(RouterTest, ShadowNoClusterHeaderInHeader) { EXPECT_TRUE(verifyHostUpstreamStats(1, 0)); } -TEST_F(RouterTest, ShadowClusterNameEmptyInHeader) { +TEST_P(RouterShadowingTest, ShadowClusterNameEmptyInHeader) { ShadowPolicyPtr policy = makeShadowPolicy("", "some_header", "bar"); callbacks_.route_->route_entry_.shadow_policies_.push_back(policy); ON_CALL(callbacks_, streamId()).WillByDefault(Return(43)); @@ -4499,14 +4544,17 @@ TEST_F(RouterTest, ShadowClusterNameEmptyInHeader) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); headers.addCopy("some_header", ""); - router_.decodeHeaders(headers, false); + EXPECT_CALL(*shadow_writer_, streamingShadow_(_, _, _)).Times(0); + router_->decodeHeaders(headers, false); Buffer::InstancePtr body_data(new Buffer::OwnedImpl("hello")); - EXPECT_CALL(callbacks_, addDecodedData(_, true)); - EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_.decodeData(*body_data, false)); + if (!streaming_shadow_) { + EXPECT_CALL(callbacks_, addDecodedData(_, true)); + } + EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_->decodeData(*body_data, false)); Http::TestRequestTrailerMapImpl trailers{{"some", "trailer"}}; - router_.decodeTrailers(trailers); + router_->decodeTrailers(trailers); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -4517,7 +4565,80 @@ TEST_F(RouterTest, ShadowClusterNameEmptyInHeader) { EXPECT_TRUE(verifyHostUpstreamStats(1, 0)); } -TEST_F(RouterTest, Shadow) { +TEST_P(RouterShadowingTest, StreamingShadow) { + if (!streaming_shadow_) { + GTEST_SKIP(); + } + ShadowPolicyPtr policy = makeShadowPolicy("foo", "", "bar"); + callbacks_.route_->route_entry_.shadow_policies_.push_back(policy); + policy = makeShadowPolicy("fizz", "", "buzz", envoy::type::v3::FractionalPercent(), false); + callbacks_.route_->route_entry_.shadow_policies_.push_back(policy); + ON_CALL(callbacks_, streamId()).WillByDefault(Return(43)); + + NiceMock encoder; + Http::ResponseDecoder* response_decoder = nullptr; + expectNewStreamWithImmediateEncoder(encoder, &response_decoder, Http::Protocol::Http10); + + expectResponseTimerCreate(); + + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("bar", testing::Matcher(Percent(0)), + 43)) + .WillOnce(Return(true)); + EXPECT_CALL( + runtime_.snapshot_, + featureEnabled("buzz", + testing::Matcher(Percent(0)), 43)) + .WillOnce(Return(true)); + + Http::TestRequestHeaderMapImpl headers; + HttpTestUtility::addDefaultHeaders(headers); + NiceMock foo_client; + NiceMock foo_request(&foo_client); + EXPECT_CALL(*shadow_writer_, streamingShadow_("foo", _, _)) + .WillOnce(Invoke([&](const std::string&, Http::RequestHeaderMapPtr&, + const Http::AsyncClient::RequestOptions& options) { + EXPECT_EQ(absl::optional(10), options.timeout); + EXPECT_TRUE(options.sampled_.value()); + return &foo_request; + })); + NiceMock fizz_client; + NiceMock fizz_request(&fizz_client); + EXPECT_CALL(*shadow_writer_, streamingShadow_("fizz", _, _)) + .Times(1) + .WillOnce(Invoke([&](const std::string&, Http::RequestHeaderMapPtr&, + const Http::AsyncClient::RequestOptions& options) { + EXPECT_EQ(absl::optional(10), options.timeout); + EXPECT_FALSE(options.sampled_.value()); + return &fizz_request; + })); + router_->decodeHeaders(headers, false); + + Buffer::InstancePtr body_data(new Buffer::OwnedImpl("hello")); + EXPECT_CALL(callbacks_, addDecodedData(_, _)).Times(0); + EXPECT_CALL(foo_request, sendData(BufferStringEqual("hello"), false)); + EXPECT_CALL(fizz_request, sendData(BufferStringEqual("hello"), false)); + EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_->decodeData(*body_data, false)); + + Http::TestRequestTrailerMapImpl trailers{{"some", "trailer"}}; + EXPECT_CALL(callbacks_, decodingBuffer()).Times(0); + EXPECT_CALL(foo_request, captureAndSendTrailers_(Http::HeaderValueOf("some", "trailer"))); + EXPECT_CALL(fizz_request, captureAndSendTrailers_(Http::HeaderValueOf("some", "trailer"))); + router_->decodeTrailers(trailers); + EXPECT_EQ(1U, + callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); + + Http::ResponseHeaderMapPtr response_headers( + new Http::TestResponseHeaderMapImpl{{":status", "200"}}); + response_decoder->decodeHeaders(std::move(response_headers), true); + EXPECT_TRUE(verifyHostUpstreamStats(1, 0)); +} + +TEST_P(RouterShadowingTest, BufferingShadow) { + if (streaming_shadow_) { + GTEST_SKIP(); + } ShadowPolicyPtr policy = makeShadowPolicy("foo", "", "bar"); callbacks_.route_->route_entry_.shadow_policies_.push_back(policy); policy = makeShadowPolicy("fizz", "", "buzz", envoy::type::v3::FractionalPercent(), false); @@ -4543,11 +4664,11 @@ TEST_F(RouterTest, Shadow) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); Buffer::InstancePtr body_data(new Buffer::OwnedImpl("hello")); EXPECT_CALL(callbacks_, addDecodedData(_, true)); - EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_.decodeData(*body_data, false)); + EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_->decodeData(*body_data, false)); Http::TestRequestTrailerMapImpl trailers{{"some", "trailer"}}; EXPECT_CALL(callbacks_, decodingBuffer()) @@ -4569,7 +4690,7 @@ TEST_F(RouterTest, Shadow) { EXPECT_EQ(absl::optional(10), options.timeout); EXPECT_FALSE(options.sampled_.value()); })); - router_.decodeTrailers(trailers); + router_->decodeTrailers(trailers); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -4579,7 +4700,7 @@ TEST_F(RouterTest, Shadow) { EXPECT_TRUE(verifyHostUpstreamStats(1, 0)); } -TEST_F(RouterTest, NoShadowForConnect) { +TEST_P(RouterShadowingTest, NoShadowForConnect) { ShadowPolicyPtr policy = makeShadowPolicy("foo"); callbacks_.route_->route_entry_.shadow_policies_.push_back(policy); ON_CALL(callbacks_, streamId()).WillByDefault(Return(43)); @@ -4587,13 +4708,13 @@ TEST_F(RouterTest, NoShadowForConnect) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); headers.setMethod("CONNECT"); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); Buffer::InstancePtr body_data(new Buffer::OwnedImpl("hello")); EXPECT_CALL(callbacks_, addDecodedData(_, true)).Times(0); - EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_.decodeData(*body_data, false)); + EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, router_->decodeData(*body_data, false)); - router_.onDestroy(); + router_->onDestroy(); } TEST_F(RouterTest, AltStatName) { @@ -4609,7 +4730,7 @@ TEST_F(RouterTest, AltStatName) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-alt-stat-name", "alt_stat"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -4658,7 +4779,7 @@ TEST_F(RouterTest, Redirect) { EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), true)); Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_FALSE(callbacks_.stream_info_.attemptCount().has_value()); @@ -4682,7 +4803,7 @@ TEST_F(RouterTest, RedirectFound) { EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), true)); Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_FALSE(callbacks_.stream_info_.attemptCount().has_value()); @@ -4703,12 +4824,12 @@ TEST_F(RouterTest, DirectResponse) { EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), true)); Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_FALSE(callbacks_.stream_info_.attemptCount().has_value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); - EXPECT_EQ(1UL, router_.stats().rq_direct_response_.value()); + EXPECT_EQ(1UL, router_->stats().rq_direct_response_.value()); } TEST_F(RouterTest, DirectResponseWithBody) { @@ -4728,12 +4849,12 @@ TEST_F(RouterTest, DirectResponseWithBody) { EXPECT_CALL(callbacks_, encodeData(_, true)); Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_FALSE(callbacks_.stream_info_.attemptCount().has_value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); - EXPECT_EQ(1UL, router_.stats().rq_direct_response_.value()); + EXPECT_EQ(1UL, router_->stats().rq_direct_response_.value()); } TEST_F(RouterTest, DirectResponseWithLocation) { @@ -4752,12 +4873,12 @@ TEST_F(RouterTest, DirectResponseWithLocation) { EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), true)); Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_FALSE(callbacks_.stream_info_.attemptCount().has_value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); - EXPECT_EQ(1UL, router_.stats().rq_direct_response_.value()); + EXPECT_EQ(1UL, router_->stats().rq_direct_response_.value()); } TEST_F(RouterTest, DirectResponseWithoutLocation) { @@ -4775,12 +4896,12 @@ TEST_F(RouterTest, DirectResponseWithoutLocation) { EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), true)); Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_FALSE(callbacks_.stream_info_.attemptCount().has_value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); - EXPECT_EQ(1UL, router_.stats().rq_direct_response_.value()); + EXPECT_EQ(1UL, router_->stats().rq_direct_response_.value()); } // Verifies that we propagate the upstream connection filter state to the upstream and downstream @@ -4791,7 +4912,7 @@ TEST_F(RouterTest, PropagatesUpstreamFilterState) { // This pattern helps ensure that we're actually invoking the callback. bool filter_state_verified = false; - router_.config().upstream_logs_.push_back( + router_->config().upstream_logs_.push_back( std::make_shared([&](const auto& stream_info) { filter_state_verified = stream_info.upstreamInfo()->upstreamFilterState()->hasDataWithName("upstream data"); @@ -4805,7 +4926,7 @@ TEST_F(RouterTest, PropagatesUpstreamFilterState) { Http::TestRequestHeaderMapImpl headers{}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); @@ -4832,7 +4953,7 @@ TEST_F(RouterTest, UpstreamSSLConnection) { Http::TestRequestHeaderMapImpl headers{}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -4863,11 +4984,11 @@ TEST_F(RouterTest, UpstreamTimingSingleRequest) { Http::TestRequestHeaderMapImpl headers{}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); test_time_.advanceTimeWait(std::chrono::milliseconds(32)); Buffer::OwnedImpl data; - router_.decodeData(data, true); + router_->decodeData(data, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -4917,13 +5038,13 @@ TEST_F(RouterTest, UpstreamTimingRetry) { // Check that upstream timing is updated after the first request. Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); - router_.retry_state_->expectHeadersRetry(); + router_->retry_state_->expectHeadersRetry(); test_time_.advanceTimeWait(std::chrono::milliseconds(32)); Buffer::OwnedImpl data; - router_.decodeData(data, true); + router_->decodeData(data, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -4936,8 +5057,9 @@ TEST_F(RouterTest, UpstreamTimingRetry) { // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) response_decoder->decodeHeaders(std::move(bad_response_headers), true); - router_.retry_state_->callback_(); - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + router_->retry_state_->callback_(); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); MonotonicTime retry_time = test_time_.monotonicTime(); Http::ResponseHeaderMapPtr good_response_headers( @@ -4988,13 +5110,13 @@ TEST_F(RouterTest, UpstreamTimingTimeout) { // Check that upstream timing is updated after the first request. Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-rq-timeout-ms", "50"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); auto& upstream_timing = stream_info.upstreamInfo()->upstreamTiming(); EXPECT_FALSE(upstream_timing.last_upstream_rx_byte_received_.has_value()); test_time_.advanceTimeWait(std::chrono::milliseconds(13)); Buffer::OwnedImpl data; - router_.decodeData(data, true); + router_->decodeData(data, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -5600,7 +5722,7 @@ TEST_F(RouterTest, CanaryStatusTrue) { const absl::optional virtual_cluster_name = absl::optional("fake_virtual_cluster"); EXPECT_CALL(callbacks_.stream_info_, setVirtualClusterName(virtual_cluster_name)); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -5632,7 +5754,7 @@ TEST_F(RouterTest, CanaryStatusFalse) { const absl::optional virtual_cluster_name = absl::optional("fake_virtual_cluster"); EXPECT_CALL(callbacks_.stream_info_, setVirtualClusterName(virtual_cluster_name)); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -5679,10 +5801,10 @@ TEST_F(RouterTest, AutoHostRewriteEnabled) { .WillOnce(InvokeWithoutArgs([] {})); EXPECT_CALL(callbacks_.route_->route_entry_, autoHostRewrite()).WillOnce(Return(true)); EXPECT_CALL(callbacks_.route_->route_entry_, appendXfh()).WillOnce(Return(true)); - router_.decodeHeaders(incoming_headers, true); + router_->decodeHeaders(incoming_headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); - router_.onDestroy(); + router_->onDestroy(); } TEST_F(RouterTest, AutoHostRewriteDisabled) { @@ -5709,14 +5831,14 @@ TEST_F(RouterTest, AutoHostRewriteDisabled) { })); EXPECT_CALL(callbacks_.route_->route_entry_, autoHostRewrite()).WillOnce(Return(false)); - router_.decodeHeaders(incoming_headers, true); + router_->decodeHeaders(incoming_headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); - router_.onDestroy(); + router_->onDestroy(); } TEST_F(RouterTest, UpstreamSocketOptionsReturnedEmpty) { - auto options = router_.upstreamSocketOptions(); + auto options = router_->upstreamSocketOptions(); EXPECT_EQ(options.get(), nullptr); } @@ -5730,9 +5852,9 @@ TEST_F(RouterTest, IpTransparentOptions) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); headers.setMethod("CONNECT"); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); - auto options = router_.upstreamSocketOptions(); + auto options = router_->upstreamSocketOptions(); EXPECT_EQ(expected_options->size(), options->size()); for (size_t i = 0; i < 2; i++) { @@ -5742,7 +5864,7 @@ TEST_F(RouterTest, IpTransparentOptions) { auto returned_details = options->at(i)->getOptionDetails(dummy_socket, state); EXPECT_TRUE(expected_details == returned_details); } - router_.onDestroy(); + router_->onDestroy(); } TEST_F(RouterTest, RedirectRecords) { @@ -5750,22 +5872,22 @@ TEST_F(RouterTest, RedirectRecords) { memcpy(redirect_records->buf_, reinterpret_cast(redirect_records_data_.data()), redirect_records_data_.size()); redirect_records->buf_size_ = redirect_records_data_.size(); - router_.downstream_connection_.stream_info_.filterState()->setData( + router_->downstream_connection_.stream_info_.filterState()->setData( Network::UpstreamSocketOptionsFilterState::key(), std::make_unique(), StreamInfo::FilterState::StateType::Mutable, StreamInfo::FilterState::LifeSpan::Connection); - router_.downstream_connection_.stream_info_.filterState() + router_->downstream_connection_.stream_info_.filterState() ->getDataMutable( Network::UpstreamSocketOptionsFilterState::key()) ->addOption(Network::SocketOptionFactory::buildWFPRedirectRecordsOptions(*redirect_records)); Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); headers.setMethod("CONNECT"); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); Network::Socket::OptionsSharedPtr expected_options = Network::SocketOptionFactory::buildWFPRedirectRecordsOptions(*redirect_records); - auto options = router_.upstreamSocketOptions(); + auto options = router_->upstreamSocketOptions(); EXPECT_EQ(1, options->size()); NiceMock dummy_socket; @@ -5773,7 +5895,7 @@ TEST_F(RouterTest, RedirectRecords) { auto expected_details = expected_options->at(0)->getOptionDetails(dummy_socket, state); auto returned_details = options->at(0)->getOptionDetails(dummy_socket, state); EXPECT_TRUE(expected_details == returned_details); - router_.onDestroy(); + router_->onDestroy(); } TEST_F(RouterTest, ApplicationProtocols) { @@ -5801,11 +5923,11 @@ TEST_F(RouterTest, ApplicationProtocols) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); // When the router filter gets reset we should cancel the pool request. EXPECT_CALL(cancellable_, cancel(_)); - router_.onDestroy(); + router_->onDestroy(); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -5825,12 +5947,12 @@ TEST_F(RouterTest, ConnectPauseAndResume) { HttpTestUtility::addDefaultHeaders(headers); headers.setMethod("CONNECT"); headers.removePath(); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); // Make sure any early data does not go upstream. EXPECT_CALL(encoder, encodeData(_, _)).Times(0); Buffer::OwnedImpl data; - router_.decodeData(data, true); + router_->decodeData(data, true); // Now send the response headers, and ensure the deferred payload is proxied. EXPECT_CALL(encoder, encodeData(_, _)); @@ -5845,12 +5967,11 @@ TEST_F(RouterTest, ConnectPauseAndResume) { TEST_F(RouterTest, InvalidUpstream) { // Explicitly configure an HTTP upstream, to test factory creation. cm_.thread_local_cluster_.cluster_.info_->upstream_config_ = - absl::make_optional(); + std::make_unique(); // Configure a TCP upstream rather than an HTTP upstream. envoy::extensions::upstreams::tcp::generic::v3::GenericConnectionPoolProto generic_config; - cm_.thread_local_cluster_.cluster_.info_->upstream_config_.value() - .mutable_typed_config() - ->PackFrom(generic_config); + cm_.thread_local_cluster_.cluster_.info_->upstream_config_->mutable_typed_config()->PackFrom( + generic_config); NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; @@ -5868,20 +5989,20 @@ TEST_F(RouterTest, InvalidUpstream) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); headers.setMethod("CONNECT"); - EXPECT_ENVOY_BUG(router_.decodeHeaders(headers, false), "envoy bug failure: factory != nullptr."); + EXPECT_ENVOY_BUG(router_->decodeHeaders(headers, false), + "envoy bug failure: factory != nullptr."); - router_.onDestroy(); + router_->onDestroy(); } // Verify that CONNECT payload is not sent upstream if non-200 response headers are received. TEST_F(RouterTest, ConnectPauseNoResume) { // Explicitly configure an HTTP upstream, to test factory creation. cm_.thread_local_cluster_.cluster_.info_->upstream_config_ = - absl::make_optional(); + std::make_unique(); envoy::extensions::upstreams::http::http::v3::HttpConnectionPoolProto http_config; - cm_.thread_local_cluster_.cluster_.info_->upstream_config_.value() - .mutable_typed_config() - ->PackFrom(http_config); + cm_.thread_local_cluster_.cluster_.info_->upstream_config_->mutable_typed_config()->PackFrom( + http_config); NiceMock encoder; Http::ResponseDecoder* response_decoder = nullptr; @@ -5894,12 +6015,12 @@ TEST_F(RouterTest, ConnectPauseNoResume) { HttpTestUtility::addDefaultHeaders(headers); headers.setMethod("CONNECT"); headers.removePath(); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); // Make sure any early data does not go upstream. EXPECT_CALL(encoder, encodeData(_, _)).Times(0); Buffer::OwnedImpl data; - router_.decodeData(data, true); + router_->decodeData(data, true); // Now send the response headers, and ensure the deferred payload is not proxied. EXPECT_CALL(encoder, encodeData(_, _)).Times(0); @@ -5911,11 +6032,10 @@ TEST_F(RouterTest, ConnectPauseNoResume) { TEST_F(RouterTest, ConnectExplicitTcpUpstream) { // Explicitly configure a TCP upstream, to test factory creation. cm_.thread_local_cluster_.cluster_.info_->upstream_config_ = - absl::make_optional(); + std::make_unique(); envoy::extensions::upstreams::http::tcp::v3::TcpConnectionPoolProto tcp_config; - cm_.thread_local_cluster_.cluster_.info_->upstream_config_.value() - .mutable_typed_config() - ->PackFrom(tcp_config); + cm_.thread_local_cluster_.cluster_.info_->upstream_config_->mutable_typed_config()->PackFrom( + tcp_config); callbacks_.route_->route_entry_.connect_config_ = absl::make_optional(); @@ -5925,19 +6045,18 @@ TEST_F(RouterTest, ConnectExplicitTcpUpstream) { HttpTestUtility::addDefaultHeaders(headers); headers.setMethod("CONNECT"); headers.removePath(); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); - router_.onDestroy(); + router_->onDestroy(); } TEST_F(RouterTest, PostExplicitTcpUpstream) { // Explicitly configure a generic upstream, to test factory creation. cm_.thread_local_cluster_.cluster_.info_->upstream_config_ = - absl::make_optional(); + std::make_unique(); envoy::extensions::upstreams::http::generic::v3::GenericConnectionPoolProto generic_config; - cm_.thread_local_cluster_.cluster_.info_->upstream_config_.value() - .mutable_typed_config() - ->PackFrom(generic_config); + cm_.thread_local_cluster_.cluster_.info_->upstream_config_->mutable_typed_config()->PackFrom( + generic_config); callbacks_.route_->route_entry_.connect_config_ = absl::make_optional(); callbacks_.route_->route_entry_.connect_config_.value().set_allow_post(true); @@ -5947,19 +6066,18 @@ TEST_F(RouterTest, PostExplicitTcpUpstream) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); headers.setMethod("POST"); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); - router_.onDestroy(); + router_->onDestroy(); } TEST_F(RouterTest, PostHttpUpstream) { // Explicitly configure a generic upstream, to test factory creation. cm_.thread_local_cluster_.cluster_.info_->upstream_config_ = - absl::make_optional(); + std::make_unique(); envoy::extensions::upstreams::http::generic::v3::GenericConnectionPoolProto generic_config; - cm_.thread_local_cluster_.cluster_.info_->upstream_config_.value() - .mutable_typed_config() - ->PackFrom(generic_config); + cm_.thread_local_cluster_.cluster_.info_->upstream_config_->mutable_typed_config()->PackFrom( + generic_config); callbacks_.route_->route_entry_.connect_config_ = absl::make_optional(); @@ -5969,9 +6087,9 @@ TEST_F(RouterTest, PostHttpUpstream) { Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); headers.setMethod("POST"); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); - router_.onDestroy(); + router_->onDestroy(); } TEST_F(RouterTest, SetDynamicMaxStreamDuration) { @@ -5983,10 +6101,10 @@ TEST_F(RouterTest, SetDynamicMaxStreamDuration) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-stream-duration-ms", "500"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); max_stream_duration_timer_->invokeCallback(); - router_.onDestroy(); + router_->onDestroy(); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); } @@ -5999,9 +6117,9 @@ TEST_F(RouterTest, NotSetDynamicMaxStreamDurationIfZero) { Http::TestRequestHeaderMapImpl headers{{"x-envoy-upstream-stream-duration-ms", "0"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, false); + router_->decodeHeaders(headers, false); - router_.onDestroy(); + router_->onDestroy(); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); } @@ -6039,7 +6157,7 @@ TEST_F(RouterTest, ExpectedUpstreamTimeoutUpdatedDuringRetries) { {"x-envoy-internal", "true"}, {"x-envoy-upstream-rq-timeout-ms", "200"}}; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -6050,7 +6168,7 @@ TEST_F(RouterTest, ExpectedUpstreamTimeoutUpdatedDuringRetries) { EXPECT_EQ(200, atoi(std::string(headers.getEnvoyExpectedRequestTimeoutMsValue()).c_str())); // 5xx response. - router_.retry_state_->expectHeadersRetry(); + router_->retry_state_->expectHeadersRetry(); Upstream::RetryOptionsPredicate::UpdateOptionsReturn update_options_return{ std::make_shared()}; EXPECT_CALL(*retry_options_predicate, updateOptions(_)).WillOnce(Return(update_options_return)); @@ -6064,14 +6182,14 @@ TEST_F(RouterTest, ExpectedUpstreamTimeoutUpdatedDuringRetries) { // Verify retry options predicate return values have been updated. EXPECT_EQ(update_options_return.new_upstream_socket_options_.value(), - router_.upstreamSocketOptions()); + router_->upstreamSocketOptions()); // We expect the 5xx response to kick off a new request. EXPECT_CALL(encoder1.stream_, resetStream(_)).Times(0); NiceMock encoder2; expectNewStreamWithImmediateEncoder(encoder2, &response_decoder, Http::Protocol::Http10); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); EXPECT_EQ(2U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -6081,7 +6199,8 @@ TEST_F(RouterTest, ExpectedUpstreamTimeoutUpdatedDuringRetries) { EXPECT_EQ(150, atoi(std::string(headers.getEnvoyExpectedRequestTimeoutMsValue()).c_str())); // Normal response. - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_.host_->health_checker_, setUnhealthy(_)) .Times(0); Http::ResponseHeaderMapPtr response_headers2( @@ -6195,7 +6314,7 @@ TEST_F(RouterTest, HasEarlyDataAndRetryUpon425) { // This is a GET request. HttpTestUtility::addDefaultHeaders(headers); - router_.retry_425_response_ = true; + router_->retry_425_response_ = true; NiceMock encoder1; Http::ResponseDecoder* response_decoder1 = nullptr; EXPECT_CALL(cm_.thread_local_cluster_.conn_pool_, newStream(_, _, _)) @@ -6212,14 +6331,14 @@ TEST_F(RouterTest, HasEarlyDataAndRetryUpon425) { })); expectResponseTimerCreate(); EXPECT_CALL(encoder1, encodeHeaders(_, _)); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); Http::ResponseHeaderMapPtr response_headers1( new Http::TestResponseHeaderMapImpl{{":status", "425"}}); - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, HeaderMapEqualRef(&headers), _)) + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, HeaderMapEqualRef(&headers), _)) .WillOnce(Invoke([this](const Http::ResponseHeaderMap&, const Http::RequestHeaderMap&, RetryState::DoRetryHeaderCallback callback) { - router_.retry_state_->callback_ = [callback]() { callback(/*disable_early_data=*/true); }; + router_->retry_state_->callback_ = [callback]() { callback(/*disable_early_data=*/true); }; return RetryStatus::Yes; })); ASSERT(response_decoder1); @@ -6242,11 +6361,12 @@ TEST_F(RouterTest, HasEarlyDataAndRetryUpon425) { return nullptr; })); EXPECT_CALL(encoder2, encodeHeaders(HeaderMapEqualRef(&headers), _)); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); Http::ResponseHeaderMapPtr response_headers2( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); EXPECT_CALL(callbacks_, encodeHeaders_(_, _)); response_decoder2->decodeHeaders(std::move(response_headers2), true); EXPECT_TRUE(verifyHostUpstreamStats(1, 1)); @@ -6274,17 +6394,17 @@ TEST_F(RouterTest, RequestWithUpstreamOverrideHost) { EXPECT_CALL(callbacks_, upstreamOverrideHost()) .WillOnce(Return(absl::make_optional("1.2.3.4"))); - auto override_host = router_.overrideHostToSelect(); + auto override_host = router_->overrideHostToSelect(); EXPECT_EQ("1.2.3.4", override_host.value()); Http::TestRequestHeaderMapImpl headers{{"x-envoy-retry-on", "5xx"}, {"x-envoy-internal", "true"}}; HttpTestUtility::addDefaultHeaders(headers); // Simulate the normal first request. - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); // Mock response with status 503. - router_.retry_state_->expectHeadersRetry(); + router_->retry_state_->expectHeadersRetry(); Http::ResponseHeaderMapPtr response_headers_503( new Http::TestResponseHeaderMapImpl{{":status", "503"}}); ASSERT(response_decoder != nullptr); @@ -6303,25 +6423,26 @@ TEST_F(RouterTest, RequestWithUpstreamOverrideHost) { upstream_stream_info_, Http::Protocol::Http10); return nullptr; })); - router_.retry_state_->callback_(); + router_->retry_state_->callback_(); // Simulate the load balancer to call the `overrideHostToSelect` again. The upstream override host // will be ignored when the request is retried. EXPECT_CALL(callbacks_, upstreamOverrideHost()).Times(0); - EXPECT_EQ(absl::nullopt, router_.overrideHostToSelect()); + EXPECT_EQ(absl::nullopt, router_->overrideHostToSelect()); // Normal response. Http::ResponseHeaderMapPtr response_headers_200( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); - EXPECT_CALL(*router_.retry_state_, shouldRetryHeaders(_, _, _)).WillOnce(Return(RetryStatus::No)); + EXPECT_CALL(*router_->retry_state_, shouldRetryHeaders(_, _, _)) + .WillOnce(Return(RetryStatus::No)); ASSERT(response_decoder != nullptr); // NOLINTNEXTLINE: Silence null pointer access warning response_decoder->decodeHeaders(std::move(response_headers_200), true); EXPECT_EQ(2, callbacks_.stream_info_.attemptCount().value()); - router_.onDestroy(); + router_->onDestroy(); } } // namespace Router diff --git a/test/common/router/router_test_base.cc b/test/common/router/router_test_base.cc index c69a906abf99..c1d3f236aad7 100644 --- a/test/common/router/router_test_base.cc +++ b/test/common/router/router_test_base.cc @@ -19,17 +19,17 @@ RouterTestBase::RouterTestBase(bool start_child_span, bool suppress_envoy_header ShadowWriterPtr{shadow_writer_}, true, start_child_span, suppress_envoy_headers, false, suppress_grpc_request_failure_code_stats, std::move(strict_headers_to_check), test_time_.timeSystem(), http_context_, router_context_), - router_(config_, config_.default_stats_) { - router_.setDecoderFilterCallbacks(callbacks_); + router_(std::make_unique(config_, config_.default_stats_)) { + router_->setDecoderFilterCallbacks(callbacks_); upstream_locality_.set_zone("to_az"); cm_.initializeThreadLocalClusters({"fake_cluster"}); ON_CALL(*cm_.thread_local_cluster_.conn_pool_.host_, address()) .WillByDefault(Return(host_address_)); ON_CALL(*cm_.thread_local_cluster_.conn_pool_.host_, locality()) .WillByDefault(ReturnRef(upstream_locality_)); - router_.downstream_connection_.stream_info_.downstream_connection_info_provider_->setLocalAddress( - host_address_); - router_.downstream_connection_.stream_info_.downstream_connection_info_provider_ + router_->downstream_connection_.stream_info_.downstream_connection_info_provider_ + ->setLocalAddress(host_address_); + router_->downstream_connection_.stream_info_.downstream_connection_info_provider_ ->setRemoteAddress(Network::Utility::parseInternetAddressAndPort("1.2.3.4:80")); // Make the "system time" non-zero, because 0 is considered invalid by DateUtil. @@ -140,11 +140,11 @@ void RouterTestBase::verifyMetadataMatchCriteriaFromRequest(bool route_entry_has Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); // When the router filter gets reset we should cancel the pool request. EXPECT_CALL(cancellable_, cancel(_)); - router_.onDestroy(); + router_->onDestroy(); } void RouterTestBase::verifyAttemptCountInRequestBasic(bool set_include_attempt_count_in_request, @@ -161,14 +161,14 @@ void RouterTestBase::verifyAttemptCountInRequestBasic(bool set_include_attempt_c if (preset_count) { headers.setEnvoyAttemptCount(preset_count.value()); } - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(expected_count, atoi(std::string(headers.getEnvoyAttemptCountValue()).c_str())); EXPECT_EQ(1U, callbacks_.stream_info_.attemptCount().value()); // When the router filter gets reset we should cancel the pool request. EXPECT_CALL(cancellable_, cancel(_)); - router_.onDestroy(); + router_->onDestroy(); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -188,7 +188,7 @@ void RouterTestBase::verifyAttemptCountInResponseBasic(bool set_include_attempt_ Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); Http::ResponseHeaderMapPtr response_headers( new Http::TestResponseHeaderMapImpl{{":status", "200"}}); @@ -217,7 +217,7 @@ void RouterTestBase::sendRequest(bool end_stream) { Http::Protocol::Http10); HttpTestUtility::addDefaultHeaders(default_request_headers_, false); - router_.decodeHeaders(default_request_headers_, end_stream); + router_->decodeHeaders(default_request_headers_, end_stream); } void RouterTestBase::enableRedirects(uint32_t max_internal_redirects) { @@ -289,7 +289,7 @@ void RouterTestBase::testAppendCluster(absl::optional clu Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -334,7 +334,7 @@ void RouterTestBase::testAppendUpstreamHost( Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(1U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); @@ -382,7 +382,7 @@ void RouterTestBase::testDoNotForward( Http::TestRequestHeaderMapImpl headers; HttpTestUtility::addDefaultHeaders(headers); - router_.decodeHeaders(headers, true); + router_->decodeHeaders(headers, true); EXPECT_EQ(0U, callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value()); EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); diff --git a/test/common/router/router_test_base.h b/test/common/router/router_test_base.h index 120a75eb2864..f143998c66c1 100644 --- a/test/common/router/router_test_base.h +++ b/test/common/router/router_test_base.h @@ -104,7 +104,7 @@ class RouterTestBase : public testing::Test { MockShadowWriter* shadow_writer_; NiceMock local_info_; FilterConfig config_; - RouterTestFilter router_; + std::unique_ptr router_; Event::MockTimer* response_timeout_{}; Event::MockTimer* per_try_timeout_{}; Event::MockTimer* per_try_idle_timeout_{}; diff --git a/test/common/router/upstream_request_test.cc b/test/common/router/upstream_request_test.cc index da05aeee445b..ab8b8772c556 100644 --- a/test/common/router/upstream_request_test.cc +++ b/test/common/router/upstream_request_test.cc @@ -187,6 +187,11 @@ TEST_F(UpstreamRequestTest, DumpsStateWithoutAllocatingMemory) { EXPECT_THAT(ostream.contents(), HasSubstr("request_headers: \n")); } +TEST_F(UpstreamRequestTest, TestSetStreamInfoFields) { + initialize(); + EXPECT_EQ(upstream_request_->streamInfo().route(), router_filter_interface_.callbacks_.route()); +} + } // namespace } // namespace Router } // namespace Envoy diff --git a/test/common/stats/stat_test_utility.cc b/test/common/stats/stat_test_utility.cc index 4a60e4a22934..b076a12840e4 100644 --- a/test/common/stats/stat_test_utility.cc +++ b/test/common/stats/stat_test_utility.cc @@ -183,11 +183,18 @@ Histogram& TestScope::histogramFromString(const std::string& leaf_name, Histogra return *histogram_ref; } -void TestScope::verifyConsistency(StatName ref_stat_name, StatName stat_name) { +std::string TestScope::statNameWithTags(const StatName& stat_name, + StatNameTagVectorOptConstRef tags) { + TagUtility::TagStatNameJoiner joiner(prefix(), stat_name, tags, symbolTable()); + return symbolTable().toString(joiner.nameWithTags()); +} + +void TestScope::verifyConsistency(StatName ref_stat_name, StatName stat_name, + StatNameTagVectorOptConstRef tags) { // Ensures StatNames with the same string representation are specified // consistently using symbolic/dynamic components on every access. - SymbolTable::StoragePtr joined = symbolTable().join({prefix(), stat_name}); - StatName joined_stat_name(joined.get()); + TagUtility::TagStatNameJoiner joiner(prefix(), stat_name, tags, symbolTable()); + StatName joined_stat_name = joiner.nameWithTags(); ASSERT(ref_stat_name == joined_stat_name, absl::StrCat("Inconsistent dynamic vs symbolic stat name specification: ref_stat_name=", symbolTable().toString(ref_stat_name), @@ -196,12 +203,12 @@ void TestScope::verifyConsistency(StatName ref_stat_name, StatName stat_name) { Counter& TestScope::counterFromStatNameWithTags(const StatName& stat_name, StatNameTagVectorOptConstRef tags) { - std::string name = prefix_str_ + symbolTable().toString(stat_name); + std::string name = statNameWithTags(stat_name, tags); Counter*& counter_ref = store_.counter_map_[name]; if (counter_ref == nullptr) { counter_ref = &IsolatedScopeImpl::counterFromStatNameWithTags(stat_name, tags); } else { - verifyConsistency(counter_ref->statName(), stat_name); + verifyConsistency(counter_ref->statName(), stat_name, tags); } return *counter_ref; } @@ -209,12 +216,12 @@ Counter& TestScope::counterFromStatNameWithTags(const StatName& stat_name, Gauge& TestScope::gaugeFromStatNameWithTags(const StatName& stat_name, StatNameTagVectorOptConstRef tags, Gauge::ImportMode import_mode) { - std::string name = prefix_str_ + symbolTable().toString(stat_name); + std::string name = statNameWithTags(stat_name, tags); Gauge*& gauge_ref = store_.gauge_map_[name]; if (gauge_ref == nullptr) { gauge_ref = &IsolatedScopeImpl::gaugeFromStatNameWithTags(stat_name, tags, import_mode); } else { - verifyConsistency(gauge_ref->statName(), stat_name); + verifyConsistency(gauge_ref->statName(), stat_name, tags); } return *gauge_ref; } @@ -222,12 +229,12 @@ Gauge& TestScope::gaugeFromStatNameWithTags(const StatName& stat_name, Histogram& TestScope::histogramFromStatNameWithTags(const StatName& stat_name, StatNameTagVectorOptConstRef tags, Histogram::Unit unit) { - std::string name = prefix_str_ + symbolTable().toString(stat_name); + std::string name = statNameWithTags(stat_name, tags); Histogram*& histogram_ref = store_.histogram_map_[name]; if (histogram_ref == nullptr) { histogram_ref = &IsolatedScopeImpl::histogramFromStatNameWithTags(stat_name, tags, unit); } else { - verifyConsistency(histogram_ref->statName(), stat_name); + verifyConsistency(histogram_ref->statName(), stat_name, tags); } return *histogram_ref; } @@ -289,7 +296,7 @@ std::vector serializeDeserializeNumber(uint64_t number) { block_size, " num_bytes=", num_bytes)); absl::Span span = mem_block.span(); RELEASE_ASSERT(number == SymbolTableImpl::Encoding::decodeNumber(span.data()).first, ""); - return std::vector(span.data(), span.data() + span.size()); + return {span.data(), span.data() + span.size()}; } void serializeDeserializeString(absl::string_view in) { diff --git a/test/common/stats/stat_test_utility.h b/test/common/stats/stat_test_utility.h index 05fb2fcbb773..830f70677ad1 100644 --- a/test/common/stats/stat_test_utility.h +++ b/test/common/stats/stat_test_utility.h @@ -183,6 +183,7 @@ class TestScope : public IsolatedScopeImpl { const TestStore& constStore() const override { return store_; } private: + std::string statNameWithTags(const StatName& stat_name, StatNameTagVectorOptConstRef tags); static std::string addDot(const std::string& prefix) { if (prefix.empty() || prefix[prefix.size() - 1] == '.') { return prefix; @@ -190,7 +191,8 @@ class TestScope : public IsolatedScopeImpl { return prefix + "."; } - void verifyConsistency(StatName ref_stat_name, StatName stat_name); + void verifyConsistency(StatName ref_stat_name, StatName stat_name, + StatNameTagVectorOptConstRef tags); TestStore& store_; const std::string prefix_str_; diff --git a/test/common/stats/stat_test_utility_test.cc b/test/common/stats/stat_test_utility_test.cc index 856714c80057..f11e4eea91e0 100644 --- a/test/common/stats/stat_test_utility_test.cc +++ b/test/common/stats/stat_test_utility_test.cc @@ -24,30 +24,52 @@ class StatTestUtilityTest : public testing::Test { TEST_F(StatTestUtilityTest, Counters) { test_scope_->counterFromStatName(dynamic_.add("dynamic.stat")).inc(); test_scope_->counterFromStatName(symbolic_.add("symbolic.stat")).inc(); - EXPECT_EQ(1, test_store_.counter("dynamic.stat").value()); + EXPECT_EQ(1, test_store_.findCounterByString("dynamic.stat").value().get().value()); EXPECT_FALSE(test_store_.findCounterByString("dynamic.stat2")); - EXPECT_EQ(1, test_store_.counter("symbolic.stat").value()); + EXPECT_EQ(1, test_store_.findCounterByString("symbolic.stat").value().get().value()); EXPECT_FALSE(test_store_.findCounterByString("symbolic.stat2")); } TEST_F(StatTestUtilityTest, Gauges) { - test_scope_->counterFromStatName(dynamic_.add("dynamic.stat")).inc(); - test_scope_->counterFromStatName(symbolic_.add("symbolic.stat")).inc(); - EXPECT_EQ(1, test_store_.counter("dynamic.stat").value()); + test_scope_->gaugeFromStatName(dynamic_.add("dynamic.stat"), Gauge::ImportMode::Accumulate).inc(); + test_scope_->gaugeFromStatName(symbolic_.add("symbolic.stat"), Gauge::ImportMode::Accumulate) + .inc(); + EXPECT_EQ(1, test_store_.findGaugeByString("dynamic.stat").value().get().value()); EXPECT_FALSE(test_store_.findGaugeByString("dynamic.stat2")); - EXPECT_EQ(1, test_store_.counter("symbolic.stat").value()); + EXPECT_EQ(1, test_store_.findGaugeByString("symbolic.stat").value().get().value()); EXPECT_FALSE(test_store_.findGaugeByString("symbolic.stat2")); } TEST_F(StatTestUtilityTest, Histograms) { - test_scope_->counterFromStatName(dynamic_.add("dynamic.stat")).inc(); - test_scope_->counterFromStatName(symbolic_.add("symbolic.stat")).inc(); - EXPECT_EQ(1, test_store_.counter("dynamic.stat").value()); + test_scope_->histogramFromStatName(dynamic_.add("dynamic.stat"), Histogram::Unit::Milliseconds) + .recordValue(1); + test_scope_->histogramFromStatName(symbolic_.add("symbolic.stat"), Histogram::Unit::Milliseconds) + .recordValue(1); + EXPECT_EQ(Histogram::Unit::Milliseconds, + test_store_.findHistogramByString("dynamic.stat").value().get().unit()); EXPECT_FALSE(test_store_.findHistogramByString("dynamic.stat2")); - EXPECT_EQ(1, test_store_.counter("symbolic.stat").value()); + EXPECT_EQ(Histogram::Unit::Milliseconds, + test_store_.findHistogramByString("symbolic.stat").value().get().unit()); EXPECT_FALSE(test_store_.findHistogramByString("symbolic.stat2")); } +TEST_F(StatTestUtilityTest, CountersWithTags) { + StatNameTagVector dynamic_tags = { + {dynamic_.add("dynamic_tag_name"), dynamic_.add("dynamic_tag_value")}}; + test_scope_->counterFromStatNameWithTags(dynamic_.add("dynamic.stat"), dynamic_tags).inc(); + StatNameTagVector symbolic_tags = { + {symbolic_.add("symbolic_tag_name"), symbolic_.add("symbolic_tag_value")}}; + test_scope_->counterFromStatNameWithTags(symbolic_.add("symbolic.stat"), symbolic_tags).inc(); + EXPECT_EQ(1, test_store_.findCounterByString("dynamic.stat.dynamic_tag_name.dynamic_tag_value") + .value() + .get() + .value()); + EXPECT_EQ(1, test_store_.findCounterByString("symbolic.stat.symbolic_tag_name.symbolic_tag_value") + .value() + .get() + .value()); +} + } // namespace } // namespace Stats } // namespace Envoy diff --git a/test/common/stream_info/stream_info_impl_test.cc b/test/common/stream_info/stream_info_impl_test.cc index 87fd10117d7b..e2c136528d6a 100644 --- a/test/common/stream_info/stream_info_impl_test.cc +++ b/test/common/stream_info/stream_info_impl_test.cc @@ -6,6 +6,7 @@ #include "envoy/upstream/host_description.h" #include "source/common/common/fmt.h" +#include "source/common/network/address_impl.h" #include "source/common/protobuf/utility.h" #include "source/common/stream_info/stream_id_provider_impl.h" #include "source/common/stream_info/stream_info_impl.h" @@ -35,6 +36,13 @@ std::chrono::nanoseconds checkDuration(std::chrono::nanoseconds last, class StreamInfoImplTest : public testing::Test { protected: + void assertStreamInfoSize(StreamInfoImpl stream_info) { + ASSERT_TRUE(sizeof(stream_info) == 800 || sizeof(stream_info) == 816 || + sizeof(stream_info) == 840) + << "If adding fields to StreamInfoImpl, please check to see if you " + "need to add them to setFromForRecreateStream or setFrom! Current size " + << sizeof(stream_info); + } DangerousDeprecatedTestTime test_time_; }; @@ -84,6 +92,10 @@ TEST_F(StreamInfoImplTest, TimingTest) { info.downstreamTiming().onDownstreamHandshakeComplete(test_time_.timeSystem()); dur = checkDuration(dur, timing.downstreamHandshakeComplete()); + EXPECT_FALSE(timing.lastDownstreamAckReceived()); + info.downstreamTiming().onLastDownstreamAckReceived(test_time_.timeSystem()); + dur = checkDuration(dur, timing.lastDownstreamAckReceived()); + EXPECT_FALSE(info.requestComplete()); info.onRequestComplete(); dur = checkDuration(dur, info.requestComplete()); @@ -224,7 +236,7 @@ TEST_F(StreamInfoImplTest, MiscSettersAndGetters) { } } -TEST_F(StreamInfoImplTest, SetFrom) { +TEST_F(StreamInfoImplTest, SetFromForRecreateStream) { StreamInfoImpl s1(Http::Protocol::Http2, test_time_.timeSystem(), nullptr); s1.addBytesReceived(1); @@ -233,10 +245,7 @@ TEST_F(StreamInfoImplTest, SetFrom) { #ifdef __clang__ #if defined(__linux__) #if defined(__has_feature) && !(__has_feature(thread_sanitizer)) - ASSERT_TRUE(sizeof(s1) == 784 || sizeof(s1) == 800 || sizeof(s1) == 808 || sizeof(s1) == 824) - << "If adding fields to StreamInfoImpl, please check to see if you " - "need to add them to setFromForRecreateStream! Current size " - << sizeof(s1); + assertStreamInfoSize(s1); #endif #endif #endif @@ -252,6 +261,90 @@ TEST_F(StreamInfoImplTest, SetFrom) { EXPECT_EQ(s1.getDownstreamBytesMeter(), s2.getDownstreamBytesMeter()); } +TEST_F(StreamInfoImplTest, SetFrom) { + StreamInfoImpl s1(Http::Protocol::Http2, test_time_.timeSystem(), nullptr); + + // setFromForRecreateStream + s1.addBytesReceived(1); + s1.downstreamTiming().onLastDownstreamRxByteReceived(test_time_.timeSystem()); + + // setFrom + s1.setRouteName("foo"); + s1.setVirtualClusterName(absl::optional("bar")); + s1.setResponseCode(200); + s1.setResponseCodeDetails("OK"); + s1.setConnectionTerminationDetails("baz"); + s1.setUpstreamInfo(std::make_shared()); + s1.upstreamInfo()->upstreamTiming().onLastUpstreamTxByteSent(test_time_.timeSystem()); + s1.onRequestComplete(); + s1.setResponseFlag(FailedLocalHealthCheck); + s1.healthCheck(true); + s1.route_ = std::make_shared>(); + s1.setDynamicMetadata("com.test", MessageUtil::keyValueStruct("test_key", "test_value")); + s1.filterState()->setData("test", std::make_unique(1), + FilterState::StateType::ReadOnly, FilterState::LifeSpan::FilterChain); + Http::TestRequestHeaderMapImpl headers1; + s1.setRequestHeaders(headers1); + Upstream::ClusterInfoConstSharedPtr cluster_info(new NiceMock()); + s1.setUpstreamClusterInfo(cluster_info); + s1.setStreamIdProvider( + std::make_shared("a121e9e1-feae-4136-9e0e-6fac343d56c9")); + s1.setTraceReason(Tracing::Reason::ClientForced); + s1.setFilterChainName("foobar"); + s1.setAttemptCount(5); + +#ifdef __clang__ +#if defined(__linux__) +#if defined(__has_feature) && !(__has_feature(thread_sanitizer)) + assertStreamInfoSize(s1); +#endif +#endif +#endif + + StreamInfoImpl s2(Http::Protocol::Http11, test_time_.timeSystem(), nullptr); + Http::TestRequestHeaderMapImpl headers2; + s2.setFrom(s1, &headers2); + + // Copied by setFromForRecreateStream + EXPECT_EQ(s1.startTime(), s2.startTime()); + EXPECT_EQ(s1.startTimeMonotonic(), s2.startTimeMonotonic()); + EXPECT_EQ(s1.downstreamTiming().lastDownstreamRxByteReceived(), + s2.downstreamTiming().lastDownstreamRxByteReceived()); + EXPECT_EQ(s1.protocol(), s2.protocol()); + EXPECT_EQ(s1.bytesReceived(), s2.bytesReceived()); + EXPECT_EQ(s1.getDownstreamBytesMeter(), s2.getDownstreamBytesMeter()); + + // Copied by setFrom + EXPECT_EQ(s1.getRouteName(), s2.getRouteName()); + EXPECT_EQ(s1.virtualClusterName(), s2.virtualClusterName()); + EXPECT_EQ(s1.responseCode(), s2.responseCode()); + EXPECT_EQ(s1.responseCodeDetails(), s2.responseCodeDetails()); + EXPECT_EQ(s1.connectionTerminationDetails(), s2.connectionTerminationDetails()); + EXPECT_EQ(s1.upstreamInfo()->upstreamTiming().last_upstream_tx_byte_sent_, + s2.upstreamInfo()->upstreamTiming().last_upstream_tx_byte_sent_); + EXPECT_EQ(s1.requestComplete(), s2.requestComplete()); + EXPECT_EQ(s1.responseFlags(), s2.responseFlags()); + EXPECT_EQ(s1.healthCheck(), s2.healthCheck()); + EXPECT_NE(s1.route(), nullptr); + EXPECT_EQ(s1.route(), s2.route()); + EXPECT_EQ( + Config::Metadata::metadataValue(&s1.dynamicMetadata(), "com.test", "test_key").string_value(), + Config::Metadata::metadataValue(&s2.dynamicMetadata(), "com.test", "test_key") + .string_value()); + EXPECT_EQ(s1.filterState()->getDataReadOnly("test")->access(), + s2.filterState()->getDataReadOnly("test")->access()); + EXPECT_EQ(*s1.getRequestHeaders(), headers1); + EXPECT_EQ(*s2.getRequestHeaders(), headers2); + EXPECT_TRUE(s2.upstreamClusterInfo().has_value()); + EXPECT_EQ(s1.upstreamClusterInfo(), s2.upstreamClusterInfo()); + EXPECT_EQ(s1.getStreamIdProvider().value().get().toStringView().value(), + s2.getStreamIdProvider().value().get().toStringView().value()); + EXPECT_EQ(s1.traceReason(), s2.traceReason()); + EXPECT_EQ(s1.filterChainName(), s2.filterChainName()); + EXPECT_EQ(s1.attemptCount(), s2.attemptCount()); + EXPECT_EQ(s1.getUpstreamBytesMeter(), s2.getUpstreamBytesMeter()); +} + TEST_F(StreamInfoImplTest, DynamicMetadataTest) { StreamInfoImpl stream_info(Http::Protocol::Http2, test_time_.timeSystem(), nullptr); diff --git a/test/common/tcp_proxy/tcp_proxy_test.cc b/test/common/tcp_proxy/tcp_proxy_test.cc index 79f0e9679331..05d5822d61dc 100644 --- a/test/common/tcp_proxy/tcp_proxy_test.cc +++ b/test/common/tcp_proxy/tcp_proxy_test.cc @@ -194,9 +194,9 @@ TEST_F(TcpProxyTest, HalfCloseProxy) { TEST_F(TcpProxyTest, ExplicitFactory) { // Explicitly configure an HTTP upstream, to test factory creation. auto& info = factory_context_.cluster_manager_.thread_local_cluster_.cluster_.info_; - info->upstream_config_ = absl::make_optional(); + info->upstream_config_ = std::make_unique(); envoy::extensions::upstreams::tcp::generic::v3::GenericConnectionPoolProto generic_config; - info->upstream_config_.value().mutable_typed_config()->PackFrom(generic_config); + info->upstream_config_->mutable_typed_config()->PackFrom(generic_config); setup(1); raiseEventUpstreamConnected(0); @@ -216,10 +216,10 @@ TEST_F(TcpProxyTest, ExplicitFactory) { // Test nothing bad happens if an invalid factory is configured. TEST_F(TcpProxyTest, BadFactory) { auto& info = factory_context_.cluster_manager_.thread_local_cluster_.cluster_.info_; - info->upstream_config_ = absl::make_optional(); + info->upstream_config_ = std::make_unique(); // The HTTP Generic connection pool is not a valid type for TCP upstreams. envoy::extensions::upstreams::http::generic::v3::GenericConnectionPoolProto generic_config; - info->upstream_config_.value().mutable_typed_config()->PackFrom(generic_config); + info->upstream_config_->mutable_typed_config()->PackFrom(generic_config); envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig(); @@ -367,7 +367,7 @@ TEST_F(TcpProxyTest, ConnectAttemptsLimit) { EXPECT_CALL(upstream_hosts_.at(2)->outlier_detector_, putResult(Upstream::Outlier::Result::LocalOriginConnectFailed, _)); - EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); // Try both failure modes raiseEventUpstreamConnectFailed(0, ConnectionPool::PoolFailureReason::Timeout); @@ -441,7 +441,7 @@ TEST_F(TcpProxyTest, DownstreamDisconnectRemote) { EXPECT_CALL(filter_callbacks_.connection_, write(BufferEqual(&response), _)); upstream_callbacks_->onUpstreamData(response, false); - EXPECT_CALL(*upstream_connections_.at(0), close(Network::ConnectionCloseType::FlushWrite)); + EXPECT_CALL(*upstream_connections_.at(0), close(Network::ConnectionCloseType::FlushWrite, _)); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); } @@ -458,14 +458,14 @@ TEST_F(TcpProxyTest, DownstreamDisconnectLocal) { EXPECT_CALL(filter_callbacks_.connection_, write(BufferEqual(&response), _)); upstream_callbacks_->onUpstreamData(response, false); - EXPECT_CALL(*upstream_connections_.at(0), close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(*upstream_connections_.at(0), close(Network::ConnectionCloseType::NoFlush, _)); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::LocalClose); } TEST_F(TcpProxyTest, UpstreamConnectTimeout) { setup(1, accessLogConfig("%RESPONSE_FLAGS%")); - EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); raiseEventUpstreamConnectFailed(0, ConnectionPool::PoolFailureReason::Timeout); filter_.reset(); @@ -484,7 +484,7 @@ TEST_F(TcpProxyTest, UpstreamClusterNotFound) { } TEST_F(TcpProxyTest, NoHost) { - EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); setup(0, accessLogConfig("%RESPONSE_FLAGS%")); EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onNewConnection()); filter_.reset(); @@ -736,7 +736,7 @@ TEST_F(TcpProxyTest, LocalClosedBeforeUpstreamConnected) { TEST_F(TcpProxyTest, UpstreamConnectFailure) { setup(1, accessLogConfig("%RESPONSE_FLAGS%")); - EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); raiseEventUpstreamConnectFailed(0, ConnectionPool::PoolFailureReason::RemoteConnectionFailure); filter_.reset(); @@ -751,7 +751,7 @@ TEST_F(TcpProxyTest, UpstreamConnectionLimit) { // setup sets up expectation for tcpConnForCluster but this test is expected to NOT call that filter_ = std::make_unique(config_, factory_context_.cluster_manager_); // The downstream connection closes if the proxy can't make an upstream connection. - EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); filter_->initializeReadFilterCallbacks(filter_callbacks_); filter_->onNewConnection(); @@ -784,8 +784,8 @@ TEST_F(TcpProxyTest, IdleTimeout) { EXPECT_CALL(*idle_timer, enableTimer(std::chrono::milliseconds(1000), _)); upstream_connections_.at(0)->raiseBytesSentCallbacks(2); - EXPECT_CALL(*upstream_connections_.at(0), close(Network::ConnectionCloseType::NoFlush)); - EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(*upstream_connections_.at(0), close(Network::ConnectionCloseType::NoFlush, _)); + EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); EXPECT_CALL(*idle_timer, disableTimer()); idle_timer->invokeCallback(); } @@ -858,11 +858,11 @@ TEST_F(TcpProxyTest, IdleTimeoutWithOutstandingDataFlushed) { // In this case we expect readDisable to not be called on the already closed // connection. EXPECT_CALL(filter_callbacks_.connection_, readDisable(true)).Times(0); - EXPECT_CALL(*upstream_connections_.at(0), close(Network::ConnectionCloseType::NoFlush)) + EXPECT_CALL(*upstream_connections_.at(0), close(Network::ConnectionCloseType::NoFlush, _)) .WillOnce(InvokeWithoutArgs( [&]() -> void { upstream_connections_.at(0)->runLowWatermarkCallbacks(); })); - EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); EXPECT_CALL(*idle_timer, disableTimer()); idle_timer->invokeCallback(); } @@ -1000,8 +1000,7 @@ TEST_F(TcpProxyTest, UpstreamFlushNoTimeout) { setup(1); raiseEventUpstreamConnected(0); - EXPECT_CALL(*upstream_connections_.at(0), - close(Network::ConnectionCloseType::FlushWrite)) + EXPECT_CALL(*upstream_connections_.at(0), close(Network::ConnectionCloseType::FlushWrite, _)) .WillOnce(Return()); // Cancel default action of raising LocalClose EXPECT_CALL(*upstream_connections_.at(0), state()) .WillOnce(Return(Network::Connection::State::Closing)); @@ -1031,8 +1030,7 @@ TEST_F(TcpProxyTest, UpstreamFlushTimeoutConfigured) { EXPECT_CALL(*idle_timer, enableTimer(_, _)); raiseEventUpstreamConnected(0); - EXPECT_CALL(*upstream_connections_.at(0), - close(Network::ConnectionCloseType::FlushWrite)) + EXPECT_CALL(*upstream_connections_.at(0), close(Network::ConnectionCloseType::FlushWrite, _)) .WillOnce(Return()); // Cancel default action of raising LocalClose EXPECT_CALL(*upstream_connections_.at(0), state()) .WillOnce(Return(Network::Connection::State::Closing)); @@ -1063,8 +1061,7 @@ TEST_F(TcpProxyTest, UpstreamFlushTimeoutExpired) { EXPECT_CALL(*idle_timer, enableTimer(_, _)); raiseEventUpstreamConnected(0); - EXPECT_CALL(*upstream_connections_.at(0), - close(Network::ConnectionCloseType::FlushWrite)) + EXPECT_CALL(*upstream_connections_.at(0), close(Network::ConnectionCloseType::FlushWrite, _)) .WillOnce(Return()); // Cancel default action of raising LocalClose EXPECT_CALL(*upstream_connections_.at(0), state()) .WillOnce(Return(Network::Connection::State::Closing)); @@ -1086,8 +1083,7 @@ TEST_F(TcpProxyTest, UpstreamFlushReceiveUpstreamData) { setup(1); raiseEventUpstreamConnected(0); - EXPECT_CALL(*upstream_connections_.at(0), - close(Network::ConnectionCloseType::FlushWrite)) + EXPECT_CALL(*upstream_connections_.at(0), close(Network::ConnectionCloseType::FlushWrite, _)) .WillOnce(Return()); // Cancel default action of raising LocalClose EXPECT_CALL(*upstream_connections_.at(0), state()) .WillOnce(Return(Network::Connection::State::Closing)); @@ -1233,7 +1229,7 @@ TEST_F(TcpProxyTest, OnDemandCallbackStickToTheSelectedCluster) { .counter("upstream_cx_connect_attempts_exceeded") .value()); - EXPECT_CALL(filter_callbacks_.connection_, close(_)); + EXPECT_CALL(filter_callbacks_.connection_, close(_, _)); std::invoke(*cluster_discovery_callback, Upstream::ClusterDiscoveryStatus::Missing); } @@ -1308,7 +1304,7 @@ TEST_F(TcpProxyTest, OdcdsBasicDownstreamLocalClose) { EXPECT_CALL(filter_callbacks_.connection_, write(BufferEqual(&response), _)); upstream_callbacks_->onUpstreamData(response, false); - EXPECT_CALL(*upstream_connections_.at(0), close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(*upstream_connections_.at(0), close(Network::ConnectionCloseType::NoFlush, _)); filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::LocalClose); } @@ -1331,7 +1327,7 @@ TEST_F(TcpProxyTest, OdcdsClusterMissingCauseConnectionClose) { setup(0, config); EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onNewConnection()); - EXPECT_CALL(filter_callbacks_.connection_, close(_)); + EXPECT_CALL(filter_callbacks_.connection_, close(_, _)); std::invoke(*cluster_discovery_callback, Upstream::ClusterDiscoveryStatus::Missing); } diff --git a/test/common/upstream/BUILD b/test/common/upstream/BUILD index ce7344cca6c4..6d5162d2d9ad 100644 --- a/test/common/upstream/BUILD +++ b/test/common/upstream/BUILD @@ -138,23 +138,6 @@ envoy_cc_test( deps = ["//source/common/upstream:scheduler_lib"], ) -envoy_cc_test( - name = "multiplexed_subscription_factory_test", - srcs = ["multiplexed_subscription_factory_test.cc"], - deps = [ - "//source/common/upstream:multiplexed_subscription_factory_lib", - "//test/mocks/api:api_mocks", - "//test/mocks/config:config_mocks", - "//test/mocks/config:custom_config_validators_mocks", - "//test/mocks/local_info:local_info_mocks", - "//test/mocks/protobuf:protobuf_mocks", - "//test/mocks/runtime:runtime_mocks", - "//test/mocks/server:server_mocks", - "//test/mocks/stats:stats_mocks", - "//test/mocks/upstream:upstream_mocks", - ], -) - envoy_cc_test_library( name = "health_check_fuzz_utils_lib", srcs = [ diff --git a/test/common/upstream/cluster_manager_impl_test.cc b/test/common/upstream/cluster_manager_impl_test.cc index 5696d59e1aa6..0a1f5065a23d 100644 --- a/test/common/upstream/cluster_manager_impl_test.cc +++ b/test/common/upstream/cluster_manager_impl_test.cc @@ -2320,7 +2320,7 @@ TEST_F(ClusterManagerImplTest, CloseTcpConnectionsOnHealthFailure) { outlier_detector.runCallbacks(test_host); health_checker.runCallbacks(test_host, HealthTransition::Unchanged); - EXPECT_CALL(*connection1, close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(*connection1, close(Network::ConnectionCloseType::NoFlush, _)); test_host->healthFlagSet(Host::HealthFlag::FAILED_OUTLIER_CHECK); outlier_detector.runCallbacks(test_host); @@ -2335,8 +2335,8 @@ TEST_F(ClusterManagerImplTest, CloseTcpConnectionsOnHealthFailure) { } // Order of these calls is implementation dependent, so can't sequence them! - EXPECT_CALL(*connection1, close(Network::ConnectionCloseType::NoFlush)); - EXPECT_CALL(*connection2, close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(*connection1, close(Network::ConnectionCloseType::NoFlush, _)); + EXPECT_CALL(*connection2, close(Network::ConnectionCloseType::NoFlush, _)); test_host->healthFlagSet(Host::HealthFlag::FAILED_ACTIVE_HC); health_checker.runCallbacks(test_host, HealthTransition::Changed); diff --git a/test/common/upstream/least_request_load_balancer_corpus/least_request-max-request-value b/test/common/upstream/least_request_load_balancer_corpus/least_request-max-request-value new file mode 100644 index 000000000000..39c30e83db51 --- /dev/null +++ b/test/common/upstream/least_request_load_balancer_corpus/least_request-max-request-value @@ -0,0 +1,24 @@ +zone_aware_load_balancer_test_case { + load_balancer_test_case { + common_lb_config { + } + setup_priority_levels { + num_hosts_in_priority_level: 96 + random_bytestring: 5 + } + seed_for_prng: 7 + } + random_bytestring_for_weights: "K" +} +least_request_lb_config { + slow_start_config { + slow_start_window { + nanos: 1048576 + } + aggression { + default_value: 2 + runtime_key: "(" + } + } +} +random_bytestring_for_requests: "\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201h" diff --git a/test/common/upstream/load_balancer_benchmark.cc b/test/common/upstream/load_balancer_benchmark.cc index 1a2bf5f53316..2c0e9233fa74 100644 --- a/test/common/upstream/load_balancer_benchmark.cc +++ b/test/common/upstream/load_balancer_benchmark.cc @@ -67,8 +67,9 @@ class BaseTester : public Event::TestUsingSimulatedTime { PrioritySetImpl priority_set_; PrioritySetImpl local_priority_set_; Stats::IsolatedStoreImpl stats_store_; + Stats::Scope& stats_scope_{*stats_store_.rootScope()}; ClusterLbStatNames stat_names_{stats_store_.symbolTable()}; - ClusterLbStats stats_{stat_names_, *stats_store_.rootScope()}; + ClusterLbStats stats_{stat_names_, stats_scope_}; NiceMock runtime_; Random::RandomGeneratorImpl random_; envoy::config::cluster::v3::Cluster::CommonLbConfig common_config_; @@ -156,7 +157,7 @@ class RingHashTester : public BaseTester { config_ = envoy::config::cluster::v3::Cluster::RingHashLbConfig(); config_.value().mutable_minimum_ring_size()->set_value(min_ring_size); ring_hash_lb_ = std::make_unique( - priority_set_, stats_, stats_store_, runtime_, random_, + priority_set_, stats_, stats_scope_, runtime_, random_, config_.has_value() ? makeOptRef( config_.value()) @@ -173,7 +174,7 @@ class MaglevTester : public BaseTester { MaglevTester(uint64_t num_hosts, uint32_t weighted_subset_percent = 0, uint32_t weight = 0) : BaseTester(num_hosts, weighted_subset_percent, weight) { maglev_lb_ = std::make_unique( - priority_set_, stats_, stats_store_, runtime_, random_, + priority_set_, stats_, stats_scope_, runtime_, random_, config_.has_value() ? makeOptRef(config_.value()) : absl::nullopt, @@ -554,9 +555,9 @@ class SubsetLbTester : public BaseTester { subset_info_ = std::make_unique(subset_config); lb_ = std::make_unique( - LoadBalancerType::Random, priority_set_, &local_priority_set_, stats_, - *stats_store_.rootScope(), runtime_, random_, *subset_info_, absl::nullopt, absl::nullopt, - absl::nullopt, absl::nullopt, common_config_, simTime()); + LoadBalancerType::Random, priority_set_, &local_priority_set_, stats_, stats_scope_, + runtime_, random_, *subset_info_, absl::nullopt, absl::nullopt, absl::nullopt, + absl::nullopt, common_config_, simTime()); const HostVector& hosts = priority_set_.getOrCreateHostSet(0).hosts(); ASSERT(hosts.size() == num_hosts); diff --git a/test/common/upstream/multiplexed_subscription_factory_test.cc b/test/common/upstream/multiplexed_subscription_factory_test.cc deleted file mode 100644 index c818d4143c25..000000000000 --- a/test/common/upstream/multiplexed_subscription_factory_test.cc +++ /dev/null @@ -1,256 +0,0 @@ -#include - -#include "envoy/common/exception.h" -#include "envoy/stats/scope.h" - -#include "source/common/upstream/multiplexed_subscription_factory.h" - -#include "test/mocks/api/mocks.h" -#include "test/mocks/config/custom_config_validators.h" -#include "test/mocks/config/mocks.h" -#include "test/mocks/local_info/mocks.h" -#include "test/mocks/protobuf/mocks.h" -#include "test/mocks/runtime/mocks.h" -#include "test/mocks/server/mocks.h" -#include "test/mocks/stats/mocks.h" -#include "test/mocks/upstream/mocks.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -using ::testing::_; -using ::testing::Invoke; -using ::testing::ReturnRef; - -namespace Envoy { -namespace Upstream { - -class MultiplexedSubscriptionFactoryPeer { -public: - static size_t optimizedMuxesSize(MultiplexedSubscriptionFactory& factory) { - return factory.muxes_.size(); - } -}; - -class MultiplexedSubscriptionFactoryForTesting : public MultiplexedSubscriptionFactory { -public: - MultiplexedSubscriptionFactoryForTesting( - const LocalInfo::LocalInfo& local_info, Event::Dispatcher& dispatcher, - Upstream::ClusterManager& cm, Api::Api& api, - ProtobufMessage::ValidationVisitor& validation_visitor, const Server::Instance& server, - Config::XdsResourcesDelegateOptRef xds_resources_delegate, - Config::XdsConfigTrackerOptRef xds_config_tracker) - : MultiplexedSubscriptionFactory(local_info, dispatcher, cm, validation_visitor, api, server, - xds_resources_delegate, xds_config_tracker){}; - - Config::GrpcMuxSharedPtr - testGetOrCreateMux(const envoy::config::core::v3::ApiConfigSource& api_config_source, - absl::string_view type_url, Stats::Scope& scope, - Config::CustomConfigValidatorsPtr& custom_config_validators) { - return MultiplexedSubscriptionFactory::getOrCreateMux(api_config_source, type_url, scope, - custom_config_validators); - } -}; - -class MultiplexedSubscriptionFactoryTest - : public ::testing::TestWithParam { -public: - MultiplexedSubscriptionFactoryTest() - : type_url_("type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment"), - resource_decoder_(std::make_shared>()){}; - - NiceMock cm_; - Event::MockDispatcher dispatcher_; - NiceMock random_; - NiceMock callbacks_; - Stats::MockIsolatedStatsStore stats_store_; - NiceMock api_; - NiceMock local_info_; - Grpc::MockAsyncClient* async_client_; - const std::string type_url_; - Config::RateLimitSettings rate_limit_settings_; - NiceMock server_; - NiceMock validation_visitor_; - Config::OpaqueResourceDecoderSharedPtr resource_decoder_; - NiceMock xds_resources_delegate_; - NiceMock xds_config_tracker_; -}; - -using MultiplexedSubscriptionFactoryForGrpcTest = MultiplexedSubscriptionFactoryTest; - -INSTANTIATE_TEST_SUITE_P(GrpcApiConfigSource, MultiplexedSubscriptionFactoryForGrpcTest, - ::testing::Values(envoy::config::core::v3::ApiConfigSource::GRPC, - envoy::config::core::v3::ApiConfigSource::DELTA_GRPC)); - -// Verify the same mux instance is returned for same config source. -TEST_P(MultiplexedSubscriptionFactoryForGrpcTest, ShouldReturnSameMuxForSameConfigSource) { - envoy::config::core::v3::ConfigSource config1; - auto config_source = config1.mutable_api_config_source(); - config_source->add_grpc_services()->mutable_envoy_grpc()->set_cluster_name("primary_xds_cluster"); - config_source->set_api_type(GetParam()); - auto factory = MultiplexedSubscriptionFactoryForTesting( - local_info_, dispatcher_, cm_, api_, validation_visitor_, server_, xds_resources_delegate_, - xds_config_tracker_); - - EXPECT_CALL(dispatcher_, createTimer_(_)); - Config::CustomConfigValidatorsPtr config_validators = - std::make_unique>(); - auto first_mux = factory.testGetOrCreateMux(config1.api_config_source(), type_url_, stats_store_, - config_validators); - config_validators = std::make_unique>(); - auto second_mux = factory.testGetOrCreateMux(config1.api_config_source(), type_url_, stats_store_, - config_validators); - EXPECT_EQ(first_mux.get(), second_mux.get()); - EXPECT_EQ(1, MultiplexedSubscriptionFactoryPeer::optimizedMuxesSize(factory)); -} - -// Verify the same mux instance is returned when the same management servers are used. -TEST_P(MultiplexedSubscriptionFactoryForGrpcTest, ShouldReturnSameMuxForSameGrpcService) { - envoy::config::core::v3::ConfigSource config1; - auto config_source = config1.mutable_api_config_source(); - config_source->add_grpc_services()->mutable_envoy_grpc()->set_cluster_name("primary_xds_cluster"); - config_source->set_api_type(GetParam()); - envoy::config::core::v3::ConfigSource config2; - config_source = config2.mutable_api_config_source(); - config_source->add_grpc_services()->mutable_envoy_grpc()->set_cluster_name("primary_xds_cluster"); - config_source->set_api_type(GetParam()); - config_source = config2.mutable_api_config_source(); - config_source->add_grpc_services()->mutable_envoy_grpc()->set_cluster_name( - "fallback_xds_cluster"); - auto factory = MultiplexedSubscriptionFactoryForTesting( - local_info_, dispatcher_, cm_, api_, validation_visitor_, server_, xds_resources_delegate_, - xds_config_tracker_); - - EXPECT_CALL(dispatcher_, createTimer_(_)); - Config::CustomConfigValidatorsPtr config_validators = - std::make_unique>(); - auto first_mux = factory.testGetOrCreateMux(config1.api_config_source(), type_url_, stats_store_, - config_validators); - config_validators = std::make_unique>(); - auto second_mux = factory.testGetOrCreateMux(config1.api_config_source(), type_url_, stats_store_, - config_validators); - EXPECT_EQ(first_mux.get(), second_mux.get()); - EXPECT_EQ(1, MultiplexedSubscriptionFactoryPeer::optimizedMuxesSize(factory)); -} - -// Verify that a new mux instance is created if a different config_source is used. -TEST_P(MultiplexedSubscriptionFactoryForGrpcTest, ShouldReturnDiffMuxesForDiffXdsServers) { - auto factory = MultiplexedSubscriptionFactoryForTesting( - local_info_, dispatcher_, cm_, api_, validation_visitor_, server_, xds_resources_delegate_, - xds_config_tracker_); - - EXPECT_CALL(dispatcher_, createTimer_(_)).Times(2); - envoy::config::core::v3::ConfigSource first_config; - auto config_source = first_config.mutable_api_config_source(); - config_source->set_api_type(GetParam()); - config_source->add_grpc_services()->mutable_envoy_grpc()->set_cluster_name("first_cluster"); - Config::CustomConfigValidatorsPtr config_validators = - std::make_unique>(); - auto first_mux = factory.testGetOrCreateMux(first_config.api_config_source(), type_url_, - stats_store_, config_validators); - envoy::config::core::v3::ConfigSource second_config; - config_source = second_config.mutable_api_config_source(); - config_source->set_api_type(GetParam()); - config_source->add_grpc_services()->mutable_envoy_grpc()->set_cluster_name("second_cluster"); - config_validators = std::make_unique>(); - auto second_mux = factory.testGetOrCreateMux(second_config.api_config_source(), type_url_, - stats_store_, config_validators); - EXPECT_NE(first_mux.get(), second_mux.get()); - EXPECT_EQ(2, MultiplexedSubscriptionFactoryPeer::optimizedMuxesSize(factory)); -} - -// Verify that a new mux instance is created if a different xds method is used. -TEST_P(MultiplexedSubscriptionFactoryForGrpcTest, ShouldReturnDiffMuxesForDiffXdsTypes) { - envoy::config::core::v3::ConfigSource config; - auto config_source = config.mutable_api_config_source(); - config_source->add_grpc_services()->mutable_envoy_grpc()->set_cluster_name("xds_cluster"); - config_source->set_api_type(GetParam()); - auto factory = MultiplexedSubscriptionFactoryForTesting( - local_info_, dispatcher_, cm_, api_, validation_visitor_, server_, xds_resources_delegate_, - xds_config_tracker_); - EXPECT_CALL(dispatcher_, createTimer_(_)).Times(3); - Config::CustomConfigValidatorsPtr config_validators = - std::make_unique>(); - auto first_mux = factory.testGetOrCreateMux(config.api_config_source(), type_url_, stats_store_, - config_validators); - config_validators = std::make_unique>(); - auto second_mux = factory.testGetOrCreateMux( - config.api_config_source(), "type.googleapis.com/envoy.config.cluster.v3.Cluster", - stats_store_, config_validators); - EXPECT_NE(first_mux.get(), second_mux.get()); - config_validators = std::make_unique>(); - auto third_mux = factory.testGetOrCreateMux( - config.api_config_source(), "type.googleapis.com/envoy.config.listener.v3.Listener", - stats_store_, config_validators); - EXPECT_NE(first_mux.get(), third_mux.get()); - EXPECT_NE(second_mux.get(), third_mux.get()); - EXPECT_EQ(3, MultiplexedSubscriptionFactoryPeer::optimizedMuxesSize(factory)); -} - -// Verify that muxes managed by MultiplexedSubscriptionFactory are used when ApiConfigSource::GRPC -// api type is used. -TEST_P(MultiplexedSubscriptionFactoryForGrpcTest, - ShouldUseGetOrCreateMuxWhenApiConfigSourceIsUsed) { - auto factory = MultiplexedSubscriptionFactoryForTesting( - local_info_, dispatcher_, cm_, api_, validation_visitor_, server_, xds_resources_delegate_, - xds_config_tracker_); - envoy::config::core::v3::ConfigSource config; - auto config_source = config.mutable_api_config_source(); - config_source->set_api_type(GetParam()); - config_source->set_transport_api_version(envoy::config::core::v3::V3); - config_source->add_grpc_services()->mutable_envoy_grpc()->set_cluster_name("first_cluster"); - Upstream::ClusterManager::ClusterSet primary_clusters; - primary_clusters.insert("first_cluster"); - EXPECT_CALL(cm_, primaryClusters()).WillOnce(ReturnRef(primary_clusters)); - NiceMock* async_client_factory{ - new NiceMock()}; - EXPECT_CALL(*async_client_factory, createUncachedRawAsyncClient()).WillOnce(Invoke([] { - return std::make_unique>(); - })); - NiceMock async_client_manager; - EXPECT_CALL(async_client_manager, factoryForGrpcService(_, _, _)) - .WillOnce(Invoke( - [async_client_factory](const envoy::config::core::v3::GrpcService&, Stats::Scope&, bool) { - return Grpc::AsyncClientFactoryPtr(async_client_factory); - })); - EXPECT_CALL(cm_, grpcAsyncClientManager()).WillOnce(ReturnRef(async_client_manager)); - EXPECT_CALL(dispatcher_, createTimer_(_)); - auto subscription = - factory.subscriptionFromConfigSource(config, Config::TypeUrl::get().ClusterLoadAssignment, - stats_store_, callbacks_, resource_decoder_, {}); - Config::CustomConfigValidatorsPtr config_validators = - std::make_unique>(); - auto expected_mux = factory.testGetOrCreateMux(config.api_config_source(), type_url_, - stats_store_, config_validators); - EXPECT_EQ(expected_mux.get(), - (dynamic_cast(*subscription).grpcMux()).get()); -} - -using MultiplexedSubscriptionFactoryForNonGrpcTest = MultiplexedSubscriptionFactoryTest; - -INSTANTIATE_TEST_SUITE_P(NonGrpcApiConfigSource, MultiplexedSubscriptionFactoryForNonGrpcTest, - ::testing::Values(envoy::config::core::v3::ApiConfigSource::REST)); - -TEST_P(MultiplexedSubscriptionFactoryForNonGrpcTest, - ShouldUseBaseGetOrCreateMuxWhenNonGrpcConfigSourceIsUsed) { - auto factory = MultiplexedSubscriptionFactoryForTesting( - local_info_, dispatcher_, cm_, api_, validation_visitor_, server_, xds_resources_delegate_, - xds_config_tracker_); - envoy::config::core::v3::ConfigSource config; - auto* api_config_source = config.mutable_api_config_source(); - api_config_source->set_api_type(envoy::config::core::v3::ApiConfigSource::REST); - api_config_source->set_transport_api_version(envoy::config::core::v3::V3); - api_config_source->mutable_refresh_delay()->set_seconds(1); - api_config_source->mutable_request_timeout()->set_seconds(5); - api_config_source->add_cluster_names("xds_cluster"); - Upstream::ClusterManager::ClusterSet primary_clusters; - primary_clusters.insert("xds_cluster"); - EXPECT_CALL(cm_, primaryClusters()).WillOnce(ReturnRef(primary_clusters)); - EXPECT_CALL(dispatcher_, createTimer_(_)); - factory.subscriptionFromConfigSource(config, Config::TypeUrl::get().ClusterLoadAssignment, - stats_store_, callbacks_, resource_decoder_, {}); - EXPECT_EQ(0, MultiplexedSubscriptionFactoryPeer::optimizedMuxesSize(factory)); -} - -} // namespace Upstream -} // namespace Envoy diff --git a/test/config_test/config_test.cc b/test/config_test/config_test.cc index e5cc3d8f4ab7..b18921d5f793 100644 --- a/test/config_test/config_test.cc +++ b/test/config_test/config_test.cc @@ -57,8 +57,7 @@ static std::vector unsuported_win32_configs = { class ConfigTest { public: - ConfigTest(const OptionsImpl& options) - : api_(Api::createApiForTest(time_system_)), options_(options) { + ConfigTest(OptionsImpl& options) : api_(Api::createApiForTest(time_system_)), options_(options) { ON_CALL(*server_.server_factory_context_, api()).WillByDefault(ReturnRef(server_.api_)); ON_CALL(server_, options()).WillByDefault(ReturnRef(options_)); ON_CALL(server_, sslContextManager()).WillByDefault(ReturnRef(ssl_context_manager_)); @@ -169,7 +168,7 @@ class ConfigTest { NiceMock server_; Server::ServerFactoryContextImpl server_factory_context_{server_}; NiceMock ssl_context_manager_; - OptionsImpl options_; + OptionsImpl& options_; std::unique_ptr cluster_manager_factory_; std::unique_ptr> component_factory_ptr_{ std::make_unique>()}; @@ -242,7 +241,8 @@ uint32_t run(const std::string& directory) { Server::InstanceUtil::loadBootstrapConfig( bootstrap, options, ProtobufMessage::getStrictValidationVisitor(), *api); ENVOY_LOG_MISC(info, "testing {} as yaml.", filename); - ConfigTest test2(asConfigYaml(options, *api)); + OptionsImpl config = asConfigYaml(options, *api); + ConfigTest test2(config); } num_tested++; } diff --git a/test/config_test/example_configs_test_setup.sh b/test/config_test/example_configs_test_setup.sh index 4271f9008530..b36b2c91e139 100755 --- a/test/config_test/example_configs_test_setup.sh +++ b/test/config_test/example_configs_test_setup.sh @@ -4,7 +4,9 @@ set -e DIR="$TEST_TMPDIR"/test/config_test mkdir -p "$DIR" -tar -xvf "$TEST_SRCDIR"/"$EXAMPLE_CONFIGS_TAR_PATH" -C "$DIR" + +# Windows struggles with its own paths, so we have to help it out with `--force-local` +tar --force-local -xvf "$TEST_SRCDIR"/"$EXAMPLE_CONFIGS_TAR_PATH" -C "$DIR" # find uses full path to prevent using Windows find on Windows. /usr/bin/find "$DIR" -type f | grep -c .yaml > "$TEST_TMPDIR"/config-file-count.txt diff --git a/test/exe/version_out_test.sh b/test/exe/version_out_test.sh index ec82966ee380..1698912b57f6 100755 --- a/test/exe/version_out_test.sh +++ b/test/exe/version_out_test.sh @@ -8,7 +8,7 @@ export LC_ALL=C ENVOY_BIN="${TEST_SRCDIR}/envoy/source/exe/envoy-static" COMMIT=$(${ENVOY_BIN} --version | \ - sed -n -E 's/.*version: ([0-9a-f]{40})\/([0-9]+\.[0-9]+\.[0-9]+)(-[a-zA-Z0-9\-_]+)?\/(Clean|Modified)\/(RELEASE|DEBUG)\/([a-zA-Z-]+)$/\1/p') + sed -n -E 's/.*version: ([0-9a-f]{40})\/([0-9]+\.[0-9]+\.[0-9]+)(-[a-zA-Z0-9_\-]+)?\/(Clean|Modified)\/(RELEASE|DEBUG)\/([a-zA-Z-]+)$/\1/p') EXPECTED=$(cat "${TEST_SRCDIR}/envoy/bazel/raw_build_id.ldscript") @@ -18,7 +18,7 @@ if [[ "${COMMIT}" != "${EXPECTED}" ]]; then fi VERSION=$(${ENVOY_BIN} --version | \ - sed -n -E 's/.*version: ([0-9a-f]{40})\/([0-9]+\.[0-9]+\.[0-9]+)(-[a-zA-Z0-9\-_]+)?\/(Clean|Modified)\/(RELEASE|DEBUG)\/([a-zA-Z-]+)$/\2\3/p') + sed -n -E 's/.*version: ([0-9a-f]{40})\/([0-9]+\.[0-9]+\.[0-9]+)(-[a-zA-Z0-9_\-]+)?\/(Clean|Modified)\/(RELEASE|DEBUG)\/([a-zA-Z-]+)$/\2\3/p') EXPECTED=$(cat "${TEST_SRCDIR}/envoy/VERSION.txt") diff --git a/test/extensions/access_loggers/open_telemetry/substitution_formatter_speed_test.cc b/test/extensions/access_loggers/open_telemetry/substitution_formatter_speed_test.cc index 6ebfc5cc3748..622d22b7d883 100644 --- a/test/extensions/access_loggers/open_telemetry/substitution_formatter_speed_test.cc +++ b/test/extensions/access_loggers/open_telemetry/substitution_formatter_speed_test.cc @@ -3,6 +3,7 @@ #include "source/extensions/access_loggers/open_telemetry/substitution_formatter.h" #include "test/common/stream_info/test_util.h" +#include "test/test_common/utility.h" #include "benchmark/benchmark.h" #include "opentelemetry/proto/common/v1/common.pb.h" diff --git a/test/extensions/clusters/eds/eds_speed_test.cc b/test/extensions/clusters/eds/eds_speed_test.cc index 0d3da41d1ca6..8df731effeff 100644 --- a/test/extensions/clusters/eds/eds_speed_test.cc +++ b/test/extensions/clusters/eds/eds_speed_test.cc @@ -15,7 +15,6 @@ #include "source/common/config/protobuf_link_hacks.h" #include "source/common/config/utility.h" #include "source/common/config/xds_mux/grpc_mux_impl.h" -#include "source/common/runtime/runtime_features.h" #include "source/common/singleton/manager_impl.h" #include "source/extensions/clusters/eds/eds.h" #include "source/server/transport_socket_config_impl.h" @@ -81,26 +80,13 @@ class EdsSpeedTest { refresh_delay: 1s )EOF", Envoy::Upstream::Cluster::InitializePhase::Secondary); - if (edsMultiplexingEnabled()) { - EXPECT_CALL(*server_context_.cluster_manager_.multiplexed_subscription_factory_.subscription_, - start(_)); - } else { - EXPECT_CALL(*server_context_.cluster_manager_.subscription_factory_.subscription_, start(_)); - } + EXPECT_CALL(*server_context_.cluster_manager_.subscription_factory_.subscription_, start(_)); cluster_->initialize([this] { initialized_ = true; }); EXPECT_CALL(*async_client_, startRaw(_, _, _, _)).WillOnce(testing::Return(&async_stream_)); subscription_->start({"fare"}); } - bool edsMultiplexingEnabled() { - return Runtime::runtimeFeatureEnabled("envoy.reloadable_features.multiplex_eds") && - (eds_cluster_.eds_cluster_config().eds_config().api_config_source().api_type() == - envoy::config::core::v3::ApiConfigSource::GRPC || - eds_cluster_.eds_cluster_config().eds_config().api_config_source().api_type() == - envoy::config::core::v3::ApiConfigSource::DELTA_GRPC); - } - void resetCluster(const std::string& yaml_config, Cluster::InitializePhase initialize_phase) { local_info_.node_.mutable_locality()->set_zone("us-east-1a"); eds_cluster_ = parseClusterFromV3Yaml(yaml_config); @@ -113,11 +99,7 @@ class EdsSpeedTest { cluster_ = std::make_shared(server_context_, eds_cluster_, runtime_, factory_context, std::move(scope), false); EXPECT_EQ(initialize_phase, cluster_->initializePhase()); - eds_callbacks_ = - edsMultiplexingEnabled() - ? server_context_.cluster_manager_.multiplexed_subscription_factory_.callbacks_ - : server_context_.cluster_manager_.subscription_factory_.callbacks_; - ; + eds_callbacks_ = server_context_.cluster_manager_.subscription_factory_.callbacks_; subscription_ = std::make_unique( grpc_mux_, *eds_callbacks_, resource_decoder_, subscription_stats_, type_url_, server_context_.dispatcher_, std::chrono::milliseconds(), false, diff --git a/test/extensions/clusters/eds/eds_test.cc b/test/extensions/clusters/eds/eds_test.cc index 3d7b21d37a42..7bc0b992b2a4 100644 --- a/test/extensions/clusters/eds/eds_test.cc +++ b/test/extensions/clusters/eds/eds_test.cc @@ -134,27 +134,11 @@ class EdsTest : public testing::Test { cluster_ = std::make_shared(server_context_, eds_cluster_, runtime_.loader(), factory_context, std::move(scope), false); EXPECT_EQ(initialize_phase, cluster_->initializePhase()); - eds_callbacks_ = - edsMultiplexingEnabled() - ? server_context_.cluster_manager_.multiplexed_subscription_factory_.callbacks_ - : server_context_.cluster_manager_.subscription_factory_.callbacks_; - } - - bool edsMultiplexingEnabled() { - return Runtime::runtimeFeatureEnabled("envoy.reloadable_features.multiplex_eds") && - (eds_cluster_.eds_cluster_config().eds_config().api_config_source().api_type() == - envoy::config::core::v3::ApiConfigSource::GRPC || - eds_cluster_.eds_cluster_config().eds_config().api_config_source().api_type() == - envoy::config::core::v3::ApiConfigSource::DELTA_GRPC); + eds_callbacks_ = server_context_.cluster_manager_.subscription_factory_.callbacks_; } void initialize() { - if (edsMultiplexingEnabled()) { - EXPECT_CALL(*server_context_.cluster_manager_.multiplexed_subscription_factory_.subscription_, - start(_)); - } else { - EXPECT_CALL(*server_context_.cluster_manager_.subscription_factory_.subscription_, start(_)); - } + EXPECT_CALL(*server_context_.cluster_manager_.subscription_factory_.subscription_, start(_)); cluster_->initialize([this] { initialized_ = true; }); } @@ -200,7 +184,8 @@ class EdsWithHealthCheckUpdateTest : public EdsTest { doOnConfigUpdateVerifyNoThrow(cluster_load_assignment_); // Make sure the cluster is rebuilt. - EXPECT_EQ(0UL, stats_.counter("cluster.name.update_no_rebuild").value()); + EXPECT_EQ(0UL, + stats_.findCounterByString("cluster.name.update_no_rebuild").value().get().value()); { auto& hosts = cluster_->prioritySet().hostSetsPerPriority()[0]->hosts(); EXPECT_EQ(hosts.size(), 2); @@ -258,7 +243,8 @@ class EdsWithHealthCheckUpdateTest : public EdsTest { doOnConfigUpdateVerifyNoThrow(cluster_load_assignment_); // Always rebuild if health check config is changed. - EXPECT_EQ(0UL, stats_.counter("cluster.name.update_no_rebuild").value()); + EXPECT_EQ(0UL, + stats_.findCounterByString("cluster.name.update_no_rebuild").value().get().value()); } envoy::config::endpoint::v3::ClusterLoadAssignment cluster_load_assignment_; @@ -286,7 +272,7 @@ TEST_F(EdsTest, OnConfigUpdateEmpty) { eds_callbacks_->onConfigUpdate({}, ""); Protobuf::RepeatedPtrField removed_resources; eds_callbacks_->onConfigUpdate({}, removed_resources, ""); - EXPECT_EQ(2UL, stats_.counter("cluster.name.update_empty").value()); + EXPECT_EQ(2UL, stats_.findCounterByString("cluster.name.update_empty").value().get().value()); EXPECT_TRUE(initialized_); } @@ -313,7 +299,8 @@ TEST_F(EdsTest, OnConfigUpdateSuccess) { initialize(); doOnConfigUpdateVerifyNoThrow(cluster_load_assignment); EXPECT_TRUE(initialized_); - EXPECT_EQ(1UL, stats_.counter("cluster.name.update_no_rebuild").value()); + EXPECT_EQ(1UL, + stats_.findCounterByString("cluster.name.update_no_rebuild").value().get().value()); } // Validate that delta-style onConfigUpdate() with the expected cluster accepts config. @@ -332,7 +319,8 @@ TEST_F(EdsTest, DeltaOnConfigUpdateSuccess) { VERBOSE_EXPECT_NO_THROW(eds_callbacks_->onConfigUpdate(decoded_resources.refvec_, {}, "v1")); EXPECT_TRUE(initialized_); - EXPECT_EQ(1UL, stats_.counter("cluster.name.update_no_rebuild").value()); + EXPECT_EQ(1UL, + stats_.findCounterByString("cluster.name.update_no_rebuild").value().get().value()); } // Validate that onConfigUpdate() with no service name accepts config. @@ -381,20 +369,18 @@ TEST_F(EdsTest, EndpointWeightChangeCausesRebuild) { initialize(); doOnConfigUpdateVerifyNoThrow(cluster_load_assignment); EXPECT_TRUE(initialized_); - EXPECT_EQ(0UL, stats_.counter("cluster.name.update_no_rebuild").value()); - EXPECT_EQ( - 30UL, - stats_.gauge("cluster.name.max_host_weight", Stats::Gauge::ImportMode::Accumulate).value()); + EXPECT_EQ(0UL, + stats_.findCounterByString("cluster.name.update_no_rebuild").value().get().value()); + EXPECT_EQ(30UL, stats_.findGaugeByString("cluster.name.max_host_weight").value().get().value()); auto& hosts = cluster_->prioritySet().hostSetsPerPriority()[0]->hosts(); EXPECT_EQ(hosts.size(), 1); EXPECT_EQ(hosts[0]->weight(), 30); endpoint->mutable_load_balancing_weight()->set_value(31); doOnConfigUpdateVerifyNoThrow(cluster_load_assignment); - EXPECT_EQ(0UL, stats_.counter("cluster.name.update_no_rebuild").value()); - EXPECT_EQ( - 31UL, - stats_.gauge("cluster.name.max_host_weight", Stats::Gauge::ImportMode::Accumulate).value()); + EXPECT_EQ(0UL, + stats_.findCounterByString("cluster.name.update_no_rebuild").value().get().value()); + EXPECT_EQ(31UL, stats_.findGaugeByString("cluster.name.max_host_weight").value().get().value()); auto& new_hosts = cluster_->prioritySet().hostSetsPerPriority()[0]->hosts(); EXPECT_EQ(new_hosts.size(), 1); EXPECT_EQ(new_hosts[0]->weight(), 31); @@ -430,7 +416,8 @@ TEST_F(EdsTest, EndpointMetadata) { initialize(); doOnConfigUpdateVerifyNoThrow(cluster_load_assignment); EXPECT_TRUE(initialized_); - EXPECT_EQ(0UL, stats_.counter("cluster.name.update_no_rebuild").value()); + EXPECT_EQ(0UL, + stats_.findCounterByString("cluster.name.update_no_rebuild").value().get().value()); auto& hosts = cluster_->prioritySet().hostSetsPerPriority()[0]->hosts(); EXPECT_EQ(hosts.size(), 2); @@ -462,7 +449,8 @@ TEST_F(EdsTest, EndpointMetadata) { // We don't rebuild with the exact same config. doOnConfigUpdateVerifyNoThrow(cluster_load_assignment); - EXPECT_EQ(1UL, stats_.counter("cluster.name.update_no_rebuild").value()); + EXPECT_EQ(1UL, + stats_.findCounterByString("cluster.name.update_no_rebuild").value().get().value()); // New resources with Metadata updated. Config::Metadata::mutableMetadataValue(*canary->mutable_metadata(), @@ -620,7 +608,8 @@ TEST_F(EdsTest, EndpointHealthStatus) { EXPECT_EQ(Host::Health::Healthy, hosts[0]->coarseHealth()); } - const auto rebuild_container = stats_.counter("cluster.name.update_no_rebuild").value(); + const auto rebuild_container = + stats_.findCounterByString("cluster.name.update_no_rebuild").value().get().value(); // Now mark host 0 degraded via EDS, it should be degraded. endpoints->mutable_lb_endpoints(0)->set_health_status(envoy::config::core::v3::DEGRADED); doOnConfigUpdateVerifyNoThrow(cluster_load_assignment); @@ -630,7 +619,8 @@ TEST_F(EdsTest, EndpointHealthStatus) { } // We should rebuild the cluster since we went from healthy -> degraded. - EXPECT_EQ(rebuild_container, stats_.counter("cluster.name.update_no_rebuild").value()); + EXPECT_EQ(rebuild_container, + stats_.findCounterByString("cluster.name.update_no_rebuild").value().get().value()); // Now mark the host as having been degraded through active hc. cluster_->prioritySet().hostSetsPerPriority()[0]->hosts()[0]->healthFlagSet( @@ -645,7 +635,8 @@ TEST_F(EdsTest, EndpointHealthStatus) { } // Since the host health didn't change, expect no rebuild. - EXPECT_EQ(rebuild_container + 1, stats_.counter("cluster.name.update_no_rebuild").value()); + EXPECT_EQ(rebuild_container + 1, + stats_.findCounterByString("cluster.name.update_no_rebuild").value().get().value()); } // Validate that onConfigUpdate() updates the hostname. @@ -2271,7 +2262,8 @@ TEST_F(EdsTest, PriorityAndLocalityWeighted) { initialize(); doOnConfigUpdateVerifyNoThrow(cluster_load_assignment); EXPECT_TRUE(initialized_); - EXPECT_EQ(0UL, stats_.counter("cluster.name.update_no_rebuild").value()); + EXPECT_EQ(0UL, + stats_.findCounterByString("cluster.name.update_no_rebuild").value().get().value()); { auto& first_hosts_per_locality = @@ -2304,13 +2296,15 @@ TEST_F(EdsTest, PriorityAndLocalityWeighted) { // This should noop (regression test for earlier bug where we would still // rebuild). doOnConfigUpdateVerifyNoThrow(cluster_load_assignment); - EXPECT_EQ(1UL, stats_.counter("cluster.name.update_no_rebuild").value()); + EXPECT_EQ(1UL, + stats_.findCounterByString("cluster.name.update_no_rebuild").value().get().value()); // Adjust locality weights, validate that we observe an update. cluster_load_assignment.mutable_endpoints(0)->mutable_load_balancing_weight()->set_value(60); cluster_load_assignment.mutable_endpoints(1)->mutable_load_balancing_weight()->set_value(40); doOnConfigUpdateVerifyNoThrow(cluster_load_assignment); - EXPECT_EQ(1UL, stats_.counter("cluster.name.update_no_rebuild").value()); + EXPECT_EQ(1UL, + stats_.findCounterByString("cluster.name.update_no_rebuild").value().get().value()); } TEST_F(EdsWithHealthCheckUpdateTest, EndpointUpdateHealthCheckConfig) { @@ -2536,99 +2530,6 @@ TEST_F(EdsTest, OnConfigUpdateLedsAndEndpoints) { "(resource: xdstp://foo/leds/collection) and a list of endpoints."); } -TEST_F(EdsTest, MultiplexEdsEnabledViaRuntime) { - runtime_.mergeValues({{"envoy.reloadable_features.multiplex_eds", "true"}}); - EXPECT_CALL(server_context_.cluster_manager_.multiplexed_subscription_factory_, - subscriptionFromConfigSource(_, _, _, _, _, _)); - EXPECT_CALL(server_context_.cluster_manager_.subscription_factory_, - subscriptionFromConfigSource(_, _, _, _, _, _)) - .Times(0); - resetCluster(R"EOF( - name: some_cluster - connect_timeout: 0.25s - type: EDS - eds_cluster_config: - eds_config: - resource_api_version: V3 - api_config_source: - api_type: GRPC - transport_api_version: V3 - grpc_services: - envoy_grpc: - cluster_name: eds_cluster - )EOF", - Cluster::InitializePhase::Secondary); -} - -TEST_F(EdsTest, MultiplexEdsDisabledViaRuntime) { - runtime_.mergeValues({{"envoy.reloadable_features.multiplex_eds", "false"}}); - EXPECT_CALL(server_context_.cluster_manager_.multiplexed_subscription_factory_, - subscriptionFromConfigSource(_, _, _, _, _, _)) - .Times(0); - EXPECT_CALL(server_context_.cluster_manager_.subscription_factory_, - subscriptionFromConfigSource(_, _, _, _, _, _)); - resetCluster(R"EOF( - name: some_cluster - connect_timeout: 0.25s - type: EDS - eds_cluster_config: - eds_config: - resource_api_version: V3 - api_config_source: - api_type: GRPC - transport_api_version: V3 - grpc_services: - envoy_grpc: - cluster_name: eds_cluster - )EOF", - Cluster::InitializePhase::Secondary); -} - -TEST_F(EdsTest, MultiplexEdsWithUnsupportedApiTypeFallsbackToNonMultiplexedEds) { - runtime_.mergeValues({{"envoy.reloadable_features.multiplex_eds", "true"}}); - EXPECT_CALL(server_context_.cluster_manager_.multiplexed_subscription_factory_, - subscriptionFromConfigSource(_, _, _, _, _, _)) - .Times(0); - EXPECT_CALL(server_context_.cluster_manager_.subscription_factory_, - subscriptionFromConfigSource(_, _, _, _, _, _)); - resetCluster(R"EOF( - name: some_cluster - connect_timeout: 0.25s - type: EDS - eds_cluster_config: - eds_config: - api_config_source: - api_type: REST - cluster_names: - - eds - refresh_delay: 1s - )EOF", - Cluster::InitializePhase::Secondary); -} - -TEST_F(EdsTest, MultiplexEdsEnabledByDefault) { - EXPECT_CALL(server_context_.cluster_manager_.multiplexed_subscription_factory_, - subscriptionFromConfigSource(_, _, _, _, _, _)); - EXPECT_CALL(server_context_.cluster_manager_.subscription_factory_, - subscriptionFromConfigSource(_, _, _, _, _, _)) - .Times(0); - resetCluster(R"EOF( - name: some_cluster - connect_timeout: 0.25s - type: EDS - eds_cluster_config: - eds_config: - resource_api_version: V3 - api_config_source: - api_type: GRPC - transport_api_version: V3 - grpc_services: - envoy_grpc: - cluster_name: eds_cluster - )EOF", - Cluster::InitializePhase::Secondary); -} - } // namespace } // namespace Upstream } // namespace Envoy diff --git a/test/extensions/common/proxy_protocol/proxy_protocol_header_test.cc b/test/extensions/common/proxy_protocol/proxy_protocol_header_test.cc index af6d7e25f7b0..170810998f27 100644 --- a/test/extensions/common/proxy_protocol/proxy_protocol_header_test.cc +++ b/test/extensions/common/proxy_protocol/proxy_protocol_header_test.cc @@ -1,6 +1,8 @@ #include "envoy/network/address.h" #include "source/common/buffer/buffer_impl.h" +#include "source/common/common/logger.h" +#include "source/common/network/address_impl.h" #include "source/extensions/common/proxy_protocol/proxy_protocol_header.h" #include "test/mocks/network/connection.h" @@ -15,6 +17,8 @@ namespace Common { namespace ProxyProtocol { namespace { +using namespace std::literals::string_literals; + TEST(ProxyProtocolHeaderTest, GeneratesV1IPv4Header) { const auto expectedHeaderStr = "PROXY TCP4 174.2.2.222 172.0.0.1 50000 80\r\n"; const Buffer::OwnedImpl expectedBuff(expectedHeaderStr); @@ -116,6 +120,97 @@ TEST(ProxyProtocolHeaderTest, GeneratesV2LocalHeader) { EXPECT_TRUE(TestUtility::buffersEqual(expectedBuff, buff)); } +TEST(ProxyProtocolHeaderTest, GeneratesV2IPv4HeaderWithTLVPassAll) { + const uint8_t v2_protocol[] = {0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, + 0x0a, 0x21, 0x11, 0x00, 0x11, 0x01, 0x02, 0x03, 0x04, 0x00, 0x01, + 0x01, 0x02, 0x03, 0x05, 0x02, 0x01, 0x05, 0x00, 0x02, 0x06, 0x07}; + + const Buffer::OwnedImpl expectedBuff(v2_protocol, sizeof(v2_protocol)); + auto src_addr = + Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("1.2.3.4", 773)); + auto dst_addr = + Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("0.1.1.2", 513)); + Network::ProxyProtocolTLV tlv{0x5, {0x06, 0x07}}; + Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, {tlv}}; + Buffer::OwnedImpl buff{}; + + ASSERT_TRUE(generateV2Header(proxy_proto_data, buff, true, {})); + + EXPECT_TRUE(TestUtility::buffersEqual(expectedBuff, buff)); +} + +TEST(ProxyProtocolHeaderTest, GeneratesV2IPv4HeaderWithTLVPassEmpty) { + const uint8_t v2_protocol[] = {0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, + 0x54, 0x0a, 0x21, 0x11, 0x00, 0x0c, 0x01, 0x02, 0x03, 0x04, + 0x00, 0x01, 0x01, 0x02, 0x03, 0x05, 0x02, 0x01}; + + const Buffer::OwnedImpl expectedBuff(v2_protocol, sizeof(v2_protocol)); + auto src_addr = + Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("1.2.3.4", 773)); + auto dst_addr = + Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("0.1.1.2", 513)); + Network::ProxyProtocolTLV tlv{0x5, {0x06, 0x07}}; + Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, {tlv}}; + Buffer::OwnedImpl buff{}; + + ASSERT_TRUE(generateV2Header(proxy_proto_data, buff, false, {})); + + EXPECT_TRUE(TestUtility::buffersEqual(expectedBuff, buff)); +} + +TEST(ProxyProtocolHeaderTest, GeneratesV2IPv4HeaderWithTLVPassSpecific) { + const uint8_t v2_protocol[] = {0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, + 0x0a, 0x21, 0x11, 0x00, 0x11, 0x01, 0x02, 0x03, 0x04, 0x00, 0x01, + 0x01, 0x02, 0x03, 0x05, 0x02, 0x01, 0x05, 0x00, 0x02, 0x06, 0x07}; + + const Buffer::OwnedImpl expectedBuff(v2_protocol, sizeof(v2_protocol)); + auto src_addr = + Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("1.2.3.4", 773)); + auto dst_addr = + Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("0.1.1.2", 513)); + Network::ProxyProtocolTLV tlv{0x5, {0x06, 0x07}}; + Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, {tlv}}; + Buffer::OwnedImpl buff{}; + + ASSERT_TRUE(generateV2Header(proxy_proto_data, buff, false, {0x5})); + + EXPECT_TRUE(TestUtility::buffersEqual(expectedBuff, buff)); +} + +TEST(ProxyProtocolHeaderTest, GeneratesV2IPv6HeaderWithTLV) { + const uint8_t v2_protocol[] = { + 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, 0x0a, 0x21, 0x21, 0x00, + 0x29, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x05, 0x00, 0x02, 0x06, 0x07}; + const Buffer::OwnedImpl expectedBuff(v2_protocol, sizeof(v2_protocol)); + auto src_addr = + Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv6Instance("1:2:3::4", 8)); + auto dst_addr = Network::Address::InstanceConstSharedPtr( + new Network::Address::Ipv6Instance("1:100:200:3::", 2)); + Network::ProxyProtocolTLV tlv{0x5, {0x06, 0x07}}; + Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, {tlv}}; + + Buffer::OwnedImpl buff{}; + ASSERT_TRUE(generateV2Header(proxy_proto_data, buff, true, {})); + + EXPECT_TRUE(TestUtility::buffersEqual(expectedBuff, buff)); +} + +TEST(ProxyProtocolHeaderTest, GeneratesV2WithTLVExceedingLengthLimit) { + auto src_addr = + Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("1.2.3.4", 773)); + auto dst_addr = + Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("0.1.1.2", 513)); + const std::string long_tlv(65536, 'a'); + Network::ProxyProtocolTLV tlv{0x5, std::vector(long_tlv.begin(), long_tlv.end())}; + Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, {tlv}}; + Buffer::OwnedImpl buff{}; + + EXPECT_LOG_CONTAINS("warn", "Generating Proxy Protocol V2 header: TLVs exceed length limit 65535", + generateV2Header(proxy_proto_data, buff, true, {})); +} + } // namespace } // namespace ProxyProtocol } // namespace Common diff --git a/test/extensions/filters/common/expr/context_test.cc b/test/extensions/filters/common/expr/context_test.cc index 3a7f2ee1a256..d0f2d258e647 100644 --- a/test/extensions/filters/common/expr/context_test.cc +++ b/test/extensions/filters/common/expr/context_test.cc @@ -9,6 +9,7 @@ #include "test/mocks/ssl/mocks.h" #include "test/mocks/stream_info/mocks.h" #include "test/mocks/upstream/host.h" +#include "test/test_common/utility.h" #include "absl/time/time.h" #include "gmock/gmock.h" diff --git a/test/extensions/filters/http/dynamic_forward_proxy/proxy_filter_test.cc b/test/extensions/filters/http/dynamic_forward_proxy/proxy_filter_test.cc index 0745401a05ae..b6ae74d6d93a 100644 --- a/test/extensions/filters/http/dynamic_forward_proxy/proxy_filter_test.cc +++ b/test/extensions/filters/http/dynamic_forward_proxy/proxy_filter_test.cc @@ -65,7 +65,9 @@ class ProxyFilterTest : public testing::Test, // kind we need to do DNS entries for. CustomClusterType cluster_type; cluster_type.set_name("envoy.clusters.dynamic_forward_proxy"); - cm_.thread_local_cluster_.cluster_.info_->cluster_type_ = cluster_type; + cm_.thread_local_cluster_.cluster_.info_->cluster_type_ = + std::make_unique( + cluster_type); // Configure max pending to 1 so we can test circuit breaking. cm_.thread_local_cluster_.cluster_.info_->resetResourceManager(0, 1, 0, 0, 0, 100); @@ -284,7 +286,7 @@ TEST_F(ProxyFilterTest, NoCluster) { // No cluster type leads to skipping DNS lookups. TEST_F(ProxyFilterTest, NoClusterType) { - cm_.thread_local_cluster_.cluster_.info_->cluster_type_ = absl::nullopt; + cm_.thread_local_cluster_.cluster_.info_->cluster_type_ = nullptr; InSequence s; @@ -297,7 +299,8 @@ TEST_F(ProxyFilterTest, NoClusterType) { TEST_F(ProxyFilterTest, NonDynamicForwardProxy) { CustomClusterType cluster_type; cluster_type.set_name("envoy.cluster.static"); - cm_.thread_local_cluster_.cluster_.info_->cluster_type_ = cluster_type; + cm_.thread_local_cluster_.cluster_.info_->cluster_type_ = + std::make_unique(cluster_type); InSequence s; diff --git a/test/extensions/filters/http/local_ratelimit/filter_test.cc b/test/extensions/filters/http/local_ratelimit/filter_test.cc index 9a3a66771dfc..81ab5bdfa2e8 100644 --- a/test/extensions/filters/http/local_ratelimit/filter_test.cc +++ b/test/extensions/filters/http/local_ratelimit/filter_test.cc @@ -468,26 +468,6 @@ TEST_F(DescriptorFilterTest, RouteDescriptorNotFound) { EXPECT_EQ(0U, findCounter("test.http_local_rate_limit.rate_limited")); } -TEST_F(DescriptorFilterTest, RouteDescriptorFirstMatch) { - // Request should not be rate limited as it should match first descriptor with 10 req/min - TestScopedRuntime scoped_runtime; - scoped_runtime.mergeValues( - {{"envoy.reloadable_features.local_ratelimit_match_all_descriptors", "false"}}); - setUpTest(fmt::format(fmt::runtime(descriptor_config_yaml), "0", "\"OFF\"", "0", "0")); - - EXPECT_CALL(decoder_callbacks_.route_->route_entry_.rate_limit_policy_, - getApplicableRateLimit(0)); - - EXPECT_CALL(route_rate_limit_, populateLocalDescriptors(_, _, _, _)) - .WillOnce(testing::SetArgReferee<0>(descriptor_first_match_)); - - auto headers = Http::TestRequestHeaderMapImpl(); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(headers, false)); - EXPECT_EQ(1U, findCounter("test.http_local_rate_limit.enabled")); - EXPECT_EQ(0U, findCounter("test.http_local_rate_limit.enforced")); - EXPECT_EQ(0U, findCounter("test.http_local_rate_limit.rate_limited")); -} - TEST_F(DescriptorFilterTest, RouteDescriptorBothMatch) { // Request should also be rate limited as it should match both descriptors and global token. setUpTest(fmt::format(fmt::runtime(descriptor_config_yaml), "0", "\"OFF\"", "0", "0")); diff --git a/test/extensions/filters/http/lua/lua_filter_test.cc b/test/extensions/filters/http/lua/lua_filter_test.cc index 77ea211be5f5..cc7e725b0544 100644 --- a/test/extensions/filters/http/lua/lua_filter_test.cc +++ b/test/extensions/filters/http/lua/lua_filter_test.cc @@ -1594,7 +1594,7 @@ TEST_F(LuaHttpFilterTest, HttpCallInvalidCluster) { EXPECT_EQ(1, stats_store_.counter("test.lua.errors").value()); } -// HTTP request flow with timeout and sampled flag in options. +// HTTP request flow with timeout, sampled and send_xff flag in options. TEST_F(LuaHttpFilterTest, HttpCallWithTimeoutAndSampledInOptions) { const std::string SCRIPT{R"EOF( function envoy_on_request(request_handle) @@ -1609,6 +1609,7 @@ TEST_F(LuaHttpFilterTest, HttpCallWithTimeoutAndSampledInOptions) { { ["timeout_ms"] = 5000, ["trace_sampled"] = false, + ["send_xff"] = false, }) end )EOF"}; @@ -1627,6 +1628,7 @@ TEST_F(LuaHttpFilterTest, HttpCallWithTimeoutAndSampledInOptions) { const Http::AsyncClient::RequestOptions& options) -> Http::AsyncClient::Request* { EXPECT_EQ(options.timeout->count(), 5000); EXPECT_EQ(options.sampled_.value(), false); + EXPECT_EQ(options.send_xff, false); callbacks = &cb; return &request; })); diff --git a/test/extensions/filters/http/rate_limit_quota/client_test.cc b/test/extensions/filters/http/rate_limit_quota/client_test.cc index 02a78d90991d..52d0d99ebd6e 100644 --- a/test/extensions/filters/http/rate_limit_quota/client_test.cc +++ b/test/extensions/filters/http/rate_limit_quota/client_test.cc @@ -63,7 +63,6 @@ class RateLimitStreamTest : public testing::Test { Grpc::Status::GrpcStatus grpc_status_ = Grpc::Status::WellKnownGrpcStatus::Ok; RateLimitClientPtr client_; MockRateLimitQuotaCallbacks callbacks_; - bool grpc_closed_ = false; }; diff --git a/test/extensions/filters/http/rate_limit_quota/filter_test.cc b/test/extensions/filters/http/rate_limit_quota/filter_test.cc index df55ce457776..a2f8174007db 100644 --- a/test/extensions/filters/http/rate_limit_quota/filter_test.cc +++ b/test/extensions/filters/http/rate_limit_quota/filter_test.cc @@ -63,14 +63,145 @@ constexpr char ValidMatcherConfig[] = R"EOF( reporting_interval: 60s )EOF"; +constexpr char InvalidMatcherConfig[] = R"EOF( + matcher_list: + matchers: + predicate: + single_predicate: + input: + typed_config: + "@type": type.googleapis.com/envoy.type.matcher.v3.HttpRequestHeaderMatchInput + header_name: environment + value_match: + exact: staging + on_match: + action: + name: rate_limit_quota + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.rate_limit_quota.v3.RateLimitQuotaBucketSettings + bucket_id_builder: + bucket_id_builder: + "group": + custom_value: + name: "test_2" + typed_config: + "@type": type.googleapis.com/envoy.type.matcher.v3.HttpRequestHeaderMatchInput + # No value is defined here, it will cause the failure of generation of bucket id. + header_name: + reporting_interval: 60s + )EOF"; + constexpr char OnNoMatchConfig[] = R"EOF( - action: - typed_config: - '@type': type.googleapis.com/envoy.extensions.filters.http.rate_limit_quota.v3.RateLimitQuotaBucketSettings - no_assignment_behavior: - fallback_rate_limit: - blanket_rule: DENY_ALL - reporting_interval: 60s + matcher_list: + matchers: + predicate: + single_predicate: + input: + typed_config: + "@type": type.googleapis.com/envoy.type.matcher.v3.HttpRequestHeaderMatchInput + header_name: environment + value_match: + exact: staging + on_match: + action: + name: rate_limit_quota + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.rate_limit_quota.v3.RateLimitQuotaBucketSettings + bucket_id_builder: + bucket_id_builder: + # The on_match field here will not be matched by the request header. + "NO_MATCHED_NAME": + string_value: "NO_MATCHED" + reporting_interval: 60s + on_no_match: + action: + name: rate_limit_quota + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.rate_limit_quota.v3.RateLimitQuotaBucketSettings + bucket_id_builder: + bucket_id_builder: + "on_no_match_name": + string_value: "on_no_match_value" + "on_no_match_name_2": + string_value: "on_no_match_value_2" + deny_response_settings: + grpc_status: + code: 8 + expired_assignment_behavior: + fallback_rate_limit: + blanket_rule: ALLOW_ALL + reporting_interval: 5s +)EOF"; + +// No matcher type (matcher_list or matcher_tree) is configure here. It will read from `on_no_match` +// field directly. +constexpr char OnNoMatchConfigWithNoMatcher[] = R"EOF( + on_no_match: + action: + name: rate_limit_quota + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.rate_limit_quota.v3.RateLimitQuotaBucketSettings + bucket_id_builder: + bucket_id_builder: + "on_no_match_name": + string_value: "on_no_match_value" + "on_no_match_name_2": + string_value: "on_no_match_value_2" + deny_response_settings: + grpc_status: + code: 8 + expired_assignment_behavior: + fallback_rate_limit: + blanket_rule: ALLOW_ALL + reporting_interval: 5s +)EOF"; + +// By design, on_no_match here only supports static bucket_id generation (via string value) and +// doesn't support dynamic way (via custom_value extension). So the configuration with +// `custom_value` typed_config is invalid configuration that will cause the failure of generating +// the bucket id. +constexpr char InvalidOnNoMatcherConfig[] = R"EOF( + matcher_list: + matchers: + predicate: + single_predicate: + input: + typed_config: + "@type": type.googleapis.com/envoy.type.matcher.v3.HttpRequestHeaderMatchInput + header_name: environment + value_match: + exact: staging + # Here is on_match field that will not be matched by the request header. + on_match: + action: + name: rate_limit_quota + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.rate_limit_quota.v3.RateLimitQuotaBucketSettings + bucket_id_builder: + bucket_id_builder: + "NO_MATCHED_NAME": + string_value: "NO_MATCHED" + reporting_interval: 60s + on_no_match: + action: + name: rate_limit_quota + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.rate_limit_quota.v3.RateLimitQuotaBucketSettings + bucket_id_builder: + bucket_id_builder: + "environment": + custom_value: + name: "test_1" + typed_config: + "@type": type.googleapis.com/envoy.type.matcher.v3.HttpRequestHeaderMatchInput + header_name: environment + deny_response_settings: + grpc_status: + code: 8 + expired_assignment_behavior: + fallback_rate_limit: + blanket_rule: ALLOW_ALL + reporting_interval: 5s )EOF"; const std::string GoogleGrpcConfig = R"EOF( @@ -116,7 +247,14 @@ const std::string GoogleGrpcConfig = R"EOF( // string_value: "prod" // )EOF"; -enum class MatcherConfigType { Valid, Invalid, IncludeOnNoMatchConfig }; +enum class MatcherConfigType { + Valid, + Invalid, + Empty, + NoMatcher, + ValidOnNoMatchConfig, + InvalidOnNoMatchConfig +}; class FilterTest : public testing::Test { public: @@ -127,31 +265,40 @@ class FilterTest : public testing::Test { ~FilterTest() override { filter_->onDestroy(); } - void addMatcherConfigAndCreateFilter(MatcherConfigType config_type) { + void addMatcherConfig(MatcherConfigType config_type) { + // Add the matcher configuration. + xds::type::matcher::v3::Matcher matcher; switch (config_type) { case MatcherConfigType::Valid: { - xds::type::matcher::v3::Matcher matcher; TestUtility::loadFromYaml(ValidMatcherConfig, matcher); - config_.mutable_bucket_matchers()->MergeFrom(matcher); break; } - case MatcherConfigType::IncludeOnNoMatchConfig: { - xds::type::matcher::v3::Matcher::OnMatch on_no_matcher; - TestUtility::loadFromYaml(OnNoMatchConfig, on_no_matcher); - - xds::type::matcher::v3::Matcher matcher; - TestUtility::loadFromYaml(ValidMatcherConfig, matcher); - - matcher.mutable_on_no_match()->MergeFrom(on_no_matcher); - config_.mutable_bucket_matchers()->MergeFrom(matcher); + case MatcherConfigType::ValidOnNoMatchConfig: { + TestUtility::loadFromYaml(OnNoMatchConfig, matcher); + break; + } + case MatcherConfigType::Invalid: { + TestUtility::loadFromYaml(InvalidMatcherConfig, matcher); + break; + } + case MatcherConfigType::InvalidOnNoMatchConfig: { + TestUtility::loadFromYaml(InvalidOnNoMatcherConfig, matcher); break; } - // Invalid bucket_matcher configuration will be just empty matcher config. - case MatcherConfigType::Invalid: + case MatcherConfigType::NoMatcher: { + TestUtility::loadFromYaml(OnNoMatchConfigWithNoMatcher, matcher); + break; + } + case MatcherConfigType::Empty: default: break; } + + // Empty matcher config will not have the bucket matcher configured. + if (config_type != MatcherConfigType::Empty) { + config_.mutable_bucket_matchers()->MergeFrom(matcher); + } } void createFilter(bool set_callback = true) { @@ -181,6 +328,31 @@ class FilterTest : public testing::Test { } } + void verifyRequestMatchingSucceeded( + const absl::flat_hash_map& expected_bucket_ids) { + // Perform request matching. + auto match_result = filter_->requestMatching(default_headers_); + // Asserts that the request matching succeeded. + // OK status is expected to be returned even if the exact request matching failed. It is because + // `on_no_match` field is configured. + ASSERT_TRUE(match_result.ok()); + // Retrieve the matched action. + const RateLimitOnMactchAction* match_action = + dynamic_cast(match_result.value().get()); + + RateLimitQuotaValidationVisitor visitor = {}; + // Generate the bucket ids. + auto ret = match_action->generateBucketId(filter_->matchingData(), context_, visitor); + // Asserts that the bucket id generation succeeded and then retrieve the bucket ids. + ASSERT_TRUE(ret.ok()); + auto bucket_ids = ret.value().bucket(); + auto serialized_bucket_ids = + absl::flat_hash_map(bucket_ids.begin(), bucket_ids.end()); + // Verifies that the expected bucket ids are generated for `on_no_match` case. + EXPECT_THAT(expected_bucket_ids, + testing::UnorderedPointwise(testing::Eq(), serialized_bucket_ids)); + } + NiceMock context_; NiceMock decoder_callbacks_; @@ -191,17 +363,17 @@ class FilterTest : public testing::Test { {":method", "GET"}, {":path", "/"}, {":scheme", "http"}, {":authority", "host"}}; }; -TEST_F(FilterTest, InvalidBucketMatcherConfig) { - addMatcherConfigAndCreateFilter(MatcherConfigType::Invalid); +TEST_F(FilterTest, EmptyMatcherConfig) { + addMatcherConfig(MatcherConfigType::Empty); createFilter(); - auto match = filter_->requestMatching(default_headers_); - EXPECT_FALSE(match.ok()); - EXPECT_THAT(match, StatusIs(absl::StatusCode::kInternal)); - EXPECT_EQ(match.status().message(), "Matcher has not been initialized yet"); + auto match_result = filter_->requestMatching(default_headers_); + EXPECT_FALSE(match_result.ok()); + EXPECT_THAT(match_result, StatusIs(absl::StatusCode::kInternal)); + EXPECT_EQ(match_result.status().message(), "Matcher tree has not been initialized yet."); } TEST_F(FilterTest, RequestMatchingSucceeded) { - addMatcherConfigAndCreateFilter(MatcherConfigType::Valid); + addMatcherConfig(MatcherConfigType::Valid); createFilter(); // Define the key value pairs that is used to build the bucket_id dynamically via `custom_value` // in the config. @@ -214,25 +386,14 @@ TEST_F(FilterTest, RequestMatchingSucceeded) { // from the config. absl::flat_hash_map expected_bucket_ids = custom_value_pairs; expected_bucket_ids.insert({"name", "prod"}); - - // Perform request matching and get the generated bucket ids if matched. - auto match = filter_->requestMatching(default_headers_); - EXPECT_TRUE(match.ok()); - auto bucket_ids = match.value().bucket(); - - // Serialize the proto map to std map for comparison. We can avoid this conversion by using - // `EqualsProto()` directly once it is available in the Envoy code base. - auto serialized_bucket_ids = - absl::flat_hash_map(bucket_ids.begin(), bucket_ids.end()); - EXPECT_THAT(expected_bucket_ids, - testing::UnorderedPointwise(testing::Eq(), serialized_bucket_ids)); + verifyRequestMatchingSucceeded(expected_bucket_ids); envoy::service::rate_limit_quota::v3::RateLimitQuotaResponse resp; filter_->onQuotaResponse(resp); } TEST_F(FilterTest, RequestMatchingFailed) { - addMatcherConfigAndCreateFilter(MatcherConfigType::Valid); + addMatcherConfig(MatcherConfigType::Valid); createFilter(); constructMismatchedRequestHeader(); @@ -241,11 +402,11 @@ TEST_F(FilterTest, RequestMatchingFailed) { // Not_OK status is expected to be returned because the matching failed due to mismatched inputs. EXPECT_FALSE(match.ok()); EXPECT_THAT(match, StatusIs(absl::StatusCode::kNotFound)); - EXPECT_EQ(match.status().message(), "The match was completed, no match found"); + EXPECT_EQ(match.status().message(), "Matching completed but no match result was found."); } TEST_F(FilterTest, RequestMatchingFailedWithNoCallback) { - addMatcherConfigAndCreateFilter(MatcherConfigType::Valid); + addMatcherConfig(MatcherConfigType::Valid); createFilter(/*set_callback*/ false); auto match = filter_->requestMatching(default_headers_); @@ -254,23 +415,64 @@ TEST_F(FilterTest, RequestMatchingFailedWithNoCallback) { EXPECT_EQ(match.status().message(), "Filter callback has not been initialized successfully yet."); } -TEST_F(FilterTest, RequestMatchingFailedWithOnNoMatchConfigured) { - addMatcherConfigAndCreateFilter(MatcherConfigType::IncludeOnNoMatchConfig); +TEST_F(FilterTest, RequestMatchingWithOnNoMatch) { + addMatcherConfig(MatcherConfigType::ValidOnNoMatchConfig); + createFilter(); + absl::flat_hash_map expected_bucket_ids = { + {"on_no_match_name", "on_no_match_value"}, {"on_no_match_name_2", "on_no_match_value_2"}}; + verifyRequestMatchingSucceeded(expected_bucket_ids); +} + +TEST_F(FilterTest, RequestMatchingOnNoMatchWithNoMatcher) { + addMatcherConfig(MatcherConfigType::NoMatcher); + createFilter(); + absl::flat_hash_map expected_bucket_ids = { + {"on_no_match_name", "on_no_match_value"}, {"on_no_match_name_2", "on_no_match_value_2"}}; + verifyRequestMatchingSucceeded(expected_bucket_ids); +} + +TEST_F(FilterTest, RequestMatchingWithInvalidOnNoMatch) { + addMatcherConfig(MatcherConfigType::InvalidOnNoMatchConfig); createFilter(); - constructMismatchedRequestHeader(); // Perform request matching. - auto match = filter_->requestMatching(default_headers_); + auto match_result = filter_->requestMatching(default_headers_); + // Asserts that the request matching succeeded. // OK status is expected to be returned even if the exact request matching failed. It is because // `on_no_match` field is configured. - EXPECT_TRUE(match.ok()); - // Empty BucketId is expected to be returned here. - EXPECT_EQ(match.value().bucket().size(), 0); + ASSERT_TRUE(match_result.ok()); + // Retrieve the matched action. + const RateLimitOnMactchAction* match_action = + dynamic_cast(match_result.value().get()); + + RateLimitQuotaValidationVisitor visitor = {}; + // Generate the bucket ids. + auto ret = match_action->generateBucketId(filter_->matchingData(), context_, visitor); + // Bucket id generation is expected to fail, which is due to no support for dynamic id generation + // (i.e., via custom_value with for on_no_match case. + EXPECT_FALSE(ret.ok()); + EXPECT_EQ(ret.status().message(), "Failed to generate the id from custom value config."); +} + +TEST_F(FilterTest, DecodeHeaderWithValidConfig) { + addMatcherConfig(MatcherConfigType::Valid); + createFilter(); + + // Define the key value pairs that is used to build the bucket_id dynamically via `custom_value` + // in the config. + absl::flat_hash_map custom_value_pairs = {{"environment", "staging"}, + {"group", "envoy"}}; + + buildCustomHeader(custom_value_pairs); + + Http::FilterHeadersStatus status = filter_->decodeHeaders(default_headers_, false); + EXPECT_EQ(status, Envoy::Http::FilterHeadersStatus::Continue); } -TEST_F(FilterTest, DecodeHeaderSucceeded) { - addMatcherConfigAndCreateFilter(MatcherConfigType::Valid); +TEST_F(FilterTest, DecodeHeaderWithInValidConfig) { + addMatcherConfig(MatcherConfigType::Invalid); createFilter(); + // Define the key value pairs that is used to build the bucket_id dynamically via `custom_value` // in the config. absl::flat_hash_map custom_value_pairs = {{"environment", "staging"}, @@ -280,8 +482,23 @@ TEST_F(FilterTest, DecodeHeaderSucceeded) { EXPECT_EQ(status, Envoy::Http::FilterHeadersStatus::Continue); } +TEST_F(FilterTest, DecodeHeaderWithOnNoMatchConfigured) { + addMatcherConfig(MatcherConfigType::ValidOnNoMatchConfig); + createFilter(); + + Http::FilterHeadersStatus status = filter_->decodeHeaders(default_headers_, false); + EXPECT_EQ(status, Envoy::Http::FilterHeadersStatus::Continue); +} + +TEST_F(FilterTest, DecodeHeaderWithEmptyConfig) { + addMatcherConfig(MatcherConfigType::Empty); + createFilter(); + Http::FilterHeadersStatus status = filter_->decodeHeaders(default_headers_, false); + EXPECT_EQ(status, Envoy::Http::FilterHeadersStatus::Continue); +} + TEST_F(FilterTest, DecodeHeaderWithMismatchHeader) { - addMatcherConfigAndCreateFilter(MatcherConfigType::Valid); + addMatcherConfig(MatcherConfigType::Valid); createFilter(); constructMismatchedRequestHeader(); diff --git a/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc b/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc index e19f8f5f23c8..c27e0f0fd939 100644 --- a/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc +++ b/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc @@ -4,6 +4,7 @@ #include "envoy/common/platform.h" #include "envoy/config/core/v3/base.pb.h" +#include "envoy/config/core/v3/proxy_protocol.pb.h" #include "envoy/stats/scope.h" #include "source/common/api/os_sys_calls_impl.h" @@ -11,6 +12,7 @@ #include "source/common/event/dispatcher_impl.h" #include "source/common/network/connection_balancer_impl.h" #include "source/common/network/listen_socket_impl.h" +#include "source/common/network/proxy_protocol_filter_state.h" #include "source/common/network/raw_buffer_socket.h" #include "source/common/network/tcp_listener_impl.h" #include "source/common/network/utility.h" @@ -31,6 +33,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +using envoy::config::core::v3::ProxyProtocolPassThroughTLVs; using Envoy::Extensions::Common::ProxyProtocol::PROXY_PROTO_V1_SIGNATURE_LEN; using Envoy::Extensions::Common::ProxyProtocol::PROXY_PROTO_V2_SIGNATURE_LEN; using testing::_; @@ -466,6 +469,11 @@ TEST_P(ProxyProtocolTest, ErrorRecv_2) { .WillRepeatedly(Invoke([this](Api::InterfaceAddressVector& vector) -> Api::SysCallIntResult { return os_sys_calls_actual_.getifaddrs(vector); })); + EXPECT_CALL(os_sys_calls, socketTcpInfo(_, _)) + .Times(AnyNumber()) + .WillRepeatedly(Invoke([this](os_fd_t sockfd, Api::EnvoyTcpInfo* tcp_info) { + return os_sys_calls_actual_.socketTcpInfo(sockfd, tcp_info); + })); connect(false); write(buffer, sizeof(buffer)); @@ -541,6 +549,11 @@ TEST_P(ProxyProtocolTest, ErrorRecv_1) { .WillRepeatedly(Invoke([this](Api::InterfaceAddressVector& vector) -> Api::SysCallIntResult { return os_sys_calls_actual_.getifaddrs(vector); })); + EXPECT_CALL(os_sys_calls, socketTcpInfo(_, _)) + .Times(AnyNumber()) + .WillRepeatedly(Invoke([this](os_fd_t sockfd, Api::EnvoyTcpInfo* tcp_info) { + return os_sys_calls_actual_.socketTcpInfo(sockfd, tcp_info); + })); connect(false); write(buffer, sizeof(buffer)); @@ -822,6 +835,11 @@ TEST_P(ProxyProtocolTest, V2ParseExtensionsRecvError) { .WillRepeatedly(Invoke([this](Api::InterfaceAddressVector& vector) -> Api::SysCallIntResult { return os_sys_calls_actual_.getifaddrs(vector); })); + EXPECT_CALL(os_sys_calls, socketTcpInfo(_, _)) + .Times(AnyNumber()) + .WillRepeatedly(Invoke([this](os_fd_t sockfd, Api::EnvoyTcpInfo* tcp_info) { + return os_sys_calls_actual_.socketTcpInfo(sockfd, tcp_info); + })); connect(false); write(buffer, sizeof(buffer)); dispatcher_->run(Event::Dispatcher::RunType::NonBlock); @@ -1023,6 +1041,11 @@ TEST_P(ProxyProtocolTest, V2Fragmented4Error) { .WillRepeatedly(Invoke([this](Api::InterfaceAddressVector& vector) -> Api::SysCallIntResult { return os_sys_calls_actual_.getifaddrs(vector); })); + EXPECT_CALL(os_sys_calls, socketTcpInfo(_, _)) + .Times(AnyNumber()) + .WillRepeatedly(Invoke([this](os_fd_t sockfd, Api::EnvoyTcpInfo* tcp_info) { + return os_sys_calls_actual_.socketTcpInfo(sockfd, tcp_info); + })); connect(false); write(buffer, 17); dispatcher_->run(Event::Dispatcher::RunType::NonBlock); @@ -1116,6 +1139,11 @@ TEST_P(ProxyProtocolTest, V2Fragmented5Error) { .WillRepeatedly(Invoke([this](Api::InterfaceAddressVector& vector) -> Api::SysCallIntResult { return os_sys_calls_actual_.getifaddrs(vector); })); + EXPECT_CALL(os_sys_calls, socketTcpInfo(_, _)) + .Times(AnyNumber()) + .WillRepeatedly(Invoke([this](os_fd_t sockfd, Api::EnvoyTcpInfo* tcp_info) { + return os_sys_calls_actual_.socketTcpInfo(sockfd, tcp_info); + })); connect(false); write(buffer, 10); dispatcher_->run(Event::Dispatcher::RunType::NonBlock); @@ -1556,6 +1584,129 @@ TEST_P(ProxyProtocolTest, V2IncompleteTLV) { expectProxyProtoError(); } +TEST_P(ProxyProtocolTest, V2ExtractTLVToFilterState) { + // A well-formed ipv4/tcp with a pair of TLV extensions is accepted + constexpr uint8_t buffer[] = {0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, + 0x54, 0x0a, 0x21, 0x11, 0x00, 0x1a, 0x01, 0x02, 0x03, 0x04, + 0x00, 0x01, 0x01, 0x02, 0x03, 0x05, 0x00, 0x02}; + constexpr uint8_t tlv1[] = {0x0, 0x0, 0x1, 0xff}; + constexpr uint8_t tlv_type_authority[] = {0x02, 0x00, 0x07, 0x66, 0x6f, + 0x6f, 0x2e, 0x63, 0x6f, 0x6d}; + constexpr uint8_t data[] = {'D', 'A', 'T', 'A'}; + + envoy::extensions::filters::listener::proxy_protocol::v3::ProxyProtocol proto_config; + auto rule = proto_config.add_rules(); + rule->set_tlv_type(0x02); + rule->mutable_on_tlv_present()->set_key("PP2 type authority"); + + auto pass_through_tlvs = proto_config.mutable_pass_through_tlvs(); + pass_through_tlvs->set_match_type(ProxyProtocolPassThroughTLVs::INCLUDE_ALL); + + connect(true, &proto_config); + write(buffer, sizeof(buffer)); + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + + write(tlv1, sizeof(tlv1)); + write(tlv_type_authority, sizeof(tlv_type_authority)); + write(data, sizeof(data)); + expectData("DATA"); + + auto& filter_state = server_connection_->streamInfo().filterState(); + EXPECT_TRUE(filter_state->hasData( + Network::ProxyProtocolFilterState::key())); + + const auto& proxy_proto_data = filter_state + ->getDataReadOnly( + Network::ProxyProtocolFilterState::key()) + ->value(); + + EXPECT_EQ(2, proxy_proto_data.tlv_vector_.size()); + EXPECT_EQ(0x0, proxy_proto_data.tlv_vector_[0].type); + EXPECT_EQ(0xFF, proxy_proto_data.tlv_vector_[0].value[0]); + EXPECT_EQ(1, proxy_proto_data.tlv_vector_[0].value.size()); + EXPECT_EQ(0x02, proxy_proto_data.tlv_vector_[1].type); + EXPECT_EQ("foo.com", std::string(proxy_proto_data.tlv_vector_[1].value.begin(), + proxy_proto_data.tlv_vector_[1].value.end())); + + disconnect(); +} + +TEST_P(ProxyProtocolTest, V2ExtractTLVToFilterStateIncludeEmpty) { + // A well-formed ipv4/tcp with a pair of TLV extensions is accepted + constexpr uint8_t buffer[] = {0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, + 0x54, 0x0a, 0x21, 0x11, 0x00, 0x1a, 0x01, 0x02, 0x03, 0x04, + 0x00, 0x01, 0x01, 0x02, 0x03, 0x05, 0x00, 0x02}; + constexpr uint8_t tlv1[] = {0x0, 0x0, 0x1, 0xff}; + constexpr uint8_t tlv_type_authority[] = {0x02, 0x00, 0x07, 0x66, 0x6f, + 0x6f, 0x2e, 0x63, 0x6f, 0x6d}; + constexpr uint8_t data[] = {'D', 'A', 'T', 'A'}; + envoy::extensions::filters::listener::proxy_protocol::v3::ProxyProtocol proto_config; + + auto pass_through_tlvs = proto_config.mutable_pass_through_tlvs(); + pass_through_tlvs->set_match_type(ProxyProtocolPassThroughTLVs::INCLUDE); + + connect(true, &proto_config); + write(buffer, sizeof(buffer)); + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + + write(tlv1, sizeof(tlv1)); + write(tlv_type_authority, sizeof(tlv_type_authority)); + write(data, sizeof(data)); + expectData("DATA"); + + auto& filter_state = server_connection_->streamInfo().filterState(); + EXPECT_TRUE(filter_state->hasData( + Network::ProxyProtocolFilterState::key())); + + const auto& proxy_proto_data = filter_state + ->getDataReadOnly( + Network::ProxyProtocolFilterState::key()) + ->value(); + + EXPECT_EQ(0, proxy_proto_data.tlv_vector_.size()); + disconnect(); +} + +TEST_P(ProxyProtocolTest, V2ExtractTLVToFilterStateIncludeTlV) { + // A well-formed ipv4/tcp with a pair of TLV extensions is accepted + constexpr uint8_t buffer[] = {0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, + 0x54, 0x0a, 0x21, 0x11, 0x00, 0x1a, 0x01, 0x02, 0x03, 0x04, + 0x00, 0x01, 0x01, 0x02, 0x03, 0x05, 0x00, 0x02}; + constexpr uint8_t tlv1[] = {0x0, 0x0, 0x1, 0xff}; + constexpr uint8_t tlv_type_authority[] = {0x02, 0x00, 0x07, 0x66, 0x6f, + 0x6f, 0x2e, 0x63, 0x6f, 0x6d}; + constexpr uint8_t data[] = {'D', 'A', 'T', 'A'}; + envoy::extensions::filters::listener::proxy_protocol::v3::ProxyProtocol proto_config; + + auto pass_through_tlvs = proto_config.mutable_pass_through_tlvs(); + pass_through_tlvs->set_match_type(ProxyProtocolPassThroughTLVs::INCLUDE); + pass_through_tlvs->add_tlv_type(0x02); + + connect(true, &proto_config); + write(buffer, sizeof(buffer)); + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + + write(tlv1, sizeof(tlv1)); + write(tlv_type_authority, sizeof(tlv_type_authority)); + write(data, sizeof(data)); + expectData("DATA"); + + auto& filter_state = server_connection_->streamInfo().filterState(); + EXPECT_TRUE(filter_state->hasData( + Network::ProxyProtocolFilterState::key())); + + const auto& proxy_proto_data = filter_state + ->getDataReadOnly( + Network::ProxyProtocolFilterState::key()) + ->value(); + + EXPECT_EQ(1, proxy_proto_data.tlv_vector_.size()); + EXPECT_EQ(0x02, proxy_proto_data.tlv_vector_[0].type); + EXPECT_EQ("foo.com", std::string(proxy_proto_data.tlv_vector_[0].value.begin(), + proxy_proto_data.tlv_vector_[0].value.end())); + disconnect(); +} + TEST_P(ProxyProtocolTest, MalformedProxyLine) { connect(false); @@ -1733,6 +1884,11 @@ TEST_P(ProxyProtocolTest, DrainError) { .WillRepeatedly(Invoke([this](Api::InterfaceAddressVector& vector) -> Api::SysCallIntResult { return os_sys_calls_actual_.getifaddrs(vector); })); + EXPECT_CALL(os_sys_calls, socketTcpInfo(_, _)) + .Times(AnyNumber()) + .WillRepeatedly(Invoke([this](os_fd_t sockfd, Api::EnvoyTcpInfo* tcp_info) { + return os_sys_calls_actual_.socketTcpInfo(sockfd, tcp_info); + })); connect(false); write("PROXY TCP4 1.2.3.4 253.253.253.253 65535 1234\r\nmore data"); diff --git a/test/extensions/filters/network/common/fuzz/uber_readfilter.cc b/test/extensions/filters/network/common/fuzz/uber_readfilter.cc index a75f38a94200..819a36b1151e 100644 --- a/test/extensions/filters/network/common/fuzz/uber_readfilter.cc +++ b/test/extensions/filters/network/common/fuzz/uber_readfilter.cc @@ -8,6 +8,12 @@ using testing::Return; namespace Envoy { namespace Extensions { namespace NetworkFilters { +namespace { + +const char kLocalCloseReason[] = "fuzz_local_close_reason"; + +} // namespace + void UberFilterFuzzer::reset() { // Reset some changes made by current filter on some mock objects. @@ -41,6 +47,8 @@ void UberFilterFuzzer::fuzzerSetup() { read_filter_ = read_filter; read_filter_->initializeReadFilterCallbacks(*read_filter_callbacks_); })); + ON_CALL(read_filter_callbacks_->connection_, localCloseReason()) + .WillByDefault(Return(kLocalCloseReason)); // Prepare sni for sni_cluster filter and sni_dynamic_forward_proxy filter. ON_CALL(read_filter_callbacks_->connection_, requestedServerName()) diff --git a/test/extensions/filters/network/ext_authz/ext_authz_test.cc b/test/extensions/filters/network/ext_authz/ext_authz_test.cc index 81c3723e7546..29e9f6e388c4 100644 --- a/test/extensions/filters/network/ext_authz/ext_authz_test.cc +++ b/test/extensions/filters/network/ext_authz/ext_authz_test.cc @@ -214,7 +214,7 @@ TEST_F(ExtAuthzFilterTest, DeniedWithOnData) { 1U, stats_store_.gauge("ext_authz.name.active", Stats::Gauge::ImportMode::Accumulate).value()); - EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); EXPECT_CALL(filter_callbacks_.connection_.stream_info_, setResponseFlag(StreamInfo::ResponseFlag::UnauthorizedExternalService)); EXPECT_CALL( @@ -288,7 +288,7 @@ TEST_F(ExtAuthzFilterTest, FailClose) { Buffer::OwnedImpl data("hello"); EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onData(data, false)); - EXPECT_CALL(filter_callbacks_.connection_, close(_)); + EXPECT_CALL(filter_callbacks_.connection_, close(_, _)); EXPECT_CALL(filter_callbacks_, continueReading()).Times(0); EXPECT_CALL(filter_callbacks_.connection_.stream_info_, setResponseFlag(StreamInfo::ResponseFlag::UnauthorizedExternalService)); diff --git a/test/extensions/filters/network/ratelimit/ratelimit_test.cc b/test/extensions/filters/network/ratelimit/ratelimit_test.cc index e0efd357624d..f6dc09540d6e 100644 --- a/test/extensions/filters/network/ratelimit/ratelimit_test.cc +++ b/test/extensions/filters/network/ratelimit/ratelimit_test.cc @@ -264,7 +264,7 @@ TEST_F(RateLimitFilterTest, OverLimit) { Buffer::OwnedImpl data("hello"); EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onData(data, false)); - EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); EXPECT_CALL(*client_, cancel()).Times(0); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OverLimit, nullptr, nullptr, nullptr, "", nullptr); @@ -304,7 +304,7 @@ TEST_F(RateLimitFilterTest, OverLimitWithDynamicMetadata) { EXPECT_TRUE(TestUtility::protoEqual(returned_dynamic_metadata, *dynamic_metadata)); })); - EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); EXPECT_CALL(*client_, cancel()).Times(0); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OverLimit, nullptr, nullptr, nullptr, "", std::move(dynamic_metadata)); @@ -332,7 +332,7 @@ TEST_F(RateLimitFilterTest, OverLimitNotEnforcing) { EXPECT_CALL(runtime_.snapshot_, featureEnabled("ratelimit.tcp_filter_enforcing", 100)) .WillOnce(Return(false)); - EXPECT_CALL(filter_callbacks_.connection_, close(_)).Times(0); + EXPECT_CALL(filter_callbacks_.connection_, close(_, _)).Times(0); EXPECT_CALL(*client_, cancel()).Times(0); EXPECT_CALL(filter_callbacks_, continueReading()); request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OverLimit, nullptr, nullptr, diff --git a/test/extensions/filters/network/redis_proxy/conn_pool_impl_test.cc b/test/extensions/filters/network/redis_proxy/conn_pool_impl_test.cc index daa2e07ed0fb..27e9bcd0014b 100644 --- a/test/extensions/filters/network/redis_proxy/conn_pool_impl_test.cc +++ b/test/extensions/filters/network/redis_proxy/conn_pool_impl_test.cc @@ -1032,11 +1032,12 @@ TEST_F(RedisConnPoolImplTest, HostsAddedAndEndWithClusterRemoval) { TEST_F(RedisConnPoolImplTest, MakeRequestToRedisCluster) { - absl::optional cluster_type; - cluster_type.emplace(); - cluster_type->set_name("envoy.clusters.redis"); + envoy::config::cluster::v3::Cluster::CustomClusterType cluster_type; + cluster_type.set_name("envoy.clusters.redis"); + EXPECT_CALL(*cm_.thread_local_cluster_.cluster_.info_, clusterType()) - .WillOnce(ReturnRef(cluster_type)); + .WillOnce(Return( + makeOptRef(cluster_type))); EXPECT_CALL(*cm_.thread_local_cluster_.cluster_.info_, lbType()) .WillOnce(Return(Upstream::LoadBalancerType::ClusterProvided)); @@ -1052,11 +1053,12 @@ TEST_F(RedisConnPoolImplTest, MakeRequestToRedisCluster) { TEST_F(RedisConnPoolImplTest, MakeRequestToRedisClusterHashtag) { - absl::optional cluster_type; - cluster_type.emplace(); - cluster_type->set_name("envoy.clusters.redis"); + envoy::config::cluster::v3::Cluster::CustomClusterType cluster_type; + cluster_type.set_name("envoy.clusters.redis"); + EXPECT_CALL(*cm_.thread_local_cluster_.cluster_.info_, clusterType()) - .WillOnce(ReturnRef(cluster_type)); + .WillOnce(Return( + makeOptRef(cluster_type))); EXPECT_CALL(*cm_.thread_local_cluster_.cluster_.info_, lbType()) .WillOnce(Return(Upstream::LoadBalancerType::ClusterProvided)); diff --git a/test/extensions/tracers/datadog/BUILD b/test/extensions/tracers/datadog/BUILD index fd70bcc5b2e8..737dc0a6c163 100644 --- a/test/extensions/tracers/datadog/BUILD +++ b/test/extensions/tracers/datadog/BUILD @@ -15,8 +15,19 @@ envoy_extension_cc_test( name = "datadog_tracer_impl_test", srcs = [ "datadog_tracer_impl_test.cc", + "dict_util_test.cc", + "time_util_test.cc", + "tracer_stats_test.cc", + ], + copts = [ + # Make sure that headers included from dd_trace_cpp use Abseil + # equivalents of std::string_view and std::optional. + "-DDD_USE_ABSEIL_FOR_ENVOY", ], extension_names = ["envoy.tracers.datadog"], + external_deps = [ + "dd_trace_cpp", + ], # TODO(wrowe): envoy_extension_ rules don't currently exclude windows extensions tags = ["skip_on_windows"], deps = [ diff --git a/test/extensions/tracers/datadog/dict_util_test.cc b/test/extensions/tracers/datadog/dict_util_test.cc new file mode 100644 index 000000000000..d20e669c101b --- /dev/null +++ b/test/extensions/tracers/datadog/dict_util_test.cc @@ -0,0 +1,98 @@ +#include + +#include +#include + +#include "envoy/http/header_map.h" + +#include "source/extensions/tracers/datadog/dict_util.h" + +#include "test/mocks/http/mocks.h" +#include "test/test_common/utility.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace Tracers { +namespace Datadog { +namespace { + +TEST(DatadogTracerDictUtilTest, RequestHeaderWriter) { + Http::TestRequestHeaderMapImpl headers; + RequestHeaderWriter writer{headers}; + + writer.set("foo", "bar"); + writer.set("FOO", "baz"); + writer.set("sniff", "wiggle"); + + auto result = headers.get(Http::LowerCaseString{"foo"}); + ASSERT_EQ(1, result.size()); + EXPECT_EQ("baz", result[0]->value().getStringView()); + + result = headers.get(Http::LowerCaseString{"sniff"}); + ASSERT_EQ(1, result.size()); + EXPECT_EQ("wiggle", result[0]->value().getStringView()); + + result = headers.get(Http::LowerCaseString{"missing"}); + EXPECT_EQ(0, result.size()); +} + +TEST(DatadogTracerDictUtilTest, ResponseHeaderReader) { + Http::TestResponseHeaderMapImpl headers{ + {"fish", "face"}, + {"fish", "flakes"}, + {"UPPER", "case"}, + }; + ResponseHeaderReader reader{headers}; + + auto result = reader.lookup("fish"); + EXPECT_EQ("face, flakes", result); + + result = reader.lookup("upper"); + EXPECT_EQ("case", result); + + result = reader.lookup("missing"); + EXPECT_EQ(datadog::tracing::nullopt, result); + + std::vector> expected_visitation{ + // These entries are in reverse. We'll `pop_back` as we go. + {"upper", "case"}, + // `lookup` comma-separates duplicate headers, but `visit` does not. + {"fish", "flakes"}, + {"fish", "face"}, + }; + reader.visit([&](const auto& key, const auto& value) { + ASSERT_FALSE(expected_visitation.empty()); + const auto& [expected_key, expected_value] = expected_visitation.back(); + EXPECT_EQ(expected_key, key); + EXPECT_EQ(expected_value, value); + expected_visitation.pop_back(); + }); +} + +TEST(DatadogTracerDictUtilTest, TraceContextReader) { + const Tracing::TestTraceContextImpl context{{"foo", "bar"}, {"boo", "yah"}}; + const TraceContextReader reader{context}; + + auto result = reader.lookup("foo"); + EXPECT_EQ(result, "bar"); + result = reader.lookup("boo"); + EXPECT_EQ(result, "yah"); + result = reader.lookup("snark"); + EXPECT_EQ(result, datadog::tracing::nullopt); + + // `dd-trace-cpp` doesn't call `visit` when it's extracting trace context, but + // the method is nonetheless required by the `DictReader` interface. + reader.visit([&](const auto& key, const auto& value) { + const auto found = context.context_map_.find(key); + ASSERT_NE(context.context_map_.end(), found); + EXPECT_EQ(found->second, value); + }); +} + +} // namespace +} // namespace Datadog +} // namespace Tracers +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/tracers/datadog/time_util_test.cc b/test/extensions/tracers/datadog/time_util_test.cc new file mode 100644 index 000000000000..472677f6dde9 --- /dev/null +++ b/test/extensions/tracers/datadog/time_util_test.cc @@ -0,0 +1,72 @@ +#include + +#include "envoy/common/time.h" + +#include "source/extensions/tracers/datadog/time_util.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace Tracers { +namespace Datadog { +namespace { + +TEST(DatadogTracerTimeUtilTest, EstimateTime) { + // Concerns: + // + // - If the current system time is after the specified time (likely case), + // then the resulting steady time point is set back accordingly. + // - If the current system time is before the specified time (rare case), + // then the resulting steady time point is whatever the clock returns. + + // Modify `now` to change the value returned by `clock`. + datadog::tracing::TimePoint now; + datadog::tracing::Clock clock = [&]() { return now; }; + + // A little time has elapsed since the SystemTime was measured. The + // resulting steady time point should be set back by the difference. + datadog::tracing::TimePoint clock_result = datadog::tracing::default_clock(); + SystemTime argument = clock_result.wall; + clock_result.wall += std::chrono::microseconds(100); + now = clock_result; + datadog::tracing::TimePoint result = estimateTime(argument, clock); + EXPECT_EQ(result.wall, argument); + EXPECT_EQ(result.tick, clock_result.tick - std::chrono::microseconds(100)); + + // The clock has been set back since the SystemTime was measured. The + // resulting steady time can't do better than whatever the clock returns + // (we wouldn't want to set the steady time point into the future). + clock_result = datadog::tracing::default_clock(); + argument = clock_result.wall; + clock_result.wall -= std::chrono::milliseconds(100); + now = clock_result; + result = estimateTime(argument, clock); + EXPECT_EQ(result.wall, argument); + EXPECT_EQ(result.tick, clock_result.tick); +} + +TEST(DatadogTracerTimeUtilTest, DefaultClock) { + // The one-parameter overload of `estimateTime` uses the system clock. + // We can at least check that the steady (monotonic, `.tick`) portion is + // approximately non-decreasing along "before," "during," and "after." + // Only "approximately," because the `datadog::tracing::default_clock` can't + // measure both clocks exactly simultaneously, so its correction to the + // steady time might actually set it back in time some tiny amount. + const datadog::tracing::Clock clock = datadog::tracing::default_clock; + const datadog::tracing::TimePoint before = clock(); + const datadog::tracing::TimePoint estimated_before = estimateTime(before.wall); + const datadog::tracing::TimePoint after = clock(); + + const auto tolerance = std::chrono::microseconds(100); + + EXPECT_LE(before.tick, after.tick); + EXPECT_LE(estimated_before.tick - before.tick, tolerance); + EXPECT_LE(after.tick - estimated_before.tick, tolerance); +} + +} // namespace +} // namespace Datadog +} // namespace Tracers +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/tracers/datadog/tracer_stats_test.cc b/test/extensions/tracers/datadog/tracer_stats_test.cc new file mode 100644 index 000000000000..d88cb207a95f --- /dev/null +++ b/test/extensions/tracers/datadog/tracer_stats_test.cc @@ -0,0 +1,54 @@ +#include "source/extensions/tracers/datadog/tracer_stats.h" + +#include "test/common/stats/stat_test_utility.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace Tracers { +namespace Datadog { +namespace { + +TEST(DatadogTracerTracerStatsTest, TracerStats) { + Stats::TestUtil::TestStore store; + TracerStats stats = makeTracerStats(*store.rootScope()); + + // Verify the names. + EXPECT_EQ(stats.reports_skipped_no_cluster_.name(), "tracing.datadog.reports_skipped_no_cluster"); + EXPECT_EQ(stats.reports_sent_.name(), "tracing.datadog.reports_sent"); + EXPECT_EQ(stats.reports_dropped_.name(), "tracing.datadog.reports_dropped"); + EXPECT_EQ(stats.reports_failed_.name(), "tracing.datadog.reports_failed"); + + // Counters begin at zero. + EXPECT_EQ(0, store.counter("tracing.datadog.reports_skipped_no_cluster").value()); + EXPECT_EQ(0, store.counter("tracing.datadog.reports_sent").value()); + EXPECT_EQ(0, store.counter("tracing.datadog.reports_dropped").value()); + EXPECT_EQ(0, store.counter("tracing.datadog.reports_failed").value()); + + // Increments are reflected in the value. + stats.reports_skipped_no_cluster_.inc(); + EXPECT_EQ(1, store.counter("tracing.datadog.reports_skipped_no_cluster").value()); + stats.reports_sent_.inc(); + EXPECT_EQ(1, store.counter("tracing.datadog.reports_sent").value()); + stats.reports_dropped_.inc(); + EXPECT_EQ(1, store.counter("tracing.datadog.reports_dropped").value()); + stats.reports_failed_.inc(); + EXPECT_EQ(1, store.counter("tracing.datadog.reports_failed").value()); + + // And again. + stats.reports_skipped_no_cluster_.inc(); + EXPECT_EQ(2, store.counter("tracing.datadog.reports_skipped_no_cluster").value()); + stats.reports_sent_.inc(); + EXPECT_EQ(2, store.counter("tracing.datadog.reports_sent").value()); + stats.reports_dropped_.inc(); + EXPECT_EQ(2, store.counter("tracing.datadog.reports_dropped").value()); + stats.reports_failed_.inc(); + EXPECT_EQ(2, store.counter("tracing.datadog.reports_failed").value()); +} + +} // namespace +} // namespace Datadog +} // namespace Tracers +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/transport_sockets/alts/tsi_socket_test.cc b/test/extensions/transport_sockets/alts/tsi_socket_test.cc index 4e41f3e62102..18818846f52f 100644 --- a/test/extensions/transport_sockets/alts/tsi_socket_test.cc +++ b/test/extensions/transport_sockets/alts/tsi_socket_test.cc @@ -295,7 +295,7 @@ TEST_F(TsiSocketTest, HandshakeValidationFail) { EXPECT_EQ(0L, client_.read_buffer_.length()); EXPECT_CALL(*server_.raw_socket_, doRead(_)); - EXPECT_CALL(server_.callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(server_.callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); // doRead won't immediately fail, but it will result connection close. expectIoResult({Network::PostIoAction::KeepOpen, 0UL, false}, server_.tsi_socket_->doRead(server_.read_buffer_)); @@ -312,7 +312,7 @@ TEST_F(TsiSocketTest, HandshakerCreationFail) { InSequence s; EXPECT_CALL(*client_.raw_socket_, doWrite(_, _)).Times(0); - EXPECT_CALL(client_.callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(client_.callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); client_.tsi_socket_->onConnected(); expectIoResult({Network::PostIoAction::KeepOpen, 0UL, false}, client_.tsi_socket_->doWrite(client_.write_buffer_, false)); @@ -422,7 +422,7 @@ TEST_F(TsiSocketTest, HandshakeWithReadError) { return result; })); EXPECT_CALL(*client_.raw_socket_, doWrite(_, false)).Times(0); - EXPECT_CALL(client_.callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(client_.callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); expectIoResult({Network::PostIoAction::KeepOpen, 0UL, false}, client_.tsi_socket_->doRead(client_.read_buffer_)); EXPECT_EQ("", client_to_server_.toString()); @@ -450,7 +450,7 @@ TEST_F(TsiSocketTest, HandshakeWithInternalError) { InSequence s; - EXPECT_CALL(client_.callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(client_.callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); // doWrite won't immediately fail, but it will result connection close. client_.tsi_socket_->onConnected(); diff --git a/test/extensions/transport_sockets/internal_upstream/internal_upstream_integration_test.cc b/test/extensions/transport_sockets/internal_upstream/internal_upstream_integration_test.cc index ce3cdb26d43f..a643d9637147 100644 --- a/test/extensions/transport_sockets/internal_upstream/internal_upstream_integration_test.cc +++ b/test/extensions/transport_sockets/internal_upstream/internal_upstream_integration_test.cc @@ -60,8 +60,8 @@ class InternalUpstreamIntegrationTest : public testing::Test, public HttpIntegra static_resources->mutable_clusters()->Add()); // Emit metadata to access logs. - access_log_name_ = TestEnvironment::temporaryPath(TestUtility::uniqueFilename()); - access_log2_name_ = TestEnvironment::temporaryPath(TestUtility::uniqueFilename()); + access_log_name_ = TestEnvironment::temporaryPath(TestUtility::uniqueFilename("1")); + access_log2_name_ = TestEnvironment::temporaryPath(TestUtility::uniqueFilename("2")); // Insert internal listeners. TestUtility::loadFromYaml( diff --git a/test/extensions/transport_sockets/proxy_protocol/BUILD b/test/extensions/transport_sockets/proxy_protocol/BUILD index efb4f98e3739..cacd476c36e7 100644 --- a/test/extensions/transport_sockets/proxy_protocol/BUILD +++ b/test/extensions/transport_sockets/proxy_protocol/BUILD @@ -32,11 +32,13 @@ envoy_extension_cc_test( srcs = ["proxy_protocol_integration_test.cc"], extension_names = ["envoy.transport_sockets.upstream_proxy_protocol"], deps = [ + "//source/extensions/filters/listener/proxy_protocol:config", "//source/extensions/filters/network/tcp_proxy:config", "//source/extensions/transport_sockets/proxy_protocol:upstream_config", "//test/integration:http_integration_lib", "//test/integration:integration_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/filters/listener/proxy_protocol/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/transport_sockets/proxy_protocol/v3:pkg_cc_proto", ], ) diff --git a/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_integration_test.cc b/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_integration_test.cc index 8e523717c301..af6675d5f3fe 100644 --- a/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_integration_test.cc +++ b/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_integration_test.cc @@ -1,11 +1,13 @@ #include "envoy/config/core/v3/base.pb.h" #include "envoy/config/core/v3/health_check.pb.h" #include "envoy/config/core/v3/proxy_protocol.pb.h" +#include "envoy/extensions/filters/listener/proxy_protocol/v3/proxy_protocol.pb.h" #include "envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.pb.h" #include "test/integration/http_integration.h" #include "test/integration/integration.h" +using envoy::config::core::v3::ProxyProtocolPassThroughTLVs; namespace Envoy { namespace { @@ -398,5 +400,238 @@ TEST_P(ProxyProtocolHttpIntegrationTest, TestProxyProtocolHealthCheck) { ASSERT_TRUE(fake_upstream_health_connection->waitForDisconnect()); } +class ProxyProtocolTLVsIntegrationTest : public testing::TestWithParam, + public BaseIntegrationTest { +public: + ProxyProtocolTLVsIntegrationTest() + : BaseIntegrationTest(GetParam(), ConfigHelper::tcpProxyConfig()){}; + + void TearDown() override { + test_server_.reset(); + fake_upstream_connection_.reset(); + fake_upstreams_.clear(); + } + + void setup(bool pass_all_tlvs, const std::vector& tlvs_listener, + const std::vector& tlvs_upstream) { + pass_all_tlvs_ = pass_all_tlvs; + tlvs_listener_.assign(tlvs_listener.begin(), tlvs_listener.end()); + tlvs_upstream_.assign(tlvs_upstream.begin(), tlvs_upstream.end()); + } + + void initialize() override { + config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + envoy::extensions::filters::listener::proxy_protocol::v3::ProxyProtocol proxy_protocol; + auto pass_through_tlvs = proxy_protocol.mutable_pass_through_tlvs(); + if (pass_all_tlvs_) { + pass_through_tlvs->set_match_type(ProxyProtocolPassThroughTLVs::INCLUDE_ALL); + } else { + pass_through_tlvs->set_match_type(ProxyProtocolPassThroughTLVs::INCLUDE); + for (const auto& tlv_type : tlvs_listener_) { + pass_through_tlvs->add_tlv_type(tlv_type); + } + } + + auto* listener = bootstrap.mutable_static_resources()->mutable_listeners(0); + auto* ppv_filter = listener->add_listener_filters(); + ppv_filter->set_name("envoy.listener.proxy_protocol"); + ppv_filter->mutable_typed_config()->PackFrom(proxy_protocol); + }); + + config_helper_.addConfigModifier([this](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.upstream_proxy_protocol"); + envoy::config::core::v3::TransportSocket inner_socket; + inner_socket.set_name("envoy.transport_sockets.raw_buffer"); + + envoy::config::core::v3::ProxyProtocolConfig proxy_protocol; + proxy_protocol.set_version(envoy::config::core::v3::ProxyProtocolConfig::V2); + auto pass_through_tlvs = proxy_protocol.mutable_pass_through_tlvs(); + + if (pass_all_tlvs_) { + pass_through_tlvs->set_match_type(ProxyProtocolPassThroughTLVs::INCLUDE_ALL); + } else { + pass_through_tlvs->set_match_type(ProxyProtocolPassThroughTLVs::INCLUDE); + for (const auto& tlv_type : tlvs_upstream_) { + pass_through_tlvs->add_tlv_type(tlv_type); + } + } + + envoy::extensions::transport_sockets::proxy_protocol::v3::ProxyProtocolUpstreamTransport + proxy_proto_transport; + proxy_proto_transport.mutable_transport_socket()->MergeFrom(inner_socket); + proxy_proto_transport.mutable_config()->MergeFrom(proxy_protocol); + transport_socket->mutable_typed_config()->PackFrom(proxy_proto_transport); + }); + + BaseIntegrationTest::initialize(); + } + + FakeRawConnectionPtr fake_upstream_connection_; + +private: + bool pass_all_tlvs_ = false; + std::vector tlvs_listener_; + std::vector tlvs_upstream_; +}; + +INSTANTIATE_TEST_SUITE_P(IpVersions, ProxyProtocolTLVsIntegrationTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + TestUtility::ipTestParamsToString); + +// This test adding the listener proxy protocol filter and upstream proxy filter, the TLVs +// are passed by listener and re-generated in transport socket based on API config. +TEST_P(ProxyProtocolTLVsIntegrationTest, TestV2TLVProxyProtocolPassSepcificTLVs) { + setup(false, {0x05, 0x06}, {0x06}); + initialize(); + + IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("listener_0")); + std::string observed_data; + if (GetParam() == Envoy::Network::Address::IpVersion::v4) { + // 2 TLVs are included: + // 0x05, 0x00, 0x02, 0x06, 0x07 + // 0x06, 0x00, 0x02, 0x11, 0x12 + const uint8_t v2_protocol[] = {0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, + 0x54, 0x0a, 0x21, 0x11, 0x00, 0x16, 0x7f, 0x00, 0x00, 0x01, + 0x7f, 0x00, 0x00, 0x01, 0x03, 0x05, 0x02, 0x01, 0x05, 0x00, + 0x02, 0x06, 0x07, 0x06, 0x00, 0x02, 0x11, 0x12}; + Buffer::OwnedImpl buffer(v2_protocol, sizeof(v2_protocol)); + ASSERT_TRUE(tcp_client->write(buffer.toString())); + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection_)); + ASSERT_TRUE(fake_upstream_connection_->waitForData(33, &observed_data)); + + // - signature + // - version and command type, address family and protocol, length of addresses + // - src address, dest address + auto header_start = "\x0d\x0a\x0d\x0a\x00\x0d\x0a\x51\x55\x49\x54\x0a\ + \x21\x11\x00\x16\ + \x7f\x00\x00\x01\x7f\x00\x00\x01"; + EXPECT_THAT(observed_data, testing::StartsWith(header_start)); + + // Only tlv: 0x06, 0x00, 0x02, 0x11, 0x12 is sent to upstream. + EXPECT_EQ(static_cast(observed_data[28]), 0x06); + EXPECT_EQ(static_cast(observed_data[29]), 0x00); + EXPECT_EQ(static_cast(observed_data[30]), 0x02); + EXPECT_EQ(static_cast(observed_data[31]), 0x11); + EXPECT_EQ(static_cast(observed_data[32]), 0x12); + } else if (GetParam() == Envoy::Network::Address::IpVersion::v6) { + // 2 TLVs are included: + // 0x05, 0x00, 0x02, 0x06, 0x07 + // 0x06, 0x00, 0x02, 0x09, 0x0A + const uint8_t v2_protocol_ipv6[] = { + 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, 0x0a, 0x21, + 0x21, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x02, + 0x05, 0x00, 0x02, 0x06, 0x07, 0x06, 0x00, 0x02, 0x09, 0x0A}; + Buffer::OwnedImpl buffer(v2_protocol_ipv6, sizeof(v2_protocol_ipv6)); + ASSERT_TRUE(tcp_client->write(buffer.toString())); + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection_)); + + ASSERT_TRUE(fake_upstream_connection_->waitForData(57, &observed_data)); + // - signature + // - version and command type, address family and protocol, length of addresses + // - src address + // - dest address + auto header_start = "\x0d\x0a\x0d\x0a\x00\x0d\x0a\x51\x55\x49\x54\x0a\ + \x21\x21\x00\x2E\ + \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\ + \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"; + EXPECT_THAT(observed_data, testing::StartsWith(header_start)); + + // Only tlv: 0x06, 0x00, 0x02, 0x09, 0x0A is sent to upstream. + EXPECT_EQ(static_cast(observed_data[52]), 0x06); + EXPECT_EQ(static_cast(observed_data[53]), 0x00); + EXPECT_EQ(static_cast(observed_data[54]), 0x02); + EXPECT_EQ(static_cast(observed_data[55]), 0x09); + EXPECT_EQ(static_cast(observed_data[56]), 0x0A); + } + + tcp_client->close(); + ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); +} + +TEST_P(ProxyProtocolTLVsIntegrationTest, TestV2TLVProxyProtocolPassAll) { + setup(true, {}, {}); + initialize(); + + IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("listener_0")); + ; + std::string observed_data; + if (GetParam() == Envoy::Network::Address::IpVersion::v4) { + // 2 TLVs are included: + // 0x05, 0x00, 0x02, 0x06, 0x07 + // 0x06, 0x00, 0x02, 0x11, 0x12 + const uint8_t v2_protocol[] = {0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, + 0x54, 0x0a, 0x21, 0x11, 0x00, 0x16, 0x7f, 0x00, 0x00, 0x01, + 0x7f, 0x00, 0x00, 0x01, 0x03, 0x05, 0x02, 0x01, 0x05, 0x00, + 0x02, 0x06, 0x07, 0x06, 0x00, 0x02, 0x11, 0x12}; + Buffer::OwnedImpl buffer(v2_protocol, sizeof(v2_protocol)); + ASSERT_TRUE(tcp_client->write(buffer.toString())); + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection_)); + ASSERT_TRUE(fake_upstream_connection_->waitForData(38, &observed_data)); + + // - signature + // - version and command type, address family and protocol, length of addresses + // - src address, dest address + auto header_start = "\x0d\x0a\x0d\x0a\x00\x0d\x0a\x51\x55\x49\x54\x0a\ + \x21\x11\x00\x16\ + \x7f\x00\x00\x01\x7f\x00\x00\x01"; + EXPECT_THAT(observed_data, testing::StartsWith(header_start)); + + // Only tlv: 0x06, 0x00, 0x02, 0x11, 0x12 is sent to upstream. + EXPECT_EQ(static_cast(observed_data[28]), 0x05); + EXPECT_EQ(static_cast(observed_data[29]), 0x00); + EXPECT_EQ(static_cast(observed_data[30]), 0x02); + EXPECT_EQ(static_cast(observed_data[31]), 0x06); + EXPECT_EQ(static_cast(observed_data[32]), 0x07); + EXPECT_EQ(static_cast(observed_data[33]), 0x06); + EXPECT_EQ(static_cast(observed_data[34]), 0x00); + EXPECT_EQ(static_cast(observed_data[35]), 0x02); + EXPECT_EQ(static_cast(observed_data[36]), 0x11); + EXPECT_EQ(static_cast(observed_data[37]), 0x12); + } else if (GetParam() == Envoy::Network::Address::IpVersion::v6) { + // 2 TLVs are included: + // 0x05, 0x00, 0x02, 0x06, 0x07 + // 0x06, 0x00, 0x02, 0x09, 0x0A + const uint8_t v2_protocol_ipv6[] = { + 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, 0x0a, 0x21, + 0x21, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x02, + 0x05, 0x00, 0x02, 0x06, 0x07, 0x06, 0x00, 0x02, 0x09, 0x0A}; + Buffer::OwnedImpl buffer(v2_protocol_ipv6, sizeof(v2_protocol_ipv6)); + ASSERT_TRUE(tcp_client->write(buffer.toString())); + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection_)); + + ASSERT_TRUE(fake_upstream_connection_->waitForData(62, &observed_data)); + // - signature + // - version and command type, address family and protocol, length of addresses + // - src address + // - dest address + auto header_start = "\x0d\x0a\x0d\x0a\x00\x0d\x0a\x51\x55\x49\x54\x0a\ + \x21\x21\x00\x2E\ + \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\ + \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"; + EXPECT_THAT(observed_data, testing::StartsWith(header_start)); + + // Only tlv: 0x06, 0x00, 0x02, 0x09, 0x0A is sent to upstream. + EXPECT_EQ(static_cast(observed_data[52]), 0x05); + EXPECT_EQ(static_cast(observed_data[53]), 0x00); + EXPECT_EQ(static_cast(observed_data[54]), 0x02); + EXPECT_EQ(static_cast(observed_data[55]), 0x06); + EXPECT_EQ(static_cast(observed_data[56]), 0x07); + EXPECT_EQ(static_cast(observed_data[57]), 0x06); + EXPECT_EQ(static_cast(observed_data[58]), 0x00); + EXPECT_EQ(static_cast(observed_data[59]), 0x02); + EXPECT_EQ(static_cast(observed_data[60]), 0x09); + EXPECT_EQ(static_cast(observed_data[61]), 0x0A); + } + + tcp_client->close(); + ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); +} + } // namespace } // namespace Envoy diff --git a/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_test.cc b/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_test.cc index ac11d61b95cb..53d1646b50f9 100644 --- a/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_test.cc +++ b/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_test.cc @@ -25,6 +25,7 @@ using testing::ReturnRef; using envoy::config::core::v3::ProxyProtocolConfig; using envoy::config::core::v3::ProxyProtocolConfig_Version; +using envoy::config::core::v3::ProxyProtocolPassThroughTLVs; namespace Envoy { namespace Extensions { @@ -34,13 +35,13 @@ namespace { class ProxyProtocolTest : public testing::Test { public: - void initialize(ProxyProtocolConfig_Version version, + void initialize(ProxyProtocolConfig& config, Network::TransportSocketOptionsConstSharedPtr socket_options) { auto inner_socket = std::make_unique>(); inner_socket_ = inner_socket.get(); ON_CALL(transport_callbacks_, ioHandle()).WillByDefault(ReturnRef(io_handle_)); - proxy_protocol_socket_ = std::make_unique(std::move(inner_socket), - socket_options, version); + proxy_protocol_socket_ = std::make_unique( + std::move(inner_socket), socket_options, config, *stats_store_.rootScope()); proxy_protocol_socket_->setTransportSocketCallbacks(transport_callbacks_); proxy_protocol_socket_->onConnected(); } @@ -49,6 +50,7 @@ class ProxyProtocolTest : public testing::Test { NiceMock io_handle_; std::unique_ptr proxy_protocol_socket_; NiceMock transport_callbacks_; + Stats::TestUtil::TestStore stats_store_; }; // Test injects PROXY protocol header only once @@ -60,7 +62,9 @@ TEST_F(ProxyProtocolTest, InjectesHeaderOnlyOnce) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV1Header("174.2.2.222", "172.0.0.1", 50000, 80, Network::Address::IpVersion::v4, expected_buff); - initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1, nullptr); + ProxyProtocolConfig config; + config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1); + initialize(config, nullptr); EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { @@ -90,7 +94,9 @@ TEST_F(ProxyProtocolTest, BytesProcessedIncludesProxyProtocolHeader) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV1Header("174.2.2.222", "172.0.0.1", 50000, 80, Network::Address::IpVersion::v4, expected_buff); - initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1, nullptr); + ProxyProtocolConfig config; + config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1); + initialize(config, nullptr); EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { @@ -123,7 +129,9 @@ TEST_F(ProxyProtocolTest, ReturnsKeepOpenWhenWriteErrorIsAgain) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV1Header("174.2.2.222", "172.0.0.1", 50000, 80, Network::Address::IpVersion::v4, expected_buff); - initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1, nullptr); + ProxyProtocolConfig config; + config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1); + initialize(config, nullptr); auto msg = Buffer::OwnedImpl("some data"); { @@ -159,7 +167,9 @@ TEST_F(ProxyProtocolTest, ReturnsCloseWhenWriteErrorIsNotAgain) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV1Header("174.2.2.222", "172.0.0.1", 50000, 80, Network::Address::IpVersion::v4, expected_buff); - initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1, nullptr); + ProxyProtocolConfig config; + config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1); + initialize(config, nullptr); auto msg = Buffer::OwnedImpl("some data"); { @@ -185,7 +195,9 @@ TEST_F(ProxyProtocolTest, V1IPV4LocalAddressWhenTransportOptionsAreNull) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV1Header("174.2.2.222", "172.0.0.1", 50000, 80, Network::Address::IpVersion::v4, expected_buff); - initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1, nullptr); + ProxyProtocolConfig config; + config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1); + initialize(config, nullptr); EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { @@ -208,8 +220,9 @@ TEST_F(ProxyProtocolTest, V1IPV4LocalAddressesWhenHeaderOptionsAreNull) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV1Header("174.2.2.222", "172.0.0.1", 50000, 80, Network::Address::IpVersion::v4, expected_buff); - initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1, - std::make_shared()); + ProxyProtocolConfig config; + config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1); + initialize(config, std::make_shared()); EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { @@ -232,8 +245,9 @@ TEST_F(ProxyProtocolTest, V1IPV6LocalAddressesWhenHeaderOptionsAreNull) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV1Header("a:b:c:d::", "e:b:c:f::", 50000, 8080, Network::Address::IpVersion::v6, expected_buff); - initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1, - std::make_shared()); + ProxyProtocolConfig config; + config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1); + initialize(config, std::make_shared()); EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { @@ -265,7 +279,9 @@ TEST_F(ProxyProtocolTest, V1IPV4DownstreamAddresses) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV1Header("202.168.0.13", "174.2.2.222", 52000, 80, Network::Address::IpVersion::v4, expected_buff); - initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1, socket_options); + ProxyProtocolConfig config; + config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1); + initialize(config, socket_options); EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { @@ -297,7 +313,9 @@ TEST_F(ProxyProtocolTest, V1IPV6DownstreamAddresses) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV1Header("1::2:3", "a:b:c:d::", 52000, 80, Network::Address::IpVersion::v6, expected_buff); - initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1, socket_options); + ProxyProtocolConfig config; + config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1); + initialize(config, socket_options); EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { @@ -319,7 +337,10 @@ TEST_F(ProxyProtocolTest, V2IPV4LocalCommandWhenTransportOptionsAreNull) { ->setRemoteAddress(Network::Utility::resolveUrl("tcp://0.1.1.2:513")); Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV2LocalHeader(expected_buff); - initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2, nullptr); + + ProxyProtocolConfig config; + config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); + initialize(config, nullptr); EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { @@ -341,8 +362,10 @@ TEST_F(ProxyProtocolTest, V2IPV4LocalCommandWhenHeaderOptionsAreNull) { ->setRemoteAddress(Network::Utility::resolveUrl("tcp://0.1.1.2:513")); Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV2LocalHeader(expected_buff); - initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2, - std::make_shared()); + + ProxyProtocolConfig config; + config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); + initialize(config, std::make_shared()); EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { @@ -374,7 +397,9 @@ TEST_F(ProxyProtocolTest, V2IPV4DownstreamAddresses) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV2Header("1.2.3.4", "0.1.1.2", 773, 513, Network::Address::IpVersion::v4, expected_buff); - initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2, socket_options); + ProxyProtocolConfig config; + config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); + initialize(config, socket_options); EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { @@ -406,7 +431,10 @@ TEST_F(ProxyProtocolTest, V2IPV6DownstreamAddresses) { Buffer::OwnedImpl expected_buff{}; Common::ProxyProtocol::generateV2Header("1:2:3::4", "1:100:200:3::", 8, 2, Network::Address::IpVersion::v6, expected_buff); - initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2, socket_options); + + ProxyProtocolConfig config; + config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); + initialize(config, socket_options); EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { @@ -435,23 +463,250 @@ TEST_F(ProxyProtocolTest, OnConnectedCallsInnerOnConnected) { ->setLocalAddress(Network::Utility::resolveUrl("tcp://[1:100:200:3::]:50000")); transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ ->setRemoteAddress(Network::Utility::resolveUrl("tcp://[e:b:c:f::]:8080")); - initialize(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2, socket_options); + + ProxyProtocolConfig config; + config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); + initialize(config, socket_options); EXPECT_CALL(*inner_socket_, onConnected()); proxy_protocol_socket_->onConnected(); } +// Test injects V2 PROXY protocol for downstream IPV4 addresses and TLVs +TEST_F(ProxyProtocolTest, V2IPV4DownstreamAddressesAndTLVs) { + auto src_addr = + Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("1.2.3.4", 773)); + auto dst_addr = + Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("0.1.1.2", 513)); + // TLV type 0x5 is PP2_TYPE_UNIQUE_ID + Network::ProxyProtocolTLVVector tlv_vector{Network::ProxyProtocolTLV{0x5, {'a', 'b', 'c'}}}; + Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, tlv_vector}; + Network::TransportSocketOptionsConstSharedPtr socket_options = + std::make_shared( + "", std::vector{}, std::vector{}, std::vector{}, + absl::optional(proxy_proto_data)); + transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ + ->setLocalAddress(Network::Utility::resolveUrl("tcp://0.1.1.2:50000")); + transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ + ->setRemoteAddress(Network::Utility::resolveUrl("tcp://3.3.3.3:80")); + Buffer::OwnedImpl expected_buff{}; + absl::flat_hash_set pass_tlvs_set{}; + Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, true, pass_tlvs_set); + + ProxyProtocolConfig config; + config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); + auto pass_through_tlvs = config.mutable_pass_through_tlvs(); + pass_through_tlvs->set_match_type(ProxyProtocolPassThroughTLVs::INCLUDE_ALL); + initialize(config, socket_options); + + EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) + .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { + auto length = buffer.length(); + buffer.drain(length); + return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); + })); + auto msg = Buffer::OwnedImpl("some data"); + EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); + + proxy_protocol_socket_->doWrite(msg, false); +} + +// Test injects V2 PROXY protocol for downstream IPV4 addresses and TLVs with passing specific TLV. +TEST_F(ProxyProtocolTest, V2IPV4PassSpecificTLVs) { + auto src_addr = + Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("1.2.3.4", 773)); + auto dst_addr = + Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("0.1.1.2", 513)); + // TLV type 0x5 is PP2_TYPE_UNIQUE_ID + Network::ProxyProtocolTLVVector tlv_vector{Network::ProxyProtocolTLV{0x5, {'a', 'b', 'c'}}}; + Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, tlv_vector}; + Network::TransportSocketOptionsConstSharedPtr socket_options = + std::make_shared( + "", std::vector{}, std::vector{}, std::vector{}, + absl::optional(proxy_proto_data)); + transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ + ->setLocalAddress(Network::Utility::resolveUrl("tcp://0.1.1.2:50000")); + transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ + ->setRemoteAddress(Network::Utility::resolveUrl("tcp://3.3.3.3:80")); + Buffer::OwnedImpl expected_buff{}; + absl::flat_hash_set pass_tlvs_set{0x05}; + Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, false, pass_tlvs_set); + + ProxyProtocolConfig config; + config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); + auto pass_through_tlvs = config.mutable_pass_through_tlvs(); + pass_through_tlvs->set_match_type(ProxyProtocolPassThroughTLVs::INCLUDE); + pass_through_tlvs->add_tlv_type(0x05); + initialize(config, socket_options); + + EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) + .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { + auto length = buffer.length(); + buffer.drain(length); + return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); + })); + auto msg = Buffer::OwnedImpl("some data"); + EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); + + proxy_protocol_socket_->doWrite(msg, false); +} + +// Test injects V2 PROXY protocol for downstream IPV4 addresses and TLVs with empty passing TLV set. +TEST_F(ProxyProtocolTest, V2IPV4PassEmptyTLVs) { + auto src_addr = + Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("1.2.3.4", 773)); + auto dst_addr = + Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("0.1.1.2", 513)); + // TLV type 0x5 is PP2_TYPE_UNIQUE_ID + Network::ProxyProtocolTLVVector tlv_vector{Network::ProxyProtocolTLV{0x5, {'a', 'b', 'c'}}}; + Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, tlv_vector}; + Network::TransportSocketOptionsConstSharedPtr socket_options = + std::make_shared( + "", std::vector{}, std::vector{}, std::vector{}, + absl::optional(proxy_proto_data)); + transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ + ->setLocalAddress(Network::Utility::resolveUrl("tcp://0.1.1.2:50000")); + transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ + ->setRemoteAddress(Network::Utility::resolveUrl("tcp://3.3.3.3:80")); + Buffer::OwnedImpl expected_buff{}; + absl::flat_hash_set pass_tlvs_set{}; + Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, false, pass_tlvs_set); + + ProxyProtocolConfig config; + config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); + config.mutable_pass_through_tlvs()->set_match_type(ProxyProtocolPassThroughTLVs::INCLUDE); + initialize(config, socket_options); + + EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) + .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { + auto length = buffer.length(); + buffer.drain(length); + return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); + })); + auto msg = Buffer::OwnedImpl("some data"); + EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); + + proxy_protocol_socket_->doWrite(msg, false); +} + +// Test injects V2 PROXY protocol for downstream IPV4 addresses with exceeding TLV max length. +TEST_F(ProxyProtocolTest, V2IPV4TLVsExceedLengthLimit) { + auto src_addr = + Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("1.2.3.4", 773)); + auto dst_addr = + Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("0.1.1.2", 513)); + + const std::string long_tlv(65536, 'a'); + Network::ProxyProtocolTLV tlv{0x5, std::vector(long_tlv.begin(), long_tlv.end())}; + + Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, {tlv}}; + Network::TransportSocketOptionsConstSharedPtr socket_options = + std::make_shared( + "", std::vector{}, std::vector{}, std::vector{}, + absl::optional(proxy_proto_data)); + transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ + ->setLocalAddress(Network::Utility::resolveUrl("tcp://0.1.1.2:50000")); + transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ + ->setRemoteAddress(Network::Utility::resolveUrl("tcp://3.3.3.3:80")); + + ProxyProtocolConfig config; + config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); + config.mutable_pass_through_tlvs()->set_match_type(ProxyProtocolPassThroughTLVs::INCLUDE_ALL); + initialize(config, socket_options); + + auto msg = Buffer::OwnedImpl("some data"); + proxy_protocol_socket_->doWrite(msg, false); + EXPECT_EQ(stats_store_.counter("upstream.proxyprotocol.v2_tlvs_exceed_max_length").value(), 1); +} + +// Test injects V2 PROXY protocol for downstream IPV6 addresses and TLVs +TEST_F(ProxyProtocolTest, V2IPV6DownstreamAddressesAndTLVs) { + auto src_addr = + Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv6Instance("1:2:3::4", 8)); + auto dst_addr = Network::Address::InstanceConstSharedPtr( + new Network::Address::Ipv6Instance("1:100:200:3::", 2)); + // TLV type 0x5 is PP2_TYPE_UNIQUE_ID + Network::ProxyProtocolTLVVector tlv_vector{Network::ProxyProtocolTLV{0x5, {'a', 'b', 'c'}}}; + Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, tlv_vector}; + Network::TransportSocketOptionsConstSharedPtr socket_options = + std::make_shared( + "", std::vector{}, std::vector{}, std::vector{}, + absl::optional(proxy_proto_data)); + transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ + ->setLocalAddress(Network::Utility::resolveUrl("tcp://[1:100:200:3::]:50000")); + transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ + ->setRemoteAddress(Network::Utility::resolveUrl("tcp://[e:b:c:f::]:8080")); + Buffer::OwnedImpl expected_buff{}; + absl::flat_hash_set pass_through_tlvs{}; + Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, true, pass_through_tlvs); + + ProxyProtocolConfig config; + config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); + config.mutable_pass_through_tlvs()->set_match_type(ProxyProtocolPassThroughTLVs::INCLUDE_ALL); + initialize(config, socket_options); + + EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) + .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { + auto length = buffer.length(); + buffer.drain(length); + return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); + })); + auto msg = Buffer::OwnedImpl("some data"); + EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); + + proxy_protocol_socket_->doWrite(msg, false); +} + +// Test injects V2 PROXY protocol for downstream IPV6 addresses and TLVs without pass TLV config. +TEST_F(ProxyProtocolTest, V2IPV6DownstreamAddressesAndTLVsWithoutPassConfig) { + auto src_addr = + Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv6Instance("1:2:3::4", 8)); + auto dst_addr = Network::Address::InstanceConstSharedPtr( + new Network::Address::Ipv6Instance("1:100:200:3::", 2)); + // TLV type 0x5 is PP2_TYPE_UNIQUE_ID + Network::ProxyProtocolTLVVector tlv_vector{Network::ProxyProtocolTLV{0x5, {'a', 'b', 'c'}}}; + Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, tlv_vector}; + Network::TransportSocketOptionsConstSharedPtr socket_options = + std::make_shared( + "", std::vector{}, std::vector{}, std::vector{}, + absl::optional(proxy_proto_data)); + transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ + ->setLocalAddress(Network::Utility::resolveUrl("tcp://[1:100:200:3::]:50000")); + transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ + ->setRemoteAddress(Network::Utility::resolveUrl("tcp://[e:b:c:f::]:8080")); + Buffer::OwnedImpl expected_buff{}; + absl::flat_hash_set pass_through_tlvs{}; + Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, false, + pass_through_tlvs); + + ProxyProtocolConfig config; + config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); + initialize(config, socket_options); + + EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) + .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { + auto length = buffer.length(); + buffer.drain(length); + return Api::IoCallUint64Result(length, Api::IoErrorPtr(nullptr, [](Api::IoError*) {})); + })); + auto msg = Buffer::OwnedImpl("some data"); + EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); + + proxy_protocol_socket_->doWrite(msg, false); +} + class ProxyProtocolSocketFactoryTest : public testing::Test { public: void initialize() { auto inner_factory = std::make_unique>(); inner_factory_ = inner_factory.get(); - factory_ = std::make_unique(std::move(inner_factory), - ProxyProtocolConfig()); + factory_ = std::make_unique( + std::move(inner_factory), ProxyProtocolConfig(), *stats_store_.rootScope()); } NiceMock* inner_factory_; std::unique_ptr factory_; + Stats::TestUtil::TestStore stats_store_; }; // Test createTransportSocket returns nullptr if inner call returns nullptr diff --git a/test/integration/BUILD b/test/integration/BUILD index dfb107a828c0..2d4983ff4674 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -151,25 +151,6 @@ envoy_cc_test( ], ) -envoy_cc_test( - name = "eds_grpc_integration_test", - srcs = ["eds_grpc_integration_test.cc"], - deps = [ - ":http_integration_lib", - "//source/common/runtime:runtime_features_lib", - "//source/common/upstream:load_balancer_lib", - "//test/config:utility_lib", - "//test/integration/filters:eds_ready_filter_config_lib", - "//test/test_common:network_utility_lib", - "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", - "@envoy_api//envoy/config/cluster/v3:pkg_cc_proto", - "@envoy_api//envoy/config/core/v3:pkg_cc_proto", - "@envoy_api//envoy/config/endpoint/v3:pkg_cc_proto", - "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", - "@envoy_api//envoy/type/v3:pkg_cc_proto", - ], -) - envoy_cc_test( name = "leds_integration_test", srcs = ["leds_integration_test.cc"], @@ -461,9 +442,12 @@ envoy_cc_test( deps = [ ":http_integration_lib", ":integration_lib", + ":socket_interface_swap_lib", "//test/integration/filters:add_header_filter_config_lib", + "//test/integration/filters:encoder_decoder_buffer_filter_lib", "//test/integration/filters:on_local_reply_filter_config_lib", "//test/integration/filters:repick_cluster_filter_lib", + "//test/test_common:test_runtime_lib", "@envoy_api//envoy/extensions/access_loggers/file/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/http/router/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", @@ -501,6 +485,7 @@ envoy_cc_test( ":tracked_watermark_buffer_lib", "//test/integration/filters:tee_filter_lib", "//test/mocks/http:http_mocks", + "//test/test_common:test_runtime_lib", "//test/test_common:utility_lib", "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", "@envoy_api//envoy/config/cluster/v3:pkg_cc_proto", @@ -1145,9 +1130,11 @@ envoy_cc_test( "redirect_integration_test.cc", ], shard_count = 4, + tags = ["nofips"], deps = [ ":http_protocol_integration_lib", "//source/common/http:header_map_lib", + "//test/integration/filters:pause_filter_for_quic_lib", "//test/test_common:utility_lib", "@envoy_api//envoy/config/route/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", diff --git a/test/integration/ads_integration_test.cc b/test/integration/ads_integration_test.cc index 26edfafd3ad1..a46b8e932af2 100644 --- a/test/integration/ads_integration_test.cc +++ b/test/integration/ads_integration_test.cc @@ -262,7 +262,6 @@ TEST_P(AdsIntegrationTest, ClusterSharingSecretWarming) { // Make sure two clusters with different secrets send only a single SDS request. // This is a regression test of #21518. TEST_P(AdsIntegrationTest, SecretsPausedDuringCDS) { - config_helper_.addRuntimeOverride("envoy.reloadable_features.combine_sds_requests", "true"); initialize(); const auto cds_type_url = Config::getTypeUrl(); const auto sds_type_url = @@ -321,71 +320,6 @@ TEST_P(AdsIntegrationTest, SecretsPausedDuringCDS) { test_server_->waitForGaugeEq("cluster_manager.warming_clusters", 0); } -// Two clusters with different secrets send two SDS requests. -// This is a test that validates the behavior prior to #21518. -// The test will be removed one envoy.reloadable_features.combine_sds_requests -// is removed. -TEST_P(AdsIntegrationTest, SecretsNonPausedDuringCDS) { - config_helper_.addRuntimeOverride("envoy.reloadable_features.combine_sds_requests", "false"); - initialize(); - const auto cds_type_url = Config::getTypeUrl(); - const auto sds_type_url = - Config::getTypeUrl(); - - std::vector clusters; - for (int i = 0; i < 2; ++i) { - envoy::config::core::v3::TransportSocket sds_transport_socket; - TestUtility::loadFromYaml(fmt::format(R"EOF( - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - common_tls_context: - validation_context_sds_secret_config: - name: validation_context_{} - sds_config: - resource_api_version: V3 - ads: {{}} - )EOF", - i), - sds_transport_socket); - auto cluster = ConfigHelper::buildStaticCluster("cluster", 8000, "127.0.0.1"); - cluster.set_name(absl::StrCat("cluster_", i)); - *cluster.mutable_transport_socket() = sds_transport_socket; - clusters.push_back(std::move(cluster)); - } - - EXPECT_TRUE(compareDiscoveryRequest(cds_type_url, "", {}, {}, {}, true)); - sendDiscoveryResponse(cds_type_url, clusters, clusters, {}, - "1"); - - // Expect two different SDS requests. - EXPECT_TRUE(compareDiscoveryRequest(sds_type_url, "", {"validation_context_0"}, - {"validation_context_0"}, {})); - EXPECT_TRUE(compareDiscoveryRequest(sds_type_url, "", {"validation_context_1"}, - {"validation_context_1"}, {})); - test_server_->waitForGaugeGe("cluster_manager.warming_clusters", 2); - - std::vector validation_contexts; - for (int i = 0; i < 2; ++i) { - envoy::extensions::transport_sockets::tls::v3::Secret validation_context; - TestUtility::loadFromYaml( - fmt::format( - R"EOF( - name: validation_context_{} - validation_context: - trusted_ca: - filename: {} - )EOF", - i, TestEnvironment::runfilesPath("test/config/integration/certs/upstreamcacert.pem")), - validation_context); - validation_contexts.push_back(std::move(validation_context)); - } - - sendDiscoveryResponse( - sds_type_url, validation_contexts, validation_contexts, {}, "1"); - test_server_->waitForGaugeEq("cluster_manager.warming_clusters", 0); -} - // Validate basic config delivery and upgrade with RateLimiting. TEST_P(AdsIntegrationTest, BasicWithRateLimiting) { initializeAds(true); diff --git a/test/integration/base_integration_test.cc b/test/integration/base_integration_test.cc index fca24404e463..b1371f195ef3 100644 --- a/test/integration/base_integration_test.cc +++ b/test/integration/base_integration_test.cc @@ -510,7 +510,9 @@ void BaseIntegrationTest::useListenerAccessLog(absl::string_view format) { } std::string BaseIntegrationTest::waitForAccessLog(const std::string& filename, uint32_t entry, - bool allow_excess_entries) { + bool allow_excess_entries, + Network::ClientConnection* client_connection) { + // Wait a max of 1s for logs to flush to disk. std::string contents; for (int i = 0; i < 1000; ++i) { @@ -525,6 +527,11 @@ std::string BaseIntegrationTest::waitForAccessLog(const std::string& filename, u << contents; return entries[entry]; } + if (i % 25 == 0 && client_connection != nullptr) { + // The QUIC default delayed ack timer is 25ms. Wait for any pending ack timers to expire, + // then run dispatcher to send any pending acks. + client_connection->dispatcher().run(Envoy::Event::Dispatcher::RunType::NonBlock); + } absl::SleepFor(absl::Milliseconds(1)); } RELEASE_ASSERT(0, absl::StrCat("Timed out waiting for access log. Found: '", contents, "'")); @@ -588,25 +595,6 @@ AssertionResult BaseIntegrationTest::compareDiscoveryRequest( } } -AssertionResult BaseIntegrationTest::compareDiscoveryRequest( - const std::string& expected_type_url, const std::string& expected_version, - const std::vector& expected_resource_names, - const std::vector& expected_resource_names_added, - const std::vector& expected_resource_names_removed, FakeStreamPtr& stream, - bool expect_node, const Protobuf::int32 expected_error_code, - const std::string& expected_error_message) { - if (sotw_or_delta_ == Grpc::SotwOrDelta::Sotw || - sotw_or_delta_ == Grpc::SotwOrDelta::UnifiedSotw) { - return compareSotwDiscoveryRequest(expected_type_url, expected_version, expected_resource_names, - expect_node, expected_error_code, expected_error_message, - stream.get()); - } else { - return compareDeltaDiscoveryRequest(expected_type_url, expected_resource_names_added, - expected_resource_names_removed, stream, - expected_error_code, expected_error_message, expect_node); - } -} - AssertionResult compareSets(const std::set& set1, const std::set& set2, absl::string_view name) { if (set1 == set2) { @@ -820,98 +808,4 @@ void BaseIntegrationTest::checkForMissingTagExtractionRules() { test_server_->statStore().forEachGauge(nullptr, check_metric); test_server_->statStore().forEachHistogram(nullptr, check_metric); } - -AssertionResult BaseIntegrationTest::internalCompareDiscoveryRequest( - const DiscoveryRequestExpectedContents& expected_request, - const envoy::service::discovery::v3::DiscoveryRequest& actual_request, - const std::set& actual_sub) { - - if (actual_request.type_url() != expected_request.type_url_) { - return AssertionFailure() << fmt::format("type_url {} does not match expected {}.", - actual_request.type_url(), expected_request.type_url_); - } - auto sub_result = - compareSets(expected_request.subscriptions_, actual_sub, "expected_resource_subscriptions"); - if (!sub_result) { - return sub_result; - } - - if (actual_request.error_detail().code() != expected_request.error_code_) { - return AssertionFailure() << fmt::format( - "error code {} does not match expected {}. (Error message is {}).", - actual_request.error_detail().code(), expected_request.error_code_, - actual_request.error_detail().message()); - } - if (expected_request.error_code_ != Grpc::Status::WellKnownGrpcStatus::Ok && - actual_request.error_detail().message().find(expected_request.error_substring_) == - std::string::npos) { - return AssertionFailure() << "\"" << expected_request.error_substring_ - << "\" is not a substring of actual error message \"" - << actual_request.error_detail().message() << "\""; - } - return AssertionSuccess(); -} - -AssertionResult BaseIntegrationTest::internalCompareDeltaDiscoveryRequest( - const DiscoveryRequestExpectedContents& expected_request, - const envoy::service::discovery::v3::DeltaDiscoveryRequest& actual_request, - const std::set& actual_sub, const std::set& actual_unsub) { - - if (actual_request.type_url() != expected_request.type_url_) { - return AssertionFailure() << fmt::format("type_url {} does not match expected {}.", - actual_request.type_url(), expected_request.type_url_); - } - auto sub_result = - compareSets(expected_request.subscriptions_, actual_sub, "expected_resource_subscriptions"); - if (!sub_result) { - return sub_result; - } - auto unsub_result = compareSets(expected_request.unsubscriptions_, actual_unsub, - "expected_resource_unsubscriptions"); - if (!unsub_result) { - return unsub_result; - } - - if (actual_request.error_detail().code() != expected_request.error_code_) { - return AssertionFailure() << fmt::format( - "error code {} does not match expected {}. (Error message is {}).", - actual_request.error_detail().code(), expected_request.error_code_, - actual_request.error_detail().message()); - } - if (expected_request.error_code_ != Grpc::Status::WellKnownGrpcStatus::Ok && - actual_request.error_detail().message().find(expected_request.error_substring_) == - std::string::npos) { - return AssertionFailure() << "\"" << expected_request.error_substring_ - << "\" is not a substring of actual error message \"" - << actual_request.error_detail().message() << "\""; - } - return AssertionSuccess(); -} - -AssertionResult BaseIntegrationTest::assertExpectedDiscoveryRequest( - const envoy::service::discovery::v3::DiscoveryRequest& request, - const BaseIntegrationTest::DiscoveryRequestExpectedContents& expected_request) { - if (!request.has_node() || request.node().id().empty() || request.node().cluster().empty()) { - return AssertionFailure() << "Weird node field"; - } - - // Convert subscribed/unsubscribed names into sets to ignore ordering. - const std::set actual_sub{request.resource_names().begin(), - request.resource_names().end()}; - return internalCompareDiscoveryRequest(expected_request, request, actual_sub); -} - -AssertionResult BaseIntegrationTest::assertExpectedDeltaDiscoveryRequest( - const envoy::service::discovery::v3::DeltaDiscoveryRequest& request, - const BaseIntegrationTest::DiscoveryRequestExpectedContents& expected_request) { - if (!request.has_node() || request.node().id().empty() || request.node().cluster().empty()) { - return AssertionFailure() << "Weird node field"; - } - const std::set actual_sub{request.resource_names_subscribe().begin(), - request.resource_names_subscribe().end()}; - const std::set actual_unsub{request.resource_names_unsubscribe().begin(), - request.resource_names_unsubscribe().end()}; - return internalCompareDeltaDiscoveryRequest(expected_request, request, actual_sub, actual_unsub); -} - } // namespace Envoy diff --git a/test/integration/base_integration_test.h b/test/integration/base_integration_test.h index 3a7daa23ed7a..ae18ad704c5e 100644 --- a/test/integration/base_integration_test.h +++ b/test/integration/base_integration_test.h @@ -159,8 +159,10 @@ class BaseIntegrationTest : protected Logger::Loggable { void useListenerAccessLog(absl::string_view format = ""); // Returns all log entries after the nth access log entry, defaulting to log entry 0. // By default will trigger an expect failure if more than one entry is returned. + // If client_connection is provided, flush pending acks to enable deferred logging. std::string waitForAccessLog(const std::string& filename, uint32_t entry = 0, - bool allow_excess_entries = false); + bool allow_excess_entries = false, + Network::ClientConnection* client_connection = nullptr); std::string listener_access_log_name_; @@ -187,16 +189,6 @@ class BaseIntegrationTest : protected Logger::Loggable { const std::vector& expected_resource_names_removed, bool expect_node = false, const Protobuf::int32 expected_error_code = Grpc::Status::WellKnownGrpcStatus::Ok, const std::string& expected_error_message = ""); - - AssertionResult compareDiscoveryRequest( - const std::string& expected_type_url, const std::string& expected_version, - const std::vector& expected_resource_names, - const std::vector& expected_resource_names_added, - const std::vector& expected_resource_names_removed, FakeStreamPtr& stream, - bool expect_node = false, - const Protobuf::int32 expected_error_code = Grpc::Status::WellKnownGrpcStatus::Ok, - const std::string& expected_error_message = ""); - template void sendDiscoveryResponse(const std::string& type_url, const std::vector& state_of_the_world, const std::vector& added_or_updated, @@ -209,19 +201,6 @@ class BaseIntegrationTest : protected Logger::Loggable { } } - template - void sendDiscoveryResponse(const std::string& type_url, const std::vector& state_of_the_world, - const std::vector& added_or_updated, - const std::vector& removed, const std::string& version, - FakeStreamPtr& stream) { - if (sotw_or_delta_ == Grpc::SotwOrDelta::Sotw || - sotw_or_delta_ == Grpc::SotwOrDelta::UnifiedSotw) { - sendSotwDiscoveryResponse(type_url, state_of_the_world, version, stream.get()); - } else { - sendDeltaDiscoveryResponse(type_url, added_or_updated, removed, version, stream); - } - } - AssertionResult compareDeltaDiscoveryRequest( const std::string& expected_type_url, const std::vector& expected_resource_subscriptions, @@ -246,43 +225,6 @@ class BaseIntegrationTest : protected Logger::Loggable { const Protobuf::int32 expected_error_code = Grpc::Status::WellKnownGrpcStatus::Ok, const std::string& expected_error_message = "", FakeStream* stream = nullptr); - struct DiscoveryRequestExpectedContents { - DiscoveryRequestExpectedContents( - const std::string& type_url, const std::vector& subscriptions, - const std::vector& unsubscriptions, - const Protobuf::int32 error_code = Grpc::Status::WellKnownGrpcStatus::Ok, - const std::string& error_substring = "") - : type_url_(type_url), - // Convert subscribed/unsubscribed names into sets to ignore ordering. - subscriptions_(subscriptions.begin(), subscriptions.end()), - unsubscriptions_(unsubscriptions.begin(), unsubscriptions.end()), error_code_(error_code), - error_substring_(error_substring) {} - - const std::string& type_url_; - const std::set subscriptions_; - const std::set unsubscriptions_; - const Protobuf::int32 error_code_; - const std::string& error_substring_; - }; - - AssertionResult internalCompareDeltaDiscoveryRequest( - const DiscoveryRequestExpectedContents& expected_request, - const envoy::service::discovery::v3::DeltaDiscoveryRequest& actual_request, - const std::set& actual_sub, const std::set& actual_unsub); - - AssertionResult internalCompareDiscoveryRequest( - const DiscoveryRequestExpectedContents& expected_request, - const envoy::service::discovery::v3::DiscoveryRequest& actual_request, - const std::set& actual_sub); - - AssertionResult assertExpectedDeltaDiscoveryRequest( - const envoy::service::discovery::v3::DeltaDiscoveryRequest& request, - const DiscoveryRequestExpectedContents& expected_request); - - AssertionResult - assertExpectedDiscoveryRequest(const envoy::service::discovery::v3::DiscoveryRequest& request, - const DiscoveryRequestExpectedContents& expected_request); - template void sendSotwDiscoveryResponse(const std::string& type_url, const std::vector& messages, const std::string& version, FakeStream* stream = nullptr) { diff --git a/test/integration/buffer_accounting_integration_test.cc b/test/integration/buffer_accounting_integration_test.cc index abc9cc2e1bb6..d726c4f4956d 100644 --- a/test/integration/buffer_accounting_integration_test.cc +++ b/test/integration/buffer_accounting_integration_test.cc @@ -15,6 +15,7 @@ #include "test/integration/tracked_watermark_buffer.h" #include "test/integration/utility.h" #include "test/mocks/http/mocks.h" +#include "test/test_common/test_runtime.h" #include "fake_upstream.h" #include "gtest/gtest.h" @@ -96,7 +97,8 @@ class Http2BufferWatermarksTest public HttpIntegrationTest { public: std::vector - sendRequests(uint32_t num_responses, uint32_t request_body_size, uint32_t response_body_size) { + sendRequests(uint32_t num_responses, uint32_t request_body_size, uint32_t response_body_size, + absl::string_view cluster_to_wait_for = "") { std::vector responses; Http::TestRequestHeaderMapImpl header_map{ @@ -108,6 +110,10 @@ class Http2BufferWatermarksTest for (uint32_t idx = 0; idx < num_responses; ++idx) { responses.emplace_back(codec_client_->makeRequestWithBody(header_map, request_body_size)); + if (!cluster_to_wait_for.empty()) { + test_server_->waitForGaugeEq( + absl::StrCat("cluster.", cluster_to_wait_for, ".upstream_rq_active"), idx + 1); + } } return responses; @@ -340,7 +346,8 @@ TEST_P(Http2BufferWatermarksTest, ShouldTrackAllocatedBytesToUpstream) { codec_client_ = makeHttpConnection(lookupPort("http")); - auto responses = sendRequests(num_requests, request_body_size, response_body_size); + auto responses = sendRequests(num_requests, request_body_size, response_body_size, + /*cluster_to_wait_for=*/"cluster_0"); // Wait for all requests to have accounted for the requests we've sent. if (streamBufferAccounting()) { @@ -358,6 +365,63 @@ TEST_P(Http2BufferWatermarksTest, ShouldTrackAllocatedBytesToUpstream) { } } +TEST_P(Http2BufferWatermarksTest, ShouldTrackAllocatedBytesToShadowUpstream) { + const int num_requests = 5; + const uint32_t request_body_size = 4096; + const uint32_t response_body_size = 4096; + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues({{"envoy.reloadable_features.streaming_shadow", "true"}}); + + autonomous_upstream_ = true; + autonomous_allow_incomplete_streams_ = true; + setUpstreamCount(2); + config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + auto* cluster = bootstrap.mutable_static_resources()->add_clusters(); + cluster->MergeFrom(bootstrap.static_resources().clusters()[0]); + cluster->set_name("cluster_1"); + }); + config_helper_.addConfigModifier( + [=](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) -> void { + auto* mirror_policy = hcm.mutable_route_config() + ->mutable_virtual_hosts(0) + ->mutable_routes(0) + ->mutable_route() + ->add_request_mirror_policies(); + mirror_policy->set_cluster("cluster_1"); + }); + initialize(); + + buffer_factory_->setExpectedAccountBalance(request_body_size, num_requests); + + // Makes us have Envoy's writes to shadow upstream return EAGAIN + write_matcher_->setDestinationPort(fake_upstreams_[1]->localAddress()->ip()->port()); + write_matcher_->setWriteReturnsEgain(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + + auto responses = sendRequests(num_requests, request_body_size, response_body_size, + /*cluster_to_wait_for=*/"cluster_1"); + + // The main request should complete. + for (auto& response : responses) { + ASSERT_TRUE(response->waitForEndStream()); + ASSERT_TRUE(response->complete()); + } + + // Wait for all requests to have accounted for the requests we've sent. + if (streamBufferAccounting()) { + EXPECT_TRUE( + buffer_factory_->waitForExpectedAccountBalanceWithTimeout(TestUtility::DefaultTimeout)) + << "buffer total: " << buffer_factory_->totalBufferSize() << "\n" + << " buffer max: " << buffer_factory_->maxBufferSize() << "\n" + << printAccounts(); + } + + write_matcher_->setResumeWrites(); + test_server_->waitForCounterEq("cluster.cluster_1.upstream_rq_completed", num_requests); +} + TEST_P(Http2BufferWatermarksTest, ShouldTrackAllocatedBytesToDownstream) { const int num_requests = 5; const uint32_t request_body_size = 4096; diff --git a/test/integration/eds_grpc_integration_test.cc b/test/integration/eds_grpc_integration_test.cc deleted file mode 100644 index cec8ebffca1a..000000000000 --- a/test/integration/eds_grpc_integration_test.cc +++ /dev/null @@ -1,601 +0,0 @@ -#include "envoy/config/bootstrap/v3/bootstrap.pb.h" -#include "envoy/config/cluster/v3/cluster.pb.h" -#include "envoy/config/core/v3/health_check.pb.h" -#include "envoy/config/endpoint/v3/endpoint.pb.h" -#include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h" -#include "envoy/type/v3/http.pb.h" - -#include "source/common/runtime/runtime_features.h" -#include "source/common/upstream/load_balancer_impl.h" - -#include "test/config/utility.h" -#include "test/integration/http_integration.h" -#include "test/test_common/network_utility.h" - -#include "gtest/gtest.h" - -namespace Envoy { -namespace { - -class EdsOverGrpcIntegrationTest : public Grpc::MultiplexedDeltaSotwIntegrationParamTest, - public HttpIntegrationTest { -protected: - struct FakeUpstreamInfo { - FakeHttpConnectionPtr connection_; - FakeUpstream* upstream_{}; - absl::flat_hash_map stream_by_resource_name_; - static constexpr char default_stream_name[] = "default"; - // Used for cases where only a single stream is needed. - FakeStreamPtr& defaultStream() { return stream_by_resource_name_[default_stream_name]; } - }; - - EdsOverGrpcIntegrationTest() - : HttpIntegrationTest(Http::CodecClient::Type::HTTP2, ipVersion()), - codec_client_type_(envoy::type::v3::HTTP2) { - use_lds_ = true; - test_skipped_ = false; - } - - void TearDown() override { - if (!test_skipped_) { - resetConnections(); - cleanUpXdsConnection(); - } - } - - void resetConnections() { - // First disconnect upstream connections to avoid FIN messages causing unexpected - // disconnects on the fake servers. - for (auto& host_upstream_info : hosts_upstreams_info_) { - resetFakeUpstreamInfo(host_upstream_info); - } - } - - // A helper function to set the endpoints health status. - void setEndpointsHealthStatus( - const absl::flat_hash_set& endpoints_idxs, - envoy::config::core::v3::HealthStatus health_status, absl::string_view collection_prefix, - absl::flat_hash_map& - updated_endpoints) { - for (const auto endpoint_idx : endpoints_idxs) { - const std::string endpoint_name = absl::StrCat(collection_prefix, "endpoint", endpoint_idx); - envoy::config::endpoint::v3::LbEndpoint endpoint; - // Shift fake_upstreams_ by 1 (due to EDS fake upstream). - setUpstreamAddress(endpoint_idx + 1, endpoint); - endpoint.set_health_status(health_status); - updated_endpoints.emplace(endpoint_name, endpoint); - } - } - - void setEndpoints(uint32_t total_endpoints, uint32_t healthy_endpoints, - uint32_t degraded_endpoints, bool remaining_unhealthy = true, - absl::optional overprovisioning_factor = absl::nullopt, - bool await_update = true) { - envoy::config::endpoint::v3::ClusterLoadAssignment cluster_load_assignment; - cluster_load_assignment.set_cluster_name("cluster_0"); - if (overprovisioning_factor.has_value()) { - cluster_load_assignment.mutable_policy()->mutable_overprovisioning_factor()->set_value( - overprovisioning_factor.value()); - } - auto* locality_lb_endpoints = cluster_load_assignment.add_endpoints(); - locality_lb_endpoints->set_priority(1); - for (uint32_t i = 0; i < total_endpoints; ++i) { - auto* endpoint = locality_lb_endpoints->add_lb_endpoints(); - // Skip EDS upstream. - setUpstreamAddress(i + 1, *endpoint); - // First N endpoints are degraded, next M are healthy and the remaining endpoints are - // unhealthy or unknown depending on remaining_unhealthy. - if (i < degraded_endpoints) { - endpoint->set_health_status(envoy::config::core::v3::DEGRADED); - } else if (i >= healthy_endpoints + degraded_endpoints) { - endpoint->set_health_status(remaining_unhealthy ? envoy::config::core::v3::UNHEALTHY - : envoy::config::core::v3::UNKNOWN); - } - } - auto& stream = - (edsUpdateMode() == Grpc::EdsUpdateMode::Multiplexed) ? xds_stream_ : xds_streams_.front(); - sendDiscoveryResponse( - Config::TypeUrl::get().ClusterLoadAssignment, {cluster_load_assignment}, - {cluster_load_assignment}, {}, std::to_string(eds_version_++), stream); - if (await_update) { - // Receive EDS ack. - EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().ClusterLoadAssignment, - std::to_string(eds_version_ - 1), {}, {}, {}, stream, - true, Grpc::Status::WellKnownGrpcStatus::Ok, "")); - } - } - - void createUpstreams() override { - // Add EDS upstream. - addFakeUpstream(Http::CodecType::HTTP2); - HttpIntegrationTest::createUpstreams(); - hosts_upstreams_info_.reserve(fake_upstreams_count_); - // Skip the first fake upstream as it is reserved for EDS. - for (size_t i = 1; i < fake_upstreams_.size(); ++i) { - FakeUpstreamInfo host_info; - host_info.upstream_ = &(*fake_upstreams_[i]); - hosts_upstreams_info_.emplace_back(std::move(host_info)); - } - } - - void initializeTest(bool http_active_hc, uint32_t num_clusters_to_add, - bool ignore_new_hosts_until_first_hc) { - setUpstreamCount(4); - setUpstreamProtocol(Http::CodecType::HTTP2); - if (edsUpdateMode() == Grpc::EdsUpdateMode::StreamPerCluster) { - config_helper_.addRuntimeOverride("envoy.reloadable_features.multiplex_eds", "false"); - } else { - config_helper_.addRuntimeOverride("envoy.reloadable_features.multiplex_eds", "true"); - } - config_helper_.addConfigModifier( - [this, http_active_hc, num_clusters_to_add, - ignore_new_hosts_until_first_hc](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { - // Add a static EDS cluster. - auto* eds_cluster = bootstrap.mutable_static_resources()->add_clusters(); - eds_cluster->MergeFrom(bootstrap.static_resources().clusters()[0]); - eds_cluster->set_name("eds_cluster"); - eds_cluster->mutable_load_assignment()->set_cluster_name("eds_cluster"); - ConfigHelper::setHttp2(*eds_cluster); - // Remove the static cluster (cluster_0) and set up CDS. - bootstrap.mutable_dynamic_resources()->mutable_cds_config()->set_resource_api_version( - envoy::config::core::v3::ApiVersion::V3); - bootstrap.mutable_dynamic_resources() - ->mutable_cds_config() - ->mutable_path_config_source() - ->set_path(cds_helper_.cds_path()); - bootstrap.mutable_static_resources()->mutable_clusters()->erase( - bootstrap.mutable_static_resources()->mutable_clusters()->begin()); - for (uint32_t i = 0; i < num_clusters_to_add; ++i) { - auto cluster_name = "cluster_" + std::to_string(i); - auto cluster_to_add = - buildCluster(cluster_name, http_active_hc, ignore_new_hosts_until_first_hc); - envoy::config::endpoint::v3::ClusterLoadAssignment cla_to_add; - cla_to_add.set_cluster_name(cluster_name); - clas_to_add_.push_back(cla_to_add); - clusters_to_add_.push_back(cluster_to_add); - resource_names_.push_back(cluster_name); - } - cds_helper_.setCds(clusters_to_add_); - }); - // Set validate_clusters to false to allow us to reference a CDS cluster. - config_helper_.addConfigModifier( - [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& - hcm) { hcm.mutable_route_config()->mutable_validate_clusters()->set_value(false); }); - defer_listener_finalization_ = true; - HttpIntegrationTest::initialize(); - // Add the assignment and localities. - cluster_load_assignment_.set_cluster_name("cluster_0"); - acceptXdsConnection(); - - if (edsUpdateMode() == Grpc::EdsUpdateMode::Multiplexed) { - initXdsStream(xds_stream_); - for (uint32_t i = 0; i < num_clusters_to_add; ++i) { - EXPECT_TRUE(compareDiscoveryRequest( - Config::TypeUrl::get().ClusterLoadAssignment, "", - {resource_names_.begin(), resource_names_.begin() + i + 1}, {}, {}, true)); - } - sendDiscoveryResponse( - Config::TypeUrl::get().ClusterLoadAssignment, clas_to_add_, clas_to_add_, {}, - std::to_string(eds_version_)); - // Receive EDS ack. - EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().ClusterLoadAssignment, - std::to_string(eds_version_), {}, {}, {}, true)); - eds_version_++; - } else { - std::list expected_requests_contents; - for (uint32_t i = 0; i < num_clusters_to_add; ++i) { - xds_streams_.emplace_back(); - initXdsStream(xds_streams_.back()); - expected_requests_contents.push_back({Config::TypeUrl::get().ClusterLoadAssignment, - {"cluster_" + std::to_string(i)}, - {}, - Grpc::Status::WellKnownGrpcStatus::Ok, - ""}); - compareMultipleDiscoveryRequestsOnMultipleStreams(expected_requests_contents); - } - } - // Wait for our statically specified listener to become ready, and register its port in the - // test framework's downstream listener port map. - test_server_->waitUntilListenersReady(); - registerTestServerPorts({"http"}); - test_server_->waitForGaugeEq("cluster_manager.warming_clusters", 0); - } - - AssertionResult compareMultipleDiscoveryRequestsOnMultipleStreams( - std::list& expected_requests_contents) { - uint32_t curr_stream_idx = 0; - bool comparison_result; - while (curr_stream_idx < xds_streams_.size()) { - for (auto it = expected_requests_contents.begin(); it != expected_requests_contents.end(); - ++it) { - const auto expected_request = *it; - if (sotw_or_delta_ == Grpc::SotwOrDelta::Sotw) { - envoy::service::discovery::v3::DiscoveryRequest request; - VERIFY_ASSERTION( - xds_streams_[curr_stream_idx]->waitForGrpcMessage(*dispatcher_, request)); - comparison_result = assertExpectedDiscoveryRequest(request, expected_request); - } else { - envoy::service::discovery::v3::DeltaDiscoveryRequest request; - VERIFY_ASSERTION( - xds_streams_[curr_stream_idx]->waitForGrpcMessage(*dispatcher_, request)); - comparison_result = assertExpectedDeltaDiscoveryRequest(request, expected_request); - } - if (comparison_result) { - sendDiscoveryResponse( - Config::TypeUrl::get().ClusterLoadAssignment, {clas_to_add_[curr_stream_idx]}, - {clas_to_add_[curr_stream_idx]}, {}, std::to_string(eds_version_), - xds_streams_[curr_stream_idx]); - // Receive EDS ack. - EXPECT_TRUE(compareDiscoveryRequest( - Config::TypeUrl::get().ClusterLoadAssignment, std::to_string(eds_version_), {}, {}, - {}, xds_streams_[curr_stream_idx], true, Grpc::Status::WellKnownGrpcStatus::Ok, - /*expected_error_message=*/"")); - ++curr_stream_idx; - expected_requests_contents.erase(it); - ++eds_version_; - // Request received on current stream, no more requests expected for this stream. - break; - } else { - // No matching request found. - if (it == expected_requests_contents.end()) { - return AssertionFailure(); - } - continue; - } - } - } - if (!expected_requests_contents.empty()) { - return AssertionFailure(); - } else { - return AssertionSuccess(); - } - } - - envoy::config::cluster::v3::Cluster buildCluster(const std::string& name, bool http_active_hc, - bool ignore_new_hosts_until_first_hc) { - envoy::config::cluster::v3::Cluster cluster; - if (ignore_new_hosts_until_first_hc) { - cluster.mutable_common_lb_config()->set_ignore_new_hosts_until_first_hc(true); - } - cluster.set_name(name); - cluster.set_type(envoy::config::cluster::v3::Cluster::EDS); - cluster.mutable_connect_timeout()->CopyFrom(Protobuf::util::TimeUtil::SecondsToDuration(5)); - auto* eds_cluster_config = cluster.mutable_eds_cluster_config(); - eds_cluster_config->mutable_eds_config()->set_resource_api_version( - envoy::config::core::v3::ApiVersion::V3); - auto* api_config_source = eds_cluster_config->mutable_eds_config()->mutable_api_config_source(); - api_config_source->set_api_type(envoy::config::core::v3::ApiConfigSource::GRPC); - api_config_source->set_transport_api_version(envoy::config::core::v3::ApiVersion::V3); - auto* grpc_service = api_config_source->add_grpc_services(); - setGrpcService(*grpc_service, "eds_cluster", fake_upstreams_[0]->localAddress()); - if (http_active_hc) { - auto* health_check = cluster.add_health_checks(); - health_check->mutable_timeout()->set_seconds(30); - // TODO(mattklein123): Consider using simulated time here. - health_check->mutable_interval()->CopyFrom( - Protobuf::util::TimeUtil::MillisecondsToDuration(100)); - health_check->mutable_no_traffic_interval()->CopyFrom( - Protobuf::util::TimeUtil::MillisecondsToDuration(100)); - health_check->mutable_unhealthy_threshold()->set_value(1); - health_check->mutable_healthy_threshold()->set_value(1); - health_check->mutable_http_health_check()->set_path("/healthcheck"); - health_check->mutable_http_health_check()->set_codec_client_type(codec_client_type_); - } - return cluster; - } - - void acceptXdsConnection() { - AssertionResult result = - fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, xds_connection_); - RELEASE_ASSERT(result, result.message()); - } - - void initXdsStream(FakeStreamPtr& stream) { - AssertionResult result = xds_connection_->waitForNewStream(*dispatcher_, stream); - RELEASE_ASSERT(result, result.message()); - stream->startGrpcStream(); - } - - void resetFakeUpstreamInfo(FakeUpstreamInfo& upstream_info) { - if (upstream_info.connection_ == nullptr || upstream_info.upstream_ == nullptr) { - upstream_info.upstream_ = nullptr; - return; - } - AssertionResult result = upstream_info.connection_->close(); - RELEASE_ASSERT(result, result.message()); - result = upstream_info.connection_->waitForDisconnect(); - RELEASE_ASSERT(result, result.message()); - upstream_info.connection_.reset(); - upstream_info.upstream_ = nullptr; - } - - void waitForHealthCheck(uint32_t upstream_info_idx) { - auto& host_info = hosts_upstreams_info_[upstream_info_idx]; - if (host_info.connection_ == nullptr) { - ASSERT_TRUE(host_info.upstream_->waitForHttpConnection(*dispatcher_, host_info.connection_)); - } - ASSERT_TRUE(host_info.connection_->waitForNewStream(*dispatcher_, host_info.defaultStream())); - ASSERT_TRUE(host_info.defaultStream()->waitForEndStream(*dispatcher_)); - - EXPECT_EQ(host_info.defaultStream()->headers().getPathValue(), "/healthcheck"); - EXPECT_EQ(host_info.defaultStream()->headers().getMethodValue(), "GET"); - } - - void setEndpointsInPriorities(uint32_t first_priority, uint32_t second_priority, - bool await_update = true) { - envoy::config::endpoint::v3::ClusterLoadAssignment cluster_load_assignment; - cluster_load_assignment.set_cluster_name("cluster_0"); - { - for (uint32_t i = 0; i < first_priority; ++i) { - auto* locality_lb_endpoints = cluster_load_assignment.add_endpoints(); - auto* endpoint = locality_lb_endpoints->add_lb_endpoints(); - setUpstreamAddress(i, *endpoint); - } - } - { - for (uint32_t i = first_priority; i < first_priority + second_priority; ++i) { - auto* locality_lb_endpoints = cluster_load_assignment.add_endpoints(); - locality_lb_endpoints->set_priority(1); - auto* endpoint = locality_lb_endpoints->add_lb_endpoints(); - setUpstreamAddress(i, *endpoint); - } - } - auto& stream = - (edsUpdateMode() == Grpc::EdsUpdateMode::Multiplexed) ? xds_stream_ : xds_streams_.front(); - sendDiscoveryResponse( - Config::TypeUrl::get().ClusterLoadAssignment, {cluster_load_assignment}, - {cluster_load_assignment}, {}, std::to_string(eds_version_++), stream); - - if (await_update) { - // Receive EDS ack. - EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().ClusterLoadAssignment, - std::to_string(eds_version_ - 1), {}, {}, {}, stream, - true, Grpc::Status::WellKnownGrpcStatus::Ok, "")); - } - } - - void initializeTest(bool http_active_hc) { initializeTest(http_active_hc, 1, false); } - - envoy::type::v3::CodecClientType codec_client_type_{}; - CdsHelper cds_helper_; - envoy::config::endpoint::v3::ClusterLoadAssignment cluster_load_assignment_; - std::vector clusters_to_add_; - std::vector clas_to_add_; - std::vector resource_names_; - std::vector hosts_upstreams_info_; - uint32_t eds_version_{}; - bool test_skipped_{false}; - std::vector xds_streams_; -}; - -INSTANTIATE_TEST_SUITE_P(IpVersionsClientTypeSotwOrDeltaEdsMode, EdsOverGrpcIntegrationTest, - EDS_MODE_DELTA_SOTW_GRPC_CLIENT_INTEGRATION_PARAMS); - -// Validates that endpoints can be added and then moved to other priorities without causing crashes. -// Primarily as a regression test for https://github.com/envoyproxy/envoy/issues/8764 -TEST_P(EdsOverGrpcIntegrationTest, Http2UpdatePriorities) { - initializeTest(true); - setEndpointsInPriorities(2, 2); - setEndpointsInPriorities(4, 0); - setEndpointsInPriorities(0, 4); -} - -// Verify that a host stabilized via active health checking which is first removed from EDS and -// then fails health checking is removed. -TEST_P(EdsOverGrpcIntegrationTest, RemoveAfterHcFail) { - initializeTest(true); - setEndpoints(1, 0, 0, false); - EXPECT_EQ(1, test_server_->gauge("cluster.cluster_0.membership_total")->value()); - EXPECT_EQ(0, test_server_->gauge("cluster.cluster_0.membership_healthy")->value()); - // Wait for the first HC and verify the host is healthy. - waitForHealthCheck(0); - hosts_upstreams_info_[0].defaultStream()->encodeHeaders( - Http::TestResponseHeaderMapImpl{{":status", "200"}}, true); - test_server_->waitForGaugeEq("cluster.cluster_0.membership_healthy", 1); - EXPECT_EQ(1, test_server_->gauge("cluster.cluster_0.membership_total")->value()); - // Clear out the host and verify the host is still healthy. - setEndpoints(0, 0, 0); - EXPECT_EQ(1, test_server_->gauge("cluster.cluster_0.membership_total")->value()); - EXPECT_EQ(1, test_server_->gauge("cluster.cluster_0.membership_healthy")->value()); - // Fail HC and verify the host is gone. - waitForHealthCheck(0); - hosts_upstreams_info_[0].defaultStream()->encodeHeaders( - Http::TestResponseHeaderMapImpl{{":status", "503"}, {"connection", "close"}}, true); - test_server_->waitForGaugeEq("cluster.cluster_0.membership_healthy", 0); - EXPECT_EQ(0, test_server_->gauge("cluster.cluster_0.membership_total")->value()); -} - -// Verifies that endpoints are ignored until health checked when configured to. -TEST_P(EdsOverGrpcIntegrationTest, EndpointWarmingSuccessfulHc) { - // Endpoints are initially excluded. - initializeTest(true, 1, true); - setEndpoints(1, 0, 0, false); - EXPECT_EQ(1, test_server_->gauge("cluster.cluster_0.membership_total")->value()); - EXPECT_EQ(1, test_server_->gauge("cluster.cluster_0.membership_excluded")->value()); - EXPECT_EQ(0, test_server_->gauge("cluster.cluster_0.membership_healthy")->value()); - // Wait for the first HC and verify the host is healthy and that it is no longer being - // excluded. - // The other endpoint should still be excluded. - waitForNextUpstreamRequest(1); - upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}}, true); - test_server_->waitForGaugeEq("cluster.cluster_0.membership_excluded", 0); - EXPECT_EQ(1, test_server_->gauge("cluster.cluster_0.membership_total")->value()); - EXPECT_EQ(1, test_server_->gauge("cluster.cluster_0.membership_healthy")->value()); -} - -// Verifies that endpoints are ignored until health checked when configured to when the first -// health check fails. -TEST_P(EdsOverGrpcIntegrationTest, EndpointWarmingFailedHc) { - // Endpoints are initially excluded. - initializeTest(true, 1, true); - setEndpoints(1, 0, 0, false); - EXPECT_EQ(1, test_server_->gauge("cluster.cluster_0.membership_total")->value()); - EXPECT_EQ(1, test_server_->gauge("cluster.cluster_0.membership_excluded")->value()); - EXPECT_EQ(0, test_server_->gauge("cluster.cluster_0.membership_healthy")->value()); - - // Wait for the first HC and verify the host is healthy and that it is no longer being - // excluded. - // The other endpoint should still be excluded. - waitForNextUpstreamRequest(1); - upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "503"}}, true); - test_server_->waitForGaugeEq("cluster.cluster_0.membership_excluded", 0); - EXPECT_EQ(1, test_server_->gauge("cluster.cluster_0.membership_total")->value()); - EXPECT_EQ(0, test_server_->gauge("cluster.cluster_0.membership_healthy")->value()); -} - -// Validate that health status updates are consumed from EDS. -TEST_P(EdsOverGrpcIntegrationTest, HealthUpdate) { - initializeTest(false); - // Initial state, no cluster members. - EXPECT_EQ(0, test_server_->counter("cluster.cluster_0.membership_change")->value()); - EXPECT_EQ(0, test_server_->gauge("cluster.cluster_0.membership_total")->value()); - EXPECT_EQ(0, test_server_->gauge("cluster.cluster_0.membership_healthy")->value()); - // 2/2 healthy endpoints. - setEndpoints(2, 2, 0); - EXPECT_EQ(1, test_server_->counter("cluster.cluster_0.membership_change")->value()); - EXPECT_EQ(2, test_server_->gauge("cluster.cluster_0.membership_total")->value()); - EXPECT_EQ(2, test_server_->gauge("cluster.cluster_0.membership_healthy")->value()); - // Drop to 0/2 healthy endpoints. - setEndpoints(2, 0, 0); - EXPECT_EQ(1, test_server_->counter("cluster.cluster_0.membership_change")->value()); - EXPECT_EQ(2, test_server_->gauge("cluster.cluster_0.membership_total")->value()); - EXPECT_EQ(0, test_server_->gauge("cluster.cluster_0.membership_healthy")->value()); - // Increase to 1/2 healthy endpoints. - setEndpoints(2, 1, 0); - EXPECT_EQ(1, test_server_->counter("cluster.cluster_0.membership_change")->value()); - EXPECT_EQ(2, test_server_->gauge("cluster.cluster_0.membership_total")->value()); - EXPECT_EQ(1, test_server_->gauge("cluster.cluster_0.membership_healthy")->value()); - // Add host and modify health to 2/3 healthy endpoints. - setEndpoints(3, 2, 0); - EXPECT_EQ(2, test_server_->counter("cluster.cluster_0.membership_change")->value()); - EXPECT_EQ(3, test_server_->gauge("cluster.cluster_0.membership_total")->value()); - EXPECT_EQ(2, test_server_->gauge("cluster.cluster_0.membership_healthy")->value()); - // Modify health to 2/3 healthy and 1/3 degraded. - setEndpoints(3, 2, 1); - EXPECT_EQ(2, test_server_->counter("cluster.cluster_0.membership_change")->value()); - EXPECT_EQ(3, test_server_->gauge("cluster.cluster_0.membership_total")->value()); - EXPECT_EQ(2, test_server_->gauge("cluster.cluster_0.membership_healthy")->value()); - EXPECT_EQ(1, test_server_->gauge("cluster.cluster_0.membership_degraded")->value()); -} - -// Validate that overprovisioning_factor update are picked up by Envoy. -TEST_P(EdsOverGrpcIntegrationTest, OverprovisioningFactorUpdate) { - initializeTest(false); - // Default overprovisioning factor. - setEndpoints(4, 4, 0); - auto get_and_compare = [this](const uint32_t expected_factor) { - const auto& cluster_map = test_server_->server().clusterManager().clusters(); - EXPECT_EQ(2, cluster_map.active_clusters_.size()); - EXPECT_EQ(1, cluster_map.active_clusters_.count("cluster_0")); - const auto& cluster_ref = cluster_map.active_clusters_.find("cluster_0")->second; - const auto& hostset_per_priority = cluster_ref.get().prioritySet().hostSetsPerPriority(); - EXPECT_EQ(2, hostset_per_priority.size()); - const Envoy::Upstream::HostSetPtr& host_set = hostset_per_priority[0]; - EXPECT_EQ(expected_factor, host_set->overprovisioningFactor()); - }; - get_and_compare(Envoy::Upstream::kDefaultOverProvisioningFactor); - // Use new overprovisioning factor 200. - setEndpoints(4, 4, 0, true, 200); - get_and_compare(200); -} - -// Verifies that EDS update only triggers member update callbacks once per update. -TEST_P(EdsOverGrpcIntegrationTest, BatchMemberUpdateCb) { - initializeTest(false); - uint32_t member_update_count{}; - auto& priority_set = test_server_->server() - .clusterManager() - .clusters() - .active_clusters_.find("cluster_0") - ->second.get() - .prioritySet(); - // Keep track of how many times we're seeing a member update callback. - auto member_update_cb = priority_set.addMemberUpdateCb([&](const auto& hosts_added, const auto&) { - // We should see both hosts present in the member update callback. - EXPECT_EQ(2, hosts_added.size()); - member_update_count++; - }); - setEndpoints(2, 2, 0); - EXPECT_EQ(1, member_update_count); -} - -TEST_P(EdsOverGrpcIntegrationTest, StatsReadyFilter) { - config_helper_.prependFilter("name: eds-ready-filter"); - initializeTest(false); - // Initial state: no healthy endpoints - EXPECT_EQ(0, test_server_->gauge("cluster.cluster_0.membership_healthy")->value()); - BufferingStreamDecoderPtr response = IntegrationUtil::makeSingleRequest( - lookupPort("http"), "GET", "/cluster1", "", downstream_protocol_, version_, "foo.com"); - ASSERT_TRUE(response->complete()); - EXPECT_EQ("500", response->headers().getStatusValue()); - EXPECT_EQ("EDS not ready", response->body()); - cleanupUpstreamAndDownstream(); - // 2/2 healthy endpoints. - setEndpoints(2, 2, 0); - EXPECT_EQ(2, test_server_->gauge("cluster.cluster_0.membership_healthy")->value()); - response = IntegrationUtil::makeSingleRequest(lookupPort("http"), "GET", "/cluster1", "", - downstream_protocol_, version_, "foo.com"); - ASSERT_TRUE(response->complete()); - EXPECT_EQ("200", response->headers().getStatusValue()); - EXPECT_EQ("EDS is ready", response->body()); - cleanupUpstreamAndDownstream(); -} - -TEST_P(EdsOverGrpcIntegrationTest, ReuseMuxAndStreamForMultipleClusters) { - if (edsUpdateMode() == Grpc::EdsUpdateMode::Multiplexed) { - initializeTest(false, 2, false); - EXPECT_EQ(0, test_server_->gauge("cluster.cluster_0.membership_total")->value()); - EXPECT_EQ(0, test_server_->gauge("cluster.cluster_1.membership_total")->value()); - switch (clientType()) { - case Grpc::ClientType::EnvoyGrpc: - // As EDS uses HTTP2, number of streams created by Envoy for EDS cluster equals to number - // of requests. - EXPECT_EQ(1UL, test_server_->counter("cluster.eds_cluster.upstream_rq_total")->value()); - break; - case Grpc::ClientType::GoogleGrpc: - // One EDS mux/stream is created and reused for 2 clusters when initializing first EDS - // cluster (cluster_0). As a consequence, only one Google async grpc client and one - // corresponding set of client stats should be created. - EXPECT_EQ(1UL, - test_server_->counter("cluster.cluster_0.grpc.eds_cluster.streams_total")->value()); - EXPECT_EQ(TestUtility::findCounter(test_server_->statStore(), - "cluster.cluster_1.grpc.eds_cluster.streams_total"), - nullptr); - break; - default: - PANIC("reached unexpected code"); - } - } else { - test_skipped_ = true; - } -} - -TEST_P(EdsOverGrpcIntegrationTest, StreamPerClusterMultipleClusters) { - if (edsUpdateMode() == Grpc::EdsUpdateMode::StreamPerCluster) { - initializeTest(false, 2, false); - EXPECT_EQ(0, test_server_->gauge("cluster.cluster_0.membership_total")->value()); - EXPECT_EQ(0, test_server_->gauge("cluster.cluster_1.membership_total")->value()); - switch (clientType()) { - case Grpc::ClientType::EnvoyGrpc: - // As EDS uses HTTP2, number of streams created by Envoy for EDS cluster equals to number - // of requests. - EXPECT_EQ(2UL, test_server_->counter("cluster.eds_cluster.upstream_rq_total")->value()); - break; - case Grpc::ClientType::GoogleGrpc: - // One EDS mux/stream is created for each cluster. As a consequence, 2 sets of client stats - // should be instantiated. - EXPECT_EQ(1UL, - test_server_->counter("cluster.cluster_0.grpc.eds_cluster.streams_total")->value()); - EXPECT_EQ(1UL, - test_server_->counter("cluster.cluster_1.grpc.eds_cluster.streams_total")->value()); - break; - default: - PANIC("reached unexpected code"); - } - } else { - test_skipped_ = true; - } -} - -} // namespace -} // namespace Envoy diff --git a/test/integration/eds_integration_test.cc b/test/integration/eds_integration_test.cc index ce705d269a00..d3be9d2ccb20 100644 --- a/test/integration/eds_integration_test.cc +++ b/test/integration/eds_integration_test.cc @@ -315,8 +315,8 @@ TEST_P(EdsIntegrationTest, FinishWarmingIgnoreHealthCheck) { EXPECT_EQ(0, test_server_->gauge("cluster.cluster_0.membership_healthy")->value()); EXPECT_EQ(0, test_server_->gauge("cluster_manager.warming_clusters")->value()); - // Trigger a CDS update. This should cause a new cluster to require warming, blocked on the - // host being health checked. + // Trigger a CDS update. This should cause a new cluster to require warming, blocked on the host + // being health checked. cluster_.mutable_circuit_breakers()->add_thresholds()->mutable_max_connections()->set_value(100); cds_helper_.setCds({cluster_}); test_server_->waitForGaugeEq("cluster_manager.warming_clusters", 1); @@ -340,8 +340,8 @@ TEST_P(EdsIntegrationTest, EndpointWarmingSuccessfulHc) { EXPECT_EQ(1, test_server_->gauge("cluster.cluster_0.membership_excluded")->value()); EXPECT_EQ(0, test_server_->gauge("cluster.cluster_0.membership_healthy")->value()); - // Wait for the first HC and verify the host is healthy and that it is no longer being - // excluded. The other endpoint should still be excluded. + // Wait for the first HC and verify the host is healthy and that it is no longer being excluded. + // The other endpoint should still be excluded. waitForNextUpstreamRequest(0); upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}}, true); test_server_->waitForGaugeEq("cluster.cluster_0.membership_excluded", 0); @@ -362,8 +362,8 @@ TEST_P(EdsIntegrationTest, EndpointWarmingFailedHc) { EXPECT_EQ(1, test_server_->gauge("cluster.cluster_0.membership_excluded")->value()); EXPECT_EQ(0, test_server_->gauge("cluster.cluster_0.membership_healthy")->value()); - // Wait for the first HC and verify the host is healthy and that it is no longer being - // excluded. The other endpoint should still be excluded. + // Wait for the first HC and verify the host is healthy and that it is no longer being excluded. + // The other endpoint should still be excluded. waitForNextUpstreamRequest(0); upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "503"}}, true); test_server_->waitForGaugeEq("cluster.cluster_0.membership_excluded", 0); diff --git a/test/integration/fake_upstream.cc b/test/integration/fake_upstream.cc index 783439df3d2a..159a9d030535 100644 --- a/test/integration/fake_upstream.cc +++ b/test/integration/fake_upstream.cc @@ -44,7 +44,7 @@ FakeStream::FakeStream(FakeHttpConnection& parent, Http::ResponseEncoder& encode encoder.getStream().addCallbacks(*this); } -void FakeStream::decodeHeaders(Http::RequestHeaderMapPtr&& headers, bool end_stream) { +void FakeStream::decodeHeaders(Http::RequestHeaderMapSharedPtr&& headers, bool end_stream) { absl::MutexLock lock(&lock_); headers_ = std::move(headers); setEndStream(end_stream); diff --git a/test/integration/fake_upstream.h b/test/integration/fake_upstream.h index cd72f44e528b..6f3f92a64dfd 100644 --- a/test/integration/fake_upstream.h +++ b/test/integration/fake_upstream.h @@ -219,12 +219,15 @@ class FakeStream : public Http::RequestDecoder, void decodeMetadata(Http::MetadataMapPtr&& metadata_map_ptr) override; // Http::RequestDecoder - void decodeHeaders(Http::RequestHeaderMapPtr&& headers, bool end_stream) override; + void decodeHeaders(Http::RequestHeaderMapSharedPtr&& headers, bool end_stream) override; void decodeTrailers(Http::RequestTrailerMapPtr&& trailers) override; StreamInfo::StreamInfo& streamInfo() override { RELEASE_ASSERT(false, "initialize if this is needed"); return *stream_info_; } + std::list accessLogHandlers() override { + return access_log_handlers_; + } // Http::StreamCallbacks void onResetStream(Http::StreamResetReason reason, @@ -243,7 +246,7 @@ class FakeStream : public Http::RequestDecoder, protected: absl::Mutex lock_; - Http::RequestHeaderMapPtr headers_ ABSL_GUARDED_BY(lock_); + Http::RequestHeaderMapSharedPtr headers_ ABSL_GUARDED_BY(lock_); Buffer::OwnedImpl body_ ABSL_GUARDED_BY(lock_); FakeHttpConnection& parent_; @@ -258,7 +261,8 @@ class FakeStream : public Http::RequestDecoder, Event::TestTimeSystem& time_system_; Http::MetadataMap metadata_map_; absl::node_hash_map duplicated_metadata_key_count_; - std::unique_ptr stream_info_; + std::shared_ptr stream_info_; + std::list access_log_handlers_; bool received_data_{false}; bool grpc_stream_started_{false}; }; diff --git a/test/integration/filter_integration_test.cc b/test/integration/filter_integration_test.cc index 4c2caafb2534..2ce95296ede9 100644 --- a/test/integration/filter_integration_test.cc +++ b/test/integration/filter_integration_test.cc @@ -198,6 +198,29 @@ TEST_P(FilterIntegrationTest, MissingHeadersLocalReplyDownstreamBytesCount) { } } +TEST_P(FilterIntegrationTest, RoundTripTimeForUpstreamConnection) { + config_helper_.prependFilter(R"EOF( + name: stream-info-to-headers-filter + )EOF"); + initialize(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + + // Send a headers only request. + auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); + waitForNextUpstreamRequest(); + + // Make sure that the body was injected to the request. + EXPECT_TRUE(upstream_request_->complete()); + + // Send a headers only response. + upstream_request_->encodeHeaders(default_response_headers_, true); + ASSERT_TRUE(response->waitForEndStream()); + + // Make sure that round trip time was populated + EXPECT_FALSE(response->headers().get(Http::LowerCaseString("round_trip_time")).empty()); +} + TEST_P(FilterIntegrationTest, MissingHeadersLocalReplyUpstreamBytesCount) { useAccessLog("%UPSTREAM_WIRE_BYTES_SENT% %UPSTREAM_WIRE_BYTES_RECEIVED% " "%UPSTREAM_HEADER_BYTES_SENT% %UPSTREAM_HEADER_BYTES_RECEIVED%"); diff --git a/test/integration/filters/stream_info_to_headers_filter.cc b/test/integration/filters/stream_info_to_headers_filter.cc index 6d93bc4f29ed..8a9b522a5f87 100644 --- a/test/integration/filters/stream_info_to_headers_filter.cc +++ b/test/integration/filters/stream_info_to_headers_filter.cc @@ -38,6 +38,10 @@ class StreamInfoToHeadersFilter : public Http::PassThroughFilter { headers.addCopy(Http::LowerCaseString("dns_end"), toUsec(stream_info.downstreamTiming().getValue(dns_end).value())); } + if (stream_info.downstreamAddressProvider().roundTripTime().has_value()) { + headers.addCopy(Http::LowerCaseString("round_trip_time"), + stream_info.downstreamAddressProvider().roundTripTime().value().count()); + } if (conn_stream_info.downstreamTiming().has_value() && conn_stream_info.downstreamTiming()->downstreamHandshakeComplete().has_value()) { headers.addCopy( diff --git a/test/integration/http_integration.h b/test/integration/http_integration.h index 9b71dfa6e4f0..8bf48ff28c98 100644 --- a/test/integration/http_integration.h +++ b/test/integration/http_integration.h @@ -144,6 +144,15 @@ class HttpIntegrationTest : public BaseIntegrationTest { protected: void useAccessLog(absl::string_view format = "", std::vector formatters = {}); + std::string waitForAccessLog(const std::string& filename, uint32_t entry = 0, + bool allow_excess_entries = false, + Network::ClientConnection* client_connection = nullptr) { + if (client_connection == nullptr && codec_client_) { + client_connection = codec_client_->connection(); + } + return BaseIntegrationTest::waitForAccessLog(filename, entry, allow_excess_entries, + client_connection); + }; IntegrationCodecClientPtr makeHttpConnection(uint32_t port); // Makes a http connection object without checking its connected state. diff --git a/test/integration/integration_test.cc b/test/integration/integration_test.cc index 868f47fa313a..c4f5eab4e9fb 100644 --- a/test/integration/integration_test.cc +++ b/test/integration/integration_test.cc @@ -1061,11 +1061,6 @@ TEST_P(IntegrationTest, InvalidCharacterInFirstline) { } TEST_P(IntegrationTest, InvalidVersion) { - if (http1_implementation_ == Http1ParserImpl::BalsaParser) { - // TODO(#21245): Re-enable this test for BalsaParser. - return; - } - initialize(); std::string response; sendRawHttpAndWaitForResponse(lookupPort("http"), "GET / HTTP/1.01\r\nHost: host\r\n\r\n", diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index 337de9d2f2e3..50a62927a65c 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -1,3 +1,4 @@ +#include "protocol_integration_test.h" #include "test/integration/protocol_integration_test.h" #include @@ -824,6 +825,33 @@ TEST_P(ProtocolIntegrationTest, Retry) { BytesCountExpectation(2204, 520, 150, 6)); } +// Regression test to guarantee that buffering for retries and shadows doesn't double the body size. +// This test is actually irrelevant for QUIC, as this issue only shows up with header-only requests. +// QUIC will always send an empty data frame with FIN. +TEST_P(ProtocolIntegrationTest, RetryWithBuffer) { + config_helper_.prependFilter(R"EOF( + name: add-body-filter + typed_config: + "@type": type.googleapis.com/test.integration.filters.AddBodyFilterConfig + where_to_add_body: DEFAULT + )EOF"); + initialize(); + codec_client_ = makeHttpConnection(lookupPort("http")); + auto response = codec_client_->makeHeaderOnlyRequest( + Http::TestRequestHeaderMapImpl{{":method", "POST"}, + {":path", "/test/long/url"}, + {":scheme", "http"}, + {":authority", "sni.lyft.com"}, + {"x-forwarded-for", "10.0.0.1"}, + {"x-envoy-retry-on", "5xx"}}); + waitForNextUpstreamRequest(); + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_TRUE(upstream_request_->receivedData()); + EXPECT_EQ(upstream_request_->bodyLength(), 4); + upstream_request_->encodeHeaders(default_response_headers_, true); + ASSERT_TRUE(response->waitForEndStream()); +} + TEST_P(ProtocolIntegrationTest, RetryStreaming) { initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); diff --git a/test/integration/quic_http_integration_test.cc b/test/integration/quic_http_integration_test.cc index 7072bd8c86de..8a3407ce28c8 100644 --- a/test/integration/quic_http_integration_test.cc +++ b/test/integration/quic_http_integration_test.cc @@ -1182,6 +1182,207 @@ TEST_P(QuicHttpIntegrationTest, MultipleNetworkFilters) { codec_client_->close(); } +TEST_P(QuicHttpIntegrationTest, DeferredLogging) { + useAccessLog( + "%PROTOCOL%,%ROUNDTRIP_DURATION%,%REQUEST_DURATION%,%RESPONSE_DURATION%,%RESPONSE_" + "CODE%,%BYTES_RECEIVED%,%ROUTE_NAME%,%VIRTUAL_CLUSTER_NAME%,%RESPONSE_CODE_DETAILS%,%" + "CONNECTION_TERMINATION_DETAILS%,%START_TIME%,%UPSTREAM_HOST%,%DURATION%,%BYTES_SENT%,%" + "RESPONSE_FLAGS%,%DOWNSTREAM_LOCAL_ADDRESS%,%UPSTREAM_CLUSTER%,%STREAM_ID%,%DYNAMIC_" + "METADATA(" + "udp.proxy.session:bytes_sent)%,%REQ(:path)%,%STREAM_INFO_REQ(:path)%"); + initialize(); + codec_client_ = makeHttpConnection(makeClientConnection(lookupPort("http"))); + sendRequestAndWaitForResponse(default_request_headers_, /*request_size=*/0, + default_response_headers_, + /*response_size=*/0, + /*upstream_index=*/0, TestUtility::DefaultTimeout); + codec_client_->close(); + + std::string log = waitForAccessLog(access_log_name_); + + std::vector metrics = absl::StrSplit(log, ","); + ASSERT_EQ(metrics.size(), 21); + EXPECT_EQ(/* PROTOCOL */ metrics.at(0), "HTTP/3"); + EXPECT_GT(/* ROUNDTRIP_DURATION */ std::stoi(metrics.at(1)), 0); + EXPECT_GE(/* REQUEST_DURATION */ std::stoi(metrics.at(2)), 0); + EXPECT_GE(/* RESPONSE_DURATION */ std::stoi(metrics.at(3)), 0); + EXPECT_EQ(/* RESPONSE_CODE */ metrics.at(4), "200"); + EXPECT_EQ(/* BYTES_RECEIVED */ metrics.at(5), "0"); + // Ensure that request headers from top-level access logger parameter and stream info are + // consistent. + EXPECT_EQ(/* request headers */ metrics.at(19), metrics.at(20)); +} + +TEST_P(QuicHttpIntegrationTest, DeferredLoggingDisabled) { + config_helper_.addRuntimeOverride("envoy.reloadable_features.quic_defer_logging_to_ack_listener", + "false"); + useAccessLog( + "%PROTOCOL%,%ROUNDTRIP_DURATION%,%REQUEST_DURATION%,%RESPONSE_DURATION%,%RESPONSE_" + "CODE%,%BYTES_RECEIVED%,%ROUTE_NAME%,%VIRTUAL_CLUSTER_NAME%,%RESPONSE_CODE_DETAILS%,%" + "CONNECTION_TERMINATION_DETAILS%,%START_TIME%,%UPSTREAM_HOST%,%DURATION%,%BYTES_SENT%,%" + "RESPONSE_FLAGS%,%DOWNSTREAM_LOCAL_ADDRESS%,%UPSTREAM_CLUSTER%,%STREAM_ID%,%DYNAMIC_" + "METADATA(" + "udp.proxy.session:bytes_sent)%,%REQ(:path)%,%STREAM_INFO_REQ(:path)%"); + initialize(); + codec_client_ = makeHttpConnection(makeClientConnection(lookupPort("http"))); + sendRequestAndWaitForResponse(default_request_headers_, /*request_size=*/0, + default_response_headers_, + /*response_size=*/0, + /*upstream_index=*/0, TestUtility::DefaultTimeout); + codec_client_->close(); + + // Do not flush client acks. + std::string log = waitForAccessLog(access_log_name_, 0, false, nullptr); + std::vector metrics = absl::StrSplit(log, ","); + ASSERT_EQ(metrics.size(), 21); + EXPECT_EQ(/* PROTOCOL */ metrics.at(0), "HTTP/3"); + EXPECT_EQ(/* ROUNDTRIP_DURATION */ metrics.at(1), "-"); + EXPECT_GE(/* REQUEST_DURATION */ std::stoi(metrics.at(2)), 0); + EXPECT_GE(/* RESPONSE_DURATION */ std::stoi(metrics.at(3)), 0); + EXPECT_EQ(/* RESPONSE_CODE */ metrics.at(4), "200"); + EXPECT_EQ(/* BYTES_RECEIVED */ metrics.at(5), "0"); + EXPECT_EQ(/* request headers */ metrics.at(19), metrics.at(20)); +} + +TEST_P(QuicHttpIntegrationTest, DeferredLoggingWithReset) { + useAccessLog( + "%PROTOCOL%,%ROUNDTRIP_DURATION%,%REQUEST_DURATION%,%RESPONSE_DURATION%,%RESPONSE_" + "CODE%,%BYTES_RECEIVED%,%ROUTE_NAME%,%VIRTUAL_CLUSTER_NAME%,%RESPONSE_CODE_DETAILS%,%" + "CONNECTION_TERMINATION_DETAILS%,%START_TIME%,%UPSTREAM_HOST%,%DURATION%,%BYTES_SENT%,%" + "RESPONSE_FLAGS%,%DOWNSTREAM_LOCAL_ADDRESS%,%UPSTREAM_CLUSTER%,%STREAM_ID%,%DYNAMIC_" + "METADATA(" + "udp.proxy.session:bytes_sent)%,%REQ(:path)%,%STREAM_INFO_REQ(:path)%"); + initialize(); + codec_client_ = makeHttpConnection(makeClientConnection(lookupPort("http"))); + auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); + waitForNextUpstreamRequest(0); + codec_client_->close(); + ASSERT_TRUE(response->waitForReset()); + EXPECT_FALSE(response->complete()); + + std::string log = waitForAccessLog(access_log_name_); + std::vector metrics = absl::StrSplit(log, ","); + ASSERT_EQ(metrics.size(), 21); + EXPECT_EQ(/* PROTOCOL */ metrics.at(0), "HTTP/3"); + EXPECT_EQ(/* ROUNDTRIP_DURATION */ metrics.at(1), "-"); + EXPECT_GE(/* REQUEST_DURATION */ std::stoi(metrics.at(2)), 0); + EXPECT_EQ(/* RESPONSE_DURATION */ metrics.at(3), "-"); + EXPECT_EQ(/* RESPONSE_CODE */ metrics.at(4), "0"); + EXPECT_EQ(/* BYTES_RECEIVED */ metrics.at(5), "0"); + EXPECT_EQ(/* request headers */ metrics.at(19), metrics.at(20)); +} + +TEST_P(QuicHttpIntegrationTest, DeferredLoggingWithQuicReset) { + useAccessLog( + "%PROTOCOL%,%ROUNDTRIP_DURATION%,%REQUEST_DURATION%,%RESPONSE_DURATION%,%RESPONSE_" + "CODE%,%BYTES_RECEIVED%,%ROUTE_NAME%,%VIRTUAL_CLUSTER_NAME%,%RESPONSE_CODE_DETAILS%,%" + "CONNECTION_TERMINATION_DETAILS%,%START_TIME%,%UPSTREAM_HOST%,%DURATION%,%BYTES_SENT%,%" + "RESPONSE_FLAGS%,%DOWNSTREAM_LOCAL_ADDRESS%,%UPSTREAM_CLUSTER%,%STREAM_ID%,%DYNAMIC_" + "METADATA(" + "udp.proxy.session:bytes_sent)%,%REQ(:path)%,%STREAM_INFO_REQ(:path)%"); + initialize(); + codec_client_ = makeHttpConnection(lookupPort("http")); + + // omit required authority header to invoke EnvoyQuicServerStream::resetStream + auto encoder_decoder = codec_client_->startRequest(Http::TestRequestHeaderMapImpl{ + {":method", "GET"}, {":path", "/dynamo/url"}, {":scheme", "http"}}); + request_encoder_ = &encoder_decoder.first; + auto response = std::move(encoder_decoder.second); + + ASSERT_TRUE(response->waitForEndStream()); + codec_client_->close(); + ASSERT_TRUE(response->complete()); + + std::string log = waitForAccessLog(access_log_name_); + std::vector metrics = absl::StrSplit(log, ","); + ASSERT_EQ(metrics.size(), 21); + EXPECT_EQ(/* PROTOCOL */ metrics.at(0), "HTTP/3"); + EXPECT_EQ(/* ROUNDTRIP_DURATION */ metrics.at(1), "-"); + EXPECT_EQ(/* REQUEST_DURATION */ metrics.at(2), "-"); + EXPECT_EQ(/* RESPONSE_DURATION */ metrics.at(3), "-"); + EXPECT_EQ(/* RESPONSE_CODE */ metrics.at(4), "400"); + EXPECT_EQ(/* BYTES_RECEIVED */ metrics.at(5), "0"); + EXPECT_EQ(/* request headers */ metrics.at(19), metrics.at(20)); +} + +TEST_P(QuicHttpIntegrationTest, DeferredLoggingWithInternalRedirect) { + useAccessLog( + "%PROTOCOL%,%ROUNDTRIP_DURATION%,%REQUEST_DURATION%,%RESPONSE_DURATION%,%RESPONSE_" + "CODE%,%BYTES_RECEIVED%,%ROUTE_NAME%,%VIRTUAL_CLUSTER_NAME%,%RESPONSE_CODE_DETAILS%,%" + "CONNECTION_TERMINATION_DETAILS%,%START_TIME%,%UPSTREAM_HOST%,%DURATION%,%BYTES_SENT%,%" + "RESPONSE_FLAGS%,%DOWNSTREAM_LOCAL_ADDRESS%,%UPSTREAM_CLUSTER%,%STREAM_ID%,%DYNAMIC_" + "METADATA(" + "udp.proxy.session:bytes_sent)%,%REQ(:path)%,%STREAM_INFO_REQ(:path)%,%RESP(test-header)%"); + auto handle = config_helper_.createVirtualHost("handle.internal.redirect"); + handle.mutable_routes(0)->set_name("redirect"); + handle.mutable_routes(0)->mutable_route()->mutable_internal_redirect_policy(); + config_helper_.addVirtualHost(handle); + initialize(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + + default_request_headers_.setHost("handle.internal.redirect"); + IntegrationStreamDecoderPtr response = + codec_client_->makeHeaderOnlyRequest(default_request_headers_); + + waitForNextUpstreamRequest(); + + Http::TestResponseHeaderMapImpl redirect_response{{":status", "302"}, + {"content-length", "0"}, + {"location", "http://authority2/new/url"}, + // Test header added to confirm that response + // headers are populated for internal redirects + {"test-header", "test-header-value"}}; + + upstream_request_->encodeHeaders(redirect_response, true); + std::string log = waitForAccessLog(access_log_name_, 0); + std::vector metrics = absl::StrSplit(log, ","); + ASSERT_EQ(metrics.size(), 22); + EXPECT_EQ(/* PROTOCOL */ metrics.at(0), "HTTP/3"); + // no roundtrip duration for internal redirect. + EXPECT_EQ(/* ROUNDTRIP_DURATION */ metrics.at(1), "-"); + EXPECT_GE(/* REQUEST_DURATION */ std::stoi(metrics.at(2)), 0); + EXPECT_GE(/* RESPONSE_DURATION */ std::stoi(metrics.at(3)), 0); + EXPECT_EQ(/* RESPONSE_CODE */ metrics.at(4), "302"); + EXPECT_EQ(/* BYTES_RECEIVED */ metrics.at(5), "0"); + EXPECT_EQ(/* request headers */ metrics.at(19), metrics.at(20)); + EXPECT_EQ(/* RESPONSE_CODE_DETAILS */ metrics.at(8), "internal_redirect"); + EXPECT_EQ(/* RESP(test-header) */ metrics.at(21), "test-header-value"); + + waitForNextUpstreamRequest(); + ASSERT(upstream_request_->headers().EnvoyOriginalUrl() != nullptr); + EXPECT_EQ("http://handle.internal.redirect/test/long/url", + upstream_request_->headers().getEnvoyOriginalUrlValue()); + EXPECT_EQ("/new/url", upstream_request_->headers().getPathValue()); + EXPECT_EQ("authority2", upstream_request_->headers().getHostValue()); + + upstream_request_->encodeHeaders(default_response_headers_, true); + + ASSERT_TRUE(response->waitForEndStream()); + ASSERT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().getStatusValue()); + EXPECT_EQ(1, test_server_->counter("cluster.cluster_0.upstream_internal_redirect_succeeded_total") + ->value()); + // 302 was never returned downstream + EXPECT_EQ(0, test_server_->counter("http.config_test.downstream_rq_3xx")->value()); + EXPECT_EQ(1, test_server_->counter("http.config_test.downstream_rq_2xx")->value()); + + log = waitForAccessLog(access_log_name_, 1); + metrics = absl::StrSplit(log, ","); + ASSERT_EQ(metrics.size(), 22); + EXPECT_EQ(/* PROTOCOL */ metrics.at(0), "HTTP/3"); + // roundtrip duration populated on final log. + EXPECT_GT(/* ROUNDTRIP_DURATION */ std::stoi(metrics.at(1)), 0); + EXPECT_GE(/* REQUEST_DURATION */ std::stoi(metrics.at(2)), 0); + EXPECT_GE(/* RESPONSE_DURATION */ std::stoi(metrics.at(3)), 0); + EXPECT_EQ(/* RESPONSE_CODE */ metrics.at(4), "200"); + EXPECT_EQ(/* BYTES_RECEIVED */ metrics.at(5), "0"); + EXPECT_EQ(/* request headers */ metrics.at(19), metrics.at(20)); + EXPECT_EQ(/* RESPONSE_CODE_DETAILS */ metrics.at(8), "via_upstream"); + // no test header + EXPECT_EQ(/* RESP(test-header) */ metrics.at(21), "-"); +} + class QuicInplaceLdsIntegrationTest : public QuicHttpIntegrationTest { public: void inplaceInitialize(bool add_default_filter_chain = false) { diff --git a/test/integration/redirect_integration_test.cc b/test/integration/redirect_integration_test.cc index d746e0097976..bb187b731d29 100644 --- a/test/integration/redirect_integration_test.cc +++ b/test/integration/redirect_integration_test.cc @@ -219,8 +219,7 @@ TEST_P(RedirectIntegrationTest, BasicInternalRedirectDownstreamBytesCount) { upstream_request_->encodeHeaders(redirect_response_, true); expectDownstreamBytesSentAndReceived(BytesCountExpectation(0, 63, 0, 31), BytesCountExpectation(0, 42, 0, 42), - BytesCountExpectation(0, 42, 0, 42), 0); - + BytesCountExpectation(0, 8, 0, 6), 0); waitForNextUpstreamRequest(); upstream_request_->encodeHeaders(default_response_headers_, true); @@ -228,7 +227,7 @@ TEST_P(RedirectIntegrationTest, BasicInternalRedirectDownstreamBytesCount) { ASSERT_TRUE(response->complete()); expectDownstreamBytesSentAndReceived(BytesCountExpectation(140, 63, 121, 31), BytesCountExpectation(77, 42, 77, 42), - BytesCountExpectation(77, 42, 77, 42), 1); + BytesCountExpectation(9, 8, 9, 6), 1); } TEST_P(RedirectIntegrationTest, BasicInternalRedirectUpstreamBytesCount) { @@ -611,9 +610,15 @@ TEST_P(RedirectIntegrationTest, InternalRedirectToDestinationWithResponseBody) { config_helper_.addConfigModifier( [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& hcm) { hcm.set_via("via_value"); }); - config_helper_.prependFilter(R"EOF( + if (downstreamProtocol() == Http::CodecType::HTTP3) { + config_helper_.prependFilter(R"EOF( + name: pause-filter-for-quic + )EOF"); + } else { + config_helper_.prependFilter(R"EOF( name: pause-filter )EOF"); + } initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -707,7 +712,10 @@ TEST_P(RedirectIntegrationTest, InternalRedirectHandledByDirectResponse) { } INSTANTIATE_TEST_SUITE_P(Protocols, RedirectIntegrationTest, - testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams()), + testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams( + {Http::CodecType::HTTP1, Http::CodecType::HTTP2, + Http::CodecType::HTTP3}, + {Http::CodecType::HTTP1, Http::CodecType::HTTP2})), HttpProtocolIntegrationTest::protocolTestParamsToString); } // namespace Envoy diff --git a/test/integration/shadow_policy_integration_test.cc b/test/integration/shadow_policy_integration_test.cc index 2603e23dacaf..0af3779da2c8 100644 --- a/test/integration/shadow_policy_integration_test.cc +++ b/test/integration/shadow_policy_integration_test.cc @@ -1,3 +1,4 @@ +#include #include #include "envoy/extensions/access_loggers/file/v3/file.pb.h" @@ -6,14 +7,21 @@ #include "test/integration/filters/repick_cluster_filter.h" #include "test/integration/http_integration.h" +#include "test/integration/socket_interface_swap.h" +#include "test/test_common/test_runtime.h" namespace Envoy { namespace { -class ShadowPolicyIntegrationTest : public testing::TestWithParam, - public HttpIntegrationTest { +class ShadowPolicyIntegrationTest + : public testing::TestWithParam>, + public HttpIntegrationTest, + public SocketInterfaceSwap { public: - ShadowPolicyIntegrationTest() : HttpIntegrationTest(Http::CodecType::HTTP2, GetParam()) { + ShadowPolicyIntegrationTest() + : HttpIntegrationTest(Http::CodecType::HTTP2, std::get<0>(GetParam())) { + scoped_runtime_.mergeValues( + {{"envoy.reloadable_features.streaming_shadow", streaming_shadow_ ? "true" : "false"}}); setUpstreamProtocol(Http::CodecType::HTTP2); autonomous_upstream_ = true; setUpstreamCount(2); @@ -73,7 +81,6 @@ class ShadowPolicyIntegrationTest : public testing::TestWithParambody().size()); } test_server_->waitForCounterEq("cluster.cluster_1.internal.upstream_rq_completed", 1); - test_server_->waitForCounterEq("cluster.cluster_1.internal.upstream_rq_completed", 1); upstream_headers_ = reinterpret_cast(fake_upstreams_[0].get())->lastRequestHeaders(); @@ -85,15 +92,649 @@ class ShadowPolicyIntegrationTest : public testing::TestWithParam(GetParam()); absl::optional cluster_with_custom_filter_; std::string filter_name_ = "on-local-reply-filter"; std::unique_ptr upstream_headers_; std::unique_ptr mirror_headers_; + TestScopedRuntime scoped_runtime_; }; -INSTANTIATE_TEST_SUITE_P(IpVersions, ShadowPolicyIntegrationTest, - testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), - TestUtility::ipTestParamsToString); +INSTANTIATE_TEST_SUITE_P( + IpVersionsAndStreaming, ShadowPolicyIntegrationTest, + testing::Combine(testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), testing::Bool()), + [](const ::testing::TestParamInfo& params) { + return absl::StrCat(std::get<0>(params.param) == Network::Address::IpVersion::v4 ? "IPv4" + : "IPv6", + "_", std::get<1>(params.param) ? "streaming_shadow" : "buffered_shadow"); + }); + +TEST_P(ShadowPolicyIntegrationTest, RequestMirrorPolicyWithDownstreamReset) { + if (!streaming_shadow_) { + GTEST_SKIP() << "Not applicable for non-streaming shadows."; + } + autonomous_upstream_ = false; + initialConfigSetup("cluster_1", ""); + initialize(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + Http::TestRequestHeaderMapImpl request_headers = default_request_headers_; + request_headers.addCopy("potato", "salad"); + std::pair result = + codec_client_->startRequest(request_headers, false); + auto& encoder = result.first; + auto response = std::move(result.second); + + FakeHttpConnectionPtr fake_upstream_connection_main; + FakeStreamPtr upstream_request_main; + ASSERT_TRUE( + fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_main)); + ASSERT_TRUE(fake_upstream_connection_main->waitForNewStream(*dispatcher_, upstream_request_main)); + FakeHttpConnectionPtr fake_upstream_connection_shadow; + FakeStreamPtr upstream_request_shadow; + ASSERT_TRUE( + fake_upstreams_[1]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_shadow)); + ASSERT_TRUE( + fake_upstream_connection_shadow->waitForNewStream(*dispatcher_, upstream_request_shadow)); + ASSERT_TRUE(upstream_request_main->waitForHeadersComplete()); + ASSERT_TRUE(upstream_request_shadow->waitForHeadersComplete()); + EXPECT_EQ(upstream_request_main->headers().get(Http::LowerCaseString("potato"))[0]->value(), + "salad"); + EXPECT_EQ(upstream_request_shadow->headers().get(Http::LowerCaseString("potato"))[0]->value(), + "salad"); + + codec_client_->sendReset(encoder); + + ASSERT_TRUE(upstream_request_main->waitForReset()); + ASSERT_TRUE(upstream_request_shadow->waitForReset()); + ASSERT_TRUE(fake_upstream_connection_main->close()); + ASSERT_TRUE(fake_upstream_connection_shadow->close()); + ASSERT_TRUE(fake_upstream_connection_main->waitForDisconnect()); + ASSERT_TRUE(fake_upstream_connection_shadow->waitForDisconnect()); + + EXPECT_FALSE(upstream_request_main->complete()); + EXPECT_FALSE(upstream_request_shadow->complete()); + EXPECT_FALSE(response->complete()); + + cleanupUpstreamAndDownstream(); + + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_cx_total")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_cx_total")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_rq_tx_reset")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_rq_tx_reset")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_rq_completed")->value(), 0); + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_rq_completed")->value(), 0); +} + +TEST_P(ShadowPolicyIntegrationTest, RequestMirrorPolicyWithMainUpstreamReset) { + if (!streaming_shadow_) { + GTEST_SKIP() << "Not applicable for non-streaming shadows."; + } + autonomous_upstream_ = false; + initialConfigSetup("cluster_1", ""); + initialize(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + Http::TestRequestHeaderMapImpl request_headers = default_request_headers_; + request_headers.addCopy("potato", "salad"); + std::pair result = + codec_client_->startRequest(request_headers, false); + auto response = std::move(result.second); + + FakeHttpConnectionPtr fake_upstream_connection_main; + FakeStreamPtr upstream_request_main; + ASSERT_TRUE( + fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_main)); + ASSERT_TRUE(fake_upstream_connection_main->waitForNewStream(*dispatcher_, upstream_request_main)); + FakeHttpConnectionPtr fake_upstream_connection_shadow; + FakeStreamPtr upstream_request_shadow; + ASSERT_TRUE( + fake_upstreams_[1]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_shadow)); + ASSERT_TRUE( + fake_upstream_connection_shadow->waitForNewStream(*dispatcher_, upstream_request_shadow)); + ASSERT_TRUE(upstream_request_main->waitForHeadersComplete()); + ASSERT_TRUE(upstream_request_shadow->waitForHeadersComplete()); + + // Send upstream reset on main request. + upstream_request_main->encodeResetStream(); + ASSERT_TRUE(response->waitForReset()); + ASSERT_TRUE(upstream_request_shadow->waitForReset()); + + ASSERT_TRUE(upstream_request_shadow->waitForReset()); + ASSERT_TRUE(fake_upstream_connection_main->close()); + ASSERT_TRUE(fake_upstream_connection_shadow->close()); + ASSERT_TRUE(fake_upstream_connection_main->waitForDisconnect()); + ASSERT_TRUE(fake_upstream_connection_shadow->waitForDisconnect()); + + EXPECT_FALSE(upstream_request_main->complete()); + EXPECT_FALSE(upstream_request_shadow->complete()); + EXPECT_TRUE(response->complete()); + + cleanupUpstreamAndDownstream(); + + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_cx_total")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_cx_total")->value(), 1); + // Main cluster saw remote reset; shadow cluster saw local reset. + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_rq_rx_reset")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_rq_tx_reset")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_rq_completed")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_rq_completed")->value(), 0); +} + +TEST_P(ShadowPolicyIntegrationTest, RequestMirrorPolicyWithShadowUpstreamReset) { + if (!streaming_shadow_) { + GTEST_SKIP() << "Not applicable for non-streaming shadows."; + } + autonomous_upstream_ = false; + initialConfigSetup("cluster_1", ""); + initialize(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + Http::TestRequestHeaderMapImpl request_headers = default_request_headers_; + request_headers.addCopy("potato", "salad"); + std::pair result = + codec_client_->startRequest(request_headers, false); + auto& encoder = result.first; + auto response = std::move(result.second); + + FakeHttpConnectionPtr fake_upstream_connection_main; + FakeStreamPtr upstream_request_main; + ASSERT_TRUE( + fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_main)); + ASSERT_TRUE(fake_upstream_connection_main->waitForNewStream(*dispatcher_, upstream_request_main)); + FakeHttpConnectionPtr fake_upstream_connection_shadow; + FakeStreamPtr upstream_request_shadow; + ASSERT_TRUE( + fake_upstreams_[1]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_shadow)); + ASSERT_TRUE( + fake_upstream_connection_shadow->waitForNewStream(*dispatcher_, upstream_request_shadow)); + ASSERT_TRUE(upstream_request_main->waitForHeadersComplete()); + ASSERT_TRUE(upstream_request_shadow->waitForHeadersComplete()); + + // Send upstream reset on shadow request. + upstream_request_shadow->encodeResetStream(); + test_server_->waitForCounterEq("cluster.cluster_1.upstream_rq_rx_reset", 1, + std::chrono::milliseconds(1000)); + + codec_client_->sendData(encoder, 20, true); + ASSERT_TRUE(upstream_request_main->waitForData(*dispatcher_, 20)); + ASSERT_TRUE(upstream_request_main->waitForEndStream(*dispatcher_)); + upstream_request_main->encodeHeaders(default_response_headers_, true); + ASSERT_TRUE(response->waitForEndStream()); + ASSERT_TRUE(fake_upstream_connection_main->close()); + ASSERT_TRUE(fake_upstream_connection_shadow->close()); + ASSERT_TRUE(fake_upstream_connection_main->waitForDisconnect()); + ASSERT_TRUE(fake_upstream_connection_shadow->waitForDisconnect()); + + EXPECT_TRUE(upstream_request_main->complete()); + EXPECT_FALSE(upstream_request_shadow->complete()); + EXPECT_TRUE(response->complete()); + + cleanupUpstreamAndDownstream(); + + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_cx_total")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_cx_total")->value(), 1); + // Main cluster saw no reset; shadow cluster saw remote reset. + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_rq_rx_reset")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_rq_completed")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_rq_completed")->value(), 1); +} + +// Test a downstream timeout before end stream has been sent. +TEST_P(ShadowPolicyIntegrationTest, RequestMirrorPolicyWithEarlyDownstreamTimeout) { + if (!streaming_shadow_) { + GTEST_SKIP() << "Not applicable for non-streaming shadows."; + } + autonomous_upstream_ = false; + config_helper_.addConfigModifier( + [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) { + // 100 millisecond timeout. + hcm.mutable_stream_idle_timeout()->set_seconds(0); + hcm.mutable_stream_idle_timeout()->set_nanos(100 * 1000 * 1000); + }); + initialConfigSetup("cluster_1", ""); + config_helper_.disableDelayClose(); + initialize(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + Http::TestRequestHeaderMapImpl request_headers = default_request_headers_; + request_headers.addCopy("potato", "salad"); + std::pair result = + codec_client_->startRequest(request_headers, false); + auto response = std::move(result.second); + + FakeHttpConnectionPtr fake_upstream_connection_main; + FakeStreamPtr upstream_request_main; + ASSERT_TRUE( + fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_main)); + ASSERT_TRUE(fake_upstream_connection_main->waitForNewStream(*dispatcher_, upstream_request_main)); + FakeHttpConnectionPtr fake_upstream_connection_shadow; + FakeStreamPtr upstream_request_shadow; + ASSERT_TRUE( + fake_upstreams_[1]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_shadow)); + ASSERT_TRUE( + fake_upstream_connection_shadow->waitForNewStream(*dispatcher_, upstream_request_shadow)); + ASSERT_TRUE(upstream_request_main->waitForHeadersComplete()); + ASSERT_TRUE(upstream_request_shadow->waitForHeadersComplete()); + + // Eventually the request will time out. + ASSERT_TRUE(response->waitForReset()); + ASSERT_TRUE(upstream_request_main->waitForReset()); + ASSERT_TRUE(upstream_request_shadow->waitForReset()); + + // Clean up. + ASSERT_TRUE(fake_upstream_connection_main->close()); + ASSERT_TRUE(fake_upstream_connection_shadow->close()); + ASSERT_TRUE(fake_upstream_connection_main->waitForDisconnect()); + ASSERT_TRUE(fake_upstream_connection_shadow->waitForDisconnect()); + + EXPECT_FALSE(upstream_request_main->complete()); + EXPECT_FALSE(upstream_request_shadow->complete()); + + cleanupUpstreamAndDownstream(); + + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_cx_total")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_cx_total")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_rq_tx_reset")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_rq_tx_reset")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_rq_completed")->value(), 0); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_rq_completed")->value(), 0); +} + +// Test a downstream timeout after end stream has been sent by the client where the shadow request +// completes. +TEST_P(ShadowPolicyIntegrationTest, RequestMirrorPolicyWithLateDownstreamTimeoutAndShadowComplete) { + if (!streaming_shadow_) { + GTEST_SKIP() << "Not applicable for non-streaming shadows."; + } + autonomous_upstream_ = false; + initialConfigSetup("cluster_1", ""); + config_helper_.addConfigModifier( + [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) { + // 100 millisecond timeout. + hcm.mutable_stream_idle_timeout()->set_seconds(0); + hcm.mutable_stream_idle_timeout()->set_nanos(100 * 1000 * 1000); + }); + config_helper_.disableDelayClose(); + initialize(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + Http::TestRequestHeaderMapImpl request_headers = default_request_headers_; + request_headers.addCopy("potato", "salad"); + // Send whole request. + std::pair result = + codec_client_->startRequest(request_headers, true); + auto response = std::move(result.second); + + FakeHttpConnectionPtr fake_upstream_connection_main; + FakeStreamPtr upstream_request_main; + ASSERT_TRUE( + fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_main)); + ASSERT_TRUE(fake_upstream_connection_main->waitForNewStream(*dispatcher_, upstream_request_main)); + FakeHttpConnectionPtr fake_upstream_connection_shadow; + FakeStreamPtr upstream_request_shadow; + ASSERT_TRUE( + fake_upstreams_[1]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_shadow)); + ASSERT_TRUE( + fake_upstream_connection_shadow->waitForNewStream(*dispatcher_, upstream_request_shadow)); + ASSERT_TRUE(upstream_request_main->waitForHeadersComplete()); + ASSERT_TRUE(upstream_request_shadow->waitForHeadersComplete()); + upstream_request_shadow->encodeHeaders(default_response_headers_, true); + + ASSERT_TRUE(fake_upstream_connection_shadow->close()); + ASSERT_TRUE(fake_upstream_connection_shadow->waitForDisconnect()); + EXPECT_TRUE(upstream_request_shadow->complete()); + + // Eventually the main request will time out. + ASSERT_TRUE(response->waitForEndStream()); + ASSERT_TRUE(upstream_request_main->waitForReset()); + EXPECT_TRUE(response->complete()); + EXPECT_EQ(response->headers().getStatusValue(), "504"); + + // Clean up. + ASSERT_TRUE(fake_upstream_connection_main->close()); + ASSERT_TRUE(fake_upstream_connection_main->waitForDisconnect()); + + cleanupUpstreamAndDownstream(); + + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_cx_total")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_cx_total")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_rq_tx_reset")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_rq_tx_reset")->value(), 0); + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_rq_completed")->value(), 0); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_rq_completed")->value(), 1); +} + +// Test a shadow timeout after the main request completes. +TEST_P(ShadowPolicyIntegrationTest, RequestMirrorPolicyWithShadowOnlyTimeout) { + if (!streaming_shadow_) { + GTEST_SKIP() << "Not applicable for non-streaming shadows."; + } + autonomous_upstream_ = false; + initialConfigSetup("cluster_1", ""); + config_helper_.addConfigModifier( + [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) { + // 100 millisecond timeout. The shadow inherits the timeout value from the route. + auto* route_config = hcm.mutable_route_config(); + auto* virtual_host = route_config->mutable_virtual_hosts(0); + auto* route = virtual_host->mutable_routes(0)->mutable_route(); + route->mutable_timeout()->set_seconds(0); + route->mutable_timeout()->set_nanos(100 * 1000 * 1000); + }); + config_helper_.disableDelayClose(); + initialize(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + Http::TestRequestHeaderMapImpl request_headers = default_request_headers_; + request_headers.addCopy("potato", "salad"); + // Send whole request. + std::pair result = + codec_client_->startRequest(request_headers, true); + auto response = std::move(result.second); + + FakeHttpConnectionPtr fake_upstream_connection_main; + FakeStreamPtr upstream_request_main; + ASSERT_TRUE( + fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_main)); + ASSERT_TRUE(fake_upstream_connection_main->waitForNewStream(*dispatcher_, upstream_request_main)); + FakeHttpConnectionPtr fake_upstream_connection_shadow; + FakeStreamPtr upstream_request_shadow; + ASSERT_TRUE( + fake_upstreams_[1]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_shadow)); + ASSERT_TRUE( + fake_upstream_connection_shadow->waitForNewStream(*dispatcher_, upstream_request_shadow)); + ASSERT_TRUE(upstream_request_main->waitForHeadersComplete()); + ASSERT_TRUE(upstream_request_shadow->waitForHeadersComplete()); + + upstream_request_main->encodeHeaders(default_response_headers_, true); + ASSERT_TRUE(response->waitForEndStream()); + EXPECT_TRUE(response->complete()); + + // Eventually the shadow request will time out. + ASSERT_TRUE(upstream_request_shadow->waitForReset()); + + // Clean up. + ASSERT_TRUE(fake_upstream_connection_main->close()); + ASSERT_TRUE(fake_upstream_connection_main->waitForDisconnect()); + + cleanupUpstreamAndDownstream(); + + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_cx_total")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_cx_total")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_rq_tx_reset")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_rq_timeout")->value(), 1); +} + +TEST_P(ShadowPolicyIntegrationTest, MainRequestOverBufferLimit) { + if (!streaming_shadow_) { + GTEST_SKIP() << "Not applicable for non-streaming shadows."; + } + autonomous_upstream_ = true; + cluster_with_custom_filter_ = 0; + filter_name_ = "encoder-decoder-buffer-filter"; + initialConfigSetup("cluster_1", ""); + config_helper_.setBufferLimits(1024, 1024); + config_helper_.disableDelayClose(); + initialize(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + Http::TestRequestHeaderMapImpl request_headers = default_request_headers_; + request_headers.addCopy("potato", "salad"); + // Send whole (large) request. + auto response = codec_client_->makeRequestWithBody( + Http::TestRequestHeaderMapImpl{{":method", "POST"}, + {":path", "/dynamo/url"}, + {":scheme", "http"}, + {":authority", "sni.lyft.com"}, + {"x-forwarded-for", "10.0.0.1"}, + {"x-envoy-retry-on", "5xx"}}, + 1024 * 65); + + ASSERT_TRUE(response->waitForEndStream()); + EXPECT_TRUE(response->complete()); + + cleanupUpstreamAndDownstream(); + + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_cx_total")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_cx_total")->value(), 1); + test_server_->waitForCounterEq("cluster.cluster_1.upstream_rq_completed", 1); +} + +TEST_P(ShadowPolicyIntegrationTest, ShadowRequestOverBufferLimit) { + if (!streaming_shadow_) { + GTEST_SKIP() << "Not applicable for non-streaming shadows."; + } + autonomous_upstream_ = true; + cluster_with_custom_filter_ = 1; + filter_name_ = "encoder-decoder-buffer-filter"; + initialConfigSetup("cluster_1", ""); + config_helper_.setBufferLimits(1024, 1024); + config_helper_.disableDelayClose(); + initialize(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + Http::TestRequestHeaderMapImpl request_headers = default_request_headers_; + request_headers.addCopy("potato", "salad"); + // Send whole (large) request. + auto response = codec_client_->makeRequestWithBody( + Http::TestRequestHeaderMapImpl{{":method", "POST"}, + {":path", "/dynamo/url"}, + {":scheme", "http"}, + {":authority", "sni.lyft.com"}, + {"x-forwarded-for", "10.0.0.1"}, + {"x-envoy-retry-on", "5xx"}}, + 1024 * 65); + + ASSERT_TRUE(response->waitForEndStream()); + EXPECT_TRUE(response->complete()); + EXPECT_EQ(response->headers().getStatusValue(), "200"); + + cleanupUpstreamAndDownstream(); + + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_cx_total")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_cx_total")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_rq_completed")->value(), 1); + // The request to the shadow upstream never completed due to buffer overflow. + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_rq_completed")->value(), 0); +} + +TEST_P(ShadowPolicyIntegrationTest, ShadowRequestOverRouteBufferLimit) { + if (!streaming_shadow_) { + GTEST_SKIP() << "Not applicable for non-streaming shadows."; + } + autonomous_upstream_ = true; + cluster_with_custom_filter_ = 1; + filter_name_ = "encoder-decoder-buffer-filter"; + initialConfigSetup("cluster_1", ""); + config_helper_.addConfigModifier([](ConfigHelper::HttpConnectionManager& hcm) { + hcm.mutable_route_config() + ->mutable_virtual_hosts(0) + ->mutable_per_request_buffer_limit_bytes() + ->set_value(0); + }); + config_helper_.disableDelayClose(); + initialize(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + Http::TestRequestHeaderMapImpl request_headers = default_request_headers_; + request_headers.addCopy("potato", "salad"); + // Send whole (large) request. + auto response = codec_client_->makeRequestWithBody( + Http::TestRequestHeaderMapImpl{{":method", "POST"}, + {":path", "/dynamo/url"}, + {":scheme", "http"}, + {":authority", "sni.lyft.com"}, + {"x-forwarded-for", "10.0.0.1"}, + {"x-envoy-retry-on", "5xx"}}, + 1024 * 65); + + ASSERT_TRUE(response->waitForEndStream()); + EXPECT_TRUE(response->complete()); + EXPECT_EQ(response->headers().getStatusValue(), "200"); + + cleanupUpstreamAndDownstream(); + + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_cx_total")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_cx_total")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_rq_completed")->value(), 1); + // The request to the shadow upstream never completed due to buffer overflow. + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_rq_completed")->value(), 0); +} + +TEST_P(ShadowPolicyIntegrationTest, BackedUpConnectionBeforeShadowBegins) { + if (!streaming_shadow_) { + GTEST_SKIP() << "Not applicable for non-streaming shadows."; + } + autonomous_upstream_ = true; + autonomous_allow_incomplete_streams_ = true; + initialConfigSetup("cluster_1", ""); + // Shrink the shadow buffer size. + config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bs) { + auto* shadow_cluster = bs.mutable_static_resources()->mutable_clusters(1); + shadow_cluster->mutable_per_connection_buffer_limit_bytes()->set_value(1024); + }); + + // Add a route directly for the shadow. + config_helper_.addConfigModifier( + [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) { + auto* route_1 = hcm.mutable_route_config()->mutable_virtual_hosts(0)->add_routes(); + route_1->mutable_route()->set_cluster("cluster_1"); + route_1->mutable_match()->set_prefix("/shadow"); + hcm.mutable_route_config() + ->mutable_virtual_hosts(0) + ->mutable_routes(0) + ->mutable_match() + ->set_prefix("/main"); + }); + config_helper_.addRuntimeOverride(Runtime::defer_processing_backedup_streams, "true"); + initialize(); + + write_matcher_->setDestinationPort(fake_upstreams_[1]->localAddress()->ip()->port()); + write_matcher_->setWriteReturnsEgain(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + // Send an initial request to `cluster_1` directly, which will fill the connection buffer. + auto shadow_direct_response = codec_client_->makeRequestWithBody( + Http::TestRequestHeaderMapImpl{{":method", "POST"}, + {":path", "/shadow"}, + {":scheme", "http"}, + {":authority", "sni.lyft.com"}, + {"x-forwarded-for", "10.0.0.1"}, + {"x-envoy-retry-on", "5xx"}}, + 1024 * 3); + + // Initiate a request to the main route. + std::pair result = + codec_client_->startRequest(Http::TestRequestHeaderMapImpl{{":method", "POST"}, + {":path", "/main"}, + {":scheme", "http"}, + {":authority", "sni.lyft.com"}, + {"x-forwarded-for", "10.0.0.1"}, + {"x-envoy-retry-on", "5xx"}}, + false); + auto& encoder = result.first; + auto main_response = std::move(result.second); + + // Connecting to the shadow stream should cause backpressure due to connection backup. + test_server_->waitForCounterEq("http.config_test.downstream_flow_control_paused_reading_total", 1, + std::chrono::milliseconds(500)); + + codec_client_->sendData(encoder, 1023, false); + + codec_client_->sendData(encoder, 10, true); + // Main request must not have been completed due to backpressure. + EXPECT_FALSE(main_response->waitForEndStream(std::chrono::milliseconds(500))); + + // Unblock the port for `cluster_1` + write_matcher_->setResumeWrites(); + + EXPECT_TRUE(main_response->waitForEndStream()); + EXPECT_TRUE(main_response->complete()); + EXPECT_EQ(main_response->headers().getStatusValue(), "200"); + EXPECT_TRUE(shadow_direct_response->waitForEndStream()); + EXPECT_TRUE(shadow_direct_response->complete()); + EXPECT_EQ(shadow_direct_response->headers().getStatusValue(), "200"); + + // Two requests were sent over a single connection to cluster_1. + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_cx_total")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_rq_completed")->value(), 2); + EXPECT_EQ(test_server_->counter("http.config_test.downstream_flow_control_paused_reading_total") + ->value(), + 1); +} + +TEST_P(ShadowPolicyIntegrationTest, RequestMirrorPolicyWithShadowBackpressure) { + if (!streaming_shadow_) { + GTEST_SKIP() << "Not applicable for non-streaming shadows."; + } + autonomous_upstream_ = false; + initialConfigSetup("cluster_1", ""); + // Shrink the shadow buffer size. + config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bs) { + auto* shadow_cluster = bs.mutable_static_resources()->mutable_clusters(1); + shadow_cluster->mutable_per_connection_buffer_limit_bytes()->set_value(1024); + }); + initialize(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + Http::TestRequestHeaderMapImpl request_headers = default_request_headers_; + request_headers.addCopy("potato", "salad"); + std::pair result = + codec_client_->startRequest(request_headers, false); + auto& encoder = result.first; + auto response = std::move(result.second); + + FakeHttpConnectionPtr fake_upstream_connection_main; + FakeStreamPtr upstream_request_main; + ASSERT_TRUE( + fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_main)); + ASSERT_TRUE(fake_upstream_connection_main->waitForNewStream(*dispatcher_, upstream_request_main)); + FakeHttpConnectionPtr fake_upstream_connection_shadow; + FakeStreamPtr upstream_request_shadow; + ASSERT_TRUE( + fake_upstreams_[1]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_shadow)); + ASSERT_TRUE( + fake_upstream_connection_shadow->waitForNewStream(*dispatcher_, upstream_request_shadow)); + ASSERT_TRUE(upstream_request_main->waitForHeadersComplete()); + ASSERT_TRUE(upstream_request_shadow->waitForHeadersComplete()); + + // This will result in one call of high watermark on the shadow stream, as + // end_stream will not trigger watermark calls. + codec_client_->sendData(encoder, 2048, false); + test_server_->waitForCounterGe("http.config_test.downstream_flow_control_paused_reading_total", + 1); + codec_client_->sendData(encoder, 2048, true); + ASSERT_TRUE(upstream_request_main->waitForData(*dispatcher_, 2048 * 2)); + ASSERT_TRUE(upstream_request_shadow->waitForData(*dispatcher_, 2048 * 2)); + + ASSERT_TRUE(upstream_request_main->waitForEndStream(*dispatcher_)); + ASSERT_TRUE(upstream_request_shadow->waitForEndStream(*dispatcher_)); + upstream_request_main->encodeHeaders(default_response_headers_, true); + upstream_request_shadow->encodeHeaders(default_response_headers_, true); + ASSERT_TRUE(response->waitForEndStream()); + ASSERT_TRUE(fake_upstream_connection_main->close()); + ASSERT_TRUE(fake_upstream_connection_shadow->close()); + ASSERT_TRUE(fake_upstream_connection_main->waitForDisconnect()); + ASSERT_TRUE(fake_upstream_connection_shadow->waitForDisconnect()); + EXPECT_TRUE(upstream_request_main->complete()); + EXPECT_TRUE(upstream_request_shadow->complete()); + EXPECT_TRUE(response->complete()); + + cleanupUpstreamAndDownstream(); + + EXPECT_EQ(test_server_->counter("http.config_test.downstream_flow_control_paused_reading_total") + ->value(), + 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_cx_total")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_cx_total")->value(), 1); + // Main cluster saw no reset; shadow cluster saw remote reset. + EXPECT_EQ(test_server_->counter("cluster.cluster_0.upstream_rq_completed")->value(), 1); + EXPECT_EQ(test_server_->counter("cluster.cluster_1.upstream_rq_completed")->value(), 1); +} // Test request mirroring / shadowing with the cluster name in policy. TEST_P(ShadowPolicyIntegrationTest, RequestMirrorPolicyWithCluster) { diff --git a/test/mocks/api/mocks.h b/test/mocks/api/mocks.h index 95f8bf8a8a17..9782e14524bb 100644 --- a/test/mocks/api/mocks.h +++ b/test/mocks/api/mocks.h @@ -126,7 +126,6 @@ class MockOsSysCalls : public OsSysCallsImpl { MOCK_METHOD(bool, supportsIpTransparent, (), (const)); MOCK_METHOD(bool, supportsMptcp, (), (const)); MOCK_METHOD(bool, supportsGetifaddrs, (), (const)); - MOCK_METHOD(void, setAlternateGetifaddrs, (AlternateGetifaddrs alternate_getifaddrs)); MOCK_METHOD(SysCallIntResult, getifaddrs, (InterfaceAddressVector & interfaces)); MOCK_METHOD(SysCallIntResult, getaddrinfo, (const char* node, const char* service, const addrinfo* hints, addrinfo** res)); diff --git a/test/mocks/http/mocks.cc b/test/mocks/http/mocks.cc index 318af3f218f4..819dfd55484c 100644 --- a/test/mocks/http/mocks.cc +++ b/test/mocks/http/mocks.cc @@ -213,7 +213,11 @@ MockAsyncClientRequest::MockAsyncClientRequest(MockAsyncClient* client) : client MockAsyncClientRequest::~MockAsyncClientRequest() { client_->onRequestDestroy(); } MockAsyncClientStream::MockAsyncClientStream() = default; -MockAsyncClientStream::~MockAsyncClientStream() = default; +MockAsyncClientStream::~MockAsyncClientStream() { + if (destructor_callback_) { + (*destructor_callback_)(); + } +}; MockFilterChainFactoryCallbacks::MockFilterChainFactoryCallbacks() = default; MockFilterChainFactoryCallbacks::~MockFilterChainFactoryCallbacks() = default; diff --git a/test/mocks/http/mocks.h b/test/mocks/http/mocks.h index 7e8649de039c..39468287bae0 100644 --- a/test/mocks/http/mocks.h +++ b/test/mocks/http/mocks.h @@ -479,10 +479,14 @@ class MockAsyncClient : public AsyncClient { const RequestOptions& args) override { return send_(request, callbacks, args); } - + OngoingRequest* startRequest(RequestHeaderMapPtr&& headers, Callbacks& callbacks, + const RequestOptions& args) override { + return startRequest_(headers, callbacks, args); + } MOCK_METHOD(Request*, send_, (RequestMessagePtr & request, Callbacks& callbacks, const RequestOptions& args)); - + MOCK_METHOD(OngoingRequest*, startRequest_, + (RequestHeaderMapPtr & request, Callbacks& callbacks, const RequestOptions& args)); MOCK_METHOD(Stream*, start, (StreamCallbacks & callbacks, const StreamOptions& args)); MOCK_METHOD(Event::Dispatcher&, dispatcher, ()); @@ -525,7 +529,7 @@ class MockAsyncClientStreamCallbacks : public AsyncClient::StreamCallbacks { MOCK_METHOD(void, onReset, ()); }; -class MockAsyncClientRequest : public AsyncClient::Request { +class MockAsyncClientRequest : public virtual AsyncClient::Request { public: MockAsyncClientRequest(MockAsyncClient* client); ~MockAsyncClientRequest() override; @@ -535,7 +539,7 @@ class MockAsyncClientRequest : public AsyncClient::Request { MockAsyncClient* client_; }; -class MockAsyncClientStream : public AsyncClient::Stream { +class MockAsyncClientStream : public virtual AsyncClient::Stream { public: MockAsyncClientStream(); ~MockAsyncClientStream() override; @@ -545,6 +549,27 @@ class MockAsyncClientStream : public AsyncClient::Stream { MOCK_METHOD(void, sendTrailers, (RequestTrailerMap & trailers)); MOCK_METHOD(void, reset, ()); MOCK_METHOD(bool, isAboveWriteBufferHighWatermark, (), (const)); + void setDestructorCallback(AsyncClient::StreamDestructorCallbacks callback) override { + destructor_callback_ = callback; + } + void removeDestructorCallback() override { destructor_callback_.reset(); } + MOCK_METHOD(void, setWatermarkCallbacks, (DecoderFilterWatermarkCallbacks & callback), + (override)); + MOCK_METHOD(void, removeWatermarkCallbacks, (), (override)); + +private: + absl::optional destructor_callback_; +}; + +class MockAsyncClientOngoingRequest : public virtual AsyncClient::OngoingRequest, + public MockAsyncClientStream, + public MockAsyncClientRequest { +public: + MockAsyncClientOngoingRequest(MockAsyncClient* client) : MockAsyncClientRequest(client) {} + void captureAndSendTrailers(RequestTrailerMapPtr&& trailers) override { + return captureAndSendTrailers_(*trailers); + } + MOCK_METHOD(void, captureAndSendTrailers_, (RequestTrailerMap & trailers), ()); }; class MockDownstreamWatermarkCallbacks : public DownstreamWatermarkCallbacks { diff --git a/test/mocks/http/stream_decoder.cc b/test/mocks/http/stream_decoder.cc index 76145b6dc43f..ae778dff5c88 100644 --- a/test/mocks/http/stream_decoder.cc +++ b/test/mocks/http/stream_decoder.cc @@ -7,11 +7,12 @@ namespace Envoy { namespace Http { MockRequestDecoder::MockRequestDecoder() { - ON_CALL(*this, decodeHeaders_(_, _)).WillByDefault(Invoke([](RequestHeaderMapPtr& headers, bool) { - // Check to see that method is not-null. Path can be null for CONNECT and authority can be null - // at the codec level. - ASSERT_NE(nullptr, headers->Method()); - })); + ON_CALL(*this, decodeHeaders_(_, _)) + .WillByDefault(Invoke([](RequestHeaderMapSharedPtr& headers, bool) { + // Check to see that method is not-null. Path can be null for CONNECT and authority can be + // null at the codec level. + ASSERT_NE(nullptr, headers->Method()); + })); } MockRequestDecoder::~MockRequestDecoder() = default; diff --git a/test/mocks/http/stream_decoder.h b/test/mocks/http/stream_decoder.h index 8bf5e5305d4f..30e2ccf8a5a9 100644 --- a/test/mocks/http/stream_decoder.h +++ b/test/mocks/http/stream_decoder.h @@ -23,14 +23,15 @@ class MockRequestDecoder : public RequestDecoder { absl::string_view details)); MOCK_METHOD(StreamInfo::StreamInfo&, streamInfo, ()); - void decodeHeaders(RequestHeaderMapPtr&& headers, bool end_stream) override { + void decodeHeaders(RequestHeaderMapSharedPtr&& headers, bool end_stream) override { decodeHeaders_(headers, end_stream); } void decodeTrailers(RequestTrailerMapPtr&& trailers) override { decodeTrailers_(trailers); } // Http::RequestDecoder - MOCK_METHOD(void, decodeHeaders_, (RequestHeaderMapPtr & headers, bool end_stream)); + MOCK_METHOD(void, decodeHeaders_, (RequestHeaderMapSharedPtr & headers, bool end_stream)); MOCK_METHOD(void, decodeTrailers_, (RequestTrailerMapPtr & trailers)); + MOCK_METHOD(std::list, accessLogHandlers, ()); }; class MockResponseDecoder : public ResponseDecoder { diff --git a/test/mocks/http/stream_encoder.h b/test/mocks/http/stream_encoder.h index efe3626af3ab..e8ce13c2b698 100644 --- a/test/mocks/http/stream_encoder.h +++ b/test/mocks/http/stream_encoder.h @@ -49,6 +49,11 @@ class MockResponseEncoder : public ResponseEncoder { MOCK_METHOD(void, encodeHeaders, (const ResponseHeaderMap& headers, bool end_stream)); MOCK_METHOD(void, encodeTrailers, (const ResponseTrailerMap& trailers)); MOCK_METHOD(void, setRequestDecoder, (RequestDecoder & decoder)); + MOCK_METHOD(void, setDeferredLoggingHeadersAndTrailers, + (Http::RequestHeaderMapConstSharedPtr request_header_map, + Http::ResponseHeaderMapConstSharedPtr response_header_map, + Http::ResponseTrailerMapConstSharedPtr response_trailer_map, + StreamInfo::StreamInfo& stream_info)); // Http::StreamEncoder MOCK_METHOD(void, encodeData, (Buffer::Instance & data, bool end_stream)); diff --git a/test/mocks/network/connection.cc b/test/mocks/network/connection.cc index 1a754e4d47c1..4a7d27f2b108 100644 --- a/test/mocks/network/connection.cc +++ b/test/mocks/network/connection.cc @@ -82,6 +82,11 @@ template static void initializeMockConnection(T& connection) { ON_CALL(connection, close(_)).WillByDefault(Invoke([&connection](ConnectionCloseType) -> void { connection.raiseEvent(Network::ConnectionEvent::LocalClose); })); + ON_CALL(connection, close(_, _)) + .WillByDefault(Invoke([&connection](ConnectionCloseType, absl::string_view details) -> void { + connection.local_close_reason_ = std::string(details); + connection.raiseEvent(Network::ConnectionEvent::LocalClose); + })); ON_CALL(connection, id()).WillByDefault(Return(connection.next_id_)); connection.stream_info_.downstream_connection_info_provider_->setConnectionID(connection.id_); ON_CALL(connection, state()).WillByDefault(ReturnPointee(&connection.state_)); @@ -94,6 +99,9 @@ template static void initializeMockConnection(T& connection) { connection.stream_info_.setUpstreamBytesMeter(std::make_shared()); ON_CALL(connection, streamInfo()).WillByDefault(ReturnRef(connection.stream_info_)); ON_CALL(Const(connection), streamInfo()).WillByDefault(ReturnRef(connection.stream_info_)); + ON_CALL(connection, localCloseReason()).WillByDefault(Invoke([&connection]() { + return absl::string_view(connection.local_close_reason_); + })); } MockConnection::MockConnection() { diff --git a/test/mocks/network/connection.h b/test/mocks/network/connection.h index d86f6f67753e..e739c130fce8 100644 --- a/test/mocks/network/connection.h +++ b/test/mocks/network/connection.h @@ -41,6 +41,7 @@ class MockConnectionBase { uint64_t id_{next_id_++}; bool read_enabled_{true}; testing::NiceMock stream_info_; + std::string local_close_reason_{"unset_local_close_reason"}; Connection::State state_{Connection::State::Open}; }; @@ -56,6 +57,7 @@ class MockConnectionBase { MOCK_METHOD(void, enableHalfClose, (bool enabled)); \ MOCK_METHOD(bool, isHalfCloseEnabled, ()); \ MOCK_METHOD(void, close, (ConnectionCloseType type)); \ + MOCK_METHOD(void, close, (ConnectionCloseType type, absl::string_view details)); \ MOCK_METHOD(Event::Dispatcher&, dispatcher, ()); \ MOCK_METHOD(uint64_t, id, (), (const)); \ MOCK_METHOD(void, hashKey, (std::vector&), (const)); \ @@ -85,6 +87,7 @@ class MockConnectionBase { MOCK_METHOD(const StreamInfo::StreamInfo&, streamInfo, (), (const)); \ MOCK_METHOD(void, setDelayedCloseTimeout, (std::chrono::milliseconds)); \ MOCK_METHOD(absl::string_view, transportFailureReason, (), (const)); \ + MOCK_METHOD(absl::string_view, localCloseReason, (), (const)); \ MOCK_METHOD(bool, startSecureTransport, ()); \ MOCK_METHOD(absl::optional, lastRoundTripTime, (), (const)); \ MOCK_METHOD(void, configureInitialCongestionWindow, \ diff --git a/test/mocks/router/mocks.h b/test/mocks/router/mocks.h index 77e0b728e82a..a50d79f5f5b7 100644 --- a/test/mocks/router/mocks.h +++ b/test/mocks/router/mocks.h @@ -267,6 +267,15 @@ class MockShadowWriter : public ShadowWriter { MOCK_METHOD(void, shadow_, (const std::string& cluster, Http::RequestMessagePtr& request, const Http::AsyncClient::RequestOptions& options)); + + Http::AsyncClient::OngoingRequest* + streamingShadow(const std::string& cluster, Http::RequestHeaderMapPtr&& request, + const Http::AsyncClient::RequestOptions& options) override { + return streamingShadow_(cluster, request, options); + } + MOCK_METHOD(Http::AsyncClient::OngoingRequest*, streamingShadow_, + (const std::string& cluster, Http::RequestHeaderMapPtr& request, + const Http::AsyncClient::RequestOptions& options)); }; class TestVirtualCluster : public VirtualCluster { diff --git a/test/mocks/upstream/cluster_info.cc b/test/mocks/upstream/cluster_info.cc index 0449af5b6514..8000f7bf9803 100644 --- a/test/mocks/upstream/cluster_info.cc +++ b/test/mocks/upstream/cluster_info.cc @@ -153,7 +153,13 @@ MockClusterInfo::MockClusterInfo() const envoy::config::cluster::v3::Cluster::OriginalDstLbConfig>( lb_original_dst_config_.get()); })); - ON_CALL(*this, upstreamConfig()).WillByDefault(ReturnRef(upstream_config_)); + ON_CALL(*this, upstreamConfig()) + .WillByDefault( + Invoke([this]() -> OptRef { + return makeOptRefFromPtr( + upstream_config_.get()); + })); + ON_CALL(*this, lbConfig()).WillByDefault(ReturnRef(lb_config_)); ON_CALL(*this, metadata()).WillByDefault(ReturnRef(metadata_)); ON_CALL(*this, upstreamHttpProtocolOptions()) @@ -169,7 +175,12 @@ MockClusterInfo::MockClusterInfo() } return *typed_metadata_; })); - ON_CALL(*this, clusterType()).WillByDefault(ReturnRef(cluster_type_)); + ON_CALL(*this, clusterType()) + .WillByDefault( + Invoke([this]() -> OptRef { + return makeOptRefFromPtr( + cluster_type_.get()); + })); ON_CALL(*this, upstreamHttpProtocol(_)) .WillByDefault(Return(std::vector{Http::Protocol::Http11})); ON_CALL(*this, createFilterChain(_, _)) diff --git a/test/mocks/upstream/cluster_info.h b/test/mocks/upstream/cluster_info.h index 0f1713af1203..033ecbd9fe01 100644 --- a/test/mocks/upstream/cluster_info.h +++ b/test/mocks/upstream/cluster_info.h @@ -131,8 +131,8 @@ class MockClusterInfo : public ClusterInfo { MOCK_METHOD(const envoy::config::cluster::v3::Cluster::CommonLbConfig&, lbConfig, (), (const)); MOCK_METHOD(LoadBalancerType, lbType, (), (const)); MOCK_METHOD(envoy::config::cluster::v3::Cluster::DiscoveryType, type, (), (const)); - MOCK_METHOD(const absl::optional&, - clusterType, (), (const)); + MOCK_METHOD(OptRef, clusterType, (), + (const)); MOCK_METHOD(OptRef, lbRingHashConfig, (), (const)); MOCK_METHOD(OptRef, lbMaglevConfig, (), @@ -143,8 +143,8 @@ class MockClusterInfo : public ClusterInfo { lbLeastRequestConfig, (), (const)); MOCK_METHOD(OptRef, lbOriginalDstConfig, (), (const)); - MOCK_METHOD(const absl::optional&, upstreamConfig, - (), (const)); + MOCK_METHOD(OptRef, upstreamConfig, (), + (const)); MOCK_METHOD(bool, maintenanceMode, (), (const)); MOCK_METHOD(uint32_t, maxResponseHeadersCount, (), (const)); MOCK_METHOD(uint64_t, maxRequestsPerConnection, (), (const)); @@ -229,7 +229,7 @@ class MockClusterInfo : public ClusterInfo { LoadBalancerType lb_type_{LoadBalancerType::RoundRobin}; envoy::config::cluster::v3::Cluster::DiscoveryType type_{ envoy::config::cluster::v3::Cluster::STRICT_DNS}; - absl::optional cluster_type_; + std::unique_ptr cluster_type_; NiceMock lb_subset_; absl::optional upstream_http_protocol_options_; @@ -241,7 +241,7 @@ class MockClusterInfo : public ClusterInfo { std::unique_ptr lb_maglev_config_; std::unique_ptr lb_original_dst_config_; - absl::optional upstream_config_; + std::unique_ptr upstream_config_; Network::ConnectionSocket::OptionsSharedPtr cluster_socket_options_; envoy::config::cluster::v3::Cluster::CommonLbConfig lb_config_; envoy::config::core::v3::Metadata metadata_; diff --git a/test/mocks/upstream/cluster_manager.cc b/test/mocks/upstream/cluster_manager.cc index 1943e5e71d4b..44d845b6c3ff 100644 --- a/test/mocks/upstream/cluster_manager.cc +++ b/test/mocks/upstream/cluster_manager.cc @@ -26,8 +26,6 @@ MockClusterManager::MockClusterManager() ON_CALL(*this, grpcAsyncClientManager()).WillByDefault(ReturnRef(async_client_manager_)); ON_CALL(*this, localClusterName()).WillByDefault((ReturnRef(local_cluster_name_))); ON_CALL(*this, subscriptionFactory()).WillByDefault(ReturnRef(subscription_factory_)); - ON_CALL(*this, multiplexedSubscriptionFactory()) - .WillByDefault(ReturnRef(multiplexed_subscription_factory_)); ON_CALL(*this, allocateOdCdsApi(_, _, _)) .WillByDefault(Invoke([](const envoy::config::core::v3::ConfigSource&, OptRef, diff --git a/test/mocks/upstream/cluster_manager.h b/test/mocks/upstream/cluster_manager.h index e1daacc5f0b6..50d26fed8317 100644 --- a/test/mocks/upstream/cluster_manager.h +++ b/test/mocks/upstream/cluster_manager.h @@ -56,7 +56,6 @@ class MockClusterManager : public ClusterManager { MOCK_METHOD(ClusterUpdateCallbacksHandle*, addThreadLocalClusterUpdateCallbacks_, (ClusterUpdateCallbacks & callbacks)); MOCK_METHOD(Config::SubscriptionFactory&, subscriptionFactory, ()); - MOCK_METHOD(Config::SubscriptionFactory&, multiplexedSubscriptionFactory, ()); const ClusterTrafficStatNames& clusterStatNames() const override { return cluster_stat_names_; } const ClusterConfigUpdateStatNames& clusterConfigUpdateStatNames() const override { return cluster_config_update_stat_names_; @@ -95,7 +94,6 @@ class MockClusterManager : public ClusterManager { absl::optional local_cluster_name_; NiceMock cluster_manager_factory_; NiceMock subscription_factory_; - NiceMock multiplexed_subscription_factory_; absl::flat_hash_map> active_clusters_; absl::flat_hash_map> warming_clusters_; Stats::TestUtil::TestSymbolTable symbol_table_; diff --git a/test/per_file_coverage.sh b/test/per_file_coverage.sh index 00e95ce9fee2..3774360a81e1 100755 --- a/test/per_file_coverage.sh +++ b/test/per_file_coverage.sh @@ -4,7 +4,7 @@ # for existing directories with low coverage. declare -a KNOWN_LOW_COVERAGE=( "source/common:96.0" -"source/common/api:82.5" +"source/common/api:82.4" "source/common/api/posix:81.3" "source/common/common/posix:92.7" "source/common/config:96.4" diff --git a/test/server/connection_handler_test.cc b/test/server/connection_handler_test.cc index e73726875372..9a61f7a0cd39 100644 --- a/test/server/connection_handler_test.cc +++ b/test/server/connection_handler_test.cc @@ -968,7 +968,7 @@ TEST_F(ConnectionHandlerTest, CloseConnectionOnEmptyFilterChain) { auto* connection = new NiceMock(); EXPECT_CALL(dispatcher_, createServerConnection_()).WillOnce(Return(connection)); EXPECT_CALL(factory_, createNetworkFilterChain(_, _)).WillOnce(Return(false)); - EXPECT_CALL(*connection, close(Network::ConnectionCloseType::NoFlush)); + EXPECT_CALL(*connection, close(Network::ConnectionCloseType::NoFlush, _)); EXPECT_CALL(*connection, addConnectionCallbacks(_)).Times(0); EXPECT_CALL(*access_log_, log(_, _, _, _)); Network::MockConnectionSocket* accepted_socket = new NiceMock(); diff --git a/test/test_common/BUILD b/test/test_common/BUILD index 0d35be537730..8af44a3ad37f 100644 --- a/test/test_common/BUILD +++ b/test/test_common/BUILD @@ -112,6 +112,15 @@ envoy_cc_test_library( deps = ["//source/common/singleton:const_singleton"], ) +envoy_cc_test_library( + name = "test_random_generator_lib", + srcs = ["test_random_generator.cc"], + hdrs = ["test_random_generator.h"], + deps = [ + "//source/common/common:utility_lib", + ], +) + envoy_cc_test_library( name = "utility_lib", srcs = ["utility.cc"], @@ -124,6 +133,7 @@ envoy_cc_test_library( ":logging_lib", ":printers_lib", ":resources_lib", + ":test_random_generator_lib", ":test_time_lib", ":thread_factory_for_test_lib", "//envoy/buffer:buffer_interface", @@ -187,6 +197,7 @@ envoy_cc_test_library( srcs = ["file_system_for_test.cc"], hdrs = ["file_system_for_test.h"], deps = [ + ":simulated_time_system_lib", "//source/common/common:utility_lib", "//source/common/filesystem:filesystem_lib", ], @@ -273,8 +284,9 @@ envoy_cc_test_library( srcs = ["simulated_time_system.cc"], hdrs = ["simulated_time_system.h"], deps = [ + ":test_random_generator_lib", ":test_time_system_interface", - ":utility_lib", + ":thread_factory_for_test_lib", "//source/common/event:event_impl_base_lib", "//source/common/event:real_time_system_lib", "//source/common/event:timer_lib", diff --git a/test/test_common/simulated_time_system.h b/test/test_common/simulated_time_system.h index c6dd4405b7e6..998b3cd4125f 100644 --- a/test/test_common/simulated_time_system.h +++ b/test/test_common/simulated_time_system.h @@ -4,10 +4,10 @@ #include "source/common/common/lock_guard.h" #include "source/common/common/thread.h" -#include "source/common/common/utility.h" +#include "test/test_common/test_random_generator.h" #include "test/test_common/test_time_system.h" -#include "test/test_common/utility.h" +#include "test/test_common/thread_factory_for_test.h" #include "absl/container/flat_hash_map.h" diff --git a/test/test_common/simulated_time_system_test.cc b/test/test_common/simulated_time_system_test.cc index 03720a5a767b..86f52e4d747d 100644 --- a/test/test_common/simulated_time_system_test.cc +++ b/test/test_common/simulated_time_system_test.cc @@ -6,7 +6,6 @@ #include "test/mocks/common.h" #include "test/mocks/event/mocks.h" #include "test/test_common/simulated_time_system.h" -#include "test/test_common/utility.h" #include "event2/event.h" #include "gtest/gtest.h" diff --git a/test/test_common/test_random_generator.cc b/test/test_common/test_random_generator.cc new file mode 100644 index 000000000000..ae586e56f735 --- /dev/null +++ b/test/test_common/test_random_generator.cc @@ -0,0 +1,25 @@ +#include "test/test_common/test_random_generator.h" + +#include "gtest/gtest.h" + +using testing::GTEST_FLAG(random_seed); + +namespace Envoy { + +// The purpose of using the static seed here is to use --test_arg=--gtest_random_seed=[seed] +// to specify the seed of the problem to replay. +int32_t getSeed() { + static const int32_t seed = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + return seed; +} + +TestRandomGenerator::TestRandomGenerator() + : seed_(GTEST_FLAG(random_seed) == 0 ? getSeed() : GTEST_FLAG(random_seed)), generator_(seed_) { + ENVOY_LOG_MISC(info, "TestRandomGenerator running with seed {}", seed_); +} + +uint64_t TestRandomGenerator::random() { return generator_(); } + +} // namespace Envoy diff --git a/test/test_common/test_random_generator.h b/test/test_common/test_random_generator.h new file mode 100644 index 000000000000..7f0c1958d89d --- /dev/null +++ b/test/test_common/test_random_generator.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include "source/common/common/utility.h" + +namespace Envoy { + +// Random number generator which logs its seed to stderr. To repeat a test run with a non-zero seed +// one can run the test with --test_arg=--gtest_random_seed=[seed] +class TestRandomGenerator { +public: + TestRandomGenerator(); + + uint64_t random(); + +private: + const int32_t seed_; + std::ranlux48 generator_; +}; + +} // namespace Envoy diff --git a/test/test_common/utility.cc b/test/test_common/utility.cc index a39d0abb4c84..33e3706886ed 100644 --- a/test/test_common/utility.cc +++ b/test/test_common/utility.cc @@ -49,22 +49,6 @@ using testing::GTEST_FLAG(random_seed); namespace Envoy { -// The purpose of using the static seed here is to use --test_arg=--gtest_random_seed=[seed] -// to specify the seed of the problem to replay. -int32_t getSeed() { - static const int32_t seed = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - return seed; -} - -TestRandomGenerator::TestRandomGenerator() - : seed_(GTEST_FLAG(random_seed) == 0 ? getSeed() : GTEST_FLAG(random_seed)), generator_(seed_) { - ENVOY_LOG_MISC(info, "TestRandomGenerator running with seed {}", seed_); -} - -uint64_t TestRandomGenerator::random() { return generator_(); } - bool TestUtility::headerMapEqualIgnoreOrder(const Http::HeaderMap& lhs, const Http::HeaderMap& rhs) { absl::flat_hash_set lhs_keys; @@ -319,6 +303,11 @@ std::vector TestUtility::listFiles(const std::string& path, bool re return file_names; } +std::string TestUtility::uniqueFilename(absl::string_view prefix) { + return absl::StrCat(prefix, "_", getpid(), "_", + std::chrono::system_clock::now().time_since_epoch().count()); +} + std::string TestUtility::addLeftAndRightPadding(absl::string_view to_pad, int desired_length) { int line_fill_len = desired_length - to_pad.length(); int first_half_len = line_fill_len / 2; diff --git a/test/test_common/utility.h b/test/test_common/utility.h index 541e9fde7a8e..8519809fd9b2 100644 --- a/test/test_common/utility.h +++ b/test/test_common/utility.h @@ -29,6 +29,7 @@ #include "test/test_common/file_system_for_test.h" #include "test/test_common/logging.h" #include "test/test_common/printers.h" +#include "test/test_common/test_random_generator.h" #include "test/test_common/test_time_system.h" #include "test/test_common/thread_factory_for_test.h" @@ -129,20 +130,6 @@ class TestEnvoyBug { static void callEnvoyBug() { ENVOY_BUG(false, ""); } }; -// Random number generator which logs its seed to stderr. To repeat a test run with a non-zero seed -// one can run the test with --test_arg=--gtest_random_seed=[seed] -class TestRandomGenerator { -public: - TestRandomGenerator(); - - uint64_t random(); - -private: - const int32_t seed_; - std::ranlux48 generator_; - RealTimeSource real_time_source_; -}; - // See https://github.com/envoyproxy/envoy/issues/21245. enum class Http1ParserImpl { HttpParser, // http-parser from node.js @@ -356,10 +343,7 @@ class TestUtility { * * @return a filename based on the process id and current time. */ - - static std::string uniqueFilename() { - return absl::StrCat(getpid(), "_", std::chrono::system_clock::now().time_since_epoch().count()); - } + static std::string uniqueFilename(absl::string_view prefix = ""); /** * Compare two protos of the same type for equality. diff --git a/third_party/android/BUILD b/third_party/android/BUILD new file mode 100644 index 000000000000..0db8523dd856 --- /dev/null +++ b/third_party/android/BUILD @@ -0,0 +1,21 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_package", + "envoy_cc_library", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_library( + name = "android_lib", + hdrs = select({ + "//bazel:android": [ + "ifaddrs-android.h", + "LocalArray.h", + "ScopedFd.h", + ], + "//conditions:default": [], + }) +) diff --git a/mobile/third_party/android/LocalArray.h b/third_party/android/LocalArray.h similarity index 100% rename from mobile/third_party/android/LocalArray.h rename to third_party/android/LocalArray.h diff --git a/mobile/third_party/android/ScopedFd.h b/third_party/android/ScopedFd.h similarity index 100% rename from mobile/third_party/android/ScopedFd.h rename to third_party/android/ScopedFd.h diff --git a/mobile/third_party/android/ifaddrs-android.h b/third_party/android/ifaddrs-android.h similarity index 100% rename from mobile/third_party/android/ifaddrs-android.h rename to third_party/android/ifaddrs-android.h diff --git a/tools/base/requirements.in b/tools/base/requirements.in index 6cb8073c95de..5b40e1e6a889 100644 --- a/tools/base/requirements.in +++ b/tools/base/requirements.in @@ -4,16 +4,16 @@ aiohttp>=3.8.1 cffi>=1.15.0 colorama coloredlogs -envoy.base.utils>=0.3.8 -envoy.code.check>=0.3.4 -envoy.dependency.check>=0.1.5 -envoy.dependency.pip_check>=0.1.2 -envoy.distribution.release>=0.0.8 -envoy.distribution.repo>=0.0.5 -envoy.distribution.verify>=0.0.8 -envoy.docs.sphinx-runner>=0.1.9 +envoy.base.utils>=0.3.10 +envoy.code.check>=0.3.6 +envoy.dependency.check>=0.1.7 +envoy.dependency.pip_check>=0.1.4 +envoy.distribution.release>=0.0.9 +envoy.distribution.repo>=0.0.8 +envoy.distribution.verify>=0.0.11 +envoy.docs.sphinx-runner>=0.1.11 envoy.gpg.identity>=0.1.0 -envoy.gpg.sign>=0.1.0 +envoy.gpg.sign>=0.1.1 flake8 frozendict gitpython diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index da926d254a18..cdf875e7a1ff 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -1,6 +1,6 @@ # -# This file is autogenerated by pip-compile with python 3.10 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: # # pip-compile --allow-unsafe --generate-hashes requirements.in # @@ -25,9 +25,9 @@ aio-api-bazel==0.0.2 \ --hash=sha256:56e36463d236e477b7e282f2d870185a0b978b50e2c3803c1ebf8b8ac4b18f5b \ --hash=sha256:d3f563b7698e874437d80538a89dd4d79bc37de2e850c846330ae456e3f21dcc # via -r requirements.in -aio-api-github==0.1.4 \ - --hash=sha256:0a09e476ca4eea3d8a0caa861223f170b4a7f4c1b96976574b8d2a282544d353 \ - --hash=sha256:9796778d6b7993e7a3b0ba68af46a314091dcbae2ab98e8f42e3dd5631ed7ab3 +aio-api-github==0.1.6 \ + --hash=sha256:0732ed7c2238ba747fcb9ad1b754828f0dacec3286a1b40b21d9624603d769d5 \ + --hash=sha256:4c9a9722b5444b5630f1cff41f957fee7168dbb5cc0367b86de6ea21b36468af # via # envoy-base-utils # envoy-dependency-check @@ -324,9 +324,9 @@ docutils==0.16 \ # sphinx # sphinx-rtd-theme # sphinx-tabs -envoy-base-utils==0.3.8 \ - --hash=sha256:9c4190bae708a75a955a7c09f9a674121c92dd7e33a359de3715332651ffe2bb \ - --hash=sha256:ae44642f2f43ac778f854f41c931d1969fe96dc323e9112876bc02b94fda05b2 +envoy-base-utils==0.3.10 \ + --hash=sha256:a00722576b9845abee44df4e63e177596b99bf494acde5d077acbaf779e7aa92 \ + --hash=sha256:ddc99293b8ff976fc85952ff094fddf3e4d279dd1bb2dd72cdf4c719f4f7f13e # via # -r requirements.in # envoy-code-check @@ -339,50 +339,50 @@ envoy-base-utils==0.3.8 \ # envoy-docs-sphinx-runner # envoy-github-release # envoy-gpg-sign -envoy-code-check==0.3.5 \ - --hash=sha256:290cda68ccd29239ce35c81a594e0c196737231c37d3d8ec3cc86d9f387731d4 \ - --hash=sha256:d37fa940d585fbc569328128c0ab0053da42ea6a79f4bf734d1f687f0fb21565 +envoy-code-check==0.3.6 \ + --hash=sha256:599342c18dc94b470d417f76007472cf5dd90c2e76b8a88e284e04e0e2f3fdf5 \ + --hash=sha256:bdd0b9f383a627c69136688597ac0eaa2e1dffa0fd0d3e7df92a5ab384374bd4 # via -r requirements.in -envoy-dependency-check==0.1.5 \ - --hash=sha256:2394626236e7b43c0273d4a0a43e1e13d3990ebde9ebdacea253464000927961 \ - --hash=sha256:6940c727980669ffe1fc71c2c1921dfa3bec9d5a0ca4fb7cb4f09ecbbe803e0e +envoy-dependency-check==0.1.7 \ + --hash=sha256:27906f882bd3e22a9a8dc90cc6e178f702b06d30d7a39aa70d264594cd5df5ee \ + --hash=sha256:28b7d61c73bfb22de4f2e63eb3c5a47c1ec20c9170816c5f5e4cc4b412c6b1ac # via -r requirements.in -envoy-dependency-pip-check==0.1.3 \ - --hash=sha256:b07ca0c1f2786b13b0dfcdfc82647afece3918d44dc7ddaca5739dfaadc9d0e8 \ - --hash=sha256:f7bafe720f499009173625268af15374dea9e733b8dadb0fc26bfa7c822a7028 +envoy-dependency-pip-check==0.1.4 \ + --hash=sha256:1feadfa0e8e3c42e38aca31f383ffe6b15720f320a8660301badf64e8ae19e5a \ + --hash=sha256:a689b128856379f6f512b8561ef95691171b08deaf428c5de6221c17fc85c296 # via -r requirements.in envoy-distribution-distrotest==0.0.7 \ --hash=sha256:3de711ba72cc78158cc70aa6957dc7f029f4c0877196d42a4d5a11757fda03e0 \ --hash=sha256:50cdba5f94bc82a632774cfd9e64a288d96dfbc1c2b5db33189dfb2dd913fc96 # via envoy-distribution-verify -envoy-distribution-release==0.0.8 \ - --hash=sha256:60c20f5b74780c46e0d813d1a7fc1af53805cf2c51139badbee39374fcf6c5a9 \ - --hash=sha256:7d54b0364d9f1bb22d81f1280822eceb96ff400566838fe37579f49d8f55be24 +envoy-distribution-release==0.0.9 \ + --hash=sha256:592bdc8bc6847daa7e677011d72163b507e3fee821f5ea13a944e27c2fda334f \ + --hash=sha256:974308468be49d034e5b174745bd6a5671364d090a9a810f0f6f36e81afbcb5d # via -r requirements.in -envoy-distribution-repo==0.0.5 \ - --hash=sha256:6ee86168a83758ef6ed0884c9ca17fdcec5517a24c13b13c4e3ed10609f3b49e \ - --hash=sha256:cc2e9504d0769b82404b94bb14a35cb536a2d2a3742d5aeeeeba5cd17684a75b +envoy-distribution-repo==0.0.8 \ + --hash=sha256:84151ae1c77e63a6967404b5e4fd1130138010b540d3081a0c016c28a657a170 \ + --hash=sha256:c264232b666964696dbbc0ced1a82a4aefcf8f0af89ffd88c05ca8428f2557b5 # via -r requirements.in -envoy-distribution-verify==0.0.8 \ - --hash=sha256:218fbd757abc3ef1745a87525a78723f307fa6ca52bd4f7d5fee4a9c3a90d906 \ - --hash=sha256:b2f719a640fc0a477f591a7704828bd4e2da1da9133562595d276f070d3a14cc +envoy-distribution-verify==0.0.11 \ + --hash=sha256:7a560cd283321ec00e206c3d6938751836e16ba686fe9585af2383fb11499b38 \ + --hash=sha256:f62a64d158aa656b25629714bef2a1d20d0cbfcab040c6351fd6e960567885e9 # via -r requirements.in envoy-docker-utils==0.0.2 \ --hash=sha256:a12cb57f0b6e204d646cbf94f927b3a8f5a27ed15f60d0576176584ec16a4b76 # via envoy-distribution-distrotest -envoy-docs-sphinx-runner==0.1.10 \ - --hash=sha256:6d97c5f0392aa569810da91f0fe42f934ef3d9ecc7430335c3c65892d5b0b277 \ - --hash=sha256:e78fe5e8933bd2c7d7e7acd5f564a1f4985ba749f7a0a89cdbbc8e923f7cf026 +envoy-docs-sphinx-runner==0.1.11 \ + --hash=sha256:4856b311f79b2dd97549a6823513c4d73e221103ccfe898e16f2bbffb8765226 \ + --hash=sha256:e8860f9a65f2f4c5bc8874bcf0fbf50cfe5ab6f6fdeda5159a5f9be1075d8488 # via -r requirements.in -envoy-github-abstract==0.0.21 \ - --hash=sha256:243dae9457243fb42e4643b1f45006c3b0d3151c808884217447d29a081b26a1 \ - --hash=sha256:af4086c79120a523f6978bdf0a91c582b672cdc300241c6eb16ad91ed72b7a0c +envoy-github-abstract==0.0.22 \ + --hash=sha256:2dd65e2f247a4947d0198b295c82716c13162e30c433b7625c27d59eee7bcf78 \ + --hash=sha256:86de8bbe2ecf9db896ecc4ff30ab48fc44a516d868ab1748cd4ae538facacb10 # via # envoy-distribution-release # envoy-github-release -envoy-github-release==0.0.14 \ - --hash=sha256:51bc7c46fdc5954502c55886c321c68300ff8d7c9048151848b6a5d71710226f \ - --hash=sha256:58d7916eca90bcb3b47a523db352a35849d4cf6f2fa3e9d16c7d8fdf1eccdfbf +envoy-github-release==0.0.15 \ + --hash=sha256:955671aa0c664ade3d8dbcd60bc9c72284139b81aeaa3fe72157c4e8383fd35b \ + --hash=sha256:d571422a79e535ec36130dae5a3c56b429040fb20ca60cd3b8cf8588c52b546c # via envoy-distribution-release envoy-gpg-identity==0.1.0 \ --hash=sha256:1df6989e1a6a3a9f5809ac056829b840f57326d41ae1181db415a722216bff48 \ @@ -390,9 +390,9 @@ envoy-gpg-identity==0.1.0 \ # via # -r requirements.in # envoy-gpg-sign -envoy-gpg-sign==0.1.0 \ - --hash=sha256:14b8efee80916fa78857198fb98357c9ddfaa9dec34eb8a453bda1a364f4b973 \ - --hash=sha256:36bb56534a6c5947c18bd10c43185345f092664de86b49318e2afc6cac7d9158 +envoy-gpg-sign==0.1.1 \ + --hash=sha256:5149360c2394a450ee9293d0d2a3a5d62ba8a06c5b92820c201241f61406c790 \ + --hash=sha256:533154f983b3ccbbbf085aba9d79aa7f5452563f7854a6cdc4080ce60d1f3d04 # via -r requirements.in flake8==5.0.4 \ --hash=sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db \ @@ -491,7 +491,6 @@ gidgethub==5.0.1 \ --hash=sha256:67245e93eb0918b37df038148af675df43b62e832c529d7f859f6b90d9f3e70d # via # aio-api-github - # envoy-dependency-check # envoy-distribution-release # envoy-github-abstract # envoy-github-release @@ -517,9 +516,9 @@ imagesize==1.2.0 \ --hash=sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1 \ --hash=sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1 # via sphinx -jinja2==3.0.3 \ - --hash=sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8 \ - --hash=sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7 +jinja2==3.1.2 \ + --hash=sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \ + --hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61 # via # -r requirements.in # envoy-base-utils @@ -713,9 +712,9 @@ orjson==3.8.5 \ # -r requirements.in # aio-core # envoy-base-utils -packaging==21.0 \ - --hash=sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7 \ - --hash=sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14 +packaging==23.0 \ + --hash=sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2 \ + --hash=sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97 # via # aio-api-github # aio-api-nist @@ -805,10 +804,6 @@ pynacl==1.4.0 \ --hash=sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff \ --hash=sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80 # via pygithub -pyparsing==2.4.7 \ - --hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1 \ - --hash=sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b - # via packaging pyreadline==2.1 \ --hash=sha256:4530592fc2e85b25b1a9f79664433da09237c1a270e4d78ea5aa3a2c7229e2d1 # via -r requirements.in @@ -867,9 +862,9 @@ requests==2.26.0 \ # via # pygithub # sphinx -setuptools==66.1.0 \ - --hash=sha256:78a02bdea8a5cb66dec1c507598c443bcc75562817d2556c1a17f7a344615bb4 \ - --hash=sha256:fc19f9f62120a763300fd78e234b3cbd3417be098f08c156eaaf36420627e57b +setuptools==67.0.0 \ + --hash=sha256:883131c5b6efa70b9101c7ef30b2b7b780a4283d5fc1616383cdf22c83cbefe6 \ + --hash=sha256:9d790961ba6219e9ff7d9557622d2fe136816a264dd01d5997cfc057d804853d # via # -r requirements.in # yamllint diff --git a/tools/code_format/check_format.py b/tools/code_format/check_format.py index bbf3e7d2f050..1974f529a804 100755 --- a/tools/code_format/check_format.py +++ b/tools/code_format/check_format.py @@ -729,13 +729,6 @@ def check_source_line(self, line, file_path, report_error): + "Lua API (bad light userdata pointer) on ARM64 architecture. See " + "https://github.com/LuaJIT/LuaJIT/issues/450#issuecomment-433659873 for details.") - if file_path.endswith(self.config.suffixes["proto"]): - exclude_path = ['v1', 'v2'] - result = self.config.re["proto_validation_string"].search(line) - if result is not None: - if not any(x in file_path for x in exclude_path): - report_error("min_bytes is DEPRECATED, Use min_len.") - def check_build_line(self, line, file_path, report_error): if "@bazel_tools" in line and not (self.is_starlark_file(file_path) or file_path.startswith("./bazel/") diff --git a/tools/code_format/check_format_test_helper.py b/tools/code_format/check_format_test_helper.py index 7ad59ac3550f..aeb6b70f25aa 100755 --- a/tools/code_format/check_format_test_helper.py +++ b/tools/code_format/check_format_test_helper.py @@ -260,7 +260,6 @@ def run_checks(): errors += check_unfixable_error("std_visit.cc", "Don't use std::visit; use absl::visit instead") errors += check_unfixable_error( "throw.cc", "Don't introduce throws into exception-free files, use error statuses instead.") - errors += check_unfixable_error("pgv_string.proto", "min_bytes is DEPRECATED, Use min_len.") errors += check_file_expecting_ok("commented_throw.cc") errors += check_unfixable_error( "repository_url.bzl", "Only repository_locations.bzl may contains URL references") diff --git a/tools/code_format/config.yaml b/tools/code_format/config.yaml index 49bb02d04ae4..344b6584e619 100644 --- a/tools/code_format/config.yaml +++ b/tools/code_format/config.yaml @@ -147,10 +147,10 @@ paths: - test/integration/integration.h - test/test_common/simulated_time_system.cc - test/test_common/simulated_time_system.h + - test/test_common/test_random_generator.cc - test/test_common/test_time.cc - test/test_common/test_time.h - test/test_common/utility.cc - - test/test_common/utility.h - test/tools/wee8_compile/wee8_compile.cc # Tests in these paths may make use of the Registry::RegisterFactory constructor or the @@ -245,7 +245,6 @@ re: mangled_protobuf_name: envoy::[a-z0-9_:]+::[A-Z][a-z]\w*_\w*_[A-Z]{2} maintainers: .*github.com.(.*)\)\) old_mock_method: MOCK_METHOD\d - proto_validation_string: \bmin_bytes\b owner: "@\\S+" runtime_guard_flag: RUNTIME_GUARD\((.*)\); test_name_starting_lc: TEST(_.\(.*,\s|\()[a-z].*\)\s\{ diff --git a/tools/dependency/requirements.txt b/tools/dependency/requirements.txt deleted file mode 100644 index c21e295d9816..000000000000 --- a/tools/dependency/requirements.txt +++ /dev/null @@ -1,294 +0,0 @@ -# -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: -# -# pip-compile --allow-unsafe --generate-hashes requirements.in -# -certifi==2022.12.7 \ - --hash=sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3 \ - --hash=sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18 - # via requests -cffi==1.15.1 \ - --hash=sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5 \ - --hash=sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef \ - --hash=sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104 \ - --hash=sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426 \ - --hash=sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405 \ - --hash=sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375 \ - --hash=sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a \ - --hash=sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e \ - --hash=sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc \ - --hash=sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf \ - --hash=sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185 \ - --hash=sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497 \ - --hash=sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3 \ - --hash=sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35 \ - --hash=sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c \ - --hash=sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83 \ - --hash=sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21 \ - --hash=sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca \ - --hash=sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984 \ - --hash=sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac \ - --hash=sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd \ - --hash=sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee \ - --hash=sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a \ - --hash=sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2 \ - --hash=sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192 \ - --hash=sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7 \ - --hash=sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585 \ - --hash=sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f \ - --hash=sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e \ - --hash=sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27 \ - --hash=sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b \ - --hash=sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e \ - --hash=sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e \ - --hash=sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d \ - --hash=sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c \ - --hash=sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415 \ - --hash=sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82 \ - --hash=sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02 \ - --hash=sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314 \ - --hash=sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325 \ - --hash=sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c \ - --hash=sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3 \ - --hash=sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914 \ - --hash=sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045 \ - --hash=sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d \ - --hash=sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9 \ - --hash=sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5 \ - --hash=sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2 \ - --hash=sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c \ - --hash=sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3 \ - --hash=sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2 \ - --hash=sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8 \ - --hash=sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d \ - --hash=sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d \ - --hash=sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9 \ - --hash=sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162 \ - --hash=sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76 \ - --hash=sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4 \ - --hash=sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e \ - --hash=sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9 \ - --hash=sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6 \ - --hash=sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b \ - --hash=sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01 \ - --hash=sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0 - # via pynacl -charset-normalizer==3.0.1 \ - --hash=sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b \ - --hash=sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42 \ - --hash=sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d \ - --hash=sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b \ - --hash=sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a \ - --hash=sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59 \ - --hash=sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154 \ - --hash=sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1 \ - --hash=sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c \ - --hash=sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a \ - --hash=sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d \ - --hash=sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6 \ - --hash=sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b \ - --hash=sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b \ - --hash=sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783 \ - --hash=sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5 \ - --hash=sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918 \ - --hash=sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555 \ - --hash=sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639 \ - --hash=sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786 \ - --hash=sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e \ - --hash=sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed \ - --hash=sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820 \ - --hash=sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8 \ - --hash=sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3 \ - --hash=sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541 \ - --hash=sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14 \ - --hash=sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be \ - --hash=sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e \ - --hash=sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76 \ - --hash=sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b \ - --hash=sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c \ - --hash=sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b \ - --hash=sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3 \ - --hash=sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc \ - --hash=sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6 \ - --hash=sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59 \ - --hash=sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4 \ - --hash=sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d \ - --hash=sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d \ - --hash=sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3 \ - --hash=sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a \ - --hash=sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea \ - --hash=sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6 \ - --hash=sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e \ - --hash=sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603 \ - --hash=sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24 \ - --hash=sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a \ - --hash=sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58 \ - --hash=sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678 \ - --hash=sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a \ - --hash=sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c \ - --hash=sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6 \ - --hash=sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18 \ - --hash=sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174 \ - --hash=sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317 \ - --hash=sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f \ - --hash=sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc \ - --hash=sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837 \ - --hash=sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41 \ - --hash=sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c \ - --hash=sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579 \ - --hash=sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753 \ - --hash=sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8 \ - --hash=sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291 \ - --hash=sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087 \ - --hash=sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866 \ - --hash=sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3 \ - --hash=sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d \ - --hash=sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1 \ - --hash=sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca \ - --hash=sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e \ - --hash=sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db \ - --hash=sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72 \ - --hash=sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d \ - --hash=sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc \ - --hash=sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539 \ - --hash=sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d \ - --hash=sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af \ - --hash=sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b \ - --hash=sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602 \ - --hash=sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f \ - --hash=sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478 \ - --hash=sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c \ - --hash=sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e \ - --hash=sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479 \ - --hash=sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7 \ - --hash=sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8 - # via requests -colorama==0.4.6 \ - --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ - --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 - # via -r requirements.in -deprecated==1.2.13 \ - --hash=sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d \ - --hash=sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d - # via pygithub -idna==3.4 \ - --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ - --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 - # via requests -packaging==23.0 \ - --hash=sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2 \ - --hash=sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97 - # via -r requirements.in -pycparser==2.21 \ - --hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \ - --hash=sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206 - # via cffi -pygithub==1.57 \ - --hash=sha256:5822febeac2391f1306c55a99af2bc8f86c8bf82ded000030cd02c18f31b731f \ - --hash=sha256:c273f252b278fb81f1769505cc6921bdb6791e1cebd6ac850cc97dad13c31ff3 - # via -r requirements.in -pyjwt==2.6.0 \ - --hash=sha256:69285c7e31fc44f68a1feb309e948e0df53259d579295e6cfe2b1792329f05fd \ - --hash=sha256:d83c3d892a77bbb74d3e1a2cfa90afaadb60945205d1095d9221f04466f64c14 - # via pygithub -pynacl==1.5.0 \ - --hash=sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1 \ - --hash=sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92 \ - --hash=sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394 \ - --hash=sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d \ - --hash=sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858 \ - --hash=sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b \ - --hash=sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff \ - --hash=sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543 \ - --hash=sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93 \ - --hash=sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba - # via pygithub -pyparsing==3.0.9 \ - --hash=sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb \ - --hash=sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc - # via - # -r requirements.in - # packaging -pytz==2022.7.1 \ - --hash=sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0 \ - --hash=sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a - # via -r requirements.in -requests==2.28.2 \ - --hash=sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa \ - --hash=sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf - # via pygithub -six==1.16.0 \ - --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ - --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 - # via pynacl -urllib3==1.26.14 \ - --hash=sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72 \ - --hash=sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1 - # via requests -wrapt==1.14.1 \ - --hash=sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3 \ - --hash=sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b \ - --hash=sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4 \ - --hash=sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2 \ - --hash=sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656 \ - --hash=sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3 \ - --hash=sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff \ - --hash=sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310 \ - --hash=sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a \ - --hash=sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57 \ - --hash=sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069 \ - --hash=sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383 \ - --hash=sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe \ - --hash=sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87 \ - --hash=sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d \ - --hash=sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b \ - --hash=sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907 \ - --hash=sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f \ - --hash=sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0 \ - --hash=sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28 \ - --hash=sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1 \ - --hash=sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853 \ - --hash=sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc \ - --hash=sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3 \ - --hash=sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3 \ - --hash=sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164 \ - --hash=sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1 \ - --hash=sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c \ - --hash=sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1 \ - --hash=sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7 \ - --hash=sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1 \ - --hash=sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320 \ - --hash=sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed \ - --hash=sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1 \ - --hash=sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248 \ - --hash=sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c \ - --hash=sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456 \ - --hash=sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77 \ - --hash=sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef \ - --hash=sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1 \ - --hash=sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7 \ - --hash=sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86 \ - --hash=sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4 \ - --hash=sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d \ - --hash=sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d \ - --hash=sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8 \ - --hash=sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5 \ - --hash=sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471 \ - --hash=sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00 \ - --hash=sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68 \ - --hash=sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3 \ - --hash=sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d \ - --hash=sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735 \ - --hash=sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d \ - --hash=sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569 \ - --hash=sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7 \ - --hash=sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59 \ - --hash=sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5 \ - --hash=sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb \ - --hash=sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b \ - --hash=sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f \ - --hash=sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462 \ - --hash=sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015 \ - --hash=sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af - # via deprecated diff --git a/tools/dev/requirements.in b/tools/dev/requirements.in new file mode 100644 index 000000000000..01b6ef719be2 --- /dev/null +++ b/tools/dev/requirements.in @@ -0,0 +1,26 @@ +# Remote-pdb can be used inside build jobs, or parallelized aspects +# remote-pdb + +# Add temporary dev requirements here +# +# Note: This file should always be empty on `main`. +# +# After updating this file you will need to re-run `pip-compile` - eg: +# +# $ cd tools/dev +# $ pip-compile --generate-hashes --reuse-hashes --allow-unsafe requirements.in +# +# Only paths to files are allowed (ie no git etc), so if loading from the filesystem +# you will either need to create a tarball from the egg directory or build +# it into a wheel before re-running `pip-compile` +# +# Examples: +# +# Github at commit: +# +# https://github.com/envoyproxy/pytooling/archive/135fd2cf03f5327eecbaf3ffaa43c5bec31d2f2c.zip#egg=envoy.code.check&subdirectory=envoy.code.check +# +# Local filesystem (path must be available to bazel, eg inside container): +# +# file:///src/workspace/pytooling/envoy.code.check/dist/envoy.code.check-0.3.5.dev0-py3-none-any.whl +# diff --git a/tools/dev/requirements.txt b/tools/dev/requirements.txt index 6e7a7e1569a8..75802834291d 100644 --- a/tools/dev/requirements.txt +++ b/tools/dev/requirements.txt @@ -1,19 +1 @@ -# Remote-pdb can be used inside build jobs, or parallelized aspects -remote-pdb - -# Add temporary dev requirements with `-e` here -# -# Note: This should always be empty on `main`. -# -# Examples: -# -# Github pull request: -# -# -e git+https://github.com/envoyproxy/pytooling@f862768556e747ce5a1d309850f9ba8ad72b0afa#egg=envoy.dependency.check&subdirectory=envoy.dependency.check -# -# Local filesystem (path must be available to bazel, eg inside container) -# -# -e file:///src/workspace/pytooling/envoy.dependency.check#egg=envoy.dependency.check&cachebust=011 -# -# `cachebust` can be used to signify change to bazel. -# +# DO NOT COMMIT CHANGES TO THIS FILE!!! diff --git a/tools/protoprint/protoprint.py b/tools/protoprint/protoprint.py index 05953bb53c86..463a2bc6049a 100644 --- a/tools/protoprint/protoprint.py +++ b/tools/protoprint/protoprint.py @@ -32,6 +32,7 @@ from envoy.annotations import deprecation_pb2 from udpa.annotations import migrate_pb2, status_pb2 from xds.annotations.v3 import status_pb2 as xds_status_pb2 +from validate import validate_pb2 import envoy_repo @@ -699,6 +700,13 @@ def visit_message(self, msg_proto, type_context, nested_msgs, nested_enums): fields = '' oneof_index = None for index, field in enumerate(msg_proto.field): + if "/v3" in type_context.source_code_info.name: + if field.options.Extensions[validate_pb2.rules].string.min_bytes != 0: + field.options.Extensions[ + validate_pb2.rules].string.min_len = field.options.Extensions[ + validate_pb2.rules].string.min_bytes + field.options.Extensions[validate_pb2.rules].string.ClearField("min_bytes") + if oneof_index is not None: if not field.HasField('oneof_index') or field.oneof_index != oneof_index: fields += '}\n\n' diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index 2ba06fbfb282..fa2451d316b3 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -61,6 +61,7 @@ ESC ETB ETX FS +FIXME HEXDIG HEXDIGIT HPE @@ -158,6 +159,7 @@ FIPS FIRSTHDR FQDN FREEBIND +freeifaddrs FUZZER FUZZERS delims @@ -201,6 +203,7 @@ IAM IANA IDL IETF +IFADDRS INADDR INET INVAL @@ -924,7 +927,6 @@ mutator mutex mutexes mux -muxes muxed mysql namelen @@ -941,6 +943,9 @@ negative netblock netblocks netfilter +netlink +netmask +NLMSG nonblocking noncopyable nonresponsive @@ -1136,6 +1141,7 @@ responder restarter resync ret +retransmitted retransmitting retriable retriggers diff --git a/tools/testdata/check_format/pgv_string.proto b/tools/testdata/check_format/pgv_string.proto deleted file mode 100644 index 2a73f8a6a830..000000000000 --- a/tools/testdata/check_format/pgv_string.proto +++ /dev/null @@ -1 +0,0 @@ -// this proto file is used to check proto validation ERROR min_bytes