diff --git a/.github/config.yml b/.github/config.yml index 846771e06a19..189bbc77eae1 100644 --- a/.github/config.yml +++ b/.github/config.yml @@ -53,11 +53,6 @@ checks: on-run: - mobile-compile-time-cc - mobile-compile-time-options - mobile-core: - name: Mobile/Core - required: true - on-run: - - mobile-core mobile-coverage: name: Mobile/Coverage required: true @@ -258,22 +253,6 @@ run: - mobile/.bazelrc - mobile/**/* - tools/code_format/check_format.py - mobile-core: - paths: - - .bazelrc - - .bazelversion - - .github/config.yml - - api/**/* - - bazel/external/quiche.BUILD - - bazel/repository_locations.bzl - - envoy/**/* - - mobile/.bazelrc - - mobile/**/* - - source/**/* - - test/config/**/* - - test/integration/* - - test/mocks/**/* - - test/test_common/**/* mobile-format: paths: - .bazelrc diff --git a/.github/workflows/_precheck_deps.yml b/.github/workflows/_precheck_deps.yml index 17facaf412d0..14ccb10ff127 100644 --- a/.github/workflows/_precheck_deps.yml +++ b/.github/workflows/_precheck_deps.yml @@ -55,4 +55,4 @@ jobs: ref: ${{ fromJSON(inputs.request).request.sha }} persist-credentials: false - name: Dependency Review - uses: actions/dependency-review-action@0c155c5e8556a497adf53f2c18edabf945ed8e70 # v4.3.2 + uses: actions/dependency-review-action@72eb03d02c7872a771aacd928f3123ac62ad6d3a # v4.3.3 diff --git a/.github/workflows/codeql-daily.yml b/.github/workflows/codeql-daily.yml index 6b889ae351cc..0d8347258ed3 100644 --- a/.github/workflows/codeql-daily.yml +++ b/.github/workflows/codeql-daily.yml @@ -37,7 +37,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@f079b8493333aace61c81488f8bd40919487bd9f # codeql-bundle-v3.25.7 + uses: github/codeql-action/init@2e230e8fe0ad3a14a340ad0815ddb96d599d2aff # codeql-bundle-v3.25.8 # Override language selection by uncommenting this and choosing your languages with: languages: cpp @@ -71,4 +71,4 @@ jobs: git clean -xdf - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@f079b8493333aace61c81488f8bd40919487bd9f # codeql-bundle-v3.25.7 + uses: github/codeql-action/analyze@2e230e8fe0ad3a14a340ad0815ddb96d599d2aff # codeql-bundle-v3.25.8 diff --git a/.github/workflows/codeql-push.yml b/.github/workflows/codeql-push.yml index c0572bef4caf..4a82d699b216 100644 --- a/.github/workflows/codeql-push.yml +++ b/.github/workflows/codeql-push.yml @@ -68,7 +68,7 @@ jobs: - name: Initialize CodeQL if: ${{ env.BUILD_TARGETS != '' }} - uses: github/codeql-action/init@f079b8493333aace61c81488f8bd40919487bd9f # codeql-bundle-v3.25.7 + uses: github/codeql-action/init@2e230e8fe0ad3a14a340ad0815ddb96d599d2aff # codeql-bundle-v3.25.8 with: languages: cpp @@ -112,4 +112,4 @@ jobs: - name: Perform CodeQL Analysis if: ${{ env.BUILD_TARGETS != '' }} - uses: github/codeql-action/analyze@f079b8493333aace61c81488f8bd40919487bd9f # codeql-bundle-v3.25.7 + uses: github/codeql-action/analyze@2e230e8fe0ad3a14a340ad0815ddb96d599d2aff # codeql-bundle-v3.25.8 diff --git a/.github/workflows/mobile-compile_time_options.yml b/.github/workflows/mobile-compile_time_options.yml index 20522bfc21fb..a94335618cc0 100644 --- a/.github/workflows/mobile-compile_time_options.yml +++ b/.github/workflows/mobile-compile_time_options.yml @@ -56,6 +56,18 @@ jobs: build --config=mobile-remote-ci-cc-no-exceptions //test/performance:test_binary_size //library/cc/... + - name: Running C++ tests with xDS enabled + target: cc-tests-xds-enabled + args: >- + test + --config=mobile-remote-ci-cc-xds-enabled + //test/common/integration/... + - name: Running C++ tests with full protos enabled + target: cc-tests-full-protos-enabled + args: >- + test + --config=mobile-remote-ci-cc-full-protos-enabled + //test/common/... //test/cc/... build: permissions: diff --git a/.github/workflows/mobile-core.yml b/.github/workflows/mobile-core.yml deleted file mode 100644 index 70de7033c9a0..000000000000 --- a/.github/workflows/mobile-core.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Mobile/Core - -permissions: - contents: read - -on: - workflow_run: - workflows: - - Request - types: - - completed - -concurrency: - group: ${{ github.head_ref || github.run_id }}-${{ github.workflow }} - cancel-in-progress: true - - -jobs: - load: - secrets: - app-key: ${{ secrets.ENVOY_CI_APP_KEY }} - app-id: ${{ secrets.ENVOY_CI_APP_ID }} - lock-app-key: ${{ secrets.ENVOY_CI_MUTEX_APP_KEY }} - lock-app-id: ${{ secrets.ENVOY_CI_MUTEX_APP_ID }} - permissions: - actions: read - contents: read - packages: read - pull-requests: read - if: ${{ github.event.workflow_run.conclusion == 'success' }} - uses: ./.github/workflows/_load.yml - with: - check-name: mobile-core - - unit-tests: - permissions: - contents: read - packages: read - if: ${{ fromJSON(needs.load.outputs.request).run.mobile-core }} - needs: load - uses: ./.github/workflows/_mobile_container_ci.yml - with: - args: >- - test - --config=mobile-remote-ci-core - //test/common/... - request: ${{ needs.load.outputs.request }} - target: unit-tests - timeout-minutes: 120 - - request: - secrets: - app-id: ${{ secrets.ENVOY_CI_APP_ID }} - app-key: ${{ secrets.ENVOY_CI_APP_KEY }} - permissions: - actions: read - contents: read - if: >- - ${{ always() - && github.event.workflow_run.conclusion == 'success' - && fromJSON(needs.load.outputs.request).run.mobile-core }} - needs: - - load - - unit-tests - uses: ./.github/workflows/_finish.yml - with: - needs: ${{ toJSON(needs) }} diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index c6afce282318..fac066899006 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -40,6 +40,6 @@ jobs: retention-days: 5 - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@f079b8493333aace61c81488f8bd40919487bd9f # v3.25.7 + uses: github/codeql-action/upload-sarif@2e230e8fe0ad3a14a340ad0815ddb96d599d2aff # v3.25.8 with: sarif_file: results.sarif diff --git a/CODEOWNERS b/CODEOWNERS index e0d262de1d4d..030d1385a036 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -90,8 +90,8 @@ extensions/filters/common/original_src @klarose @mattklein123 /*/extensions/filters/http/cache @toddmgreer @jmarantz @penguingao @mpwarres @capoferro /*/extensions/http/cache/simple_http_cache @toddmgreer @jmarantz @penguingao @mpwarres @capoferro # aws_iam grpc credentials -/*/extensions/grpc_credentials/aws_iam @suniltheta @lavignes @mattklein123 -/*/extensions/common/aws @suniltheta @lavignes @mattklein123 +/*/extensions/grpc_credentials/aws_iam @suniltheta @mattklein123 @nbaws +/*/extensions/common/aws @suniltheta @mattklein123 @nbaws # adaptive concurrency limit extension. /*/extensions/filters/http/adaptive_concurrency @tonya11en @mattklein123 # admission control extension. @@ -153,8 +153,8 @@ extensions/filters/common/original_src @klarose @mattklein123 # support for on-demand VHDS requests /*/extensions/filters/http/on_demand @dmitri-d @htuch @kyessenov /*/extensions/filters/network/connection_limit @mattklein123 @alyssawilk @delong-coder -/*/extensions/filters/http/aws_request_signing @derekargueta @suniltheta @mattklein123 @marcomagdy -/*/extensions/filters/http/aws_lambda @suniltheta @mattklein123 @marcomagdy @lavignes +/*/extensions/filters/http/aws_request_signing @derekargueta @suniltheta @mattklein123 @marcomagdy @nbaws +/*/extensions/filters/http/aws_lambda @suniltheta @mattklein123 @marcomagdy @nbaws /*/extensions/filters/http/buffer @alyssawilk @mattklein123 /*/extensions/transport_sockets/raw_buffer @alyssawilk @mattklein123 # Watchdog Extensions diff --git a/RELEASES.md b/RELEASES.md index 3d645c62ce9f..bab49dae35e8 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -65,6 +65,7 @@ actual mechanics of the release itself. | 2022 Q4 | Can Cecen ([cancecen](https://github.com/cancecen)) | Tony Allen ([tonya11en](https://github.com/tonya11en)) | | 2023 Q3 | Boteng Yao ([botengyao](https://github.com/botengyao)) | Kateryna Nezdolii ([nezdolik](https://github.com/nezdolik)) | | 2023 Q4 | Paul Merrison ([pmerrison](https://github.com/pmerrison)) | Brian Sonnenberg ([briansonnenberg](https://github.com/briansonnenberg)) | +| 2024 Q2 | Ryan Northey ([phlax](https://github.com/phlax)) | Boteng Yao ([botengyao](https://github.com/botengyao)) | ## Major release schedule @@ -135,6 +136,7 @@ Security releases are published on a 3-monthly cycle, around the mid point betwe | Quarter | Expected | Actual | Difference | |:-------:|:----------:|:----------:|:----------:| -| 2024 Q2 | 2024/06/04 | | | +| 2024 Q2 | 2024/06/04 | 2024/06/04 | 0 days | +| 2024 Q3 | 2024/09/03 | NOTE: Zero-day vulnerabilities, and upstream vulnerabilities disclosed to us under embargo, may necessitate an emergency release with little or no warning. diff --git a/api/envoy/extensions/access_loggers/open_telemetry/v3/BUILD b/api/envoy/extensions/access_loggers/open_telemetry/v3/BUILD index 95eff6986b7f..dad7cd1aef35 100644 --- a/api/envoy/extensions/access_loggers/open_telemetry/v3/BUILD +++ b/api/envoy/extensions/access_loggers/open_telemetry/v3/BUILD @@ -6,6 +6,7 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ + "//envoy/config/core/v3:pkg", "//envoy/extensions/access_loggers/grpc/v3:pkg", "@com_github_cncf_xds//udpa/annotations:pkg", "@opentelemetry_proto//:common", diff --git a/api/envoy/extensions/access_loggers/open_telemetry/v3/logs_service.proto b/api/envoy/extensions/access_loggers/open_telemetry/v3/logs_service.proto index 0e4e89cdb6ac..641276a64bd6 100644 --- a/api/envoy/extensions/access_loggers/open_telemetry/v3/logs_service.proto +++ b/api/envoy/extensions/access_loggers/open_telemetry/v3/logs_service.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package envoy.extensions.access_loggers.open_telemetry.v3; +import "envoy/config/core/v3/extension.proto"; import "envoy/extensions/access_loggers/grpc/v3/als.proto"; import "opentelemetry/proto/common/v1/common.proto"; @@ -22,7 +23,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // populate `opentelemetry.proto.collector.v1.logs.ExportLogsServiceRequest.resource_logs `_. // In addition, the request start time is set in the dedicated field. // [#extension: envoy.access_loggers.open_telemetry] -// [#next-free-field: 7] +// [#next-free-field: 8] message OpenTelemetryAccessLogConfig { // [#comment:TODO(itamarkam): add 'filter_state_objects_to_log' to logs.] grpc.v3.CommonGrpcAccessLogConfig common_config = 1 [(validate.rules).message = {required: true}]; @@ -51,4 +52,9 @@ message OpenTelemetryAccessLogConfig { // ``access_logs.open_telemetry_access_log.``. If non-empty, stats will be rooted at // ``access_logs.open_telemetry_access_log..``. string stat_prefix = 6; + + // Specifies a collection of Formatter plugins that can be called from the access log configuration. + // See the formatters extensions documentation for details. + // [#extension-category: envoy.formatter] + repeated config.core.v3.TypedExtensionConfig formatters = 7; } diff --git a/api/envoy/extensions/filters/http/ext_authz/v3/BUILD b/api/envoy/extensions/filters/http/ext_authz/v3/BUILD index cabe849e71d1..7bbb39173ac6 100644 --- a/api/envoy/extensions/filters/http/ext_authz/v3/BUILD +++ b/api/envoy/extensions/filters/http/ext_authz/v3/BUILD @@ -7,6 +7,7 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ "//envoy/annotations:pkg", + "//envoy/config/common/mutation_rules/v3:pkg", "//envoy/config/core/v3:pkg", "//envoy/type/matcher/v3:pkg", "//envoy/type/v3:pkg", diff --git a/api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto b/api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto index f4750864c91b..986fa2c9166c 100644 --- a/api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto +++ b/api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package envoy.extensions.filters.http.ext_authz.v3; +import "envoy/config/common/mutation_rules/v3/mutation_rules.proto"; import "envoy/config/core/v3/base.proto"; import "envoy/config/core/v3/config_source.proto"; import "envoy/config/core/v3/grpc_service.proto"; @@ -28,10 +29,10 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // External Authorization :ref:`configuration overview `. // [#extension: envoy.filters.http.ext_authz] -// [#next-free-field: 26] +// [#next-free-field: 27] message ExtAuthz { option (udpa.annotations.versioning).previous_message_type = - "envoy.config.filter.http.ext_authz.v2.ExtAuthz"; + "envoy.config.filter.http.ext_authz.v3.ExtAuthz"; reserved 4; @@ -261,6 +262,23 @@ message ExtAuthz { // It's recommended you set this to true unless you already rely on the old behavior. False is the // default only for backwards compatibility. bool encode_raw_headers = 23; + + // Rules for what modifications an ext_authz server may make to the request headers before + // continuing decoding / forwarding upstream. + // + // If set to anything, enables header mutation checking against configured rules. Note that + // :ref:`HeaderMutationRules ` + // has defaults that change ext_authz behavior. Also note that if this field is set to anything, + // ext_authz can no longer append to :-prefixed headers. + // + // If empty, header mutation rule checking is completely disabled. + // + // Regardless of what is configured here, ext_authz cannot remove :-prefixed headers. + // + // This field and ``validate_mutations`` have different use cases. ``validate_mutations`` enables + // correctness checks for all header / query parameter mutations (e.g. for invalid characters). + // This field allows the filter to reject mutations to specific headers. + config.common.mutation_rules.v3.HeaderMutationRules decoder_header_mutation_rules = 26; } // Configuration for buffering the request data. diff --git a/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto b/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto index aa5f70b2897a..22696745f630 100644 --- a/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto +++ b/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto @@ -74,7 +74,7 @@ message OAuth2Credentials { // OAuth config // -// [#next-free-field: 16] +// [#next-free-field: 17] message OAuth2Config { enum AuthType { // The ``client_id`` and ``client_secret`` will be sent in the URL encoded request body. @@ -111,6 +111,12 @@ message OAuth2Config { // Forward the OAuth token as a Bearer to upstream web service. bool forward_bearer_token = 7; + // If set to true, preserve the existing authorization header. + // By default Envoy strips the existing authorization header before forwarding upstream. + // Can not be set to true if forward_bearer_token is already set to true. + // Default value is false. + bool preserve_authorization_header = 16; + // Any request that matches any of the provided matchers will be passed through without OAuth validation. repeated config.route.v3.HeaderMatcher pass_through_matcher = 8; 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 31ca8a6950e4..cc96c4822e23 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 @@ -18,6 +18,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // PROXY protocol listener filter. // [#extension: envoy.filters.listener.proxy_protocol] +// [#next-free-field: 6] message ProxyProtocol { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.listener.proxy_protocol.v2.ProxyProtocol"; @@ -85,4 +86,10 @@ message ProxyProtocol { // and an incoming request matches the V2 signature, the filter will allow the request through without any modification. // The filter treats this request as if it did not have any PROXY protocol information. repeated config.core.v3.ProxyProtocolConfig.Version disallowed_versions = 4; + + // The human readable prefix to use when emitting statistics for the filter. + // If not configured, statistics will be emitted without the prefix segment. + // See the :ref:`filter's statistics documentation ` for + // more information. + string stat_prefix = 5; } diff --git a/bazel/BUILD b/bazel/BUILD index c4749d3140d3..b6c11d2f029c 100644 --- a/bazel/BUILD +++ b/bazel/BUILD @@ -351,6 +351,64 @@ selects.config_setting_group( ], ) +selects.config_setting_group( + name = "disable_http3_on_linux_ppc", + match_all = [ + ":disable_http3", + ":linux_ppc", + ], +) + +selects.config_setting_group( + name = "disable_http3_on_windows_x86_64", + match_all = [ + ":disable_http3", + ":windows_x86_64", + ], +) + +bool_flag( + name = "enabled", + build_setting_default = True, + visibility = ["//visibility:private"], +) + +bool_flag( + name = "disabled", + build_setting_default = False, + visibility = ["//visibility:private"], +) + +# Alias equal to "not(":disable_http3")" (if "not()" existed). +alias( + name = "enable_http3_setting", + actual = select({ + ":disable_http3": ":disabled", + "//conditions:default": ":enabled", + }), +) + +config_setting( + name = "enable_http3", + flag_values = {":enable_http3_setting": "True"}, +) + +selects.config_setting_group( + name = "enable_http3_on_linux_ppc", + match_all = [ + ":enable_http3", + ":linux_ppc", + ], +) + +selects.config_setting_group( + name = "enable_http3_on_windows_x86_64", + match_all = [ + ":enable_http3", + ":windows_x86_64", + ], +) + config_setting( name = "disable_admin_html", values = {"define": "admin_html=disabled"}, diff --git a/bazel/external/quiche_sequencer_fix.patch b/bazel/external/quiche_sequencer_fix.patch new file mode 100644 index 000000000000..b4203e92b6e3 --- /dev/null +++ b/bazel/external/quiche_sequencer_fix.patch @@ -0,0 +1,16 @@ +# Fix https://github.com/envoyproxy/envoy-setec/issues/1496#issue-2251291349 + +diff --git a/quiche/quic/core/quic_stream_sequencer_buffer.cc b/quiche/quic/core/quic_stream_sequencer_buffer.cc +index d364d61bc..0966af4b0 100644 +--- a/quiche/quic/core/quic_stream_sequencer_buffer.cc ++++ b/quiche/quic/core/quic_stream_sequencer_buffer.cc +@@ -388,7 +388,8 @@ bool QuicStreamSequencerBuffer::PeekRegion(QuicStreamOffset offset, + + // Determine if entire block has been received. + size_t end_block_idx = GetBlockIndex(FirstMissingByte()); +- if (block_idx == end_block_idx) { ++ if (block_idx == end_block_idx && ++ block_offset < GetInBlockOffset(FirstMissingByte())) { + // Only read part of block before FirstMissingByte(). + iov->iov_len = GetInBlockOffset(FirstMissingByte()) - block_offset; + } else { diff --git a/bazel/external/quiche_stream_fix.patch b/bazel/external/quiche_stream_fix.patch new file mode 100644 index 000000000000..898a54843f95 --- /dev/null +++ b/bazel/external/quiche_stream_fix.patch @@ -0,0 +1,51 @@ +# Fix https://github.com/envoyproxy/envoy-setec/issues/1496#issuecomment-2064844217 + +diff --git a/quiche/quic/core/http/quic_spdy_stream.cc b/quiche/quic/core/http/quic_spdy_stream.cc +index 4a5c2ede2..d69895055 100644 +--- a/quiche/quic/core/http/quic_spdy_stream.cc ++++ b/quiche/quic/core/http/quic_spdy_stream.cc +@@ -1865,6 +1865,18 @@ bool QuicSpdyStream::AreHeaderFieldValuesValid( + return true; + } + ++void QuicSpdyStream::StopReading() { ++ QuicStream::StopReading(); ++ if (GetQuicReloadableFlag( ++ quic_stop_reading_also_stops_header_decompression) && ++ VersionUsesHttp3(transport_version()) && !fin_received() && ++ spdy_session_->qpack_decoder()) { ++ // Clean up Qpack decoding states. ++ spdy_session_->qpack_decoder()->OnStreamReset(id()); ++ qpack_decoded_headers_accumulator_.reset(); ++ } ++} ++ + void QuicSpdyStream::OnInvalidHeaders() { Reset(QUIC_BAD_APPLICATION_PAYLOAD); } + + void QuicSpdyStream::CloseReadSide() { +diff --git a/quiche/quic/core/http/quic_spdy_stream.h b/quiche/quic/core/http/quic_spdy_stream.h +index 10c34b10f..5c0cb0128 100644 +--- a/quiche/quic/core/http/quic_spdy_stream.h ++++ b/quiche/quic/core/http/quic_spdy_stream.h +@@ -117,6 +117,7 @@ class QUICHE_EXPORT QuicSpdyStream + + // QuicStream implementation + void OnClose() override; ++ void StopReading() override; + + // Override to maybe close the write side after writing. + void OnCanWrite() override; +diff --git a/quiche/quic/core/quic_flags_list.h b/quiche/quic/core/quic_flags_list.h +index 61fa4f9e6..6737823a5 100644 +--- a/quiche/quic/core/quic_flags_list.h ++++ b/quiche/quic/core/quic_flags_list.h +@@ -6,7 +6,8 @@ + + #ifdef QUIC_FLAG + +-QUIC_FLAG(quic_reloadable_flag_quic_stop_reading_also_stops_header_decompression, false) ++// If true, QUIC stream will not continue decompressing buffer headers after StopReading() called. ++QUIC_FLAG(quic_reloadable_flag_quic_stop_reading_also_stops_header_decompression, true) + // A testonly reloadable flag that will always default to false. + QUIC_FLAG(quic_reloadable_flag_quic_testonly_default_false, false) + // A testonly reloadable flag that will always default to true. diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index bcaa502620bf..a54efde0664b 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -1141,6 +1141,11 @@ def _com_github_google_quiche(): external_http_archive( name = "com_github_google_quiche", patch_cmds = ["find quiche/ -type f -name \"*.bazel\" -delete"], + patches = [ + "@envoy//bazel/external:quiche_sequencer_fix.patch", + "@envoy//bazel/external:quiche_stream_fix.patch", + ], + patch_args = ["-p1"], build_file = "@envoy//bazel/external:quiche.BUILD", ) native.bind( diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index ddf8a30a8a96..a675c338594c 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -1192,12 +1192,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "QUICHE", project_desc = "QUICHE (QUIC, HTTP/2, Etc) is Google‘s implementation of QUIC and related protocols", project_url = "https://github.com/google/quiche", - version = "cea6f57f9ce03a5aa2bb0e0d8adcdf3ab452c0c3", - sha256 = "d0187c4c3c74a709727549b020ef90471113d70047dff7d8fd9f2bfd37a6da5b", + version = "c5d1ed730f6062c6647aebe130d78f088028e52f", + sha256 = "d4d61c3189d989d7e51323823ce6f9de6970a09803624e80283e9f8135c9fe4b", urls = ["https://github.com/google/quiche/archive/{version}.tar.gz"], strip_prefix = "quiche-{version}", use_category = ["controlplane", "dataplane_core"], - release_date = "2024-05-29", + release_date = "2024-06-04", cpe = "N/A", license = "BSD-3-Clause", license_url = "https://github.com/google/quiche/blob/{version}/LICENSE", diff --git a/changelogs/1.27.6.yaml b/changelogs/1.27.6.yaml new file mode 100644 index 000000000000..cc73ba5da9a9 --- /dev/null +++ b/changelogs/1.27.6.yaml @@ -0,0 +1,33 @@ +date: June 4, 2024 + +bug_fixes: +- area: router + change: | + Fix a timing issue when upstream requests are empty when decoding data and send local reply when that happends. This is + controlled by ``envoy_reloadable_features_send_local_reply_when_no_buffer_and_upstream_request``. +- area: quic + change: | + Applied 2 QUICHE patches for crash bugs in ``QuicSpdyStream`` ``OnDataAvailable()`` and ``OnInitialHeaderComplete()``. +- area: quic + change: | + Fixed crash bug when QUIC downstream stream was read closed and then timed out. +- area: decompression + change: | + Fixed a bug where Envoy will go into an endless loop when using the brotli decompressor. If the input stream has + redundant data, the decompressor will loop forever. +- area: websocket + change: | + Only 101 is considered a successful response for websocket handshake for HTTP/1.1, and Envoy as a proxy will proxy the response + header from upstream to downstream and then close the request if other status is received. This behavior can be + reverted by ``envoy_reloadable_features_check_switch_protocol_websocket_handshake``. +- area: async http client + change: | + Added one option to disable the response body buffering for mirror request. Also introduced a 32MB cap for the response + buffer, which can be changed by the runtime flag ``http.async_response_buffer_limit`` based on the product needs. + +removed_config_or_runtime: +# *Normally occurs at the end of the* :ref:`deprecation period ` + +new_features: + +deprecated: diff --git a/changelogs/1.28.4.yaml b/changelogs/1.28.4.yaml new file mode 100644 index 000000000000..d4f34e447097 --- /dev/null +++ b/changelogs/1.28.4.yaml @@ -0,0 +1,26 @@ +date: June 5, 2024 + +bug_fixes: +- area: router + change: | + Fix a timing issue when upstream requests are empty when decoding data and send local reply when that happends. This is + controlled by ``envoy_reloadable_features_send_local_reply_when_no_buffer_and_upstream_request``. +- area: quic + change: | + Applied 2 QUICHE patches for crash bugs in ``QuicSpdyStream`` ``OnDataAvailable()`` and ``OnInitialHeaderComplete()``. +- area: quic + change: | + Fixed crash bug when QUIC downstream stream was read closed and then timed out. +- area: decompression + change: | + Fixed a bug where Envoy will go into an endless loop when using the brotli decompressor. If the input stream has + redundant data, the decompressor will loop forever. +- area: websocket + change: | + Only 101 is considered a successful response for websocket handshake for HTTP/1.1, and Envoy as a proxy will proxy the response + header from upstream to downstream and then close the request if other status is received. This behavior can be + reverted by ``envoy_reloadable_features_check_switch_protocol_websocket_handshake``. +- area: async http client + change: | + Added one option to disable the response body buffering for mirror request. Also introduced a 32MB cap for the response + buffer, which can be changed by the runtime flag ``http.async_response_buffer_limit`` based on the product needs. diff --git a/changelogs/1.29.5.yaml b/changelogs/1.29.5.yaml new file mode 100644 index 000000000000..d4f34e447097 --- /dev/null +++ b/changelogs/1.29.5.yaml @@ -0,0 +1,26 @@ +date: June 5, 2024 + +bug_fixes: +- area: router + change: | + Fix a timing issue when upstream requests are empty when decoding data and send local reply when that happends. This is + controlled by ``envoy_reloadable_features_send_local_reply_when_no_buffer_and_upstream_request``. +- area: quic + change: | + Applied 2 QUICHE patches for crash bugs in ``QuicSpdyStream`` ``OnDataAvailable()`` and ``OnInitialHeaderComplete()``. +- area: quic + change: | + Fixed crash bug when QUIC downstream stream was read closed and then timed out. +- area: decompression + change: | + Fixed a bug where Envoy will go into an endless loop when using the brotli decompressor. If the input stream has + redundant data, the decompressor will loop forever. +- area: websocket + change: | + Only 101 is considered a successful response for websocket handshake for HTTP/1.1, and Envoy as a proxy will proxy the response + header from upstream to downstream and then close the request if other status is received. This behavior can be + reverted by ``envoy_reloadable_features_check_switch_protocol_websocket_handshake``. +- area: async http client + change: | + Added one option to disable the response body buffering for mirror request. Also introduced a 32MB cap for the response + buffer, which can be changed by the runtime flag ``http.async_response_buffer_limit`` based on the product needs. diff --git a/changelogs/1.30.2.yaml b/changelogs/1.30.2.yaml new file mode 100644 index 000000000000..2e4316c5184c --- /dev/null +++ b/changelogs/1.30.2.yaml @@ -0,0 +1,23 @@ +date: June 5, 2024 + +bug_fixes: +# *Changes expected to improve the state of the world and are unlikely to have negative effects* +- area: quic + change: | + Applied 2 QUICHE patches for crash bugs in ``QuicSpdyStream`` ``OnDataAvailable()`` and ``OnInitialHeaderComplete()``. +- area: quic + change: | + Fixed crash bug when QUIC downstream stream was read closed and then timed out. +- area: decompression + change: | + Fixed a bug where Envoy will go into an endless loop when using the brotli decompressor. If the input stream has + redundant data, the decompressor will loop forever. +- area: websocket + change: | + Only 101 is considered a successful response for websocket handshake for HTTP/1.1, and Envoy as a proxy will proxy the response + header from upstream to downstream and then close the request if other status is received. This behavior can be + reverted by ``envoy_reloadable_features_check_switch_protocol_websocket_handshake``. +- area: async http client + change: | + Added one option to disable the response body buffering for mirror request. Also introduced a 32MB cap for the response + buffer, which can be changed by the runtime flag ``http.async_response_buffer_limit`` based on the product needs. diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 75038cddb542..3c3e6ba20947 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -82,6 +82,9 @@ minor_behavior_changes: - area: filters change: | Set ``WWW-Authenticate`` header for 401 responses from the Basic Auth filter. +- area: http + change: | + Removed runtime guard ``envoy.reloadable_features.refresh_rtt_after_request`` and legacy code path. bug_fixes: # *Changes expected to improve the state of the world and are unlikely to have negative effects* @@ -96,10 +99,19 @@ bug_fixes: Fixed :ref:`successful_active_health_check_uneject_host `. Before, a failed health check could uneject the host if the ``FAILED_ACTIVE_HC`` health flag had not been set. +- area: quic + change: | + Applied 2 QUICHE patches for crash bugs in ``QuicSpdyStream`` ``OnDataAvailable()`` and ``OnInitialHeaderComplete()``. +- area: quic + change: | + Fixed crash bug when QUIC downstream stream was read closed and then timed out. - area: tls change: | Fix a RELEASE_ASSERT when using :ref:`auto_sni ` if the downstream request ``:authority`` was longer than 255 characters. +- area: tracing + change: | + Fix an issue where span id is missing from opentelemetry access log entries. - area: ext_authz change: | Added field @@ -136,6 +148,19 @@ bug_fixes: Set the SNI value from the requested server name if it isn't available on the connection/socket. This applies when ``include_tls_session`` is true. The requested server name is set on a connection when filters such as the TLS inspector are used. +- area: decompression + change: | + Fixed a bug where Envoy will go into an endless loop when using the brotli decompressor. If the input stream has + redundant data, the decompressor will loop forever. +- area: websocket + change: | + Only 101 is considered a successful response for websocket handshake for HTTP/1.1, and Envoy as a proxy will proxy the response + header from upstream to downstream and then close the request if other status is received. This behavior can be + reverted by ``envoy_reloadable_features_check_switch_protocol_websocket_handshake``. +- area: async http client + change: | + Added one option to disable the response body buffering for mirror request. Also introduced a 32MB cap for the response + buffer, which can be changed by the runtime flag ``http.async_response_buffer_limit`` based on the product needs. removed_config_or_runtime: # *Normally occurs at the end of the* :ref:`deprecation period ` @@ -200,6 +225,11 @@ new_features: - area: redis change: | Added support for `inline commands `_. +- area: proxy_protocol + change: | + Added field :ref:`stat_prefix ` + to the proxy protocol listener filter configuration, allowing for differentiating statistics when multiple proxy + protocol listener filters are configured. - area: aws_lambda change: | The ``aws_lambda`` filter now supports the @@ -216,6 +246,12 @@ new_features: change: | Added support to healthcheck with ProxyProtocol in TCP Healthcheck by setting :ref:`health_check_config `. +- area: ext_authz + change: | + added + :ref:`decoder_header_mutation_rules ` + which allows you to configure what decoder header mutations are allowed from the ext_authz + service as well as whether to fail the downstream request if disallowed mutations are requested. - area: access_log change: | added new ``access_log`` command operators to retrieve upstream connection information change: ``%UPSTREAM_PEER_URI_SAN%``, @@ -226,6 +262,17 @@ new_features: added :ref:`stat_prefix ` configuration to support additional stat prefix for the OpenTelemetry logger. +- area: open_telemetry + change: | + added :ref:`formatters + ` + configuration to support extension formatter for the OpenTelemetry logger. +- area: routing + change: | + added support in :ref:`file datasource ` implementation + to listen to file changes and dynamically update the response when :ref:`watched_directory + ` + is configured in :ref:`DataSource `. - area: listener change: | Added :ref:`bypass_overload_manager ` diff --git a/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.h b/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.h index 2480bd98282c..c9b4da229373 100644 --- a/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.h +++ b/contrib/generic_proxy/filters/network/source/codecs/dubbo/config.h @@ -165,17 +165,25 @@ class DubboDecoderBase : public DubboCodecBase, public CodecType { } } - void encode(const StreamFrame& frame, EncodingCallbacks& callbacks) override { + EncodingResult encode(const StreamFrame& frame, EncodingContext&) override { ASSERT(dynamic_cast(&frame) != nullptr); const auto* typed_message = static_cast(&frame); - Buffer::OwnedImpl buffer; - codec_->encode(buffer, *typed_message->inner_metadata_); - callbacks.onEncodingSuccess(buffer, true); + codec_->encode(encoding_buffer_, *typed_message->inner_metadata_); + const uint64_t encoded_size = encoding_buffer_.length(); + + // Write the encoded data to the connection and clean the buffer for the next encoding. + callback_->writeToConnection(encoding_buffer_); + encoding_buffer_.drain(encoding_buffer_.length()); + + return encoded_size; } Common::Dubbo::MessageMetadataSharedPtr metadata_; CallBackType* callback_{}; + +private: + Buffer::OwnedImpl encoding_buffer_; }; class DubboServerCodec diff --git a/contrib/generic_proxy/filters/network/source/codecs/http1/config.cc b/contrib/generic_proxy/filters/network/source/codecs/http1/config.cc index 61761c07b787..c7e61180387e 100644 --- a/contrib/generic_proxy/filters/network/source/codecs/http1/config.cc +++ b/contrib/generic_proxy/filters/network/source/codecs/http1/config.cc @@ -434,12 +434,12 @@ Http::Http1::CallbackResult Http1ServerCodec::onMessageCompleteImpl() { return Http::Http1::CallbackResult::Success; } -void Http1ServerCodec::encode(const StreamFrame& frame, EncodingCallbacks& callbacks) { +EncodingResult Http1ServerCodec::encode(const StreamFrame& frame, EncodingContext&) { const bool response_end_stream = frame.frameFlags().endStream(); if (auto* headers = dynamic_cast(&frame); headers != nullptr) { - ENVOY_LOG(debug, "encoding response headers (end_stream={}):\n{}", response_end_stream, - *headers->response_); + ENVOY_LOG(debug, "Generic proxy HTTP1 codec: encoding response headers (end_stream={}):\n{}", + response_end_stream, *headers->response_); active_request_->response_chunk_encoding_ = Utility::isChunked(*headers->response_, response_end_stream); @@ -449,8 +449,7 @@ void Http1ServerCodec::encode(const StreamFrame& frame, EncodingCallbacks& callb if (!status.ok()) { ENVOY_LOG(error, "Generic proxy HTTP1 codec: failed to encode response headers: {}", status.message()); - callbacks_->connection()->close(Network::ConnectionCloseType::FlushWrite); - return; + return status; } // Encode the optional buffer if it exists. This is used for local response or for the @@ -462,27 +461,31 @@ void Http1ServerCodec::encode(const StreamFrame& frame, EncodingCallbacks& callb } } else if (auto* body = dynamic_cast(&frame); body != nullptr) { - ENVOY_LOG(debug, "encoding response body (end_stream={} size={})", response_end_stream, - body->buffer().length()); + ENVOY_LOG(debug, "Generic proxy HTTP1 codec: encoding response body (end_stream={} size={})", + response_end_stream, body->buffer().length()); Utility::encodeBody(encoding_buffer_, body->buffer(), active_request_->response_chunk_encoding_, response_end_stream); } - callbacks.onEncodingSuccess(encoding_buffer_, response_end_stream); + const uint64_t encoded_size = encoding_buffer_.length(); + + // Send the encoded data to the connection and reset the buffer for the next frame. + callbacks_->writeToConnection(encoding_buffer_); + ASSERT(encoding_buffer_.length() == 0); if (response_end_stream) { if (active_request_->request_complete_) { active_request_.reset(); - return; + return encoded_size; } ENVOY_LOG(debug, "Generic proxy HTTP1 server codec: response complete before request complete"); - if (callbacks_->connection().has_value()) { - callbacks_->connection()->close(Network::ConnectionCloseType::FlushWrite); - } + return absl::InvalidArgumentError("response complete before request complete"); } + + return encoded_size; } -void Http1ClientCodec::encode(const StreamFrame& frame, EncodingCallbacks& callbacks) { +EncodingResult Http1ClientCodec::encode(const StreamFrame& frame, EncodingContext&) { const bool request_end_stream = frame.frameFlags().endStream(); if (auto* headers = dynamic_cast(&frame); headers != nullptr) { @@ -501,8 +504,7 @@ void Http1ClientCodec::encode(const StreamFrame& frame, EncodingCallbacks& callb if (!status.ok()) { ENVOY_LOG(error, "Generic proxy HTTP1 codec: failed to encode request headers: {}", status.message()); - callbacks_->connection()->close(Network::ConnectionCloseType::FlushWrite); - return; + return status; } // Encode the optional buffer if it exists. This is used for local response or for the @@ -524,7 +526,13 @@ void Http1ClientCodec::encode(const StreamFrame& frame, EncodingCallbacks& callb expect_response_->request_complete_ = true; } - callbacks.onEncodingSuccess(encoding_buffer_, request_end_stream); + const uint64_t encoded_size = encoding_buffer_.length(); + + // Send the encoded data to the connection and reset the buffer for the next frame. + callbacks_->writeToConnection(encoding_buffer_); + ASSERT(encoding_buffer_.length() == 0); + + return encoded_size; } Http::Http1::CallbackResult Http1ClientCodec::onMessageBeginImpl() { diff --git a/contrib/generic_proxy/filters/network/source/codecs/http1/config.h b/contrib/generic_proxy/filters/network/source/codecs/http1/config.h index 92838bd0897e..253665c62926 100644 --- a/contrib/generic_proxy/filters/network/source/codecs/http1/config.h +++ b/contrib/generic_proxy/filters/network/source/codecs/http1/config.h @@ -291,7 +291,7 @@ class Http1ServerCodec : public Http1CodecBase, public ServerCodec { callbacks_->onDecodingFailure(); } } - void encode(const StreamFrame& frame, EncodingCallbacks& callbacks) override; + EncodingResult encode(const StreamFrame& frame, EncodingContext& ctx) override; ResponsePtr respond(absl::Status status, absl::string_view data, const Request&) override { auto response = Http::ResponseHeaderMapImpl::create(); response->setStatus(std::to_string(Utility::statusToHttpStatus(status.code()))); @@ -352,7 +352,7 @@ class Http1ClientCodec : public Http1CodecBase, public ClientCodec { callbacks_->onDecodingFailure(); } } - void encode(const StreamFrame& frame, EncodingCallbacks& callbacks) override; + EncodingResult encode(const StreamFrame& frame, EncodingContext& ctx) override; void onDecodingSuccess(RequestHeaderFramePtr, absl::optional) override {} void onDecodingSuccess(ResponseHeaderFramePtr response_header_frame, diff --git a/contrib/generic_proxy/filters/network/source/codecs/kafka/config.cc b/contrib/generic_proxy/filters/network/source/codecs/kafka/config.cc index 5cf98e2f6935..35c6d9572311 100644 --- a/contrib/generic_proxy/filters/network/source/codecs/kafka/config.cc +++ b/contrib/generic_proxy/filters/network/source/codecs/kafka/config.cc @@ -22,23 +22,28 @@ void KafkaServerCodec::decode(Envoy::Buffer::Instance& buffer, bool) { request_buffer_.drain(request_buffer_.length()); } -void KafkaServerCodec::encode(const GenericProxy::StreamFrame& frame, - GenericProxy::EncodingCallbacks& callbacks) { +GenericProxy::EncodingResult KafkaServerCodec::encode(const GenericProxy::StreamFrame& frame, + GenericProxy::EncodingContext&) { auto* typed_response = dynamic_cast(&frame); if (typed_response == nullptr) { ENVOY_LOG(error, "Kafka codec: invalid response frame type and cannot encode"); - return; + return absl::InvalidArgumentError("Invalid response frame type"); } if (typed_response->response_ != nullptr) { response_encoder_.encode(*typed_response->response_); } else { - ENVOY_LOG(error, "Kafka codec: invalid empty response frame type and close connection"); - request_callbacks_->callbacks_.connection()->close(Network::ConnectionCloseType::FlushWrite); - return; + ENVOY_LOG(error, "Kafka codec: invalid empty response frame"); + return absl::InvalidArgumentError("Invalid empty response frame"); } - callbacks.onEncodingSuccess(response_buffer_, true); + + const uint64_t encoded_size = response_buffer_.length(); + + // Write the encoded data to the connection and clean the buffer for the next encoding. + request_callbacks_->callbacks_.writeToConnection(response_buffer_); // All data should be consumed by the generic proxy and send to the network. ASSERT(response_buffer_.length() == 0); + + return encoded_size; } GenericProxy::ResponsePtr KafkaServerCodec::respond(absl::Status, absl::string_view, const GenericProxy::Request&) { @@ -60,20 +65,26 @@ void KafkaClientCodec::decode(Envoy::Buffer::Instance& buffer, bool) { response_buffer_.drain(response_buffer_.length()); } -void KafkaClientCodec::encode(const GenericProxy::StreamFrame& frame, - GenericProxy::EncodingCallbacks& callbacks) { +GenericProxy::EncodingResult KafkaClientCodec::encode(const GenericProxy::StreamFrame& frame, + GenericProxy::EncodingContext&) { auto* typed_request = dynamic_cast(&frame); if (typed_request == nullptr) { ENVOY_LOG(error, "Kafka codec: invalid request frame type and cannot encode"); - return; + return absl::InvalidArgumentError("Invalid request frame type"); } response_decoder_->expectResponse(typed_request->request_->request_header_.correlation_id_, typed_request->request_->request_header_.api_key_, typed_request->request_->request_header_.api_version_); request_encoder_.encode(*typed_request->request_); - callbacks.onEncodingSuccess(request_buffer_, true); + + const uint64_t encoded_size = request_buffer_.length(); + + // Write the encoded data to the connection and clean the buffer for the next encoding. + response_callbacks_->callbacks_.writeToConnection(request_buffer_); // All data should be consumed by the generic proxy and send to the network. ASSERT(request_buffer_.length() == 0); + + return encoded_size; } CodecFactoryPtr diff --git a/contrib/generic_proxy/filters/network/source/codecs/kafka/config.h b/contrib/generic_proxy/filters/network/source/codecs/kafka/config.h index d694ac53ce00..1c8e72aa6d76 100644 --- a/contrib/generic_proxy/filters/network/source/codecs/kafka/config.h +++ b/contrib/generic_proxy/filters/network/source/codecs/kafka/config.h @@ -91,7 +91,6 @@ class KafkaResponseCallbacks : public NetworkFilters::Kafka::ResponseCallback, callbacks_.onDecodingFailure(); } -private: GenericProxy::ClientCodecCallbacks& callbacks_; }; @@ -102,8 +101,8 @@ class KafkaServerCodec : public GenericProxy::ServerCodec, void setCodecCallbacks(GenericProxy::ServerCodecCallbacks& callbacks) override; void decode(Envoy::Buffer::Instance& buffer, bool end_stream) override; - void encode(const GenericProxy::StreamFrame& frame, - GenericProxy::EncodingCallbacks& callbacks) override; + GenericProxy::EncodingResult encode(const GenericProxy::StreamFrame& frame, + GenericProxy::EncodingContext& ctx) override; GenericProxy::ResponsePtr respond(absl::Status, absl::string_view, const GenericProxy::Request&) override; @@ -123,8 +122,8 @@ class KafkaClientCodec : public GenericProxy::ClientCodec, void setCodecCallbacks(GenericProxy::ClientCodecCallbacks& callbacks) override; void decode(Envoy::Buffer::Instance& buffer, bool end_stream) override; - void encode(const GenericProxy::StreamFrame& frame, - GenericProxy::EncodingCallbacks& callbacks) override; + GenericProxy::EncodingResult encode(const GenericProxy::StreamFrame& frame, + GenericProxy::EncodingContext& ctx) override; Envoy::Buffer::OwnedImpl request_buffer_; Envoy::Buffer::OwnedImpl response_buffer_; diff --git a/contrib/generic_proxy/filters/network/source/interface/codec.h b/contrib/generic_proxy/filters/network/source/interface/codec.h index eedb24862213..e9ca99af5674 100644 --- a/contrib/generic_proxy/filters/network/source/interface/codec.h +++ b/contrib/generic_proxy/filters/network/source/interface/codec.h @@ -23,8 +23,8 @@ class ServerCodec { /** * Set callbacks of server codec. - * @param callbacks callbacks of server codec. This callback will have same lifetime - * as the server codec. + * @param callbacks callbacks of server codec. This callback will have same or longer + * lifetime as the server codec. */ virtual void setCodecCallbacks(ServerCodecCallbacks& callbacks) PURE; @@ -36,20 +36,26 @@ class ServerCodec { virtual void decode(Buffer::Instance& buffer, bool end_stream) PURE; /** - * Encode response frame. - * @param frame response frame to encode. - * @param callbacks callbacks of encoding. This callback should be used to notify the - * generic proxy filter that the response is encoded and should be called only once. + * Encode response frame and send it to upstream connection by the writeToConnection() + * method of the codec callbacks. + * @param frame response frame to encode. NOTE: the generic proxy will assume this is + * sync encoding and the frame may be destroyed after this method is called. + * @param ctx context of encoding that will be used to provide additional information + * to the codec. Like the route that the downstream request is matched to. + * @return the size of the encoded data or error message if encoding failed. */ - virtual void encode(const StreamFrame& frame, EncodingCallbacks& callbacks) PURE; + virtual EncodingResult encode(const StreamFrame& frame, EncodingContext& ctx) PURE; /** * Create a response frame with specified status and flags. * @param status status of the response. * @param data any data that generic proxy filter wants to tell the codec. * @param request origin request that the response is created for. + * @return ResponseHeaderFramePtr the response frame. Only single frame is allowed for + * local response. */ - virtual ResponsePtr respond(Status status, absl::string_view data, const Request& request) PURE; + virtual ResponseHeaderFramePtr respond(Status status, absl::string_view data, + const RequestHeaderFrame& request) PURE; }; /** @@ -75,12 +81,15 @@ class ClientCodec { virtual void decode(Buffer::Instance& buffer, bool end_stream) PURE; /** - * Encode request frame. - * @param frame request frame to encode. - * @param callbacks callbacks of encoding. This callbacks should be used to notify the - * generic proxy filter that the request is encoded and should be called only once. + * Encode request frame and send it to upstream connection by the writeToConnection() + * method of the codec callbacks. + * @param frame request frame to encode. NOTE: the generic proxy will assume this is + * sync encoding and the frame may be destroyed after this method is called. + * @param ctx context of encoding that will be used to provide additional information + * to the codec. Like the route that the request is matched to. + * @return the size of the encoded data or error message if encoding failed. */ - virtual void encode(const StreamFrame& frame, EncodingCallbacks& callbacks) PURE; + virtual EncodingResult encode(const StreamFrame& frame, EncodingContext& ctx) PURE; }; using ServerCodecPtr = std::unique_ptr; diff --git a/contrib/generic_proxy/filters/network/source/interface/codec_callbacks.h b/contrib/generic_proxy/filters/network/source/interface/codec_callbacks.h index 1cd87e432648..367cb8783e4b 100644 --- a/contrib/generic_proxy/filters/network/source/interface/codec_callbacks.h +++ b/contrib/generic_proxy/filters/network/source/interface/codec_callbacks.h @@ -149,25 +149,11 @@ class ClientCodecCallbacks { }; /** - * Callback of request/response frame. + * Context of stream that will be used to encode request/response frame. */ -class EncodingCallbacks { +class EncodingContext { public: - virtual ~EncodingCallbacks() = default; - - /** - * If encoding success then this method will be called to write the data to upstream - * or downstream connection and notify the generic proxy if the encoding is completed. - * @param buffer encoding result buffer. - * @param end_stream if last frame is encoded. - */ - virtual void onEncodingSuccess(Buffer::Instance& buffer, bool end_stream) PURE; - - /** - * If encoding failure then this method will be called. - * @param reason the reason of encoding failure. - */ - virtual void onEncodingFailure(absl::string_view reason = {}) PURE; + virtual ~EncodingContext() = default; /** * The route that the request is matched to. This is optional when encoding the response @@ -179,6 +165,8 @@ class EncodingCallbacks { virtual OptRef routeEntry() const PURE; }; +using EncodingResult = absl::StatusOr; + } // namespace GenericProxy } // namespace NetworkFilters } // namespace Extensions diff --git a/contrib/generic_proxy/filters/network/source/interface/filter.h b/contrib/generic_proxy/filters/network/source/interface/filter.h index ec2c3b042a75..bb651594e7a3 100644 --- a/contrib/generic_proxy/filters/network/source/interface/filter.h +++ b/contrib/generic_proxy/filters/network/source/interface/filter.h @@ -104,13 +104,13 @@ class DecoderFilterCallback : public virtual StreamFilterCallbacks { * Called when the upstream response frame is received. This should only be called once. * @param frame supplies the upstream response frame. */ - virtual void onResponseStart(ResponseHeaderFramePtr frame) PURE; + virtual void onResponseHeaderFrame(ResponseHeaderFramePtr frame) PURE; /** * Called when the upstream response frame is received. * @param frame supplies the upstream frame. */ - virtual void onResponseFrame(ResponseCommonFramePtr frame) PURE; + virtual void onResponseCommonFrame(ResponseCommonFramePtr frame) PURE; /** * Register a request frames handler to used to handle the request frames (except the special diff --git a/contrib/generic_proxy/filters/network/source/interface/stream.h b/contrib/generic_proxy/filters/network/source/interface/stream.h index 4c7100257e72..8ca9e12e2c6d 100644 --- a/contrib/generic_proxy/filters/network/source/interface/stream.h +++ b/contrib/generic_proxy/filters/network/source/interface/stream.h @@ -132,9 +132,21 @@ class StreamFrame { virtual FrameFlags frameFlags() const { return {}; } }; +/** + * Common frame that used to represent any data or structure of L7 protocols. No specific + * interface is provided for the common frame. + */ class CommonFrame : public StreamFrame {}; using CommonFramePtr = std::unique_ptr; +/** + * Header frame of generic request or response. This provide some basic interfaces that are + * used to get/set attributes of the request or response. + * NOTE: Header frame should always be the first frame of the request or response. And there + * has no requirement that the header frame could only contain the 'header' of L7 protocols. + * For example, for short HTTP request, the header frame could contain the whole request + * header map, body, and even trailer. + */ class HeaderFrame : public StreamFrame { public: using IterateCallback = std::function; @@ -183,10 +195,6 @@ using StreamBase = HeaderFrame; /** * Interface of generic request. This is derived from StreamFrame that contains the request * specific information. First frame of the request MUST be a RequestHeaderFrame. - * - * NOTE: using interface that provided by the TraceContext as the interface of generic request - * here to simplify the tracing integration. This is not a good design. This should be changed - * in the future. */ class RequestHeaderFrame : public HeaderFrame { public: diff --git a/contrib/generic_proxy/filters/network/source/proxy.cc b/contrib/generic_proxy/filters/network/source/proxy.cc index eeca48c4a028..6b17f371cfab 100644 --- a/contrib/generic_proxy/filters/network/source/proxy.cc +++ b/contrib/generic_proxy/filters/network/source/proxy.cc @@ -128,15 +128,13 @@ void ActiveStream::resetStream(DownstreamStreamResetReason reason) { completeRequest(); } -void ActiveStream::sendResponseStartToDownstream() { +void ActiveStream::sendHeaderFrameToDownstream() { ASSERT(response_stream_ != nullptr); response_filter_chain_complete_ = true; - // The first frame of response is sent. - stream_info_.downstreamTiming().onFirstDownstreamTxByteSent(parent_.time_source_); - parent_.sendFrameToDownstream(*response_stream_, *this); + sendFrameToDownstream(*response_stream_, true); } -void ActiveStream::sendResponseFrameToDownstream() { +void ActiveStream::sendCommonFrameToDownstream() { if (!response_filter_chain_complete_) { // Wait for the response header frame to be sent first. It may be blocked by // the filter chain. @@ -149,10 +147,41 @@ void ActiveStream::sendResponseFrameToDownstream() { response_stream_frames_.pop_front(); // Send the frame to downstream. - parent_.sendFrameToDownstream(*frame, *this); + if (!sendFrameToDownstream(*frame, false)) { + break; + } } } +bool ActiveStream::sendFrameToDownstream(const StreamFrame& frame, bool header_frame) { + const bool end_stream = frame.frameFlags().endStream(); + + const auto result = parent_.server_codec_->encode(frame, *this); + if (!result.ok()) { + ENVOY_LOG(error, "Generic proxy: response encoding failure: {}", result.status().message()); + resetStream(DownstreamStreamResetReason::ProtocolError); + return false; + } + + ENVOY_LOG(debug, "Generic proxy: send {} bytes to client, complete: {}", result.value(), + end_stream); + + if (header_frame) { + stream_info_.downstreamTiming().onFirstDownstreamTxByteSent(parent_.time_source_); + } + + // If the request is fully sent, record the last downstream tx byte sent time and clean + // up the stream. + if (end_stream) { + stream_info_.downstreamTiming().onLastDownstreamTxByteSent(parent_.time_source_); + + ASSERT(response_stream_end_); + ASSERT(response_stream_frames_.empty()); + completeRequest(); + } + return true; +} + void ActiveStream::sendRequestFrameToUpstream() { if (!request_filter_chain_complete_) { // Wait for the request header frame to be sent first. It may be blocked by @@ -196,7 +225,7 @@ void ActiveStream::sendLocalReply(Status status, absl::string_view data, // Set the response code to the stream info. stream_info_.setResponseCode(response_stream_->status().code()); - sendResponseStartToDownstream(); + sendHeaderFrameToDownstream(); } void ActiveStream::continueDecoding() { @@ -251,7 +280,7 @@ void ActiveStream::onRequestFrame(RequestCommonFramePtr request_common_frame) { sendRequestFrameToUpstream(); } -void ActiveStream::onResponseStart(ResponsePtr response) { +void ActiveStream::onResponseHeaderFrame(ResponsePtr response) { ASSERT(response_stream_ == nullptr); response_stream_ = std::move(response); ASSERT(response_stream_ != nullptr); @@ -265,11 +294,11 @@ void ActiveStream::onResponseStart(ResponsePtr response) { continueEncoding(); } -void ActiveStream::onResponseFrame(ResponseCommonFramePtr response_common_frame) { +void ActiveStream::onResponseCommonFrame(ResponseCommonFramePtr response_common_frame) { response_stream_end_ = response_common_frame->frameFlags().endStream(); response_stream_frames_.emplace_back(std::move(response_common_frame)); // Try to send the frame to downstream immediately. - sendResponseFrameToDownstream(); + sendCommonFrameToDownstream(); } void ActiveStream::completeDirectly() { @@ -298,33 +327,9 @@ void ActiveStream::continueEncoding() { if (next_encoder_filter_index_ == encoder_filters_.size()) { ENVOY_LOG(debug, "Complete encoder filters"); - sendResponseStartToDownstream(); - sendResponseFrameToDownstream(); - } -} - -void ActiveStream::onEncodingSuccess(Buffer::Instance& buffer, bool end_stream) { - ASSERT(parent_.downstreamConnection().state() == Network::Connection::State::Open); - parent_.downstreamConnection().write(buffer, false); - - if (!end_stream) { - return; + sendHeaderFrameToDownstream(); + sendCommonFrameToDownstream(); } - - // The response is fully sent. - stream_info_.downstreamTiming().onLastDownstreamTxByteSent(parent_.time_source_); - - ENVOY_LOG(debug, "Generic proxy: downstream response complete"); - - ASSERT(response_stream_end_); - ASSERT(response_stream_frames_.empty()); - - completeRequest(); -} - -void ActiveStream::onEncodingFailure(absl::string_view reason) { - ENVOY_LOG(error, "Generic proxy: response encoding failure: {}", reason); - resetStream(DownstreamStreamResetReason::ProtocolError); } void ActiveStream::initializeFilterChain(FilterChainFactory& factory) { @@ -440,10 +445,6 @@ OptRef Filter::connection() { return {downstreamConnection()}; } -void Filter::sendFrameToDownstream(StreamFrame& frame, EncodingCallbacks& callbacks) { - server_codec_->encode(frame, callbacks); -} - void Filter::registerFrameHandler(uint64_t stream_id, ActiveStream* raw_stream) { // If the stream expects variable length frames, then add it to the frame // handler map. diff --git a/contrib/generic_proxy/filters/network/source/proxy.h b/contrib/generic_proxy/filters/network/source/proxy.h index 1f3c161fe9e5..f4e24df9ed45 100644 --- a/contrib/generic_proxy/filters/network/source/proxy.h +++ b/contrib/generic_proxy/filters/network/source/proxy.h @@ -127,7 +127,7 @@ class FilterConfigImpl : public FilterConfig { class ActiveStream : public FilterChainManager, public LinkedObject, public Envoy::Event::DeferredDeletable, - public EncodingCallbacks, + public EncodingContext, public Tracing::Config, Logger::Loggable { public: @@ -173,11 +173,11 @@ class ActiveStream : public FilterChainManager, parent_.sendLocalReply(status, data, std::move(func)); } void continueDecoding() override { parent_.continueDecoding(); } - void onResponseStart(ResponseHeaderFramePtr frame) override { - parent_.onResponseStart(std::move(frame)); + void onResponseHeaderFrame(ResponseHeaderFramePtr frame) override { + parent_.onResponseHeaderFrame(std::move(frame)); } - void onResponseFrame(ResponseCommonFramePtr frame) override { - parent_.onResponseFrame(std::move(frame)); + void onResponseCommonFrame(ResponseCommonFramePtr frame) override { + parent_.onResponseCommonFrame(std::move(frame)); } void setRequestFramesHandler(RequestFramesHandler& handler) override { ASSERT(parent_.request_stream_frames_handler_ == nullptr, @@ -249,8 +249,8 @@ class ActiveStream : public FilterChainManager, void sendLocalReply(Status status, absl::string_view data, ResponseUpdateFunction func); void continueDecoding(); - void onResponseStart(ResponseHeaderFramePtr response_header_frame); - void onResponseFrame(ResponseCommonFramePtr response_common_frame); + void onResponseHeaderFrame(ResponseHeaderFramePtr response_header_frame); + void onResponseCommonFrame(ResponseCommonFramePtr response_common_frame); void completeDirectly(); void continueEncoding(); @@ -261,9 +261,7 @@ class ActiveStream : public FilterChainManager, factory(callbacks); } - // ResponseEncoderCallback - void onEncodingSuccess(Buffer::Instance& buffer, bool end_stream) override; - void onEncodingFailure(absl::string_view reason = {}) override; + // EncodingContext OptRef routeEntry() const override { return makeOptRefFromPtr(cached_route_entry_.get()); } @@ -309,8 +307,9 @@ class ActiveStream : public FilterChainManager, void sendRequestFrameToUpstream(); - void sendResponseStartToDownstream(); - void sendResponseFrameToDownstream(); + void sendHeaderFrameToDownstream(); + void sendCommonFrameToDownstream(); + bool sendFrameToDownstream(const StreamFrame& frame, bool header_frame); bool active_stream_reset_{false}; @@ -395,8 +394,6 @@ class Filter : public Envoy::Network::ReadFilter, void onAboveWriteBufferHighWatermark() override {} void onBelowWriteBufferLowWatermark() override {} - void sendFrameToDownstream(StreamFrame& frame, EncodingCallbacks& callbacks); - Network::Connection& downstreamConnection() { ASSERT(callbacks_ != nullptr); return callbacks_->connection(); diff --git a/contrib/generic_proxy/filters/network/source/router/router.cc b/contrib/generic_proxy/filters/network/source/router/router.cc index 3850fb870737..238c2341208a 100644 --- a/contrib/generic_proxy/filters/network/source/router/router.cc +++ b/contrib/generic_proxy/filters/network/source/router/router.cc @@ -130,16 +130,12 @@ void UpstreamRequest::deferredDelete() { } } -void UpstreamRequest::sendRequestStartToUpstream() { +void UpstreamRequest::sendHeaderFrameToUpstream() { request_stream_header_sent_ = true; - ASSERT(generic_upstream_ != nullptr); - - // The first frame of request is sent. - upstream_info_->upstreamTiming().onFirstUpstreamTxByteSent(parent_.time_source_); - generic_upstream_->clientCodec().encode(*parent_.request_stream_, *this); + sendFrameToUpstream(*parent_.request_stream_, true); } -void UpstreamRequest::sendRequestFrameToUpstream() { +void UpstreamRequest::sendCommonFrameToUpstream() { if (!request_stream_header_sent_) { // Do not send request frame to upstream until the request header is sent. It may be blocked // by the upstream connecting. @@ -149,36 +145,43 @@ void UpstreamRequest::sendRequestFrameToUpstream() { while (!parent_.request_stream_frames_.empty()) { auto frame = std::move(parent_.request_stream_frames_.front()); parent_.request_stream_frames_.pop_front(); - - ASSERT(generic_upstream_ != nullptr); - generic_upstream_->clientCodec().encode(*frame, *this); + if (!sendFrameToUpstream(*frame, false)) { + break; + } } } -void UpstreamRequest::onEncodingSuccess(Buffer::Instance& buffer, bool end_stream) { - encodeBufferToUpstream(buffer); +bool UpstreamRequest::sendFrameToUpstream(const StreamFrame& frame, bool header_frame) { + ASSERT(generic_upstream_ != nullptr); + const bool end_stream = frame.frameFlags().endStream(); - if (!end_stream) { - return; + const auto result = generic_upstream_->clientCodec().encode(frame, *this); + if (!result.ok()) { + ENVOY_LOG(error, "Generic proxy: request encoding failure: {}", result.status().message()); + // The request encoding failure is treated as a protocol error. + resetStream(StreamResetReason::ProtocolError, result.status().message()); + return false; } - // The request is fully sent. - upstream_info_->upstreamTiming().onLastUpstreamTxByteSent(parent_.time_source_); - - // Request is complete. - ENVOY_LOG(debug, "upstream request encoding success"); + ENVOY_LOG(debug, "Generic proxy: send {} bytes to server, complete: {}", result.value(), + end_stream); - // Need not to wait for the upstream response and complete directly. - if (!expects_response_) { - clearStream(false); - parent_.completeDirectly(); - return; + if (header_frame) { + upstream_info_->upstreamTiming().onFirstUpstreamTxByteSent(parent_.time_source_); } -} -void UpstreamRequest::onEncodingFailure(absl::string_view reason) { - // The request encoding failure is treated as a protocol error. - resetStream(StreamResetReason::ProtocolError, reason); + // If the request is fully sent, record the last downstream tx byte sent time and clean + // up the stream. + if (end_stream) { + upstream_info_->upstreamTiming().onLastUpstreamTxByteSent(parent_.time_source_); + + // Oneway requests need not to wait for the upstream response and complete directly. + if (!expects_response_) { + clearStream(false); + parent_.completeDirectly(); + } + } + return true; } OptRef UpstreamRequest::routeEntry() const { @@ -215,8 +218,8 @@ void UpstreamRequest::onUpstreamSuccess() { parent_.callbacks_->activeSpan().injectContext(trace_context, upstream_context); } - sendRequestStartToUpstream(); - sendRequestFrameToUpstream(); + sendHeaderFrameToUpstream(); + sendCommonFrameToUpstream(); } void UpstreamRequest::onUpstreamResponseComplete(bool drain_close) { @@ -244,7 +247,7 @@ void UpstreamRequest::onDecodingSuccess(ResponseHeaderFramePtr response_header_f onUpstreamResponseComplete(response_header_frame->frameFlags().streamFlags().drainClose()); } - parent_.onResponseStart(std::move(response_header_frame)); + parent_.onResponseHeaderFrame(std::move(response_header_frame)); } void UpstreamRequest::onDecodingSuccess(ResponseCommonFramePtr response_common_frame) { @@ -258,7 +261,7 @@ void UpstreamRequest::onDecodingSuccess(ResponseCommonFramePtr response_common_f onUpstreamResponseComplete(response_common_frame->frameFlags().streamFlags().drainClose()); } - parent_.onResponseFrame(std::move(response_common_frame)); + parent_.onResponseCommonFrame(std::move(response_common_frame)); } void UpstreamRequest::onDecodingFailure(absl::string_view reason) { @@ -311,26 +314,18 @@ void UpstreamRequest::onUpstreamConnectionReady() { connecting_start_time_.value(), parent_.time_source_); } -void UpstreamRequest::encodeBufferToUpstream(Buffer::Instance& buffer) { - ENVOY_LOG(trace, "proxying {} bytes", buffer.length()); - - ASSERT(generic_upstream_ != nullptr); - ASSERT(generic_upstream_->upstreamConnection().has_value()); - generic_upstream_->upstreamConnection()->write(buffer, false); -} - -void RouterFilter::onResponseStart(ResponseHeaderFramePtr response_header_frame) { +void RouterFilter::onResponseHeaderFrame(ResponseHeaderFramePtr response_header_frame) { if (response_header_frame->frameFlags().endStream()) { onFilterComplete(); } - callbacks_->onResponseStart(std::move(response_header_frame)); + callbacks_->onResponseHeaderFrame(std::move(response_header_frame)); } -void RouterFilter::onResponseFrame(ResponseCommonFramePtr response_common_frame) { +void RouterFilter::onResponseCommonFrame(ResponseCommonFramePtr response_common_frame) { if (response_common_frame->frameFlags().endStream()) { onFilterComplete(); } - callbacks_->onResponseFrame(std::move(response_common_frame)); + callbacks_->onResponseCommonFrame(std::move(response_common_frame)); } void RouterFilter::completeDirectly() { @@ -468,7 +463,7 @@ void RouterFilter::onRequestCommonFrame(RequestCommonFramePtr frame) { return; } - upstream_requests_.front()->sendRequestFrameToUpstream(); + upstream_requests_.front()->sendCommonFrameToUpstream(); } FilterStatus RouterFilter::onStreamDecoded(StreamRequest& request) { diff --git a/contrib/generic_proxy/filters/network/source/router/router.h b/contrib/generic_proxy/filters/network/source/router/router.h index 3acee5fa862c..7bae2856615a 100644 --- a/contrib/generic_proxy/filters/network/source/router/router.h +++ b/contrib/generic_proxy/filters/network/source/router/router.h @@ -45,7 +45,7 @@ class RouterFilter; class UpstreamRequest : public UpstreamRequestCallbacks, public LinkedObject, - public EncodingCallbacks, + public EncodingContext, Logger::Loggable { public: UpstreamRequest(RouterFilter& parent, StreamFlags stream_flags, @@ -68,17 +68,16 @@ class UpstreamRequest : public UpstreamRequestCallbacks, void onDecodingSuccess(ResponseCommonFramePtr response_common_frame) override; void onDecodingFailure(absl::string_view reason) override; - // RequestEncoderCallback - void onEncodingSuccess(Buffer::Instance& buffer, bool end_stream) override; - void onEncodingFailure(absl::string_view reason) override; + // EncodingContext OptRef routeEntry() const override; void onUpstreamHostSelected(Upstream::HostDescriptionConstSharedPtr host); void onUpstreamConnectionReady(); void encodeBufferToUpstream(Buffer::Instance& buffer); - void sendRequestStartToUpstream(); - void sendRequestFrameToUpstream(); + void sendHeaderFrameToUpstream(); + void sendCommonFrameToUpstream(); + bool sendFrameToUpstream(const StreamFrame& frame, bool header_frame); void onUpstreamResponseComplete(bool drain_close); @@ -140,8 +139,8 @@ class RouterFilter : public DecoderFilter, } FilterStatus onStreamDecoded(StreamRequest& request) override; - void onResponseStart(ResponseHeaderFramePtr response_header_frame); - void onResponseFrame(ResponseCommonFramePtr response_common_frame); + void onResponseHeaderFrame(ResponseHeaderFramePtr response_header_frame); + void onResponseCommonFrame(ResponseCommonFramePtr response_common_frame); void completeDirectly(); void onUpstreamRequestReset(UpstreamRequest& upstream_request, StreamResetReason reason, diff --git a/contrib/generic_proxy/filters/network/test/codecs/dubbo/config_test.cc b/contrib/generic_proxy/filters/network/test/codecs/dubbo/config_test.cc index eb6f579e4d37..17792dc903ed 100644 --- a/contrib/generic_proxy/filters/network/test/codecs/dubbo/config_test.cc +++ b/contrib/generic_proxy/filters/network/test/codecs/dubbo/config_test.cc @@ -243,15 +243,15 @@ TEST(DubboServerCodecTest, DubboServerCodecTest) { // Encode response. { - MockEncodingCallbacks encoding_callbacks; + MockEncodingContext encoding_context; DubboRequest request(createDubboRequst(false)); DubboResponse response( createDubboResponse(request, ResponseStatus::Ok, RpcResponseType::ResponseWithValue)); EXPECT_CALL(*raw_serializer, serializeRpcResponse(_, _)); - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, _)); + EXPECT_CALL(callbacks, writeToConnection(_)); - server_codec.encode(response, encoding_callbacks); + EXPECT_TRUE(server_codec.encode(response, encoding_context).ok()); } { @@ -368,26 +368,26 @@ TEST(DubboClientCodecTest, DubboClientCodecTest) { // Encode normal request. { - MockEncodingCallbacks encoding_callbacks; + MockEncodingContext encoding_context; DubboRequest request(createDubboRequst(false)); EXPECT_CALL(*raw_serializer, serializeRpcRequest(_, _)); - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, _)); + EXPECT_CALL(callbacks, writeToConnection(_)); - client_codec.encode(request, encoding_callbacks); + EXPECT_TRUE(client_codec.encode(request, encoding_context).ok()); } // Encode one-way request. { - MockEncodingCallbacks encoding_callbacks; + MockEncodingContext encoding_context; DubboRequest request(createDubboRequst(true)); EXPECT_CALL(*raw_serializer, serializeRpcRequest(_, _)); - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, _)); + EXPECT_CALL(callbacks, writeToConnection(_)); - client_codec.encode(request, encoding_callbacks); + EXPECT_TRUE(client_codec.encode(request, encoding_context).ok()); } } diff --git a/contrib/generic_proxy/filters/network/test/codecs/http1/config_test.cc b/contrib/generic_proxy/filters/network/test/codecs/http1/config_test.cc index 14ee1d70ed6d..ee5416ddb45a 100644 --- a/contrib/generic_proxy/filters/network/test/codecs/http1/config_test.cc +++ b/contrib/generic_proxy/filters/network/test/codecs/http1/config_test.cc @@ -524,27 +524,53 @@ TEST_F(Http1ServerCodecTest, RespondTest) { } TEST_F(Http1ServerCodecTest, HeaderOnlyResponseEncodingTest) { + // Mock request. + codec_->active_request_ = ActiveRequest{nullptr, true}; + // Create a response. auto headers = Http::ResponseHeaderMapImpl::create(); headers->setStatus(200); HttpResponseFrame response(std::move(headers), true); - NiceMock encoding_callbacks; + NiceMock encoding_context; // Encode the response. { - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) - .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_CALL(codec_callbacks_, writeToConnection(_)) + .WillOnce(Invoke([](Buffer::Instance& buffer) { EXPECT_EQ(buffer.toString(), "HTTP/1.1 200 OK\r\n" "\r\n"); + buffer.drain(buffer.length()); })); - codec_->encode(response, encoding_callbacks); + EXPECT_TRUE(codec_->encode(response, encoding_context).ok()); + } +} + +TEST_F(Http1ServerCodecTest, MissingRequiredHeadersEncodingTest) { + // Mock request. + codec_->active_request_ = ActiveRequest{nullptr, true}; + + // Create a request without method. + auto headers = Http::ResponseHeaderMapImpl::create(); + + HttpResponseFrame response(std::move(headers), true); + + NiceMock encoding_context; + + // Encode the request. + { + auto status_or = codec_->encode(response, encoding_context); + EXPECT_FALSE(status_or.ok()); + EXPECT_EQ(status_or.status().message(), "missing required headers"); } } TEST_F(Http1ServerCodecTest, ResponseEncodingTest) { + // Mock request. + codec_->active_request_ = ActiveRequest{nullptr, true}; + // Create a response. auto headers = Http::ResponseHeaderMapImpl::create(); headers->setStatus(200); @@ -555,32 +581,34 @@ TEST_F(Http1ServerCodecTest, ResponseEncodingTest) { Buffer::OwnedImpl body_buffer("body"); HttpRawBodyFrame body(body_buffer, true); - NiceMock encoding_callbacks; + NiceMock encoding_context; // Encode the response. { - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, false)) - .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_CALL(codec_callbacks_, writeToConnection(_)) + .WillOnce(Invoke([](Buffer::Instance& buffer) { EXPECT_EQ(buffer.toString(), "HTTP/1.1 200 OK\r\n" "content-length: 4\r\n" "\r\n"); buffer.drain(buffer.length()); })); - codec_->encode(response, encoding_callbacks); + EXPECT_TRUE(codec_->encode(response, encoding_context).ok()); - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) - .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_CALL(codec_callbacks_, writeToConnection(_)) + .WillOnce(Invoke([](Buffer::Instance& buffer) { EXPECT_EQ(buffer.toString(), "body"); buffer.drain(buffer.length()); })); - - codec_->encode(body, encoding_callbacks); + EXPECT_TRUE(codec_->encode(body, encoding_context).ok()); } } TEST_F(Http1ServerCodecTest, ChunkedResponseEncodingTest) { + // Mock request. + codec_->active_request_ = ActiveRequest{nullptr, true}; + // Create a response. auto headers = Http::ResponseHeaderMapImpl::create(); headers->setStatus(200); @@ -591,22 +619,22 @@ TEST_F(Http1ServerCodecTest, ChunkedResponseEncodingTest) { Buffer::OwnedImpl body_buffer("body"); HttpRawBodyFrame body(body_buffer, true); - NiceMock encoding_callbacks; + NiceMock encoding_context; // Encode the response. { - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, false)) - .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_CALL(codec_callbacks_, writeToConnection(_)) + .WillOnce(Invoke([](Buffer::Instance& buffer) { EXPECT_EQ(buffer.toString(), "HTTP/1.1 200 OK\r\n" "transfer-encoding: chunked\r\n" "\r\n"); buffer.drain(buffer.length()); })); - codec_->encode(response, encoding_callbacks); + EXPECT_TRUE(codec_->encode(response, encoding_context).ok()); - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) - .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_CALL(codec_callbacks_, writeToConnection(_)) + .WillOnce(Invoke([](Buffer::Instance& buffer) { EXPECT_EQ(buffer.toString(), "4\r\n" // Chunk header. "body" // Chunk body. "\r\n" // Chunk footer. @@ -615,7 +643,7 @@ TEST_F(Http1ServerCodecTest, ChunkedResponseEncodingTest) { buffer.drain(buffer.length()); })); - codec_->encode(body, encoding_callbacks); + EXPECT_TRUE(codec_->encode(body, encoding_context).ok()); } } @@ -653,12 +681,12 @@ TEST_F(Http1ServerCodecTest, RequestAndResponseTest) { Buffer::OwnedImpl body_buffer("body"); HttpRawBodyFrame body(body_buffer, true); - NiceMock encoding_callbacks; - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, _)).Times(2); + NiceMock encoding_context; + EXPECT_CALL(codec_callbacks_, writeToConnection(_)).Times(2); // Encode the response. - codec_->encode(response, encoding_callbacks); - codec_->encode(body, encoding_callbacks); + EXPECT_TRUE(codec_->encode(response, encoding_context).ok()); + EXPECT_TRUE(codec_->encode(body, encoding_context).ok()); } } @@ -693,17 +721,17 @@ TEST_F(Http1ServerCodecTest, ResponseCompleteBeforeRequestCompleteTest) { Buffer::OwnedImpl body_buffer("body"); HttpRawBodyFrame body(body_buffer, true); - NiceMock encoding_callbacks; + NiceMock encoding_context; - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, false)); + EXPECT_CALL(codec_callbacks_, writeToConnection(_)); // Encode the response. - codec_->encode(response, encoding_callbacks); + EXPECT_TRUE(codec_->encode(response, encoding_context).ok()); - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)); - // Response is complete, but request is not complete, so the codec should close the connection. - EXPECT_CALL(mock_connection, close(Network::ConnectionCloseType::FlushWrite)); + EXPECT_CALL(codec_callbacks_, writeToConnection(_)); - codec_->encode(body, encoding_callbacks); + auto status_or = codec_->encode(body, encoding_context); + EXPECT_FALSE(status_or.ok()); + EXPECT_EQ(status_or.status().message(), "response complete before request complete"); } TEST_F(Http1ServerCodecTest, NewRequestBeforeFirstRequestCompleteTest) { @@ -888,19 +916,19 @@ TEST_F(Http1ServerCodecTest, SingleFrameModeResponseEncodingTest) { HttpResponseFrame response(std::move(headers), true); response.optionalBuffer().add("body"); - NiceMock encoding_callbacks; + NiceMock encoding_context; // Encode the response. { - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) - .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_CALL(codec_callbacks_, writeToConnection(_)) + .WillOnce(Invoke([](Buffer::Instance& buffer) { EXPECT_EQ(buffer.toString(), "HTTP/1.1 200 OK\r\n" "content-length: 4\r\n" "\r\n" "body"); buffer.drain(buffer.length()); })); - codec_->encode(response, encoding_callbacks); + EXPECT_TRUE(codec_->encode(response, encoding_context).ok()); } } @@ -929,12 +957,12 @@ class Http1ClientCodecTest : public testing::Test { Buffer::OwnedImpl body_buffer("body"); HttpRawBodyFrame body(body_buffer, true); - NiceMock encoding_callbacks; + NiceMock encoding_context; // Encode the request. { - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, false)) - .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_CALL(codec_callbacks_, writeToConnection(_)) + .WillOnce(Invoke([](Buffer::Instance& buffer) { EXPECT_EQ(buffer.toString(), "GET /path HTTP/1.1\r\n" "host: host\r\n" "content-length: 4\r\n" @@ -942,15 +970,15 @@ class Http1ClientCodecTest : public testing::Test { buffer.drain(buffer.length()); })); - codec_->encode(request, encoding_callbacks); + EXPECT_TRUE(codec_->encode(request, encoding_context).ok()); - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) - .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_CALL(codec_callbacks_, writeToConnection(_)) + .WillOnce(Invoke([](Buffer::Instance& buffer) { EXPECT_EQ(buffer.toString(), "body"); buffer.drain(buffer.length()); })); - codec_->encode(body, encoding_callbacks); + EXPECT_TRUE(codec_->encode(body, encoding_context).ok()); } } @@ -1171,19 +1199,41 @@ TEST_F(Http1ClientCodecTest, HeaderOnlyRequestEncodingTest) { HttpRequestFrame request(std::move(headers), true); - NiceMock encoding_callbacks; + NiceMock encoding_context; // Encode the request. { - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) - .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_CALL(codec_callbacks_, writeToConnection(_)) + .WillOnce(Invoke([](Buffer::Instance& buffer) { EXPECT_EQ(buffer.toString(), "GET /path HTTP/1.1\r\n" "host: host\r\n" "custom: value\r\n" "\r\n"); })); - codec_->encode(request, encoding_callbacks); + EXPECT_TRUE(codec_->encode(request, encoding_context).ok()); + } +} + +TEST_F(Http1ClientCodecTest, MissingRequiredHeadersEncodingTest) { + ON_CALL(codec_callbacks_, connection()) + .WillByDefault(testing::Return(makeOptRef(mock_connection))); + + // Create a request without method. + auto headers = Http::RequestHeaderMapImpl::create(); + headers->addCopy(Http::Headers::get().HostLegacy, "host"); + headers->addCopy(Http::Headers::get().Path, "/path"); + headers->addCopy(Http::LowerCaseString("custom"), "value"); + + HttpRequestFrame request(std::move(headers), true); + + NiceMock encoding_context; + + // Encode the request. + { + auto status_or = codec_->encode(request, encoding_context); + EXPECT_FALSE(status_or.ok()); + EXPECT_EQ(status_or.status().message(), "missing required headers"); } } @@ -1205,12 +1255,12 @@ TEST_F(Http1ClientCodecTest, ChunkedRequestEncodingTest) { Buffer::OwnedImpl body_buffer("body"); HttpRawBodyFrame body(body_buffer, true); - NiceMock encoding_callbacks; + NiceMock encoding_context; // Encode the request. { - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, false)) - .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_CALL(codec_callbacks_, writeToConnection(_)) + .WillOnce(Invoke([](Buffer::Instance& buffer) { EXPECT_EQ(buffer.toString(), "GET /path HTTP/1.1\r\n" "host: host\r\n" "transfer-encoding: chunked\r\n" @@ -1218,10 +1268,10 @@ TEST_F(Http1ClientCodecTest, ChunkedRequestEncodingTest) { buffer.drain(buffer.length()); })); - codec_->encode(request, encoding_callbacks); + EXPECT_TRUE(codec_->encode(request, encoding_context).ok()); - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) - .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_CALL(codec_callbacks_, writeToConnection(_)) + .WillOnce(Invoke([](Buffer::Instance& buffer) { EXPECT_EQ(buffer.toString(), "4\r\n" // Chunk header. "body" // Chunk body. "\r\n" // Chunk footer. @@ -1230,7 +1280,7 @@ TEST_F(Http1ClientCodecTest, ChunkedRequestEncodingTest) { buffer.drain(buffer.length()); })); - codec_->encode(body, encoding_callbacks); + EXPECT_TRUE(codec_->encode(body, encoding_context).ok()); } } @@ -1252,12 +1302,12 @@ TEST_F(Http1ClientCodecTest, RequestAndResponseTest) { Buffer::OwnedImpl body_buffer("body"); HttpRawBodyFrame body(body_buffer, true); - NiceMock encoding_callbacks; - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, _)).Times(2); + NiceMock encoding_context; + EXPECT_CALL(codec_callbacks_, writeToConnection(_)).Times(2); // Encode the request. - codec_->encode(request, encoding_callbacks); - codec_->encode(body, encoding_callbacks); + EXPECT_TRUE(codec_->encode(request, encoding_context).ok()); + EXPECT_TRUE(codec_->encode(body, encoding_context).ok()); Buffer::OwnedImpl buffer; @@ -1292,11 +1342,11 @@ TEST_F(Http1ClientCodecTest, ResponseCompleteBeforeRequestCompleteTest) { HttpRequestFrame request(std::move(headers), false); - NiceMock encoding_callbacks; - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, _)); + NiceMock encoding_context; + EXPECT_CALL(codec_callbacks_, writeToConnection(_)); // Encode the request. Only the headers are encoded and the body is not encoded. - codec_->encode(request, encoding_callbacks); + EXPECT_TRUE(codec_->encode(request, encoding_context).ok()); Buffer::OwnedImpl buffer; @@ -1336,19 +1386,19 @@ TEST_F(Http1ClientCodecTest, SingleFrameModeRequestEncodingTest) { HttpRequestFrame request(std::move(headers), true); request.optionalBuffer().add("body"); - NiceMock encoding_callbacks; + NiceMock encoding_context; // Encode the request. { - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) - .WillOnce(Invoke([](Buffer::Instance& buffer, bool) { + EXPECT_CALL(codec_callbacks_, writeToConnection(_)) + .WillOnce(Invoke([](Buffer::Instance& buffer) { EXPECT_EQ(buffer.toString(), "GET /path HTTP/1.1\r\n" "host: host\r\n" "content-length: 4\r\n" "\r\n" "body"); })); - codec_->encode(request, encoding_callbacks); + EXPECT_TRUE(codec_->encode(request, encoding_context).ok()); } } diff --git a/contrib/generic_proxy/filters/network/test/codecs/kafka/config_test.cc b/contrib/generic_proxy/filters/network/test/codecs/kafka/config_test.cc index c9ec702f133e..e2b9c80e53f0 100644 --- a/contrib/generic_proxy/filters/network/test/codecs/kafka/config_test.cc +++ b/contrib/generic_proxy/filters/network/test/codecs/kafka/config_test.cc @@ -144,7 +144,7 @@ TEST(KafkaCodecTest, KafkaServerCodecTest) { { // Test encode() method with non-response frame. - NiceMock encoding_callbacks; + NiceMock encoding_context; auto request = std::make_shared>( @@ -153,30 +153,27 @@ TEST(KafkaCodecTest, KafkaServerCodecTest) { NetworkFilters::Kafka::FetchRequest({}, {}, {}, {})); KafkaRequestFrame request_frame(request); - // Do nothing. - server_codec.encode(request_frame, encoding_callbacks); + auto status_or = server_codec.encode(request_frame, encoding_context); + EXPECT_FALSE(status_or.ok()); + EXPECT_EQ(status_or.status().message(), "Invalid response frame type"); } { // Test encode() method without actual response. - NiceMock encoding_callbacks; - NiceMock mock_connection; + NiceMock encoding_context; KafkaResponseFrame response_frame(nullptr); - // Expect close connection. - EXPECT_CALL(callbacks, connection()) - .WillOnce(testing::Return(makeOptRef(mock_connection))); - EXPECT_CALL(mock_connection, close(Network::ConnectionCloseType::FlushWrite)); - - server_codec.encode(response_frame, encoding_callbacks); + auto status_or = server_codec.encode(response_frame, encoding_context); + EXPECT_FALSE(status_or.ok()); + EXPECT_EQ(status_or.status().message(), "Invalid empty response frame"); } { // Test encode() method with response. - NiceMock encoding_callbacks; + NiceMock encoding_context; auto response = std::make_shared>( @@ -191,11 +188,12 @@ TEST(KafkaCodecTest, KafkaServerCodecTest) { dst_buffer.add(&size, sizeof(size)); // Encode data length. response->encode(dst_buffer); - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) - .WillOnce(testing::Invoke([&](Buffer::Instance& buffer, bool) { + EXPECT_CALL(callbacks, writeToConnection(_)) + .WillOnce(testing::Invoke([&](Buffer::Instance& buffer) { EXPECT_EQ(buffer.toString(), dst_buffer.toString()); + buffer.drain(buffer.length()); })); - server_codec.encode(response_frame, encoding_callbacks); + EXPECT_TRUE(server_codec.encode(response_frame, encoding_context).ok()); } } @@ -233,7 +231,7 @@ TEST(KafkaCodecTest, KafkaClientCodecTest) { { // Test encode() method with non-request frame. - NiceMock encoding_callbacks; + NiceMock encoding_context; auto response = std::make_shared>( @@ -242,14 +240,15 @@ TEST(KafkaCodecTest, KafkaClientCodecTest) { NetworkFilters::Kafka::FetchResponse({}, {})); KafkaResponseFrame response_frame(response); - // Do nothing. - client_codec.encode(response_frame, encoding_callbacks); + auto status_or = client_codec.encode(response_frame, encoding_context); + EXPECT_FALSE(status_or.ok()); + EXPECT_EQ(status_or.status().message(), "Invalid request frame type"); } { // Test encode() method with request. - NiceMock encoding_callbacks; + NiceMock encoding_context; auto request = std::make_shared>( @@ -264,12 +263,13 @@ TEST(KafkaCodecTest, KafkaClientCodecTest) { dst_buffer.add(&size, sizeof(size)); // Encode data length. request->encode(dst_buffer); - EXPECT_CALL(encoding_callbacks, onEncodingSuccess(_, true)) - .WillOnce(testing::Invoke([&](Buffer::Instance& buffer, bool) { + EXPECT_CALL(callbacks, writeToConnection(_)) + .WillOnce(testing::Invoke([&](Buffer::Instance& buffer) { EXPECT_EQ(buffer.toString(), dst_buffer.toString()); + buffer.drain(buffer.length()); })); - client_codec.encode(request_frame, encoding_callbacks); + EXPECT_TRUE(client_codec.encode(request_frame, encoding_context).ok()); } } diff --git a/contrib/generic_proxy/filters/network/test/fake_codec.h b/contrib/generic_proxy/filters/network/test/fake_codec.h index 0cad7217edb3..e25a525e6320 100644 --- a/contrib/generic_proxy/filters/network/test/fake_codec.h +++ b/contrib/generic_proxy/filters/network/test/fake_codec.h @@ -178,7 +178,7 @@ class FakeStreamCodecFactory : public CodecFactory { } } - void encode(const StreamFrame& response, EncodingCallbacks& callback) override { + EncodingResult encode(const StreamFrame& response, EncodingContext&) override { std::string buffer; buffer.reserve(512); buffer += "FAKE-RSP|"; @@ -212,7 +212,11 @@ class FakeStreamCodecFactory : public CodecFactory { encoding_buffer_.writeBEInt(buffer.size()); encoding_buffer_.add(buffer); - callback.onEncodingSuccess(encoding_buffer_, response.frameFlags().endStream()); + const uint64_t encoded_size = encoding_buffer_.length(); + + callback_->writeToConnection(encoding_buffer_); + + return encoded_size; } ResponsePtr respond(Status status, absl::string_view, const Request&) override { @@ -332,7 +336,7 @@ class FakeStreamCodecFactory : public CodecFactory { } } - void encode(const StreamFrame& request, EncodingCallbacks& callback) override { + EncodingResult encode(const StreamFrame& request, EncodingContext&) override { std::string buffer; buffer.reserve(512); buffer += "FAKE-REQ|"; @@ -366,7 +370,11 @@ class FakeStreamCodecFactory : public CodecFactory { encoding_buffer_.writeBEInt(buffer.size()); encoding_buffer_.add(buffer); - callback.onEncodingSuccess(encoding_buffer_, request.frameFlags().endStream()); + const uint64_t encoded_size = encoding_buffer_.length(); + + callback_->writeToConnection(encoding_buffer_); + + return encoded_size; } absl::optional message_size_; diff --git a/contrib/generic_proxy/filters/network/test/integration_test.cc b/contrib/generic_proxy/filters/network/test/integration_test.cc index 0f49099a3cc3..36645c576f84 100644 --- a/contrib/generic_proxy/filters/network/test/integration_test.cc +++ b/contrib/generic_proxy/filters/network/test/integration_test.cc @@ -34,6 +34,8 @@ class GenericProxyIntegrationTest : public BaseIntegrationTest { } }; +// The integration test class for generic proxy. The chain of calls is: +// [Test client] -> [Envoy with Generic proxy] -> [Fake upstream] class IntegrationTest : public testing::TestWithParam { public: struct ConnectionCallbacks : public Network::ConnectionCallbacks { @@ -69,30 +71,27 @@ class IntegrationTest : public testing::TestWithParam; - struct TestRequestEncoderCallback : public EncodingCallbacks { + struct TestEncodingContext : public EncodingContext { OptRef routeEntry() const override { return {}; } - void onEncodingSuccess(Buffer::Instance& buffer, bool) override { buffer_.move(buffer); } - void onEncodingFailure(absl::string_view) override {} - Buffer::OwnedImpl buffer_; }; - using TestRequestEncoderCallbackSharedPtr = std::shared_ptr; + using TestEncodingContextSharedPtr = std::shared_ptr; - struct TestResponseEncoderCallback : public EncodingCallbacks { - OptRef routeEntry() const override { return {}; } - void onEncodingSuccess(Buffer::Instance& buffer, bool) override { buffer_.move(buffer); } - void onEncodingFailure(absl::string_view) override {} - Buffer::OwnedImpl buffer_; + struct SingleResponse { + bool end_stream_{}; + ResponseHeaderFramePtr response_; + std::list response_frames_; }; - using TestResponseEncoderCallbackSharedPtr = std::shared_ptr; - struct TestResponseDecoderCallback : public ClientCodecCallbacks { - TestResponseDecoderCallback(IntegrationTest& parent) : parent_(parent) {} + struct SingleRequest { + bool end_stream_{}; + RequestHeaderFramePtr request_; + std::list request_frames_; + }; - struct SingleResponse { - bool end_stream_{}; - ResponseHeaderFramePtr response_; - std::list response_frames_; - }; + // The callbacks for test client codec. This will used to encode request to Envoy + // and decode response from Envoy. + struct TestClientCodecCallbacks : public ClientCodecCallbacks { + TestClientCodecCallbacks(IntegrationTest& parent) : parent_(parent) {} void onDecodingSuccess(ResponseHeaderFramePtr response_frame, absl::optional) override { @@ -119,26 +118,72 @@ class IntegrationTest : public testing::TestWithParamwrite(buffer, false); + } + } OptRef connection() override { - if (parent_.upstream_connection_ != nullptr) { - return parent_.upstream_connection_->connection(); + if (parent_.client_connection_ != nullptr) { + return *parent_.client_connection_; } return {}; } - OptRef upstreamCluster() const override { - auto result = parent_.integration_->clusterManager().clusters().getCluster("cluster_0"); - if (result.has_value()) { - return makeOptRefFromPtr(result.value().get().info().get()); + OptRef upstreamCluster() const override { return {}; } + + uint64_t waiting_for_stream_id_{}; + std::map responses_; + IntegrationTest& parent_; + }; + using TestClientCodecCallbacksSharedPtr = std::shared_ptr; + + // The callbacks for fake upstream codec. This will used to encode response to Envoy + // and decode request from Envoy. + struct TestServerCodecCallbacks : public ServerCodecCallbacks { + TestServerCodecCallbacks(IntegrationTest& parent) : parent_(parent) {} + + void onDecodingSuccess(RequestHeaderFramePtr request_frame, + absl::optional) override { + auto& request = requests_[request_frame->frameFlags().streamFlags().streamId()]; + ASSERT(!request.end_stream_); + request.end_stream_ = request_frame->frameFlags().endStream(); + request.request_ = std::move(request_frame); + + // Exit dispatcher if we have received all the expected request frames. + if (requests_[waiting_for_stream_id_].end_stream_) { + parent_.integration_->dispatcher_->exit(); + } + } + void onDecodingSuccess(RequestCommonFramePtr frame) override { + auto& request = requests_[frame->frameFlags().streamFlags().streamId()]; + ASSERT(!request.end_stream_); + request.end_stream_ = frame->frameFlags().endStream(); + request.request_frames_.push_back(std::move(frame)); + + // Exit dispatcher if we have received all the expected request frames. + if (requests_[waiting_for_stream_id_].end_stream_) { + parent_.integration_->dispatcher_->exit(); + } + } + + void onDecodingFailure(absl::string_view) override {} + void writeToConnection(Buffer::Instance& buffer) override { + if (parent_.upstream_connection_ != nullptr) { + parent_.upstream_connection_->connection().write(buffer, false); + } + } + OptRef connection() override { + if (parent_.upstream_connection_ != nullptr) { + return parent_.upstream_connection_->connection(); } return {}; } uint64_t waiting_for_stream_id_{}; - std::map responses_; + std::map requests_; IntegrationTest& parent_; }; - using TestResponseDecoderCallbackSharedPtr = std::shared_ptr; + using TestServerCodecCallbacksSharedPtr = std::shared_ptr; void initialize(const std::string& config_yaml, CodecFactoryPtr codec_factory) { integration_ = std::make_unique(config_yaml); @@ -147,14 +192,15 @@ class IntegrationTest : public testing::TestWithParamcreateClientCodec(); + server_codec_ = codec_factory_->createServerCodec(); - request_encoder_callback_ = std::make_shared(); - response_decoder_callback_ = std::make_shared(*this); - client_codec_->setCodecCallbacks(*response_decoder_callback_); + test_encoding_context_ = std::make_shared(); - // Helper codec for upstream server to encode response. - server_codec_ = codec_factory_->createServerCodec(); - response_encoder_callback_ = std::make_shared(); + client_codec_callabcks_ = std::make_shared(*this); + client_codec_->setCodecCallbacks(*client_codec_callabcks_); + + server_codec_callbacks_ = std::make_shared(*this); + server_codec_->setCodecCallbacks(*server_codec_callbacks_); } std::string defaultConfig(bool bind_upstream_connection = false) { @@ -284,11 +330,10 @@ class IntegrationTest : public testing::TestWithParamencode(request, *request_encoder_callback_); - client_connection_->write(request_encoder_callback_->buffer_, false); + // Encode request and write data to client connection. + auto status_or = client_codec_->encode(request, *test_encoding_context_); + ASSERT(status_or.ok()); client_connection_->dispatcher().run(Envoy::Event::Dispatcher::RunType::NonBlock); - // Clear buffer for next encoding. - request_encoder_callback_->buffer_.drain(request_encoder_callback_->buffer_.length()); } // Waiting upstream connection to be created. @@ -307,27 +352,23 @@ class IntegrationTest : public testing::TestWithParamencode(response, *response_encoder_callback_); - - auto result = - upstream_connection_->write(response_encoder_callback_->buffer_.toString(), false); - // Clear buffer for next encoding. - response_encoder_callback_->buffer_.drain(response_encoder_callback_->buffer_.length()); - RELEASE_ASSERT(result, result.failure_message()); + // Encode response and write data to upstream connection. + auto status_or = server_codec_->encode(response, *test_encoding_context_); + ASSERT(status_or.ok()); } // Waiting for downstream response. AssertionResult waitDownstreamResponseForTest(std::chrono::milliseconds timeout, uint64_t stream_id) { bool timer_fired = false; - if (!response_decoder_callback_->responses_[stream_id].end_stream_) { + if (!client_codec_callabcks_->responses_[stream_id].end_stream_) { Envoy::Event::TimerPtr timer( integration_->dispatcher_->createTimer([this, &timer_fired]() -> void { timer_fired = true; integration_->dispatcher_->exit(); })); timer->enableTimer(timeout); - response_decoder_callback_->waiting_for_stream_id_ = stream_id; + client_codec_callabcks_->waiting_for_stream_id_ = stream_id; integration_->dispatcher_->run(Envoy::Event::Dispatcher::RunType::Block); if (timer_fired) { return AssertionFailure() << "Timed out waiting for response"; @@ -336,7 +377,7 @@ class IntegrationTest : public testing::TestWithParamdisableTimer(); } } - if (!response_decoder_callback_->responses_[stream_id].end_stream_) { + if (!client_codec_callabcks_->responses_[stream_id].end_stream_) { return AssertionFailure() << "No response or response not complete"; } return AssertionSuccess(); @@ -360,9 +401,9 @@ class IntegrationTest : public testing::TestWithParam integration_; @@ -406,8 +447,8 @@ TEST_P(IntegrationTest, RequestRouteNotFound) { RELEASE_ASSERT(waitDownstreamResponseForTest(TestUtility::DefaultTimeout, 0), "unexpected timeout"); - EXPECT_NE(response_decoder_callback_->responses_[0].response_, nullptr); - EXPECT_EQ(response_decoder_callback_->responses_[0].response_->status().code(), + EXPECT_NE(client_codec_callabcks_->responses_[0].response_, nullptr); + EXPECT_EQ(client_codec_callabcks_->responses_[0].response_->status().code(), static_cast(absl::StatusCode::kNotFound)); cleanup(); @@ -445,9 +486,9 @@ TEST_P(IntegrationTest, RequestAndResponse) { RELEASE_ASSERT(waitDownstreamResponseForTest(TestUtility::DefaultTimeout, 0), "unexpected timeout"); - EXPECT_NE(response_decoder_callback_->responses_[0].response_, nullptr); - EXPECT_EQ(response_decoder_callback_->responses_[0].response_->status().code(), 0); - EXPECT_EQ(response_decoder_callback_->responses_[0].response_->get("zzzz"), "xxxx"); + EXPECT_NE(client_codec_callabcks_->responses_[0].response_, nullptr); + EXPECT_EQ(client_codec_callabcks_->responses_[0].response_->status().code(), 0); + EXPECT_EQ(client_codec_callabcks_->responses_[0].response_->get("zzzz"), "xxxx"); cleanup(); } @@ -479,8 +520,8 @@ TEST_P(IntegrationTest, RequestTimeout) { RELEASE_ASSERT(waitDownstreamResponseForTest(TestUtility::DefaultTimeout, 0), "unexpected timeout"); - EXPECT_NE(response_decoder_callback_->responses_[0].response_, nullptr); - EXPECT_EQ(response_decoder_callback_->responses_[0].response_->status().code(), 4); + EXPECT_NE(client_codec_callabcks_->responses_[0].response_, nullptr); + EXPECT_EQ(client_codec_callabcks_->responses_[0].response_->status().code(), 4); cleanup(); } @@ -560,7 +601,7 @@ TEST_P(IntegrationTest, MultipleRequests) { request_2.data_ = {{"version", "v1"}, {"stream_id", "2"}, {"frame", "2_header"}}; // Reset request encoder callback. - request_encoder_callback_ = std::make_shared(); + test_encoding_context_ = std::make_shared(); // Send the second request with the different stream id and expect the connection to be alive. sendRequestForTest(request_2); @@ -581,12 +622,11 @@ TEST_P(IntegrationTest, MultipleRequests) { RELEASE_ASSERT(waitDownstreamResponseForTest(TestUtility::DefaultTimeout, 2), "unexpected timeout"); - EXPECT_NE(response_decoder_callback_->responses_[2].response_, nullptr); - EXPECT_EQ(response_decoder_callback_->responses_[2].response_->status().code(), 0); - EXPECT_EQ(response_decoder_callback_->responses_[2].response_->get("zzzz"), "xxxx"); - EXPECT_EQ( - response_decoder_callback_->responses_[2].response_->frameFlags().streamFlags().streamId(), - 2); + EXPECT_NE(client_codec_callabcks_->responses_[2].response_, nullptr); + EXPECT_EQ(client_codec_callabcks_->responses_[2].response_->status().code(), 0); + EXPECT_EQ(client_codec_callabcks_->responses_[2].response_->get("zzzz"), "xxxx"); + EXPECT_EQ(client_codec_callabcks_->responses_[2].response_->frameFlags().streamFlags().streamId(), + 2); FakeStreamCodecFactory::FakeResponse response_1; response_1.protocol_ = "fake_fake_fake"; @@ -599,12 +639,11 @@ TEST_P(IntegrationTest, MultipleRequests) { RELEASE_ASSERT(waitDownstreamResponseForTest(TestUtility::DefaultTimeout, 1), "unexpected timeout"); - EXPECT_NE(response_decoder_callback_->responses_[1].response_, nullptr); - EXPECT_EQ(response_decoder_callback_->responses_[1].response_->status().code(), 0); - EXPECT_EQ(response_decoder_callback_->responses_[1].response_->get("zzzz"), "yyyy"); - EXPECT_EQ( - response_decoder_callback_->responses_[1].response_->frameFlags().streamFlags().streamId(), - 1); + EXPECT_NE(client_codec_callabcks_->responses_[1].response_, nullptr); + EXPECT_EQ(client_codec_callabcks_->responses_[1].response_->status().code(), 0); + EXPECT_EQ(client_codec_callabcks_->responses_[1].response_->get("zzzz"), "yyyy"); + EXPECT_EQ(client_codec_callabcks_->responses_[1].response_->frameFlags().streamFlags().streamId(), + 1); cleanup(); } @@ -716,12 +755,11 @@ TEST_P(IntegrationTest, MultipleRequestsWithMultipleFrames) { RELEASE_ASSERT(waitDownstreamResponseForTest(TestUtility::DefaultTimeout, 2), "unexpected timeout"); - EXPECT_NE(response_decoder_callback_->responses_[2].response_, nullptr); - EXPECT_EQ(response_decoder_callback_->responses_[2].response_->status().code(), 0); - EXPECT_EQ(response_decoder_callback_->responses_[2].response_->get("zzzz"), "xxxx"); - EXPECT_EQ( - response_decoder_callback_->responses_[2].response_->frameFlags().streamFlags().streamId(), - 2); + EXPECT_NE(client_codec_callabcks_->responses_[2].response_, nullptr); + EXPECT_EQ(client_codec_callabcks_->responses_[2].response_->status().code(), 0); + EXPECT_EQ(client_codec_callabcks_->responses_[2].response_->get("zzzz"), "xxxx"); + EXPECT_EQ(client_codec_callabcks_->responses_[2].response_->frameFlags().streamFlags().streamId(), + 2); FakeStreamCodecFactory::FakeResponse response_1; response_1.protocol_ = "fake_fake_fake"; @@ -740,12 +778,11 @@ TEST_P(IntegrationTest, MultipleRequestsWithMultipleFrames) { RELEASE_ASSERT(waitDownstreamResponseForTest(TestUtility::DefaultTimeout, 1), "unexpected timeout"); - EXPECT_NE(response_decoder_callback_->responses_[1].response_, nullptr); - EXPECT_EQ(response_decoder_callback_->responses_[1].response_->status().code(), 0); - EXPECT_EQ(response_decoder_callback_->responses_[1].response_->get("zzzz"), "yyyy"); - EXPECT_EQ( - response_decoder_callback_->responses_[1].response_->frameFlags().streamFlags().streamId(), - 1); + EXPECT_NE(client_codec_callabcks_->responses_[1].response_, nullptr); + EXPECT_EQ(client_codec_callabcks_->responses_[1].response_->status().code(), 0); + EXPECT_EQ(client_codec_callabcks_->responses_[1].response_->get("zzzz"), "yyyy"); + EXPECT_EQ(client_codec_callabcks_->responses_[1].response_->frameFlags().streamFlags().streamId(), + 1); cleanup(); } diff --git a/contrib/generic_proxy/filters/network/test/mocks/codec.h b/contrib/generic_proxy/filters/network/test/mocks/codec.h index a1e8da1ccef4..baff48a13746 100644 --- a/contrib/generic_proxy/filters/network/test/mocks/codec.h +++ b/contrib/generic_proxy/filters/network/test/mocks/codec.h @@ -8,8 +8,16 @@ namespace Extensions { namespace NetworkFilters { namespace GenericProxy { +using testing::_; + class MockServerCodecCallbacks : public ServerCodecCallbacks { public: + MockServerCodecCallbacks() { + ON_CALL(*this, writeToConnection(_)) + .WillByDefault( + testing::Invoke([](Buffer::Instance& buffer) { buffer.drain(buffer.length()); })); + } + MOCK_METHOD(void, onDecodingSuccess, (RequestHeaderFramePtr, absl::optional)); MOCK_METHOD(void, onDecodingSuccess, (RequestCommonFramePtr)); MOCK_METHOD(void, onDecodingFailure, (absl::string_view)); @@ -20,6 +28,12 @@ class MockServerCodecCallbacks : public ServerCodecCallbacks { class MockClientCodecCallbacks : public ClientCodecCallbacks { public: + MockClientCodecCallbacks() { + ON_CALL(*this, writeToConnection(_)) + .WillByDefault( + testing::Invoke([](Buffer::Instance& buffer) { buffer.drain(buffer.length()); })); + } + MOCK_METHOD(void, onDecodingSuccess, (ResponseHeaderFramePtr, absl::optional)); MOCK_METHOD(void, onDecodingSuccess, (ResponseCommonFramePtr)); MOCK_METHOD(void, onDecodingFailure, (absl::string_view)); @@ -28,26 +42,33 @@ class MockClientCodecCallbacks : public ClientCodecCallbacks { MOCK_METHOD(OptRef, upstreamCluster, (), (const)); }; -class MockEncodingCallbacks : public EncodingCallbacks { +class MockEncodingContext : public EncodingContext { public: - MOCK_METHOD(void, onEncodingSuccess, (Buffer::Instance & buffer, bool end_stream)); - MOCK_METHOD(void, onEncodingFailure, (absl::string_view)); MOCK_METHOD(OptRef, routeEntry, (), (const)); }; class MockServerCodec : public ServerCodec { public: + MockServerCodec() { + ON_CALL(*this, encode(_, _)).WillByDefault(testing::Return(EncodingResult{0})); + } + MOCK_METHOD(void, setCodecCallbacks, (ServerCodecCallbacks & callbacks)); MOCK_METHOD(void, decode, (Buffer::Instance & buffer, bool end_stream)); - MOCK_METHOD(void, encode, (const StreamFrame&, EncodingCallbacks& callbacks)); - MOCK_METHOD(ResponsePtr, respond, (Status status, absl::string_view, const Request&)); + MOCK_METHOD(EncodingResult, encode, (const StreamFrame&, EncodingContext& ctx)); + MOCK_METHOD(ResponseHeaderFramePtr, respond, + (Status status, absl::string_view, const RequestHeaderFrame&)); }; class MockClientCodec : public ClientCodec { public: + MockClientCodec() { + ON_CALL(*this, encode(_, _)).WillByDefault(testing::Return(EncodingResult{0})); + } + MOCK_METHOD(void, setCodecCallbacks, (ClientCodecCallbacks & callbacks)); MOCK_METHOD(void, decode, (Buffer::Instance & buffer, bool end_stream)); - MOCK_METHOD(void, encode, (const StreamFrame&, EncodingCallbacks& callbacks)); + MOCK_METHOD(EncodingResult, encode, (const StreamFrame&, EncodingContext& ctx)); }; class MockCodecFactory : public CodecFactory { diff --git a/contrib/generic_proxy/filters/network/test/mocks/filter.h b/contrib/generic_proxy/filters/network/test/mocks/filter.h index 6017bf868445..7a92048bf1ae 100644 --- a/contrib/generic_proxy/filters/network/test/mocks/filter.h +++ b/contrib/generic_proxy/filters/network/test/mocks/filter.h @@ -108,8 +108,8 @@ class MockDecoderFilterCallback : public MockStreamFilterCallbacks(filter_config_, factory_context_); - EXPECT_EQ(filter_.get(), decoder_callback_); + EXPECT_EQ(filter_.get(), server_codec_callbacks_); filter_->initializeReadFilterCallbacks(filter_callbacks_); } std::shared_ptr filter_; - ServerCodecCallbacks* decoder_callback_{}; + ServerCodecCallbacks* server_codec_callbacks_{}; NiceMock* server_codec_{}; @@ -232,7 +232,7 @@ TEST_F(FilterTest, OnDecodingFailureWithoutActiveStreams) { filter_->onData(fake_empty_buffer, false); EXPECT_CALL(filter_callbacks_.connection_, close(_)); - decoder_callback_->onDecodingFailure(); + server_codec_callbacks_->onDecodingFailure(); EXPECT_EQ(filter_config_->stats().downstream_rq_decoding_error_.value(), 1); EXPECT_EQ(filter_config_->stats().downstream_rq_total_.value(), 0); @@ -258,7 +258,7 @@ TEST_F(FilterTest, OnDecodingSuccessWithNormalRequest) { // Three mock factories was added. EXPECT_CALL(*mock_stream_filter, onStreamDecoded(_)).Times(3); - decoder_callback_->onDecodingSuccess(std::move(request)); + server_codec_callbacks_->onDecodingSuccess(std::move(request)); EXPECT_EQ(filter_config_->stats().downstream_rq_total_.value(), 1); EXPECT_EQ(filter_config_->stats().downstream_rq_active_.value(), 1); @@ -283,32 +283,6 @@ TEST_F(FilterTest, OnConnectionClosedEvent) { EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onData(fake_empty_buffer, false)); } -TEST_F(FilterTest, SendReplyDownstream) { - initializeFilter(); - - NiceMock encoder_callback; - - auto response = std::make_unique(); - - Buffer::OwnedImpl response_buffer; - - EXPECT_CALL(filter_callbacks_.connection_, write(BufferStringEqual("test"), false)); - - EXPECT_CALL(encoder_callback, onEncodingSuccess(_, _)) - .WillOnce(Invoke([&](Buffer::Instance& buffer, bool) { - filter_callbacks_.connection_.write(buffer, false); - })); - - EXPECT_CALL(*server_codec_, encode(_, _)) - .WillOnce(Invoke([&](const StreamFrame&, EncodingCallbacks& callback) { - Buffer::OwnedImpl buffer; - buffer.add("test"); - callback.onEncodingSuccess(buffer, true); - })); - - filter_->sendFrameToDownstream(*response, encoder_callback); -} - TEST_F(FilterTest, GetConnection) { initializeFilter(); @@ -406,7 +380,7 @@ TEST_F(FilterTest, OnDecodingFailureWithActiveStreams) { filter_->onData(fake_empty_buffer, false); EXPECT_CALL(filter_callbacks_.connection_, close(_)); - decoder_callback_->onDecodingFailure(); + server_codec_callbacks_->onDecodingFailure(); EXPECT_EQ(0, filter_->activeStreamsForTest().size()); @@ -436,8 +410,10 @@ TEST_F(FilterTest, OnEncodingFailureWithActiveStreams) { EXPECT_EQ(filter_config_->stats().downstream_rq_total_.value(), 2); EXPECT_EQ(filter_config_->stats().downstream_rq_active_.value(), 2); - // One of stream encoding failed. - filter_->activeStreamsForTest().begin()->get()->onEncodingFailure(); + EXPECT_CALL(*server_codec_, encode(_, _)) + .WillOnce(Return(EncodingResult{absl::InvalidArgumentError("encoding-error")})); + auto response_0 = std::make_unique(); + filter_->activeStreamsForTest().begin()->get()->onResponseHeaderFrame(std::move(response_0)); EXPECT_EQ(1, filter_->activeStreamsForTest().size()); @@ -674,8 +650,8 @@ TEST_F(FilterTest, ActiveStreamFiltersContinueEncoding) { EXPECT_EQ(0, active_stream->nextEncoderFilterIndexForTest()); auto response = std::make_unique(); - // `continueEncoding` will be called in the `onResponseStart`. - active_stream->onResponseStart(std::move(response)); + // `continueEncoding` will be called in the `onResponseHeaderFrame`. + active_stream->onResponseHeaderFrame(std::move(response)); // Encoding will be stopped when `onStreamEncoded` of `mock_stream_filter_1` is called. EXPECT_EQ(2, active_stream->nextEncoderFilterIndexForTest()); @@ -683,10 +659,14 @@ TEST_F(FilterTest, ActiveStreamFiltersContinueEncoding) { EXPECT_CALL(filter_callbacks_.connection_, write(BufferStringEqual("test"), false)); EXPECT_CALL(*server_codec_, encode(_, _)) - .WillOnce(Invoke([&](const StreamFrame&, EncodingCallbacks& callback) { + .WillOnce(Invoke([&](const StreamFrame&, EncodingContext&) { Buffer::OwnedImpl buffer; buffer.add("test"); - callback.onEncodingSuccess(buffer, true); + + server_codec_callbacks_->writeToConnection(buffer); + buffer.drain(buffer.length()); + + return EncodingResult{4}; })); active_stream->encoderFiltersForTest()[1]->continueEncoding(); @@ -729,12 +709,16 @@ TEST_F(FilterTest, ActiveStreamSendLocalReply) { EXPECT_CALL(filter_callbacks_.connection_, write(BufferStringEqual("test"), false)); EXPECT_CALL(*server_codec_, encode(_, _)) - .WillOnce(Invoke([&](const StreamFrame& response, EncodingCallbacks& callback) { + .WillOnce(Invoke([&](const StreamFrame& response, EncodingContext&) { Buffer::OwnedImpl buffer; EXPECT_EQ(dynamic_cast(&response)->status().code(), static_cast(StatusCode::kUnknown)); buffer.add("test"); - callback.onEncodingSuccess(buffer, true); + + server_codec_callbacks_->writeToConnection(buffer); + buffer.drain(buffer.length()); + + return EncodingResult{4}; })); active_stream->sendLocalReply( @@ -817,10 +801,14 @@ TEST_F(FilterTest, NewStreamAndReplyNormally) { EXPECT_CALL(filter_callbacks_.connection_, write(BufferStringEqual("test"), false)); EXPECT_CALL(*server_codec_, encode(_, _)) - .WillOnce(Invoke([&](const StreamFrame&, EncodingCallbacks& callback) { + .WillOnce(Invoke([&](const StreamFrame&, EncodingContext&) { Buffer::OwnedImpl buffer; buffer.add("test"); - callback.onEncodingSuccess(buffer, true); + + server_codec_callbacks_->writeToConnection(buffer); + buffer.drain(buffer.length()); + + return EncodingResult{4}; })); EXPECT_CALL(*factory_context_.server_factory_context_.access_log_manager_.file_, @@ -834,7 +822,7 @@ TEST_F(FilterTest, NewStreamAndReplyNormally) { response->status_ = {0, true}; response->data_["response-key"] = "response-value"; - active_stream->onResponseStart(std::move(response)); + active_stream->onResponseHeaderFrame(std::move(response)); EXPECT_EQ(filter_config_->stats().downstream_rq_total_.value(), 1); EXPECT_EQ(filter_config_->stats().downstream_rq_active_.value(), 0); @@ -904,10 +892,14 @@ TEST_F(FilterTest, NewStreamAndReplyNormallyWithMultipleFrames) { EXPECT_CALL(filter_callbacks_.connection_, write(BufferStringEqual("test"), false)).Times(2); EXPECT_CALL(*server_codec_, encode(_, _)) .Times(2) - .WillRepeatedly(Invoke([&](const StreamFrame& frame, EncodingCallbacks& callback) { + .WillRepeatedly(Invoke([&](const StreamFrame&, EncodingContext&) { Buffer::OwnedImpl buffer; buffer.add("test"); - callback.onEncodingSuccess(buffer, frame.frameFlags().endStream()); + + server_codec_callbacks_->writeToConnection(buffer); + buffer.drain(buffer.length()); + + return EncodingResult{4}; })); auto response = std::make_unique(); @@ -915,12 +907,12 @@ TEST_F(FilterTest, NewStreamAndReplyNormallyWithMultipleFrames) { response->stream_frame_flags_ = FrameFlags(StreamFlags(), false); response->status_ = {123, false}; // Response non-OK. - active_stream->onResponseStart(std::move(response)); + active_stream->onResponseHeaderFrame(std::move(response)); auto response_frame_1 = std::make_unique(); response_frame_1->stream_frame_flags_ = FrameFlags(StreamFlags(), true); - active_stream->onResponseFrame(std::move(response_frame_1)); + active_stream->onResponseCommonFrame(std::move(response_frame_1)); EXPECT_EQ(filter_config_->stats().downstream_rq_total_.value(), 1); EXPECT_EQ(filter_config_->stats().downstream_rq_active_.value(), 0); @@ -950,10 +942,14 @@ TEST_F(FilterTest, NewStreamAndReplyNormallyWithDrainClose) { EXPECT_CALL(filter_callbacks_.connection_, write(BufferStringEqual("test"), false)); EXPECT_CALL(*server_codec_, encode(_, _)) - .WillOnce(Invoke([&](const StreamFrame&, EncodingCallbacks& callback) { + .WillOnce(Invoke([&](const StreamFrame&, EncodingContext&) { Buffer::OwnedImpl buffer; buffer.add("test"); - callback.onEncodingSuccess(buffer, true); + + server_codec_callbacks_->writeToConnection(buffer); + buffer.drain(buffer.length()); + + return EncodingResult{4}; })); EXPECT_CALL(factory_context_.drain_manager_, drainClose()).WillOnce(Return(true)); @@ -963,7 +959,7 @@ TEST_F(FilterTest, NewStreamAndReplyNormallyWithDrainClose) { auto response = std::make_unique(); response->status_ = {234, false}; // Response non-OK. active_stream->streamInfo().setResponseFlag(StreamInfo::CoreResponseFlag::UpstreamProtocolError); - active_stream->onResponseStart(std::move(response)); + active_stream->onResponseHeaderFrame(std::move(response)); EXPECT_EQ(filter_config_->stats().downstream_rq_total_.value(), 1); EXPECT_EQ(filter_config_->stats().downstream_rq_active_.value(), 0); @@ -992,10 +988,14 @@ TEST_F(FilterTest, NewStreamAndReplyNormallyWithStreamDrainClose) { EXPECT_CALL(filter_callbacks_.connection_, write(BufferStringEqual("test"), false)); EXPECT_CALL(*server_codec_, encode(_, _)) - .WillOnce(Invoke([&](const StreamFrame&, EncodingCallbacks& callback) { + .WillOnce(Invoke([&](const StreamFrame&, EncodingContext&) { Buffer::OwnedImpl buffer; buffer.add("test"); - callback.onEncodingSuccess(buffer, true); + + server_codec_callbacks_->writeToConnection(buffer); + buffer.drain(buffer.length()); + + return EncodingResult{4}; })); // The drain close of factory_context_.drain_manager_ is false, but the drain close of @@ -1006,7 +1006,7 @@ TEST_F(FilterTest, NewStreamAndReplyNormallyWithStreamDrainClose) { auto response = std::make_unique(); response->stream_frame_flags_ = FrameFlags(StreamFlags(0, false, true, false), true); - active_stream->onResponseStart(std::move(response)); + active_stream->onResponseHeaderFrame(std::move(response)); } TEST_F(FilterTest, NewStreamAndReplyNormallyWithTracing) { @@ -1037,17 +1037,21 @@ TEST_F(FilterTest, NewStreamAndReplyNormallyWithTracing) { EXPECT_CALL(filter_callbacks_.connection_, write(BufferStringEqual("test"), false)); EXPECT_CALL(*server_codec_, encode(_, _)) - .WillOnce(Invoke([&](const StreamFrame&, EncodingCallbacks& callback) { + .WillOnce(Invoke([&](const StreamFrame&, EncodingContext&) { Buffer::OwnedImpl buffer; buffer.add("test"); - callback.onEncodingSuccess(buffer, true); + + server_codec_callbacks_->writeToConnection(buffer); + buffer.drain(buffer.length()); + + return EncodingResult{4}; })); EXPECT_CALL(factory_context_.drain_manager_, drainClose()).WillOnce(Return(false)); EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); auto response = std::make_unique(); - active_stream->onResponseStart(std::move(response)); + active_stream->onResponseHeaderFrame(std::move(response)); } } // namespace diff --git a/contrib/generic_proxy/filters/network/test/router/router_test.cc b/contrib/generic_proxy/filters/network/test/router/router_test.cc index 42c27df6b5e3..6dfbb9bb2f91 100644 --- a/contrib/generic_proxy/filters/network/test/router/router_test.cc +++ b/contrib/generic_proxy/filters/network/test/router/router_test.cc @@ -258,7 +258,6 @@ class RouterFilterTest : public testing::Test { NiceMock tracing_config_; NiceMock active_span_; NiceMock* child_span_{}; - bool with_tracing_{}; uint32_t creating_connection_{}; }; @@ -524,21 +523,10 @@ TEST_F(RouterFilterTest, UpstreamRequestPoolReadyAndExpectNoResponse) { EXPECT_EQ(0, filter_->upstreamRequestsSize()); })); - EXPECT_CALL(*mock_generic_upstream_, upstreamConnection()); - EXPECT_CALL(mock_generic_upstream_->mock_upstream_connection_, write(_, _)) - .WillOnce(Invoke( - [](Buffer::Instance& buffer, bool) -> void { EXPECT_EQ(buffer.toString(), "hello"); })); - EXPECT_CALL(*mock_generic_upstream_, removeUpstreamRequest(_)); EXPECT_CALL(*mock_generic_upstream_, cleanUp(false)); - EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)) - .WillOnce(Invoke([&](const StreamFrame&, EncodingCallbacks& callback) -> void { - Buffer::OwnedImpl buffer; - buffer.add("hello"); - // Expect no response. - callback.onEncodingSuccess(buffer, true); - })); + EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)); expectInjectContextToUpstreamRequest(); expectFinalizeUpstreamSpanAny(); @@ -555,13 +543,7 @@ TEST_F(RouterFilterTest, UpstreamRequestPoolReadyButConnectionErrorBeforeRespons setup(); kickOffNewUpstreamRequest(); - EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)) - .WillOnce(Invoke([&](const StreamFrame&, EncodingCallbacks& callback) -> void { - Buffer::OwnedImpl buffer; - buffer.add("hello"); - // Expect response. - callback.onEncodingSuccess(buffer, true); - })); + EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)); notifyUpstreamSuccess(); @@ -585,13 +567,7 @@ TEST_F(RouterFilterTest, UpstreamRequestPoolReadyButConnectionTerminationBeforeR setup(); kickOffNewUpstreamRequest(); - EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)) - .WillOnce(Invoke([&](const StreamFrame&, EncodingCallbacks& callback) -> void { - Buffer::OwnedImpl buffer; - buffer.add("hello"); - // Expect response. - callback.onEncodingSuccess(buffer, true); - })); + EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)); notifyUpstreamSuccess(); @@ -615,13 +591,7 @@ TEST_F(RouterFilterTest, UpstreamRequestPoolReadyButStreamDestroyBeforeResponse) setup(); kickOffNewUpstreamRequest(); - EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)) - .WillOnce(Invoke([&](const StreamFrame&, EncodingCallbacks& callback) -> void { - Buffer::OwnedImpl buffer; - buffer.add("hello"); - // Expect response. - callback.onEncodingSuccess(buffer, true); - })); + EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)); notifyUpstreamSuccess(); @@ -640,19 +610,7 @@ TEST_F(RouterFilterTest, UpstreamRequestPoolReadyAndResponse) { setup(); kickOffNewUpstreamRequest(true); - EXPECT_CALL(*mock_generic_upstream_, upstreamConnection()); - EXPECT_CALL(mock_generic_upstream_->mock_upstream_connection_, write(_, _)) - .WillOnce(Invoke([](Buffer::Instance& buffer, bool) -> void { - EXPECT_EQ(buffer.toString(), "helloxxxxxx"); - })); - - EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)) - .WillOnce(Invoke([&](const StreamFrame&, EncodingCallbacks& callback) -> void { - Buffer::OwnedImpl buffer; - buffer.add("helloxxxxxx"); - // Expect response. - callback.onEncodingSuccess(buffer, true); - })); + EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)); expectInjectContextToUpstreamRequest(); @@ -662,7 +620,7 @@ TEST_F(RouterFilterTest, UpstreamRequestPoolReadyAndResponse) { EXPECT_CALL(*mock_generic_upstream_, removeUpstreamRequest(_)); EXPECT_CALL(*mock_generic_upstream_, cleanUp(false)); - EXPECT_CALL(mock_filter_callback_, onResponseStart(_)).WillOnce(Invoke([this](ResponsePtr) { + EXPECT_CALL(mock_filter_callback_, onResponseHeaderFrame(_)).WillOnce(Invoke([this](ResponsePtr) { // When the response is sent to callback, the upstream request should be removed. EXPECT_EQ(0, filter_->upstreamRequestsSize()); })); @@ -678,13 +636,7 @@ TEST_F(RouterFilterTest, UpstreamRequestPoolReadyAndResponseWithStartTime) { setup(); kickOffNewUpstreamRequest(true); - EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)) - .WillOnce(Invoke([&](const StreamFrame&, EncodingCallbacks& callback) -> void { - Buffer::OwnedImpl buffer; - buffer.add("hello"); - // Expect response. - callback.onEncodingSuccess(buffer, true); - })); + EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)); expectInjectContextToUpstreamRequest(); @@ -694,7 +646,7 @@ TEST_F(RouterFilterTest, UpstreamRequestPoolReadyAndResponseWithStartTime) { EXPECT_CALL(*mock_generic_upstream_, removeUpstreamRequest(_)); EXPECT_CALL(*mock_generic_upstream_, cleanUp(false)); - EXPECT_CALL(mock_filter_callback_, onResponseStart(_)).WillOnce(Invoke([this](ResponsePtr) { + EXPECT_CALL(mock_filter_callback_, onResponseHeaderFrame(_)).WillOnce(Invoke([this](ResponsePtr) { // When the response is sent to callback, the upstream request should be removed. EXPECT_EQ(0, filter_->upstreamRequestsSize()); })); @@ -724,13 +676,8 @@ TEST_F(RouterFilterTest, UpstreamRequestPoolReadyAndResponseAndTimeout) { kickOffNewUpstreamRequest(true); - EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)) - .WillOnce(Invoke([&](const StreamFrame&, EncodingCallbacks& callback) -> void { - Buffer::OwnedImpl buffer; - buffer.add("hello"); - // Expect response. - callback.onEncodingSuccess(buffer, true); - })); + EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)); + expectInjectContextToUpstreamRequest(); notifyUpstreamSuccess(); @@ -739,7 +686,7 @@ TEST_F(RouterFilterTest, UpstreamRequestPoolReadyAndResponseAndTimeout) { EXPECT_CALL(*mock_generic_upstream_, removeUpstreamRequest(_)); EXPECT_CALL(*mock_generic_upstream_, cleanUp(false)); - EXPECT_CALL(mock_filter_callback_, onResponseStart(_)).WillOnce(Invoke([this](ResponsePtr) { + EXPECT_CALL(mock_filter_callback_, onResponseHeaderFrame(_)).WillOnce(Invoke([this](ResponsePtr) { // When the response is sent to callback, the upstream request should be removed. EXPECT_EQ(0, filter_->upstreamRequestsSize()); })); @@ -762,26 +709,14 @@ TEST_F(RouterFilterTest, UpstreamRequestPoolReadyAndResponseWithMultipleFrames) // This only store the frame and does nothing else because the pool is not ready yet. filter_->onRequestCommonFrame(std::move(frame_1)); - EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)) - .Times(2) - .WillRepeatedly(Invoke([&](const StreamFrame&, EncodingCallbacks& callback) -> void { - Buffer::OwnedImpl buffer; - buffer.add("hello"); - // Expect response. - callback.onEncodingSuccess(buffer, false); - })); + EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)).Times(2); + expectInjectContextToUpstreamRequest(); // This will trigger two frames to be sent. notifyUpstreamSuccess(); - EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)) - .WillOnce(Invoke([&](const StreamFrame&, EncodingCallbacks& callback) -> void { - Buffer::OwnedImpl buffer; - buffer.add("hello"); - // Expect response. - callback.onEncodingSuccess(buffer, true); - })); + EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)); // End stream is set to true by default. auto frame_2 = std::make_unique(); @@ -793,8 +728,8 @@ TEST_F(RouterFilterTest, UpstreamRequestPoolReadyAndResponseWithMultipleFrames) EXPECT_CALL(*mock_generic_upstream_, removeUpstreamRequest(_)); EXPECT_CALL(*mock_generic_upstream_, cleanUp(false)); - EXPECT_CALL(mock_filter_callback_, onResponseStart(_)); - EXPECT_CALL(mock_filter_callback_, onResponseFrame(_)) + EXPECT_CALL(mock_filter_callback_, onResponseHeaderFrame(_)); + EXPECT_CALL(mock_filter_callback_, onResponseCommonFrame(_)) .Times(2) .WillRepeatedly(Invoke([this](ResponseCommonFramePtr frame) { // When the entire response is sent to callback, the upstream request should be removed. @@ -826,20 +761,14 @@ TEST_F(RouterFilterTest, UpstreamRequestPoolReadyAndResponseWithDrainCloseSetInR setup(); kickOffNewUpstreamRequest(); - EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)) - .WillOnce(Invoke([&](const StreamFrame&, EncodingCallbacks& callback) -> void { - Buffer::OwnedImpl buffer; - buffer.add("hello"); - // Expect response. - callback.onEncodingSuccess(buffer, true); - })); + EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)); notifyUpstreamSuccess(); EXPECT_CALL(*mock_generic_upstream_, removeUpstreamRequest(_)); EXPECT_CALL(*mock_generic_upstream_, cleanUp(true)); - EXPECT_CALL(mock_filter_callback_, onResponseStart(_)).WillOnce(Invoke([this](ResponsePtr) { + EXPECT_CALL(mock_filter_callback_, onResponseHeaderFrame(_)).WillOnce(Invoke([this](ResponsePtr) { // When the response is sent to callback, the upstream request should be removed. EXPECT_EQ(0, filter_->upstreamRequestsSize()); })); @@ -856,13 +785,7 @@ TEST_F(RouterFilterTest, UpstreamRequestPoolReadyAndResponseDecodingFailure) { setup(); kickOffNewUpstreamRequest(); - EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)) - .WillOnce(Invoke([&](const StreamFrame&, EncodingCallbacks& callback) -> void { - Buffer::OwnedImpl buffer; - buffer.add("hello"); - // Expect response. - callback.onEncodingSuccess(buffer, true); - })); + EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)); notifyUpstreamSuccess(); @@ -887,9 +810,7 @@ TEST_F(RouterFilterTest, UpstreamRequestPoolReadyAndRequestEncodingFailure) { kickOffNewUpstreamRequest(); EXPECT_CALL(mock_generic_upstream_->mock_client_codec_, encode(_, _)) - .WillOnce(Invoke([&](const StreamFrame&, EncodingCallbacks& callback) -> void { - callback.onEncodingFailure("encoding-failure"); - })); + .WillOnce(Return(EncodingResult(absl::InvalidArgumentError("encoding-failure")))); EXPECT_CALL(mock_filter_callback_, sendLocalReply(_, _, _)) .WillOnce(Invoke([this](Status status, absl::string_view data, ResponseUpdateFunction) { diff --git a/docs/BUILD b/docs/BUILD index 5859a9dcbe91..46df13e77e19 100644 --- a/docs/BUILD +++ b/docs/BUILD @@ -209,17 +209,9 @@ pkg_files( strip_prefix = "/configs", ) -pkg_files( - name = "examples_rst", - srcs = ["//examples:files"], - prefix = "start/sandboxes/_include", - strip_prefix = "/examples", -) - pkg_filegroup( name = "rst_files", srcs = [ - ":examples_rst", ":repo_configs", ":sphinx_base", ":sphinx_inventories", @@ -244,6 +236,7 @@ pkg_tar( ":extensions_security_rst", ":external_deps_rst", ":version_history_rst", + "//examples:docs", ], ) diff --git a/docs/inventories/v1.27/objects.inv b/docs/inventories/v1.27/objects.inv index 01563bfb7682..f875b6a42589 100644 Binary files a/docs/inventories/v1.27/objects.inv and b/docs/inventories/v1.27/objects.inv differ diff --git a/docs/inventories/v1.28/objects.inv b/docs/inventories/v1.28/objects.inv index 9727f81f7199..846ab4b39592 100644 Binary files a/docs/inventories/v1.28/objects.inv and b/docs/inventories/v1.28/objects.inv differ diff --git a/docs/inventories/v1.29/objects.inv b/docs/inventories/v1.29/objects.inv index 30df5e6e1489..8e69f5272a08 100644 Binary files a/docs/inventories/v1.29/objects.inv and b/docs/inventories/v1.29/objects.inv differ diff --git a/docs/inventories/v1.30/objects.inv b/docs/inventories/v1.30/objects.inv index ddf589cf9db8..b708ee4dfc55 100644 Binary files a/docs/inventories/v1.30/objects.inv and b/docs/inventories/v1.30/objects.inv differ diff --git a/docs/root/_static/searchtools.js b/docs/root/_static/searchtools.js index d460d8033155..02d0473059b3 100644 --- a/docs/root/_static/searchtools.js +++ b/docs/root/_static/searchtools.js @@ -129,7 +129,7 @@ const _displayItem = (item, searchTerms) => { }; const _finishSearch = (resultCount) => { Search.stopPulse(); - Search.title.innerText = _("Search Results"); + Search.title.textContent = _("Search Results"); if (!resultCount) Search.status.innerText = Documentation.gettext( "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." diff --git a/docs/root/configuration/listeners/listener_filters/proxy_protocol.rst b/docs/root/configuration/listeners/listener_filters/proxy_protocol.rst index e96d457e7725..c545f117c570 100644 --- a/docs/root/configuration/listeners/listener_filters/proxy_protocol.rst +++ b/docs/root/configuration/listeners/listener_filters/proxy_protocol.rst @@ -33,7 +33,7 @@ If there is a protocol error or an unsupported address family Statistics ---------- -This filter emits the following general statistics, rooted at *downstream_proxy_proto* +This filter emits the following general statistics, rooted at *proxy_proto.[.]* .. csv-table:: :header: Name, Type, Description @@ -42,7 +42,7 @@ This filter emits the following general statistics, rooted at *downstream_proxy_ not_found_disallowed, Counter, "Total number of connections that don't contain the PROXY protocol header and are rejected." not_found_allowed, Counter, "Total number of connections that don't contain the PROXY protocol header, but are allowed due to :ref:`allow_requests_without_proxy_protocol `." -The filter also emits the statistics rooted at *downstream_proxy_proto.versions.* +The filter also emits the statistics rooted at *proxy_proto.[.]versions.* for each matched PROXY protocol version. Proxy protocol versions include ``v1`` and ``v2``. .. csv-table:: @@ -53,7 +53,7 @@ for each matched PROXY protocol version. Proxy protocol versions include ``v1`` disallowed, Counter, "Total number of ``found`` connections that are rejected due to :ref:`disallowed_versions `." error, Counter, "Total number of connections where the PROXY protocol header was malformed (and the connection was rejected)." -The filter also emits the following legacy statistics, rooted at its own scope: +The filter also emits the following legacy statistics, rooted at its own scope and **not** including the *stat_prefix*: .. csv-table:: :header: Name, Type, Description diff --git a/docs/root/start/sandboxes/index.rst b/docs/root/start/sandboxes/index.rst index 450fd5176fd2..e84bd196d74c 100644 --- a/docs/root/start/sandboxes/index.rst +++ b/docs/root/start/sandboxes/index.rst @@ -42,44 +42,4 @@ Before you begin you will need to install the sandbox environment. The following sandboxes are available: -.. toctree:: - :maxdepth: 1 - - brotli - cache - cors - csrf - double-proxy - dynamic-configuration-filesystem - dynamic-configuration-control-plane - ext_authz - fault_injection - front_proxy - golang-http - golang-network - grpc_bridge - gzip - jaeger_tracing - kafka - load_reporting_service - locality_load_balancing - local_ratelimit - lua-cluster-specifier - lua - mysql - opentelemetry - postgres - rbac - redis - route-mirror - single-page-app - skywalking - tls-inspector - tls-sni - tls - udp - wasm-cc - websocket - win32_front_proxy - zipkin - zstd +.. include:: toctree.rst diff --git a/docs/root/start/sandboxes/win32_front_proxy.rst b/docs/root/start/sandboxes/win32_front_proxy.rst deleted file mode 100644 index 2342bacc7eb3..000000000000 --- a/docs/root/start/sandboxes/win32_front_proxy.rst +++ /dev/null @@ -1,354 +0,0 @@ -Windows based Front proxy -========================= - -.. include:: ../../_include/windows_support_ended.rst - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - -To get a flavor of what Envoy has to offer on Windows, we are releasing a -`docker compose `_ sandbox that deploys a front Envoy and a -couple of services (simple Flask apps) colocated with a running service Envoy. - -The three containers will be deployed inside a virtual network called ``envoymesh``. - -Below you can see a graphic showing the docker compose deployment: - -.. image:: /_static/docker_compose_front_proxy.svg - :width: 100% - -All incoming requests are routed via the front Envoy, which is acting as a reverse proxy sitting on -the edge of the ``envoymesh`` network. Port ``8080``, ``8443``, and ``8001`` are exposed by docker -compose (see :download:`docker-compose.yaml <_include/front-proxy/docker-compose.yaml>`) to handle -``HTTP``, ``HTTPS`` calls to the services and requests to ``/admin`` respectively. - -Moreover, notice that all traffic routed by the front Envoy to the service containers is actually -routed to the service Envoys (routes setup in :download:`envoy.yaml <_include/front-proxy/envoy.yaml>`). - -In turn the service Envoys route the request to the Flask app via the loopback -address (routes setup in :download:`service-envoy.yaml <_include/front-proxy/service-envoy.yaml>`). This -setup illustrates the advantage of running service Envoys collocated with your services: all -requests are handled by the service Envoy, and efficiently routed to your services. - -Step 1: Start all of our containers -*********************************** - -Change to the ``examples/front-proxy`` directory. - -.. code-block:: console - - PS> $PWD - D:\envoy\examples\win32-front-proxy - PS> docker-compose build --pull - PS> docker-compose up -d - PS> docker-compose ps - Name Command State Ports - ------------------------------------------------------------------------------------------------------------------------------------------------------------ - envoy-front-proxy_front-envoy_1 powershell.exe ./start_env ... Up 10000/tcp, 0.0.0.0:8003->8003/tcp, 0.0.0.0:8080->8080/tcp, 0.0.0.0:8443->8443/tcp - envoy-front-proxy_service1_1 powershell.exe ./start_ser ... Up 10000/tcp - envoy-front-proxy_service2_1 powershell.exe ./start_ser ... Up 10000/tcp - -Step 2: Test Envoy's routing capabilities -***************************************** - -You can now send a request to both services via the ``front-envoy``. - -For ``service1``: - -.. code-block:: console - - PS> curl -v localhost:8080/service/1 - * Trying ::1... - * TCP_NODELAY set - * Trying 127.0.0.1... - * TCP_NODELAY set - * Connected to localhost (127.0.0.1) port 8080 (#0) - > GET /service/1 HTTP/1.1 - > Host: localhost:8080 - > User-Agent: curl/7.55.1 - > Accept: */* - > - < HTTP/1.1 200 OK - < content-type: text/html; charset=utf-8 - < content-length: 92 - < server: envoy - < date: Wed, 05 May 2021 05:55:55 GMT - < x-envoy-upstream-service-time: 18 - < - Hello from behind Envoy (service 1)! hostname: 8a45bba91d83 resolvedhostname: 172.30.97.237 - * Connection #0 to host localhost left intact - -For ``service2``: - -.. code-block:: console - - PS> curl -v localhost:8080/service/2 - * Trying ::1... - * TCP_NODELAY set - * Trying 127.0.0.1... - * TCP_NODELAY set - * Connected to localhost (127.0.0.1) port 8080 (#0) - > GET /service/2 HTTP/1.1 - > Host: localhost:8080 - > User-Agent: curl/7.55.1 - > Accept: */* - > - < HTTP/1.1 200 OK - < content-type: text/html; charset=utf-8 - < content-length: 93 - < server: envoy - < date: Wed, 05 May 2021 05:57:03 GMT - < x-envoy-upstream-service-time: 14 - < - Hello from behind Envoy (service 2)! hostname: 51e28eb3c8b8 resolvedhostname: 172.30.109.113 - * Connection #0 to host localhost left intact - -Notice that each request, while sent to the front Envoy, was correctly routed to the respective -application. - -We can also use ``HTTPS`` to call services behind the front Envoy. For example, calling ``service1``: - -.. code-block:: console - - PS> curl https://localhost:8443/service/1 -k -v - * Trying ::1... - * TCP_NODELAY set - * Trying 127.0.0.1... - * TCP_NODELAY set - * Connected to localhost (127.0.0.1) port 8443 (#0) - * schannel: SSL/TLS connection with localhost port 8443 (step 1/3) - * schannel: disabled server certificate revocation checks - * schannel: verifyhost setting prevents Schannel from comparing the supplied target name with the subject names in server certificates. - * schannel: sending initial handshake data: sending 171 bytes... - * schannel: sent initial handshake data: sent 171 bytes - * schannel: SSL/TLS connection with localhost port 8443 (step 2/3) - * schannel: failed to receive handshake, need more data - * schannel: SSL/TLS connection with localhost port 8443 (step 2/3) - * schannel: encrypted data got 1081 - * schannel: encrypted data buffer: offset 1081 length 4096 - * schannel: sending next handshake data: sending 93 bytes... - * schannel: SSL/TLS connection with localhost port 8443 (step 2/3) - * schannel: encrypted data got 258 - * schannel: encrypted data buffer: offset 258 length 4096 - * schannel: SSL/TLS handshake complete - * schannel: SSL/TLS connection with localhost port 8443 (step 3/3) - * schannel: stored credential handle in session cache - > GET /service/1 HTTP/1.1 - > Host: localhost:8443 - > User-Agent: curl/7.55.1 - > Accept: */* - > - * schannel: client wants to read 102400 bytes - * schannel: encdata_buffer resized 103424 - * schannel: encrypted data buffer: offset 0 length 103424 - * schannel: encrypted data got 286 - * schannel: encrypted data buffer: offset 286 length 103424 - * schannel: decrypted data length: 257 - * schannel: decrypted data added: 257 - * schannel: decrypted data cached: offset 257 length 102400 - * schannel: encrypted data buffer: offset 0 length 103424 - * schannel: decrypted data buffer: offset 257 length 102400 - * schannel: schannel_recv cleanup - * schannel: decrypted data returned 257 - * schannel: decrypted data buffer: offset 0 length 102400 - < HTTP/1.1 200 OK - < content-type: text/html; charset=utf-8 - < content-length: 92 - < server: envoy - < date: Wed, 05 May 2021 05:57:45 GMT - < x-envoy-upstream-service-time: 3 - < - Hello from behind Envoy (service 1)! hostname: 8a45bba91d83 resolvedhostname: 172.30.97.237 - * Connection #0 to host localhost left intact - -Step 3: Test Envoy's load balancing capabilities -************************************************ - -Now let's scale up our ``service1`` nodes to demonstrate the load balancing abilities of Envoy: - -.. code-block:: console - - PS> docker-compose scale service1=3 - Creating and starting example_service1_2 ... done - Creating and starting example_service1_3 ... done - -Now if we send a request to ``service1`` multiple times, the front Envoy will load balance the -requests by doing a round robin of the three ``service1`` machines: - -.. code-block:: console - - PS> curl -v localhost:8080/service/1 - * Trying ::1... - * TCP_NODELAY set - * Trying 127.0.0.1... - * TCP_NODELAY set - * Connected to localhost (127.0.0.1) port 8080 (#0) - > GET /service/1 HTTP/1.1 - > Host: localhost:8080 - > User-Agent: curl/7.55.1 - > Accept: */* - > - < HTTP/1.1 200 OK - < content-type: text/html; charset=utf-8 - < content-length: 93 - < server: envoy - < date: Wed, 05 May 2021 05:58:40 GMT - < x-envoy-upstream-service-time: 22 - < - Hello from behind Envoy (service 1)! hostname: 8d2359ee21a8 resolvedhostname: 172.30.101.143 - * Connection #0 to host localhost left intact - PS> curl -v localhost:8080/service/1 - * Trying ::1... - * TCP_NODELAY set - * Trying 127.0.0.1... - * TCP_NODELAY set - * Connected to localhost (127.0.0.1) port 8080 (#0) - > GET /service/1 HTTP/1.1 - > Host: localhost:8080 - > User-Agent: curl/7.55.1 - > Accept: */* - > - < HTTP/1.1 200 OK - < content-type: text/html; charset=utf-8 - < content-length: 91 - < server: envoy - < date: Wed, 05 May 2021 05:58:43 GMT - < x-envoy-upstream-service-time: 11 - < - Hello from behind Envoy (service 1)! hostname: 41e1141eebf4 resolvedhostname: 172.30.96.11 - * Connection #0 to host localhost left intact - PS> curl -v localhost:8080/service/1 - * Trying ::1... - * TCP_NODELAY set - * Trying 127.0.0.1... - * TCP_NODELAY set - * Connected to localhost (127.0.0.1) port 8080 (#0) - > GET /service/1 HTTP/1.1 - > Host: localhost:8080 - > User-Agent: curl/7.55.1 - > Accept: */* - > - < HTTP/1.1 200 OK - < content-type: text/html; charset=utf-8 - < content-length: 92 - < server: envoy - < date: Wed, 05 May 2021 05:58:44 GMT - < x-envoy-upstream-service-time: 7 - < - Hello from behind Envoy (service 1)! hostname: 8a45bba91d83 resolvedhostname: 172.30.97.237 - * Connection #0 to host localhost left intact - -Step 4: Enter containers and curl services -****************************************** - -In addition of using ``curl`` from your host machine, you can also enter the -containers themselves and ``curl`` from inside them. To enter a container you -can use ``docker-compose exec /bin/bash``. For example we can -enter the ``front-envoy`` container, and ``curl`` for services locally: - -.. code-block:: console - - PS> docker-compose exec front-envoy powershell - PS C:\> (curl -UseBasicParsing http://localhost:8080/service/1).Content - Hello from behind Envoy (service 1)! hostname: 41e1141eebf4 resolvedhostname: 172.30.96.11 - - PS C:\> (curl -UseBasicParsing http://localhost:8080/service/1).Content - Hello from behind Envoy (service 1)! hostname: 8a45bba91d83 resolvedhostname: 172.30.97.237 - - PS C:\> (curl -UseBasicParsing http://localhost:8080/service/1).Content - Hello from behind Envoy (service 1)! hostname: 8d2359ee21a8 resolvedhostname: 172.30.101.143 - - -Step 5: Enter container and curl admin interface -************************************************ - -When Envoy runs it also attaches an ``admin`` to your desired port. - -In the example configs the admin listener is bound to port ``8001``. - -We can ``curl`` it to gain useful information: - -- :ref:`/server_info ` provides information about the Envoy version you are running. -- :ref:`/stats ` provides statistics about the Envoy server. - -In the example we can enter the ``front-envoy`` container to query admin: - -.. code-block:: console - - PS> docker-compose exec front-envoy powershell - PS C:\> (curl http://localhost:8003/server_info -UseBasicParsing).Content - -.. code-block:: json - - { - "version": "093e2ffe046313242144d0431f1bb5cf18d82544/1.15.0-dev/Clean/RELEASE/BoringSSL", - "state": "LIVE", - "hot_restart_version": "11.104", - "command_line_options": { - "base_id": "0", - "use_dynamic_base_id": false, - "base_id_path": "", - "concurrency": 8, - "config_path": "/etc/front-envoy.yaml", - "config_yaml": "", - "allow_unknown_static_fields": false, - "reject_unknown_dynamic_fields": false, - "ignore_unknown_dynamic_fields": false, - "admin_address_path": "", - "local_address_ip_version": "v4", - "log_level": "info", - "component_log_level": "", - "log_format": "[%Y-%m-%d %T.%e][%t][%l][%n] [%g:%#] %v", - "log_format_escaped": false, - "log_path": "", - "service_cluster": "front-proxy", - "service_node": "", - "service_zone": "", - "drain_strategy": "Gradual", - "mode": "Serve", - "disable_hot_restart": false, - "enable_mutex_tracing": false, - "restart_epoch": 0, - "cpuset_threads": false, - "disabled_extensions": [], - "bootstrap_version": 0, - "hidden_envoy_deprecated_max_stats": "0", - "hidden_envoy_deprecated_max_obj_name_len": "0", - "file_flush_interval": "10s", - "drain_time": "600s", - "parent_shutdown_time": "900s" - }, - "uptime_current_epoch": "188s", - "uptime_all_epochs": "188s" - } - -.. code-block:: console - - PS C:\> (curl http://localhost:8003/stats -UseBasicParsing).Content - cluster.service1.external.upstream_rq_200: 7 - ... - cluster.service1.membership_change: 2 - cluster.service1.membership_total: 3 - ... - cluster.service1.upstream_cx_http2_total: 3 - ... - cluster.service1.upstream_rq_total: 7 - ... - cluster.service2.external.upstream_rq_200: 2 - ... - cluster.service2.membership_change: 1 - cluster.service2.membership_total: 1 - ... - cluster.service2.upstream_cx_http2_total: 1 - ... - cluster.service2.upstream_rq_total: 2 - ... - -Notice that we can get the number of members of upstream clusters, number of requests fulfilled by -them, information about http ingress, and a plethora of other useful stats. - -.. seealso:: - - :ref:`Envoy admin quick start guide ` - Quick start guide to the Envoy admin interface. diff --git a/docs/versions.yaml b/docs/versions.yaml index 28287d3a0aa7..c8af4d36b78f 100644 --- a/docs/versions.yaml +++ b/docs/versions.yaml @@ -20,7 +20,7 @@ "1.24": 1.24.12 "1.25": 1.25.11 "1.26": 1.26.8 -"1.27": 1.27.5 -"1.28": 1.28.3 -"1.29": 1.29.4 -"1.30": 1.30.1 +"1.27": 1.27.6 +"1.28": 1.28.4 +"1.29": 1.29.5 +"1.30": 1.30.2 diff --git a/envoy/config/subscription_factory.h b/envoy/config/subscription_factory.h index de9d02100ef3..472fe9106295 100644 --- a/envoy/config/subscription_factory.h +++ b/envoy/config/subscription_factory.h @@ -54,9 +54,10 @@ class SubscriptionFactory { * @param resource_decoder how incoming opaque resource objects are to be decoded. * @param options subscription options. * - * @return SubscriptionPtr subscription object corresponding for config and type_url. + * @return SubscriptionPtr subscription object corresponding for config and type_url or error + * status. */ - virtual SubscriptionPtr subscriptionFromConfigSource( + virtual absl::StatusOr subscriptionFromConfigSource( const envoy::config::core::v3::ConfigSource& config, absl::string_view type_url, Stats::Scope& scope, SubscriptionCallbacks& callbacks, OpaqueResourceDecoderSharedPtr resource_decoder, const SubscriptionOptions& options) PURE; @@ -73,9 +74,10 @@ class SubscriptionFactory { * CollectionSubscription object. * @param resource_decoder how incoming opaque resource objects are to be decoded. * - * @return SubscriptionPtr subscription object corresponding for collection_locator. + * @return SubscriptionPtr subscription object corresponding for collection_locator or error + * status. */ - virtual SubscriptionPtr + virtual absl::StatusOr collectionSubscriptionFromUrl(const xds::core::v3::ResourceLocator& collection_locator, const envoy::config::core::v3::ConfigSource& config, absl::string_view type_url, Stats::Scope& scope, @@ -115,8 +117,9 @@ class MuxFactory : public Config::UntypedFactory { std::string category() const override { return "envoy.config_mux"; } virtual void shutdownAll() PURE; virtual std::shared_ptr - create(std::unique_ptr&& async_client, Event::Dispatcher& dispatcher, - Random::RandomGenerator& random, Stats::Scope& scope, + create(std::unique_ptr&& async_client, + std::unique_ptr&& async_failover_client, + Event::Dispatcher& dispatcher, Random::RandomGenerator& random, Stats::Scope& scope, const envoy::config::core::v3::ApiConfigSource& ads_config, const LocalInfo::LocalInfo& local_info, std::unique_ptr&& config_validators, diff --git a/envoy/http/async_client.h b/envoy/http/async_client.h index 3b495a795aae..5110246d902f 100644 --- a/envoy/http/async_client.h +++ b/envoy/http/async_client.h @@ -47,7 +47,9 @@ class AsyncClient { */ enum class FailureReason { // The stream has been reset. - Reset + Reset, + // The stream exceeds the response buffer limit. + ExceedResponseBufferLimit }; /** @@ -316,6 +318,11 @@ class AsyncClient { return *this; } + StreamOptions& setDiscardResponseBody(bool discard) { + discard_response_body = discard; + return *this; + } + StreamOptions& setIsShadowSuffixDisabled(bool d) { is_shadow_suffixed_disabled = d; return *this; @@ -380,6 +387,7 @@ class AsyncClient { bool is_shadow{false}; bool is_shadow_suffixed_disabled{false}; + bool discard_response_body{false}; // The parent span that child spans are created under to trace egress requests/responses. // If not set, requests will not be traced. diff --git a/envoy/http/filter.h b/envoy/http/filter.h index 9b627f1c26d7..8260924e34df 100644 --- a/envoy/http/filter.h +++ b/envoy/http/filter.h @@ -241,6 +241,11 @@ class UpstreamStreamFilterCallbacks { virtual bool pausedForConnect() const PURE; virtual void setPausedForConnect(bool value) PURE; + // Setters and getters to determine if sending body payload is paused on + // confirmation of a WebSocket upgrade. These should only be used by the upstream codec filter. + virtual bool pausedForWebsocketUpgrade() const PURE; + virtual void setPausedForWebsocketUpgrade(bool value) PURE; + // Return the upstreamStreamOptions for this stream. virtual const Http::ConnectionPool::Instance::StreamOptions& upstreamStreamOptions() const PURE; diff --git a/envoy/runtime/runtime.h b/envoy/runtime/runtime.h index a3c1f8bd1353..bd9d06a50a2b 100644 --- a/envoy/runtime/runtime.h +++ b/envoy/runtime/runtime.h @@ -227,8 +227,9 @@ class Loader { * the constructor is finished, with the exception of dynamic RTDS layers, * which require ClusterManager. * @param cm cluster manager reference. + * @return a status indicating if initialization was successful. */ - virtual void initialize(Upstream::ClusterManager& cm) PURE; + virtual absl::Status initialize(Upstream::ClusterManager& cm) PURE; /** * @return const Snapshot& the current snapshot. This reference is safe to use for the duration of diff --git a/envoy/stream_info/stream_info.h b/envoy/stream_info/stream_info.h index 8b2eb2a18b1e..f3b8232967d6 100644 --- a/envoy/stream_info/stream_info.h +++ b/envoy/stream_info/stream_info.h @@ -180,6 +180,8 @@ struct ResponseCodeDetailValues { const std::string PathNormalizationFailed = "path_normalization_failed"; // The request was rejected because it attempted an unsupported upgrade. const std::string UpgradeFailed = "upgrade_failed"; + // The websocket handshake is unsuccessful and only SwitchingProtocols is considering successful. + const std::string WebsocketHandshakeUnsuccessful = "websocket_handshake_unsuccessful"; // The request was rejected by the HCM because there was no route configuration found. const std::string RouteConfigurationNotFound = "route_configuration_not_found"; diff --git a/envoy/tracing/trace_driver.h b/envoy/tracing/trace_driver.h index 60e25ec84a68..94a7ca27050e 100644 --- a/envoy/tracing/trace_driver.h +++ b/envoy/tracing/trace_driver.h @@ -136,6 +136,12 @@ class Span { * @return trace ID */ virtual std::string getTraceId() const PURE; + + /** + * Retrieve the span's identifier. + * @return span ID as a hex string + */ + virtual std::string getSpanId() const PURE; }; /** diff --git a/examples/BUILD b/examples/BUILD index 61024d861c46..bcf5f121b8ef 100644 --- a/examples/BUILD +++ b/examples/BUILD @@ -1,3 +1,5 @@ +load("@rules_pkg//pkg:mappings.bzl", "pkg_files") +load("@rules_pkg//pkg:pkg.bzl", "pkg_tar") load( "//bazel:envoy_build_system.bzl", "envoy_package", @@ -51,6 +53,38 @@ filegroup( srcs = glob(["_extra_certs/*.pem"]), ) +filegroup( + name = "docs_rst", + srcs = glob(["**/example.rst"]) + ["//examples/wasm-cc:example.rst"], +) + +pkg_files( + name = "examples_files", + srcs = [":files"], + prefix = "_include", + strip_prefix = "/examples", +) + +genrule( + name = "examples_docs", + srcs = [":docs_rst"], + outs = ["examples_docs.tar.gz"], + cmd = """ + TEMP=$$(mktemp -d) + for location in $(locations :docs_rst); do + example=$$(echo $$location | cut -d/ -f2) + cp -a $$location "$${TEMP}/$${example}.rst" + echo " $${example}" >> "$${TEMP}/_toctree.rst" + done + echo ".. toctree::" > "$${TEMP}/toctree.rst" + echo " :maxdepth: 1" >> "$${TEMP}/toctree.rst" + echo "" >> "$${TEMP}/toctree.rst" + cat "$${TEMP}/_toctree.rst" | sort >> "$${TEMP}/toctree.rst" + rm "$${TEMP}/_toctree.rst" + tar czf $@ -C $${TEMP} . + """, +) + filegroup( name = "lua", srcs = glob(["**/*.lua"]), @@ -58,7 +92,23 @@ filegroup( filegroup( name = "files", - srcs = glob(["**/*"]) + [ + srcs = glob( + [ + "**/*", + ], + exclude = [ + "**/node_modules/**", + "**/*.rst", + ], + ) + [ "//examples/wasm-cc:files", ], ) + +pkg_tar( + name = "docs", + srcs = [":examples_files"], + extension = "tar.gz", + package_dir = "start/sandboxes", + deps = [":examples_docs"], +) diff --git a/docs/root/start/sandboxes/brotli.rst b/examples/brotli/example.rst similarity index 100% rename from docs/root/start/sandboxes/brotli.rst rename to examples/brotli/example.rst diff --git a/docs/root/start/sandboxes/cache.rst b/examples/cache/example.rst similarity index 100% rename from docs/root/start/sandboxes/cache.rst rename to examples/cache/example.rst diff --git a/docs/root/start/sandboxes/cors.rst b/examples/cors/example.rst similarity index 100% rename from docs/root/start/sandboxes/cors.rst rename to examples/cors/example.rst diff --git a/docs/root/start/sandboxes/csrf.rst b/examples/csrf/example.rst similarity index 100% rename from docs/root/start/sandboxes/csrf.rst rename to examples/csrf/example.rst diff --git a/docs/root/start/sandboxes/double-proxy.rst b/examples/double-proxy/example.rst similarity index 100% rename from docs/root/start/sandboxes/double-proxy.rst rename to examples/double-proxy/example.rst diff --git a/docs/root/start/sandboxes/_include/dynamic-config-cp/response-config-active-clusters-updated.json b/examples/dynamic-config-cp/_include/response-config-active-clusters-updated.json similarity index 100% rename from docs/root/start/sandboxes/_include/dynamic-config-cp/response-config-active-clusters-updated.json rename to examples/dynamic-config-cp/_include/response-config-active-clusters-updated.json diff --git a/docs/root/start/sandboxes/_include/dynamic-config-cp/response-config-active-clusters.json b/examples/dynamic-config-cp/_include/response-config-active-clusters.json similarity index 100% rename from docs/root/start/sandboxes/_include/dynamic-config-cp/response-config-active-clusters.json rename to examples/dynamic-config-cp/_include/response-config-active-clusters.json diff --git a/docs/root/start/sandboxes/_include/dynamic-config-cp/response-config-cluster.json b/examples/dynamic-config-cp/_include/response-config-cluster.json similarity index 100% rename from docs/root/start/sandboxes/_include/dynamic-config-cp/response-config-cluster.json rename to examples/dynamic-config-cp/_include/response-config-cluster.json diff --git a/docs/root/start/sandboxes/dynamic-configuration-control-plane.rst b/examples/dynamic-config-cp/example.rst similarity index 92% rename from docs/root/start/sandboxes/dynamic-configuration-control-plane.rst rename to examples/dynamic-config-cp/example.rst index d0c0c908102d..53968bee3c50 100644 --- a/docs/root/start/sandboxes/dynamic-configuration-control-plane.rst +++ b/examples/dynamic-config-cp/example.rst @@ -61,7 +61,7 @@ configuration and you should see the cluster named ``xds_cluster`` configured fo $ curl -s http://localhost:19000/config_dump | jq '.configs[1].static_clusters' -.. literalinclude:: _include/dynamic-config-cp/response-config-cluster.json +.. literalinclude:: /start/sandboxes/_include/dynamic-config-cp/_include/response-config-cluster.json :language: json :emphasize-lines: 10, 18-19 @@ -123,7 +123,7 @@ configuration, you should see it is configured with the ``example_proxy_cluster` $ curl -s http://localhost:19000/config_dump | jq '.configs[1].dynamic_active_clusters' -.. literalinclude:: _include/dynamic-config-cp/response-config-active-clusters.json +.. literalinclude:: /start/sandboxes/_include/dynamic-config-cp/_include/response-config-active-clusters.json :language: json :emphasize-lines: 3, 11, 19-20 @@ -147,7 +147,7 @@ Step 7: Edit ``go`` file and restart the control plane ****************************************************** The example setup starts the ``go-control-plane`` -service with a custom :download:`resource.go <_include/dynamic-config-cp/resource.go>` file which +service with a custom :download:`resource.go ` file which specifies the configuration provided to Envoy. Update this to have Envoy proxy instead to ``service2``. @@ -155,7 +155,7 @@ Update this to have Envoy proxy instead to ``service2``. Edit ``resource.go`` in the dynamic configuration example folder and change the ``UpstreamHost`` from ``service1`` to ``service2``: -.. literalinclude:: _include/dynamic-config-cp/resource.go +.. literalinclude:: /start/sandboxes/_include/dynamic-config-cp/resource.go :language: go :lines: 34-43 :lineno-start: 35 @@ -165,7 +165,7 @@ from ``service1`` to ``service2``: Further down in this file you must also change the configuration snapshot version number from ``"1"`` to ``"2"`` to ensure Envoy sees the configuration as newer: -.. literalinclude:: _include/dynamic-config-cp/resource.go +.. literalinclude:: /start/sandboxes/_include/dynamic-config-cp/resource.go :language: go :lineno-start: 175 :lines: 174-186 @@ -198,7 +198,7 @@ is configured to proxy to ``service2``: $ curl -s http://localhost:19000/config_dump | jq '.configs[1].dynamic_active_clusters' -.. literalinclude:: _include/dynamic-config-cp/response-config-active-clusters-updated.json +.. literalinclude:: /start/sandboxes/_include/dynamic-config-cp/_include/response-config-active-clusters-updated.json :language: json :emphasize-lines: 3, 11, 19-20 diff --git a/docs/root/start/sandboxes/_include/dynamic-config-fs/response-config-active-clusters-updated.json b/examples/dynamic-config-fs/_include/response-config-active-clusters-updated.json similarity index 100% rename from docs/root/start/sandboxes/_include/dynamic-config-fs/response-config-active-clusters-updated.json rename to examples/dynamic-config-fs/_include/response-config-active-clusters-updated.json diff --git a/docs/root/start/sandboxes/_include/dynamic-config-fs/response-config-active-clusters.json b/examples/dynamic-config-fs/_include/response-config-active-clusters.json similarity index 100% rename from docs/root/start/sandboxes/_include/dynamic-config-fs/response-config-active-clusters.json rename to examples/dynamic-config-fs/_include/response-config-active-clusters.json diff --git a/docs/root/start/sandboxes/dynamic-configuration-filesystem.rst b/examples/dynamic-config-fs/example.rst similarity index 88% rename from docs/root/start/sandboxes/dynamic-configuration-filesystem.rst rename to examples/dynamic-config-fs/example.rst index 73da55505409..4935cd5bbe6e 100644 --- a/docs/root/start/sandboxes/dynamic-configuration-filesystem.rst +++ b/examples/dynamic-config-fs/example.rst @@ -70,7 +70,7 @@ configuration, you should see it is configured with the ``example_proxy_cluster` $ curl -s http://localhost:19000/config_dump | jq -r '.configs[1].dynamic_active_clusters' -.. literalinclude:: _include/dynamic-config-fs/response-config-active-clusters.json +.. literalinclude:: /start/sandboxes/_include/dynamic-config-fs/_include/response-config-active-clusters.json :language: json :emphasize-lines: 10, 18-19 @@ -79,15 +79,15 @@ Step 4: Replace ``cds.yaml`` inside the container to update upstream cluster The example setup provides Envoy with two dynamic configuration files: -- :download:`configs/cds.yaml <_include/dynamic-config-fs/configs/cds.yaml>` to provide a :ref:`Cluster +- :download:`configs/cds.yaml ` to provide a :ref:`Cluster discovery service (CDS) `. -- :download:`configs/lds.yaml <_include/dynamic-config-fs/configs/lds.yaml>` to provide a :ref:`Listener +- :download:`configs/lds.yaml ` to provide a :ref:`Listener discovery service (LDS) `. Edit ``cds.yaml`` inside the container and change the cluster address from ``service1`` to ``service2``: -.. literalinclude:: _include/dynamic-config-fs/configs/cds.yaml +.. literalinclude:: /start/sandboxes/_include/dynamic-config-fs/configs/cds.yaml :language: yaml :linenos: :lines: 6-13 @@ -121,7 +121,7 @@ the ``example_proxy_cluster`` should now be configured to proxy to ``service2``: $ curl -s http://localhost:19000/config_dump | jq -r '.configs[1].dynamic_active_clusters' -.. literalinclude:: _include/dynamic-config-fs/response-config-active-clusters-updated.json +.. literalinclude:: /start/sandboxes/_include/dynamic-config-fs/_include/response-config-active-clusters-updated.json :language: json :emphasize-lines: 10, 18-19 diff --git a/docs/root/start/sandboxes/ext_authz.rst b/examples/ext_authz/example.rst similarity index 100% rename from docs/root/start/sandboxes/ext_authz.rst rename to examples/ext_authz/example.rst diff --git a/docs/root/start/sandboxes/fault_injection.rst b/examples/fault-injection/example.rst similarity index 100% rename from docs/root/start/sandboxes/fault_injection.rst rename to examples/fault-injection/example.rst diff --git a/docs/root/start/sandboxes/front_proxy.rst b/examples/front-proxy/example.rst similarity index 100% rename from docs/root/start/sandboxes/front_proxy.rst rename to examples/front-proxy/example.rst diff --git a/docs/root/start/sandboxes/golang-http.rst b/examples/golang-http/example.rst similarity index 100% rename from docs/root/start/sandboxes/golang-http.rst rename to examples/golang-http/example.rst diff --git a/docs/root/start/sandboxes/golang-network.rst b/examples/golang-network/example.rst similarity index 100% rename from docs/root/start/sandboxes/golang-network.rst rename to examples/golang-network/example.rst diff --git a/docs/root/start/sandboxes/grpc_bridge.rst b/examples/grpc-bridge/example.rst similarity index 100% rename from docs/root/start/sandboxes/grpc_bridge.rst rename to examples/grpc-bridge/example.rst diff --git a/examples/grpc-bridge/server/go.mod b/examples/grpc-bridge/server/go.mod index 784d578fb1f6..f5db949d588d 100644 --- a/examples/grpc-bridge/server/go.mod +++ b/examples/grpc-bridge/server/go.mod @@ -4,7 +4,7 @@ go 1.13 require ( github.com/golang/protobuf v1.5.4 - golang.org/x/net v0.25.0 + golang.org/x/net v0.26.0 google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/grpc v1.64.0 ) diff --git a/examples/grpc-bridge/server/go.sum b/examples/grpc-bridge/server/go.sum index 75d93af29f2c..ea3adc7978c1 100644 --- a/examples/grpc-bridge/server/go.sum +++ b/examples/grpc-bridge/server/go.sum @@ -1729,6 +1729,7 @@ golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1m golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1793,6 +1794,8 @@ golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1864,8 +1867,9 @@ golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1925,6 +1929,7 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2017,8 +2022,10 @@ golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -2040,6 +2047,7 @@ golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2061,8 +2069,9 @@ golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2137,6 +2146,7 @@ golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/docs/root/start/sandboxes/gzip.rst b/examples/gzip/example.rst similarity index 100% rename from docs/root/start/sandboxes/gzip.rst rename to examples/gzip/example.rst diff --git a/docs/root/start/sandboxes/jaeger_tracing.rst b/examples/jaeger-tracing/example.rst similarity index 100% rename from docs/root/start/sandboxes/jaeger_tracing.rst rename to examples/jaeger-tracing/example.rst diff --git a/docs/root/start/sandboxes/kafka.rst b/examples/kafka/example.rst similarity index 100% rename from docs/root/start/sandboxes/kafka.rst rename to examples/kafka/example.rst diff --git a/docs/root/start/sandboxes/load_reporting_service.rst b/examples/load-reporting-service/example.rst similarity index 100% rename from docs/root/start/sandboxes/load_reporting_service.rst rename to examples/load-reporting-service/example.rst diff --git a/docs/root/start/sandboxes/local_ratelimit.rst b/examples/local_ratelimit/example.rst similarity index 100% rename from docs/root/start/sandboxes/local_ratelimit.rst rename to examples/local_ratelimit/example.rst diff --git a/docs/root/start/sandboxes/locality_load_balancing.rst b/examples/locality-load-balancing/example.rst similarity index 100% rename from docs/root/start/sandboxes/locality_load_balancing.rst rename to examples/locality-load-balancing/example.rst diff --git a/docs/root/start/sandboxes/lua-cluster-specifier.rst b/examples/lua-cluster-specifier/example.rst similarity index 100% rename from docs/root/start/sandboxes/lua-cluster-specifier.rst rename to examples/lua-cluster-specifier/example.rst diff --git a/docs/root/start/sandboxes/lua.rst b/examples/lua/example.rst similarity index 100% rename from docs/root/start/sandboxes/lua.rst rename to examples/lua/example.rst diff --git a/docs/root/start/sandboxes/mysql.rst b/examples/mysql/example.rst similarity index 100% rename from docs/root/start/sandboxes/mysql.rst rename to examples/mysql/example.rst diff --git a/examples/opentelemetry/Dockerfile-opentelemetry b/examples/opentelemetry/Dockerfile-opentelemetry index e21ca2af794a..9e7f6e5ecced 100644 --- a/examples/opentelemetry/Dockerfile-opentelemetry +++ b/examples/opentelemetry/Dockerfile-opentelemetry @@ -1,7 +1,7 @@ FROM alpine:3.20@sha256:77726ef6b57ddf65bb551896826ec38bc3e53f75cdde31354fbffb4f25238ebd as otelc_curl RUN apk --update add curl -FROM otel/opentelemetry-collector:latest@sha256:fe7714849adfd251129be75e39c5c4fa7031d87146645afa5d391ab957845c18 +FROM otel/opentelemetry-collector:latest@sha256:8473e4b0e81ecc8aa238b5e10a53659ce0e6559d5159d77f8f01f3ecbb1f3391 COPY --from=otelc_curl / / diff --git a/docs/root/start/sandboxes/opentelemetry.rst b/examples/opentelemetry/example.rst similarity index 100% rename from docs/root/start/sandboxes/opentelemetry.rst rename to examples/opentelemetry/example.rst diff --git a/docs/root/start/sandboxes/postgres.rst b/examples/postgres/example.rst similarity index 100% rename from docs/root/start/sandboxes/postgres.rst rename to examples/postgres/example.rst diff --git a/docs/root/start/sandboxes/rbac.rst b/examples/rbac/example.rst similarity index 100% rename from docs/root/start/sandboxes/rbac.rst rename to examples/rbac/example.rst diff --git a/docs/root/start/sandboxes/redis.rst b/examples/redis/example.rst similarity index 100% rename from docs/root/start/sandboxes/redis.rst rename to examples/redis/example.rst diff --git a/docs/root/start/sandboxes/route-mirror.rst b/examples/route-mirror/example.rst similarity index 100% rename from docs/root/start/sandboxes/route-mirror.rst rename to examples/route-mirror/example.rst diff --git a/examples/shared/golang/Dockerfile b/examples/shared/golang/Dockerfile index aaea2fd502a0..25473d6681e6 100644 --- a/examples/shared/golang/Dockerfile +++ b/examples/shared/golang/Dockerfile @@ -3,7 +3,7 @@ RUN rm -f /etc/apt/apt.conf.d/docker-clean \ && echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' | tee /etc/apt/apt.conf.d/keep-cache -FROM golang:1.22.3-bookworm@sha256:5c56bd47228dd572d8a82971cf1f946cd8bb1862a8ec6dc9f3d387cc94136976 as golang-base +FROM golang:1.22.4-bookworm@sha256:aec47843e52fee4436bdd3ce931417fa980e9055658b5142140925eea3044bea as golang-base FROM golang-base as golang-control-plane-builder diff --git a/docs/root/start/sandboxes/_static/spa-cookies.png b/examples/single-page-app/_static/spa-cookies.png similarity index 100% rename from docs/root/start/sandboxes/_static/spa-cookies.png rename to examples/single-page-app/_static/spa-cookies.png diff --git a/docs/root/start/sandboxes/_static/spa-github-oauth.png b/examples/single-page-app/_static/spa-github-oauth.png similarity index 100% rename from docs/root/start/sandboxes/_static/spa-github-oauth.png rename to examples/single-page-app/_static/spa-github-oauth.png diff --git a/docs/root/start/sandboxes/_static/spa-login-github.png b/examples/single-page-app/_static/spa-login-github.png similarity index 100% rename from docs/root/start/sandboxes/_static/spa-login-github.png rename to examples/single-page-app/_static/spa-login-github.png diff --git a/docs/root/start/sandboxes/_static/spa-login.png b/examples/single-page-app/_static/spa-login.png similarity index 100% rename from docs/root/start/sandboxes/_static/spa-login.png rename to examples/single-page-app/_static/spa-login.png diff --git a/docs/root/start/sandboxes/_static/spa-resources.png b/examples/single-page-app/_static/spa-resources.png similarity index 100% rename from docs/root/start/sandboxes/_static/spa-resources.png rename to examples/single-page-app/_static/spa-resources.png diff --git a/docs/root/start/sandboxes/single-page-app.rst b/examples/single-page-app/example.rst similarity index 98% rename from docs/root/start/sandboxes/single-page-app.rst rename to examples/single-page-app/example.rst index d408748155a7..ea702bda6f5f 100644 --- a/docs/root/start/sandboxes/single-page-app.rst +++ b/examples/single-page-app/example.rst @@ -145,7 +145,7 @@ Step 4: Browse to the dev app and login The development app should now be available at http://localhost:10001 and provide a login button: -.. image:: /start/sandboxes/_static/spa-login.png +.. image:: /start/sandboxes/_include/single-page-app/_static/spa-login.png :align: center .. note:: @@ -212,7 +212,7 @@ Envoy then uses this authorization code with its client secret to confirm author Once logged in, you should be able to make queries to the API using the OAuth credentials: -.. image:: /start/sandboxes/_static/spa-resources.png +.. image:: /start/sandboxes/_include/single-page-app/_static/spa-resources.png :align: center .. warning:: @@ -252,7 +252,7 @@ For the sandbox app, :ref:`forward_bearer_token ` is set, and so Envoy also passes the acquired access token back to the user as a cookie: -.. image:: /start/sandboxes/_static/spa-cookies.png +.. image:: /start/sandboxes/_include/single-page-app/_static/spa-cookies.png :align: center This cookie is then passed through Envoy in any subsequent requests to the proxied Myhub API: @@ -476,7 +476,7 @@ more control and is generally preferable. This can be done either at the `user `_ or organization levels: -.. image:: /start/sandboxes/_static/spa-github-oauth.png +.. image:: /start/sandboxes/_include/single-page-app/_static/spa-github-oauth.png :align: center .. note:: @@ -605,7 +605,7 @@ Browse to the production server https://localhost:10000 You can now log in and use the `Github APIs `__.: -.. image:: /start/sandboxes/_static/spa-login-github.png +.. image:: /start/sandboxes/_include/single-page-app/_static/spa-login-github.png :align: center .. seealso:: diff --git a/examples/single-page-app/ui/package.json b/examples/single-page-app/ui/package.json index e57c089c2230..3df85522663a 100644 --- a/examples/single-page-app/ui/package.json +++ b/examples/single-page-app/ui/package.json @@ -25,7 +25,7 @@ "@types/react-dom": "^18.3.0", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", - "@vitejs/plugin-react": "^4.3.0", + "@vitejs/plugin-react": "^4.3.1", "eslint": "^8.57.0", "eslint-config-standard-with-typescript": "^43.0.0", "eslint-plugin-import": "^2.25.2", @@ -35,6 +35,6 @@ "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.7", "typescript": "*", - "vite": "^5.2.12" + "vite": "^5.2.13" } } diff --git a/examples/single-page-app/ui/yarn.lock b/examples/single-page-app/ui/yarn.lock index 9684f150be14..b2125546276a 100644 --- a/examples/single-page-app/ui/yarn.lock +++ b/examples/single-page-app/ui/yarn.lock @@ -1700,10 +1700,10 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== -"@vitejs/plugin-react@^4.3.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-4.3.0.tgz#f20ec2369a92d8abaaefa60da8b7157819d20481" - integrity sha512-KcEbMsn4Dpk+LIbHMj7gDPRKaTMStxxWRkRmxsg/jVdFdJCZWt1SchZcf0M4t8lIKdwwMsEyzhrcOXRrDPtOBw== +"@vitejs/plugin-react@^4.3.1": + version "4.3.1" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-4.3.1.tgz#d0be6594051ded8957df555ff07a991fb618b48e" + integrity sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg== dependencies: "@babel/core" "^7.24.5" "@babel/plugin-transform-react-jsx-self" "^7.24.5" @@ -4290,10 +4290,10 @@ use-sidecar@^1.1.2: detect-node-es "^1.1.0" tslib "^2.0.0" -vite@^5.2.12: - version "5.2.12" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.2.12.tgz#3536c93c58ba18edea4915a2ac573e6537409d97" - integrity sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA== +vite@^5.2.13: + version "5.2.13" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.2.13.tgz#945ababcbe3d837ae2479c29f661cd20bc5e1a80" + integrity sha512-SSq1noJfY9pR3I1TUENL3rQYDQCFqgD+lM6fTRAM8Nv6Lsg5hDLaXkjETVeBt+7vZBCMoibD+6IWnT2mJ+Zb/A== dependencies: esbuild "^0.20.1" postcss "^8.4.38" diff --git a/examples/skywalking/Dockerfile-elasticsearch b/examples/skywalking/Dockerfile-elasticsearch index 3942fa1cf21f..4c2b5e562218 100644 --- a/examples/skywalking/Dockerfile-elasticsearch +++ b/examples/skywalking/Dockerfile-elasticsearch @@ -1 +1 @@ -FROM elasticsearch:8.13.4@sha256:11e1717dd78f46fbecf64f7f27a358d331abb5e95b5451c00730243ee44fb665 +FROM elasticsearch:8.14.0@sha256:dfd92a2938094bdd0fc4203796d7ad3426b72b19b4a29d8b26bb032510c5bed6 diff --git a/docs/root/start/sandboxes/_static/skywalking-services.png b/examples/skywalking/_static/skywalking-services.png similarity index 100% rename from docs/root/start/sandboxes/_static/skywalking-services.png rename to examples/skywalking/_static/skywalking-services.png diff --git a/docs/root/start/sandboxes/_static/skywalking-topology.png b/examples/skywalking/_static/skywalking-topology.png similarity index 100% rename from docs/root/start/sandboxes/_static/skywalking-topology.png rename to examples/skywalking/_static/skywalking-topology.png diff --git a/docs/root/start/sandboxes/_static/skywalking-trace.png b/examples/skywalking/_static/skywalking-trace.png similarity index 100% rename from docs/root/start/sandboxes/_static/skywalking-trace.png rename to examples/skywalking/_static/skywalking-trace.png diff --git a/docs/root/start/sandboxes/skywalking.rst b/examples/skywalking/example.rst similarity index 95% rename from docs/root/start/sandboxes/skywalking.rst rename to examples/skywalking/example.rst index 3b0ffb77151a..d3c8baddc3d7 100644 --- a/docs/root/start/sandboxes/skywalking.rst +++ b/examples/skywalking/example.rst @@ -109,15 +109,15 @@ You should see the Skywalking dashboard. You may need to wait a moment for the traces to be added, but clicking on ``General Service > Services``, you should see the Envoy services listed. -.. image:: /start/sandboxes/_static/skywalking-services.png +.. image:: /start/sandboxes/_include/skywalking/_static/skywalking-services.png From here you can explore the metrics and views that skywalking offers, such as the ``Topology``: -.. image:: /start/sandboxes/_static/skywalking-topology.png +.. image:: /start/sandboxes/_include/skywalking/_static/skywalking-topology.png You can also view tracing information for the requests that you made: -.. image:: /start/sandboxes/_static/skywalking-trace.png +.. image:: /start/sandboxes/_include/skywalking/_static/skywalking-trace.png .. seealso:: diff --git a/docs/root/start/sandboxes/tls-inspector.rst b/examples/tls-inspector/example.rst similarity index 100% rename from docs/root/start/sandboxes/tls-inspector.rst rename to examples/tls-inspector/example.rst diff --git a/docs/root/start/sandboxes/tls-sni.rst b/examples/tls-sni/example.rst similarity index 100% rename from docs/root/start/sandboxes/tls-sni.rst rename to examples/tls-sni/example.rst diff --git a/docs/root/start/sandboxes/tls.rst b/examples/tls/example.rst similarity index 100% rename from docs/root/start/sandboxes/tls.rst rename to examples/tls/example.rst diff --git a/docs/root/start/sandboxes/udp.rst b/examples/udp/example.rst similarity index 100% rename from docs/root/start/sandboxes/udp.rst rename to examples/udp/example.rst diff --git a/examples/wasm-cc/BUILD b/examples/wasm-cc/BUILD index d8a2c77e2c9f..2ea0b7d13906 100644 --- a/examples/wasm-cc/BUILD +++ b/examples/wasm-cc/BUILD @@ -9,6 +9,8 @@ licenses(["notice"]) # Apache 2 envoy_package() +exports_files(["example.rst"]) + selects.config_setting_group( name = "include_wasm_config", match_all = [ @@ -46,5 +48,5 @@ envoy_wasm_cc_binary( filegroup( name = "files", - srcs = glob(["**/*"]), + srcs = glob(["**/*"], exclude = ["example.rst"]), ) diff --git a/docs/root/start/sandboxes/wasm-cc.rst b/examples/wasm-cc/example.rst similarity index 100% rename from docs/root/start/sandboxes/wasm-cc.rst rename to examples/wasm-cc/example.rst diff --git a/docs/root/start/sandboxes/websocket.rst b/examples/websocket/example.rst similarity index 100% rename from docs/root/start/sandboxes/websocket.rst rename to examples/websocket/example.rst diff --git a/docs/root/start/sandboxes/_static/zipkin-ui-dependency.png b/examples/zipkin/_static/zipkin-ui-dependency.png similarity index 100% rename from docs/root/start/sandboxes/_static/zipkin-ui-dependency.png rename to examples/zipkin/_static/zipkin-ui-dependency.png diff --git a/docs/root/start/sandboxes/_static/zipkin-ui.png b/examples/zipkin/_static/zipkin-ui.png similarity index 100% rename from docs/root/start/sandboxes/_static/zipkin-ui.png rename to examples/zipkin/_static/zipkin-ui.png diff --git a/docs/root/start/sandboxes/zipkin.rst b/examples/zipkin/example.rst similarity index 96% rename from docs/root/start/sandboxes/zipkin.rst rename to examples/zipkin/example.rst index 8d402711ae67..34c0f9c82e9e 100644 --- a/docs/root/start/sandboxes/zipkin.rst +++ b/examples/zipkin/example.rst @@ -112,11 +112,11 @@ and other contextual information. Note that Zipkin identifies the Envoy proxies by the name provided in the bootstrap ``node/cluster`` configuration. -.. image:: /start/sandboxes/_static/zipkin-ui.png +.. image:: /start/sandboxes/_include/zipkin/_static/zipkin-ui.png You can also explore the Zipkin dependency UI to view relationships between nodes and the path of traces. -.. image:: /start/sandboxes/_static/zipkin-ui-dependency.png +.. image:: /start/sandboxes/_include/zipkin/_static/zipkin-ui-dependency.png .. seealso:: diff --git a/docs/root/start/sandboxes/zstd.rst b/examples/zstd/example.rst similarity index 100% rename from docs/root/start/sandboxes/zstd.rst rename to examples/zstd/example.rst diff --git a/mobile/.bazelrc b/mobile/.bazelrc index 861a52bec7ae..e8efba686abb 100644 --- a/mobile/.bazelrc +++ b/mobile/.bazelrc @@ -254,9 +254,13 @@ build:mobile-remote-ci-cc-no-exceptions --config=mobile-remote-ci-cc build:mobile-remote-ci-cc-no-exceptions --define envoy_exceptions=disabled build:mobile-remote-ci-cc-no-exceptions --copt=-fno-exceptions -build:mobile-remote-ci-cc-test --config=mobile-remote-ci-cc -test:mobile-remote-ci-cc-test --test_output=all -test:mobile-remote-ci-cc-test --config=mobile-remote-ci-cc +build:mobile-remote-ci-cc-xds-enabled --config=mobile-remote-ci-cc +test:mobile-remote-ci-cc-xds-enabled --config=mobile-remote-ci-cc +test:mobile-remote-ci-cc-xds-enabled --define=envoy_mobile_xds=enabled + +build:mobile-remote-ci-cc-full-protos-enabled --config=mobile-remote-ci-cc +test:mobile-remote-ci-cc-full-protos-enabled --config=mobile-remote-ci-cc +test:mobile-remote-ci-cc-full-protos-enabled --define=envoy_full_protos=enabled build:mobile-remote-ci-macos-kotlin --config=mobile-remote-ci-macos build:mobile-remote-ci-macos-kotlin --fat_apk_cpu=x86_64 diff --git a/mobile/bazel/apple.bzl b/mobile/bazel/apple.bzl index 309f9aa10670..e3600e1eee32 100644 --- a/mobile/bazel/apple.bzl +++ b/mobile/bazel/apple.bzl @@ -34,7 +34,7 @@ def envoy_objc_library(name, hdrs = [], visibility = [], data = [], deps = [], m # ], # ) # -def envoy_mobile_swift_test(name, srcs, size = None, data = [], deps = [], tags = [], repository = "", visibility = [], flaky = False): +def envoy_mobile_swift_test(name, srcs, size = None, data = [], deps = [], tags = [], repository = "", visibility = [], flaky = False, exec_properties = {}): test_lib_name = name + "_lib" swift_library( name = test_lib_name, @@ -57,6 +57,7 @@ def envoy_mobile_swift_test(name, srcs, size = None, data = [], deps = [], tags tags = tags, visibility = visibility, flaky = flaky, + exec_properties = exec_properties, ) def envoy_mobile_objc_test(name, srcs, data = [], deps = [], tags = [], visibility = [], flaky = False): diff --git a/mobile/bazel/kotlin_lib.bzl b/mobile/bazel/envoy_mobile_android_jni.bzl similarity index 100% rename from mobile/bazel/kotlin_lib.bzl rename to mobile/bazel/envoy_mobile_android_jni.bzl diff --git a/mobile/bazel/kotlin_test.bzl b/mobile/bazel/envoy_mobile_android_test.bzl similarity index 89% rename from mobile/bazel/kotlin_test.bzl rename to mobile/bazel/envoy_mobile_android_test.bzl index 808fdef89f8b..b847c7ad430e 100644 --- a/mobile/bazel/kotlin_test.bzl +++ b/mobile/bazel/envoy_mobile_android_test.bzl @@ -1,6 +1,6 @@ load("@build_bazel_rules_android//android:rules.bzl", "android_library", "android_local_test") load("@io_bazel_rules_kotlin//kotlin:android.bzl", "kt_android_local_test") -load("//bazel:kotlin_lib.bzl", "native_lib_name") +load("//bazel:envoy_mobile_android_jni.bzl", "native_lib_name") # A simple macro to define the JVM flags. def jvm_flags(lib_name): @@ -27,15 +27,6 @@ def _contains_all(srcs, extension): # A basic macro to run android based (robolectric) tests with native dependencies def envoy_mobile_android_test(name, srcs, native_lib_name = "", deps = [], native_deps = [], repository = "", exec_properties = {}): - android_library( - name = name + "_test_lib", - custom_package = "io.envoyproxy.envoymobile.test", - manifest = repository + "//bazel:test_manifest.xml", - visibility = ["//visibility:public"], - data = native_deps, - exports = deps, - testonly = True, - ) dependencies = deps + [ repository + "//bazel:envoy_mobile_test_suite", "@maven//:androidx_annotation_annotation", diff --git a/mobile/examples/cc/fetch_client/fetch_client.cc b/mobile/examples/cc/fetch_client/fetch_client.cc index d731dff7cd89..f7123e94fadf 100644 --- a/mobile/examples/cc/fetch_client/fetch_client.cc +++ b/mobile/examples/cc/fetch_client/fetch_client.cc @@ -79,10 +79,10 @@ void Fetch::sendRequest(absl::string_view url_string) { << final_intel.stream_end_ms - final_intel.stream_start_ms << "ms\n"; request_finished.Notify(); }; - stream_callbacks.on_error_ = [&request_finished](EnvoyError error, envoy_stream_intel, + stream_callbacks.on_error_ = [&request_finished](const EnvoyError& error, 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 with error message: " << error.message << "\n"; + << "ms with error message: " << error.message_ << "\n"; request_finished.Notify(); }; stream_callbacks.on_cancel_ = [&request_finished](envoy_stream_intel, diff --git a/mobile/library/common/bridge/utility.cc b/mobile/library/common/bridge/utility.cc index 25d7f6629c35..f63b6a35f3dd 100644 --- a/mobile/library/common/bridge/utility.cc +++ b/mobile/library/common/bridge/utility.cc @@ -32,10 +32,10 @@ envoy_map makeEnvoyMap(std::initializer_list envoy_error toBridgeError(const EnvoyError& error) { envoy_error error_bridge{}; - error_bridge.message = copyToBridgeData(error.message); - error_bridge.error_code = error.error_code; - if (error.attempt_count.has_value()) { - error_bridge.attempt_count = *error.attempt_count; + error_bridge.message = copyToBridgeData(error.message_); + error_bridge.error_code = error.error_code_; + if (error.attempt_count_.has_value()) { + error_bridge.attempt_count = *error.attempt_count_; } return error_bridge; } diff --git a/mobile/library/common/engine_types.h b/mobile/library/common/engine_types.h index a923751d69c0..479bc344b863 100644 --- a/mobile/library/common/engine_types.h +++ b/mobile/library/common/engine_types.h @@ -38,9 +38,9 @@ struct EnvoyEventTracker { /** The Envoy error passed into `EnvoyStreamCallbacks::on_error_` callback. */ struct EnvoyError { - envoy_error_code_t error_code; - std::string message; - absl::optional attempt_count = absl::nullopt; + envoy_error_code_t error_code_; + std::string message_; + absl::optional attempt_count_ = absl::nullopt; }; /** The callbacks for the stream. */ @@ -106,8 +106,8 @@ struct EnvoyStreamCallbacks { * - stream_intel: contains internal stream metrics. * - final_stream_intel: contains final internal stream metrics. */ - absl::AnyInvocable on_error_ = - [](EnvoyError, envoy_stream_intel, envoy_final_stream_intel) {}; + absl::AnyInvocable + on_error_ = [](const EnvoyError&, envoy_stream_intel, envoy_final_stream_intel) {}; /** * The callback for when the stream is cancelled. diff --git a/mobile/library/common/http/client.cc b/mobile/library/common/http/client.cc index a1522e4982c3..463bebdd97d0 100644 --- a/mobile/library/common/http/client.cc +++ b/mobile/library/common/http/client.cc @@ -433,34 +433,45 @@ void Client::DirectStreamCallbacks::latchError() { OptRef request_decoder = direct_stream_.requestDecoder(); if (!request_decoder) { - error_->message = ""; + error_->message_ = ""; return; } const auto& info = request_decoder->streamInfo(); std::vector error_msg_details; if (info.responseCode().has_value()) { - error_->error_code = Bridge::Utility::errorCodeFromLocalStatus( + error_->error_code_ = Bridge::Utility::errorCodeFromLocalStatus( static_cast(info.responseCode().value())); error_msg_details.push_back(absl::StrCat("RESPONSE_CODE: ", info.responseCode().value())); } else if (StreamInfo::isStreamIdleTimeout(info)) { - error_->error_code = ENVOY_REQUEST_TIMEOUT; + error_->error_code_ = ENVOY_REQUEST_TIMEOUT; } else { - error_->error_code = ENVOY_STREAM_RESET; + error_->error_code_ = ENVOY_STREAM_RESET; } - error_msg_details.push_back(absl::StrCat("ERROR_CODE: ", error_->error_code)); + error_msg_details.push_back(absl::StrCat("ERROR_CODE: ", error_->error_code_)); + std::vector response_flags(info.responseFlags().size()); + std::transform(info.responseFlags().begin(), info.responseFlags().end(), response_flags.begin(), + [](StreamInfo::ResponseFlag flag) { return std::to_string(flag.value()); }); + error_msg_details.push_back(absl::StrCat("RESPONSE_FLAGS: ", absl::StrJoin(response_flags, ","))); + if (info.protocol().has_value()) { + // https://github.com/envoyproxy/envoy/blob/fbce85914421145b5ae3210c9313eced63e535b0/envoy/http/protocol.h#L13 + error_msg_details.push_back(absl::StrCat("PROTOCOL: ", *info.protocol())); + } if (std::string resp_code_details = info.responseCodeDetails().value_or(""); !resp_code_details.empty()) { error_msg_details.push_back(absl::StrCat("DETAILS: ", std::move(resp_code_details))); } // The format of the error message propogated to callbacks is: - // RESPONSE_CODE: {RESPONSE_CODE}|ERROR_CODE: {ERROR_CODE}|DETAILS: {DETAILS} + // RESPONSE_CODE: {value}|ERROR_CODE: {value}|RESPONSE_FLAGS: {value}|PROTOCOL: {value}|DETAILS: + // {value} + // // Where RESPONSE_CODE is the HTTP response code from StreamInfo::responseCode(). // ERROR_CODE is of the envoy_error_code_t enum type, and gets mapped from RESPONSE_CODE. + // RESPONSE_FLAGS comes from StreamInfo::responseFlags(). // DETAILS is the contents of StreamInfo::responseCodeDetails(). - error_->message = absl::StrJoin(std::move(error_msg_details), "|"); - error_->attempt_count = info.attemptCount().value_or(0); + error_->message_ = absl::StrJoin(std::move(error_msg_details), "|"); + error_->attempt_count_ = info.attemptCount().value_or(0); } Client::DirectStream::DirectStream(envoy_stream_t stream_handle, Client& http_client) diff --git a/mobile/library/common/internal_engine.cc b/mobile/library/common/internal_engine.cc index 46d6a09e2e61..19c25085c0b8 100644 --- a/mobile/library/common/internal_engine.cc +++ b/mobile/library/common/internal_engine.cc @@ -12,7 +12,7 @@ namespace Envoy { namespace { -constexpr absl::Duration ENGINE_RUNNING_TIMEOUT = absl::Seconds(3); +constexpr absl::Duration ENGINE_RUNNING_TIMEOUT = absl::Seconds(30); } // namespace static std::atomic current_stream_handle_{0}; diff --git a/mobile/library/java/io/envoyproxy/envoymobile/engine/JvmFilterContext.java b/mobile/library/java/io/envoyproxy/envoymobile/engine/JvmFilterContext.java index 59822278408f..9c665a699689 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/engine/JvmFilterContext.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/engine/JvmFilterContext.java @@ -68,11 +68,8 @@ public Object onRequestHeaders(long headerCount, boolean endStream, long[] strea * @return Object[], pair of HTTP filter status and optional modified data. */ public Object onRequestData(ByteBuffer data, boolean endStream, long[] streamIntel) { - // Create a copy of the `data` because the `data` uses direct `ByteBuffer` and the `data` will - // be destroyed after calling this callback. - ByteBuffer copiedData = ByteBuffers.copy(data); return toJniFilterDataStatus( - filter.onRequestData(copiedData, endStream, new EnvoyStreamIntel(streamIntel))); + filter.onRequestData(data, endStream, new EnvoyStreamIntel(streamIntel))); } /** @@ -113,11 +110,8 @@ public Object onResponseHeaders(long headerCount, boolean endStream, long[] stre * @return Object[], pair of HTTP filter status and optional modified data. */ public Object onResponseData(ByteBuffer data, boolean endStream, long[] streamIntel) { - // Create a copy of the `data` because the `data` uses direct `ByteBuffer` and the `data` will - // be destroyed after calling this callback. - ByteBuffer copiedData = ByteBuffers.copy(data); return toJniFilterDataStatus( - filter.onResponseData(copiedData, endStream, new EnvoyStreamIntel(streamIntel))); + filter.onResponseData(data, endStream, new EnvoyStreamIntel(streamIntel))); } /** diff --git a/mobile/library/java/org/chromium/net/impl/Errors.java b/mobile/library/java/org/chromium/net/impl/Errors.java index 66e686c7e419..c5709aba3b78 100644 --- a/mobile/library/java/org/chromium/net/impl/Errors.java +++ b/mobile/library/java/org/chromium/net/impl/Errors.java @@ -21,8 +21,10 @@ public class Errors { public static final int QUIC_INTERNAL_ERROR = 1; private static final Map ENVOYMOBILE_ERROR_TO_NET_ERROR = buildErrorMap(); - /**Subset of errors defined in - * https://github.com/envoyproxy/envoy/blob/main/envoy/stream_info/stream_info.h */ + /** + * Subset of errors defined in + * + */ @LongDef(flag = true, value = {EnvoyMobileError.DNS_RESOLUTION_FAILED, EnvoyMobileError.DURATION_TIMEOUT, EnvoyMobileError.STREAM_IDLE_TIMEOUT, @@ -31,12 +33,12 @@ public class Errors { EnvoyMobileError.UPSTREAM_REMOTE_RESET}) @Retention(RetentionPolicy.SOURCE) public @interface EnvoyMobileError { - long DNS_RESOLUTION_FAILED = 0x4000000; - long DURATION_TIMEOUT = 0x400000; - long STREAM_IDLE_TIMEOUT = 0x10000; - long UPSTREAM_CONNECTION_FAILURE = 0x20; - long UPSTREAM_CONNECTION_TERMINATION = 0x40; - long UPSTREAM_REMOTE_RESET = 0x10; + long DNS_RESOLUTION_FAILED = 1 << 26; // 0x4000000; + long DURATION_TIMEOUT = 1 << 22; // 0x400000; + long STREAM_IDLE_TIMEOUT = 1 << 16; // 0x10000 + long UPSTREAM_CONNECTION_FAILURE = 1 << 5; // 0x20 + long UPSTREAM_CONNECTION_TERMINATION = 1 << 6; // 0x40 + long UPSTREAM_REMOTE_RESET = 1 << 4; // 0x10 } /** Subset of errors defined in chromium/src/net/base/net_error_list.h */ @@ -69,7 +71,7 @@ public String toString() { /** * Maps Envoymobile's errorcode to chromium's net errorcode - * @param responseFlag envoymobile's finalStreamIntel responseFlag + * @param finalStreamIntel envoymobile's finalStreamIntel * @return the NetError that the EnvoyMobileError maps to */ public static NetError mapEnvoyMobileErrorToNetError(EnvoyFinalStreamIntel finalStreamIntel) { @@ -81,19 +83,19 @@ public static NetError mapEnvoyMobileErrorToNetError(EnvoyFinalStreamIntel final return NetError.ERR_INTERNET_DISCONNECTED; } - // Check if negotiated_protocol is quic - // TODO(abeyad): Assigning all errors that happen when using HTTP/3 to QUIC_PROTOCOL_ERROR - // can mask the real error (DNS, upstream connection, etc). Revisit the error conversion logic. - if (finalStreamIntel.getUpstreamProtocol() == UpstreamHttpProtocol.HTTP3) { - return NetError.ERR_QUIC_PROTOCOL_ERROR; - } - // This will only map the first matched error to a NetError code. for (Map.Entry entry : ENVOYMOBILE_ERROR_TO_NET_ERROR.entrySet()) { if ((responseFlag & entry.getKey()) != 0) { return entry.getValue(); } } + + // Check if negotiated_protocol is quic + // TODO(abeyad): Assigning all errors that happen when using HTTP/3 to QUIC_PROTOCOL_ERROR + // can mask the real error (DNS, upstream connection, etc). Revisit the error conversion logic. + if (finalStreamIntel.getUpstreamProtocol() == UpstreamHttpProtocol.HTTP3) { + return NetError.ERR_QUIC_PROTOCOL_ERROR; + } return NetError.ERR_OTHER; } diff --git a/mobile/library/jni/jni_impl.cc b/mobile/library/jni/jni_impl.cc index 0857f7671e9a..1cd9b8923e4e 100644 --- a/mobile/library/jni/jni_impl.cc +++ b/mobile/library/jni/jni_impl.cc @@ -841,7 +841,7 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibra jni_helper.getEnv()->DeleteGlobalRef(java_stream_callbacks_global_ref); }; stream_callbacks.on_error_ = [java_stream_callbacks_global_ref]( - Envoy::EnvoyError error, envoy_stream_intel stream_intel, + const Envoy::EnvoyError& error, envoy_stream_intel stream_intel, envoy_final_stream_intel final_stream_intel) { Envoy::JNI::JniHelper jni_helper(Envoy::JNI::JniHelper::getThreadLocalEnv()); auto java_stream_intel = Envoy::JNI::cppStreamIntelToJavaStreamIntel(jni_helper, stream_intel); @@ -852,10 +852,10 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibra java_stream_callbacks_class.get(), "onError", "(ILjava/lang/String;ILio/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel;" "Lio/envoyproxy/envoymobile/engine/types/EnvoyFinalStreamIntel;)V"); - auto java_error_message = Envoy::JNI::cppStringToJavaString(jni_helper, error.message); + auto java_error_message = Envoy::JNI::cppStringToJavaString(jni_helper, error.message_); jni_helper.callVoidMethod(java_stream_callbacks_global_ref, java_on_error_method_id, - static_cast(error.error_code), java_error_message.get(), - error.attempt_count.value_or(-1), java_stream_intel.get(), + static_cast(error.error_code_), java_error_message.get(), + error.attempt_count_.value_or(-1), java_stream_intel.get(), java_final_stream_intel.get()); // on_error_ is a terminal callback, delete the java_stream_callbacks_global_ref. jni_helper.getEnv()->DeleteGlobalRef(java_stream_callbacks_global_ref); diff --git a/mobile/library/objective-c/EnvoyHTTPStreamImpl.mm b/mobile/library/objective-c/EnvoyHTTPStreamImpl.mm index 430e91cd9f78..16d2d2745e7f 100644 --- a/mobile/library/objective-c/EnvoyHTTPStreamImpl.mm +++ b/mobile/library/objective-c/EnvoyHTTPStreamImpl.mm @@ -183,7 +183,8 @@ - (instancetype)initWithHandle:(envoy_stream_t)handle envoy_final_stream_intel final_stream_intel) { ios_on_complete(stream_intel, final_stream_intel, context); }; - streamCallbacks.on_error_ = [context](Envoy::EnvoyError error, envoy_stream_intel stream_intel, + streamCallbacks.on_error_ = [context](const Envoy::EnvoyError &error, + envoy_stream_intel stream_intel, envoy_final_stream_intel final_stream_intel) { envoy_error bridge_error = Envoy::Bridge::Utility::toBridgeError(error); ios_on_error(bridge_error, stream_intel, final_stream_intel, context); diff --git a/mobile/test/cc/engine_test.cc b/mobile/test/cc/engine_test.cc index b8d111bce024..5beba1d965c0 100644 --- a/mobile/test/cc/engine_test.cc +++ b/mobile/test/cc/engine_test.cc @@ -41,9 +41,8 @@ TEST(EngineTest, SetLogger) { stream_callbacks.on_complete_ = [&](envoy_stream_intel, envoy_final_stream_intel) { stream_complete.Notify(); }; - stream_callbacks.on_error_ = [&](EnvoyError, envoy_stream_intel, envoy_final_stream_intel) { - stream_complete.Notify(); - }; + stream_callbacks.on_error_ = [&](const EnvoyError&, envoy_stream_intel, + envoy_final_stream_intel) { stream_complete.Notify(); }; stream_callbacks.on_cancel_ = [&](envoy_stream_intel, envoy_final_stream_intel) { stream_complete.Notify(); }; @@ -89,9 +88,8 @@ TEST(EngineTest, SetEngineCallbacks) { stream_callbacks.on_complete_ = [&](envoy_stream_intel, envoy_final_stream_intel) { stream_complete.Notify(); }; - stream_callbacks.on_error_ = [&](EnvoyError, envoy_stream_intel, envoy_final_stream_intel) { - stream_complete.Notify(); - }; + stream_callbacks.on_error_ = [&](const EnvoyError&, envoy_stream_intel, + envoy_final_stream_intel) { stream_complete.Notify(); }; stream_callbacks.on_cancel_ = [&](envoy_stream_intel, envoy_final_stream_intel) { stream_complete.Notify(); }; @@ -160,9 +158,8 @@ TEST(EngineTest, DontWaitForOnEngineRunning) { stream_callbacks.on_complete_ = [&](envoy_stream_intel, envoy_final_stream_intel) { stream_complete.Notify(); }; - stream_callbacks.on_error_ = [&](EnvoyError, envoy_stream_intel, envoy_final_stream_intel) { - stream_complete.Notify(); - }; + stream_callbacks.on_error_ = [&](const EnvoyError&, envoy_stream_intel, + envoy_final_stream_intel) { stream_complete.Notify(); }; stream_callbacks.on_cancel_ = [&](envoy_stream_intel, envoy_final_stream_intel) { stream_complete.Notify(); }; diff --git a/mobile/test/cc/integration/lifetimes_test.cc b/mobile/test/cc/integration/lifetimes_test.cc index fc057d7396f3..8a7837e780f5 100644 --- a/mobile/test/cc/integration/lifetimes_test.cc +++ b/mobile/test/cc/integration/lifetimes_test.cc @@ -33,9 +33,8 @@ void sendRequest() { stream_callbacks.on_complete_ = [&](envoy_stream_intel, envoy_final_stream_intel) { stream_complete.Notify(); }; - stream_callbacks.on_error_ = [&](EnvoyError, envoy_stream_intel, envoy_final_stream_intel) { - stream_complete.Notify(); - }; + stream_callbacks.on_error_ = [&](const EnvoyError&, envoy_stream_intel, + envoy_final_stream_intel) { stream_complete.Notify(); }; stream_callbacks.on_cancel_ = [&](envoy_stream_intel, envoy_final_stream_intel) { stream_complete.Notify(); }; diff --git a/mobile/test/cc/integration/receive_data_test.cc b/mobile/test/cc/integration/receive_data_test.cc index e5d9499e94d6..56dd70d3d05d 100644 --- a/mobile/test/cc/integration/receive_data_test.cc +++ b/mobile/test/cc/integration/receive_data_test.cc @@ -40,9 +40,8 @@ TEST(ReceiveDataTest, Success) { stream_callbacks.on_complete_ = [&](envoy_stream_intel, envoy_final_stream_intel) { stream_complete.Notify(); }; - stream_callbacks.on_error_ = [&](EnvoyError, envoy_stream_intel, envoy_final_stream_intel) { - stream_complete.Notify(); - }; + stream_callbacks.on_error_ = [&](const EnvoyError&, envoy_stream_intel, + envoy_final_stream_intel) { stream_complete.Notify(); }; stream_callbacks.on_cancel_ = [&](envoy_stream_intel, envoy_final_stream_intel) { stream_complete.Notify(); }; diff --git a/mobile/test/cc/integration/receive_headers_test.cc b/mobile/test/cc/integration/receive_headers_test.cc index 6c635da9f956..eabe8efa3981 100644 --- a/mobile/test/cc/integration/receive_headers_test.cc +++ b/mobile/test/cc/integration/receive_headers_test.cc @@ -36,9 +36,8 @@ TEST(ReceiveHeadersTest, Success) { stream_callbacks.on_complete_ = [&](envoy_stream_intel, envoy_final_stream_intel) { stream_complete.Notify(); }; - stream_callbacks.on_error_ = [&](EnvoyError, envoy_stream_intel, envoy_final_stream_intel) { - stream_complete.Notify(); - }; + stream_callbacks.on_error_ = [&](const EnvoyError&, envoy_stream_intel, + envoy_final_stream_intel) { stream_complete.Notify(); }; stream_callbacks.on_cancel_ = [&](envoy_stream_intel, envoy_final_stream_intel) { stream_complete.Notify(); }; diff --git a/mobile/test/cc/integration/receive_trailers_test.cc b/mobile/test/cc/integration/receive_trailers_test.cc index 1519cc53d748..9e92d2a1ef2d 100644 --- a/mobile/test/cc/integration/receive_trailers_test.cc +++ b/mobile/test/cc/integration/receive_trailers_test.cc @@ -36,9 +36,8 @@ TEST(ReceiveTrailersTest, Success) { stream_callbacks.on_complete_ = [&](envoy_stream_intel, envoy_final_stream_intel) { stream_complete.Notify(); }; - stream_callbacks.on_error_ = [&](EnvoyError, envoy_stream_intel, envoy_final_stream_intel) { - stream_complete.Notify(); - }; + stream_callbacks.on_error_ = [&](const EnvoyError&, envoy_stream_intel, + envoy_final_stream_intel) { stream_complete.Notify(); }; stream_callbacks.on_cancel_ = [&](envoy_stream_intel, envoy_final_stream_intel) { stream_complete.Notify(); }; diff --git a/mobile/test/cc/integration/send_data_test.cc b/mobile/test/cc/integration/send_data_test.cc index 13a013e6138e..5f4ba1ab2887 100644 --- a/mobile/test/cc/integration/send_data_test.cc +++ b/mobile/test/cc/integration/send_data_test.cc @@ -47,9 +47,8 @@ TEST(SendDataTest, Success) { stream_callbacks.on_complete_ = [&](envoy_stream_intel, envoy_final_stream_intel) { stream_complete.Notify(); }; - stream_callbacks.on_error_ = [&](EnvoyError, envoy_stream_intel, envoy_final_stream_intel) { - stream_complete.Notify(); - }; + stream_callbacks.on_error_ = [&](const EnvoyError&, envoy_stream_intel, + envoy_final_stream_intel) { stream_complete.Notify(); }; stream_callbacks.on_cancel_ = [&](envoy_stream_intel, envoy_final_stream_intel) { stream_complete.Notify(); }; diff --git a/mobile/test/cc/integration/send_headers_test.cc b/mobile/test/cc/integration/send_headers_test.cc index 2b6f017c0b8a..a7bafda0dd44 100644 --- a/mobile/test/cc/integration/send_headers_test.cc +++ b/mobile/test/cc/integration/send_headers_test.cc @@ -47,9 +47,8 @@ TEST(SendHeadersTest, Success) { stream_callbacks.on_complete_ = [&](envoy_stream_intel, envoy_final_stream_intel) { stream_complete.Notify(); }; - stream_callbacks.on_error_ = [&](EnvoyError, envoy_stream_intel, envoy_final_stream_intel) { - stream_complete.Notify(); - }; + stream_callbacks.on_error_ = [&](const EnvoyError&, envoy_stream_intel, + envoy_final_stream_intel) { stream_complete.Notify(); }; stream_callbacks.on_cancel_ = [&](envoy_stream_intel, envoy_final_stream_intel) { stream_complete.Notify(); }; diff --git a/mobile/test/cc/integration/send_trailers_test.cc b/mobile/test/cc/integration/send_trailers_test.cc index 263c817abd46..a773d316e1ce 100644 --- a/mobile/test/cc/integration/send_trailers_test.cc +++ b/mobile/test/cc/integration/send_trailers_test.cc @@ -49,9 +49,8 @@ TEST(SendTrailersTest, Success) { stream_callbacks.on_complete_ = [&](envoy_stream_intel, envoy_final_stream_intel) { stream_complete.Notify(); }; - stream_callbacks.on_error_ = [&](EnvoyError, envoy_stream_intel, envoy_final_stream_intel) { - stream_complete.Notify(); - }; + stream_callbacks.on_error_ = [&](const EnvoyError&, envoy_stream_intel, + envoy_final_stream_intel) { stream_complete.Notify(); }; stream_callbacks.on_cancel_ = [&](envoy_stream_intel, envoy_final_stream_intel) { stream_complete.Notify(); }; diff --git a/mobile/test/common/http/client_test.cc b/mobile/test/common/http/client_test.cc index e672e3a1dc41..8d3f8d286314 100644 --- a/mobile/test/common/http/client_test.cc +++ b/mobile/test/common/http/client_test.cc @@ -22,6 +22,7 @@ using testing::_; using testing::AnyNumber; using testing::ContainsRegex; +using testing::Eq; using testing::NiceMock; using testing::Return; using testing::ReturnPointee; @@ -121,7 +122,7 @@ class ClientTest : public testing::TestWithParam { stream_callbacks.on_complete_ = [&](envoy_stream_intel, envoy_final_stream_intel) -> void { callbacks_called.on_complete_calls_++; }; - stream_callbacks.on_error_ = [&](EnvoyError, envoy_stream_intel, + stream_callbacks.on_error_ = [&](const EnvoyError&, envoy_stream_intel, envoy_final_stream_intel) -> void { callbacks_called.on_error_calls_++; }; @@ -473,12 +474,12 @@ TEST_P(ClientTest, EnvoyLocalError) { // Override the on_error default with some custom checks. StreamCallbacksCalled callbacks_called; auto stream_callbacks = createDefaultStreamCallbacks(callbacks_called); - stream_callbacks.on_error_ = [&](EnvoyError error, envoy_stream_intel, + stream_callbacks.on_error_ = [&](const EnvoyError& error, envoy_stream_intel, envoy_final_stream_intel) -> void { - EXPECT_EQ(error.error_code, ENVOY_CONNECTION_FAILURE); - EXPECT_THAT(error.message, - ContainsRegex("RESPONSE_CODE: 503|ERROR_CODE: 2|DETAILS: failed miserably")); - EXPECT_EQ(error.attempt_count, 123); + EXPECT_EQ(error.error_code_, ENVOY_CONNECTION_FAILURE); + EXPECT_THAT(error.message_, Eq("RESPONSE_CODE: 503|ERROR_CODE: 2|RESPONSE_FLAGS: " + "4,26|PROTOCOL: 3|DETAILS: failed miserably")); + EXPECT_EQ(error.attempt_count_, 123); callbacks_called.on_error_calls_++; }; @@ -498,7 +499,10 @@ TEST_P(ClientTest, EnvoyLocalError) { EXPECT_CALL(dispatcher_, deferredDelete_(_)); stream_info_.setResponseCode(503); stream_info_.setResponseCodeDetails("failed miserably"); + stream_info_.setResponseFlag(StreamInfo::ResponseFlag(StreamInfo::UpstreamRemoteReset)); + stream_info_.setResponseFlag(StreamInfo::ResponseFlag(StreamInfo::DnsResolutionFailed)); stream_info_.setAttemptCount(123); + EXPECT_CALL(stream_info_, protocol()).WillRepeatedly(Return(Http::Protocol::Http3)); response_encoder_->getStream().resetStream(Http::StreamResetReason::LocalConnectionFailure); ASSERT_EQ(callbacks_called.on_headers_calls_, 0); // Ensure that the callbacks on the EnvoyStreamCallbacks were called. @@ -549,11 +553,11 @@ TEST_P(ClientTest, RemoteResetAfterStreamStart) { callbacks_called.end_stream_with_headers_ = false; auto stream_callbacks = createDefaultStreamCallbacks(callbacks_called); - stream_callbacks.on_error_ = [&](EnvoyError error, envoy_stream_intel, + stream_callbacks.on_error_ = [&](const EnvoyError& error, envoy_stream_intel, envoy_final_stream_intel) -> void { - EXPECT_EQ(error.error_code, ENVOY_STREAM_RESET); - EXPECT_THAT(error.message, ContainsRegex("ERROR_CODE: 1")); - EXPECT_EQ(error.attempt_count, 0); + EXPECT_EQ(error.error_code_, ENVOY_STREAM_RESET); + EXPECT_THAT(error.message_, ContainsRegex("ERROR_CODE: 1")); + EXPECT_EQ(error.attempt_count_, 0); callbacks_called.on_error_calls_++; }; diff --git a/mobile/test/common/integration/BUILD b/mobile/test/common/integration/BUILD index 9ee2a0c7cbea..e7d938ce874e 100644 --- a/mobile/test/common/integration/BUILD +++ b/mobile/test/common/integration/BUILD @@ -14,10 +14,6 @@ envoy_mobile_package() envoy_cc_test( name = "client_integration_test", srcs = ["client_integration_test.cc"], - exec_properties = { - # TODO(willengflow): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", - }, linkopts = select({ "@envoy//bazel:apple": [ # For the TestProxyResolutionApi test. @@ -52,10 +48,6 @@ envoy_cc_test( data = [ "@envoy//test/config/integration/certs", ], - exec_properties = { - # TODO(willengflow): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", - }, external_deps = [ "abseil_strings", ], @@ -79,10 +71,6 @@ envoy_cc_test( data = [ "@envoy//test/config/integration/certs", ], - exec_properties = { - # TODO(willengflow): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", - }, external_deps = [ "abseil_strings", ], @@ -105,10 +93,6 @@ envoy_cc_test( data = [ "@envoy//test/config/integration/certs", ], - exec_properties = { - # TODO(willengflow): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", - }, repository = "@envoy", deps = [ ":xds_integration_test_lib", diff --git a/mobile/test/common/integration/client_integration_test.cc b/mobile/test/common/integration/client_integration_test.cc index e9d57dc0886b..37a3e94636cb 100644 --- a/mobile/test/common/integration/client_integration_test.cc +++ b/mobile/test/common/integration/client_integration_test.cc @@ -471,9 +471,8 @@ void ClientIntegrationTest::explicitFlowControlWithCancels(const uint32_t body_s // Allow reading up to 100 bytes. streams[i]->readData(100); }; - stream_callbacks.on_error_ = [](EnvoyError, envoy_stream_intel, envoy_final_stream_intel) { - RELEASE_ASSERT(0, "unexpected"); - }; + stream_callbacks.on_error_ = [](const EnvoyError&, envoy_stream_intel, + envoy_final_stream_intel) { RELEASE_ASSERT(0, "unexpected"); }; auto stream = createNewStream(std::move(stream_callbacks)); stream->sendHeaders(std::make_unique(default_request_headers_), diff --git a/mobile/test/common/integration/rtds_integration_test.cc b/mobile/test/common/integration/rtds_integration_test.cc index 6cda02b8bc5e..8845abd36188 100644 --- a/mobile/test/common/integration/rtds_integration_test.cc +++ b/mobile/test/common/integration/rtds_integration_test.cc @@ -29,7 +29,7 @@ class RtdsIntegrationTest : public XdsIntegrationTest { void createEnvoy() override { Platform::XdsBuilder xds_builder( /*xds_server_address=*/Network::Test::getLoopbackAddressUrlString(ipVersion()), - /*xds_server_port=*/fake_upstreams_[1]->localAddress()->ip()->port()); + /*xds_server_port=*/fake_upstreams_.back()->localAddress()->ip()->port()); // Add the layered runtime config, which includes the RTDS layer. xds_builder.addRuntimeDiscoveryService("some_rtds_resource", /*timeout_in_seconds=*/1) .setSslRootCerts(getUpstreamCert()); @@ -40,6 +40,7 @@ class RtdsIntegrationTest : public XdsIntegrationTest { void SetUp() override { initialize(); } void runReloadTest() { + stream_ = createNewStream(createDefaultStreamCallbacks()); // Send a request on the data plane. stream_->sendHeaders(std::make_unique(default_request_headers_), true); diff --git a/mobile/test/java/integration/BUILD b/mobile/test/java/integration/BUILD index b94e05a160ac..a08f3616130a 100644 --- a/mobile/test/java/integration/BUILD +++ b/mobile/test/java/integration/BUILD @@ -1,5 +1,5 @@ load("@envoy//bazel:envoy_build_system.bzl", "envoy_mobile_package") -load("@envoy_mobile//bazel:kotlin_test.bzl", "envoy_mobile_android_test") +load("@envoy_mobile//bazel:envoy_mobile_android_test.bzl", "envoy_mobile_android_test") licenses(["notice"]) # Apache 2 @@ -10,10 +10,6 @@ envoy_mobile_android_test( srcs = [ "AndroidEngineStartUpTest.java", ], - exec_properties = { - # TODO(lfpino): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", - }, native_deps = [ "//test/jni:libenvoy_jni_with_test_extensions.so", ] + select({ @@ -33,10 +29,6 @@ envoy_mobile_android_test( srcs = [ "AndroidEngineFlowTest.java", ], - exec_properties = { - # TODO(lfpino): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", - }, native_deps = [ "//test/jni:libenvoy_jni_with_test_extensions.so", ] + select({ @@ -57,10 +49,6 @@ envoy_mobile_android_test( srcs = [ "AndroidEngineExplicitFlowTest.java", ], - exec_properties = { - # TODO(lfpino): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", - }, native_deps = [ "//test/jni:libenvoy_jni_with_test_extensions.so", ] + select({ @@ -81,10 +69,6 @@ envoy_mobile_android_test( srcs = [ "AndroidEngineSocketTagTest.java", ], - exec_properties = { - # TODO(lfpino): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", - }, native_deps = [ "//test/jni:libenvoy_jni_with_test_extensions.so", ] + select({ diff --git a/mobile/test/java/io/envoyproxy/envoymobile/engine/BUILD b/mobile/test/java/io/envoyproxy/envoymobile/engine/BUILD index bd1e14e503ae..e441b7a9e42d 100644 --- a/mobile/test/java/io/envoyproxy/envoymobile/engine/BUILD +++ b/mobile/test/java/io/envoyproxy/envoymobile/engine/BUILD @@ -1,5 +1,5 @@ load("@envoy//bazel:envoy_build_system.bzl", "envoy_mobile_package") -load("@envoy_mobile//bazel:kotlin_test.bzl", "envoy_mobile_android_test") +load("@envoy_mobile//bazel:envoy_mobile_android_test.bzl", "envoy_mobile_android_test") licenses(["notice"]) # Apache 2 diff --git a/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/BUILD b/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/BUILD index a58af01812d3..fb431ad3148f 100644 --- a/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/BUILD +++ b/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/BUILD @@ -1,6 +1,6 @@ load("@build_bazel_rules_android//android:rules.bzl", "android_library") load("@envoy//bazel:envoy_build_system.bzl", "envoy_mobile_package") -load("@envoy_mobile//bazel:kotlin_test.bzl", "envoy_mobile_android_test") +load("@envoy_mobile//bazel:envoy_mobile_android_test.bzl", "envoy_mobile_android_test") licenses(["notice"]) # Apache 2 @@ -74,10 +74,6 @@ envoy_mobile_android_test( srcs = [ "QuicTestServerTest.java", ], - exec_properties = { - # TODO(lfpino): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", - }, native_deps = [ "//test/jni:libenvoy_jni_with_test_extensions.so", ] + select({ diff --git a/mobile/test/java/io/envoyproxy/envoymobile/jni/BUILD b/mobile/test/java/io/envoyproxy/envoymobile/jni/BUILD index 00d488778572..5e236b4fd137 100644 --- a/mobile/test/java/io/envoyproxy/envoymobile/jni/BUILD +++ b/mobile/test/java/io/envoyproxy/envoymobile/jni/BUILD @@ -1,5 +1,5 @@ load("@envoy//bazel:envoy_build_system.bzl", "envoy_mobile_package") -load("@envoy_mobile//bazel:kotlin_test.bzl", "envoy_mobile_android_test") +load("@envoy_mobile//bazel:envoy_mobile_android_test.bzl", "envoy_mobile_android_test") licenses(["notice"]) # Apache 2 diff --git a/mobile/test/java/io/envoyproxy/envoymobile/utilities/BUILD b/mobile/test/java/io/envoyproxy/envoymobile/utilities/BUILD index de8657c4d0da..4e7fe7e835d5 100644 --- a/mobile/test/java/io/envoyproxy/envoymobile/utilities/BUILD +++ b/mobile/test/java/io/envoyproxy/envoymobile/utilities/BUILD @@ -1,5 +1,5 @@ load("@envoy//bazel:envoy_build_system.bzl", "envoy_mobile_package") -load("@envoy_mobile//bazel:kotlin_test.bzl", "envoy_mobile_android_test") +load("@envoy_mobile//bazel:envoy_mobile_android_test.bzl", "envoy_mobile_android_test") licenses(["notice"]) # Apache 2 diff --git a/mobile/test/java/org/chromium/net/BUILD b/mobile/test/java/org/chromium/net/BUILD index 19b6d3e91d64..8e615ab387b2 100644 --- a/mobile/test/java/org/chromium/net/BUILD +++ b/mobile/test/java/org/chromium/net/BUILD @@ -1,5 +1,5 @@ load("@envoy//bazel:envoy_build_system.bzl", "envoy_mobile_package") -load("@envoy_mobile//bazel:kotlin_test.bzl", "envoy_mobile_android_test") +load("@envoy_mobile//bazel:envoy_mobile_android_test.bzl", "envoy_mobile_android_test") licenses(["notice"]) # Apache 2 @@ -17,10 +17,6 @@ envoy_mobile_android_test( "UploadDataProvidersTest.java", "UrlResponseInfoTest.java", ], - exec_properties = { - # TODO(lfpino): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", - }, native_deps = [ "//test/jni:libenvoy_jni_with_test_extensions.so", ] + select({ @@ -47,10 +43,6 @@ envoy_mobile_android_test( srcs = [ "CronetUrlRequestContextTest.java", ], - exec_properties = { - # TODO(lfpino): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", - }, native_deps = [ "//test/jni:libenvoy_jni_with_test_extensions.so", ] + select({ @@ -76,10 +68,6 @@ envoy_mobile_android_test( srcs = [ "CronetUrlRequestTest.java", ], - exec_properties = { - # TODO(lfpino): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", - }, native_deps = [ "//test/jni:libenvoy_jni_with_test_extensions.so", ] + select({ @@ -105,10 +93,6 @@ envoy_mobile_android_test( srcs = [ "BidirectionalStreamTest.java", ], - exec_properties = { - # TODO(lfpino): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", - }, native_deps = [ "//test/jni:libenvoy_jni_with_test_extensions.so", ] + select({ diff --git a/mobile/test/java/org/chromium/net/CronetUrlRequestTest.java b/mobile/test/java/org/chromium/net/CronetUrlRequestTest.java index b6acfda3f8d9..7e8c6d8f39ec 100644 --- a/mobile/test/java/org/chromium/net/CronetUrlRequestTest.java +++ b/mobile/test/java/org/chromium/net/CronetUrlRequestTest.java @@ -2055,25 +2055,29 @@ public void testDestroyUploadDataStreamAdapterOnSucceededCallback() throws Excep @SmallTest @Feature({"Cronet"}) public void testErrorCodes() throws Exception { - checkSpecificErrorCode(EnvoyMobileError.DNS_RESOLUTION_FAILED, NetError.ERR_NAME_NOT_RESOLVED, - NetworkException.ERROR_HOSTNAME_NOT_RESOLVED, "NAME_NOT_RESOLVED", false, - /*error_details=*/"RESPONSE_CODE: 400|ERROR_CODE: 0"); - checkSpecificErrorCode(EnvoyMobileError.UPSTREAM_CONNECTION_TERMINATION, - NetError.ERR_CONNECTION_CLOSED, NetworkException.ERROR_CONNECTION_CLOSED, - "CONNECTION_CLOSED", true, - /*error_details=*/"RESPONSE_CODE: 400|ERROR_CODE: 0"); - checkSpecificErrorCode(EnvoyMobileError.UPSTREAM_CONNECTION_FAILURE, - NetError.ERR_CONNECTION_REFUSED, - NetworkException.ERROR_CONNECTION_REFUSED, "CONNECTION_REFUSED", false, - /*error_details=*/"RESPONSE_CODE: 400|ERROR_CODE: 0"); - checkSpecificErrorCode(EnvoyMobileError.UPSTREAM_REMOTE_RESET, NetError.ERR_CONNECTION_RESET, - NetworkException.ERROR_CONNECTION_RESET, "CONNECTION_RESET", true, - /*error_details=*/"RESPONSE_CODE: 400|ERROR_CODE: 0"); - checkSpecificErrorCode(EnvoyMobileError.STREAM_IDLE_TIMEOUT, NetError.ERR_TIMED_OUT, - NetworkException.ERROR_TIMED_OUT, "TIMED_OUT", true, - /*error_details=*/"RESPONSE_CODE: 400|ERROR_CODE: 0"); - checkSpecificErrorCode(0x2000, NetError.ERR_OTHER, NetworkException.ERROR_OTHER, "OTHER", false, - /*error_details=*/"RESPONSE_CODE: 400|ERROR_CODE: 0"); + checkSpecificErrorCode( + EnvoyMobileError.DNS_RESOLUTION_FAILED, NetError.ERR_NAME_NOT_RESOLVED, + NetworkException.ERROR_HOSTNAME_NOT_RESOLVED, "NAME_NOT_RESOLVED", false, + /*error_details=*/"RESPONSE_CODE: 400|ERROR_CODE: 0|RESPONSE_FLAGS: 26|PROTOCOL: 1"); + checkSpecificErrorCode( + EnvoyMobileError.UPSTREAM_CONNECTION_TERMINATION, NetError.ERR_CONNECTION_CLOSED, + NetworkException.ERROR_CONNECTION_CLOSED, "CONNECTION_CLOSED", true, + /*error_details=*/"RESPONSE_CODE: 400|ERROR_CODE: 0|RESPONSE_FLAGS: 6|PROTOCOL: 1"); + checkSpecificErrorCode( + EnvoyMobileError.UPSTREAM_CONNECTION_FAILURE, NetError.ERR_CONNECTION_REFUSED, + NetworkException.ERROR_CONNECTION_REFUSED, "CONNECTION_REFUSED", false, + /*error_details=*/"RESPONSE_CODE: 400|ERROR_CODE: 0|RESPONSE_FLAGS: 5|PROTOCOL: 1"); + checkSpecificErrorCode( + EnvoyMobileError.UPSTREAM_REMOTE_RESET, NetError.ERR_CONNECTION_RESET, + NetworkException.ERROR_CONNECTION_RESET, "CONNECTION_RESET", true, + /*error_details=*/"RESPONSE_CODE: 400|ERROR_CODE: 0|RESPONSE_FLAGS: 4|PROTOCOL: 1"); + checkSpecificErrorCode( + EnvoyMobileError.STREAM_IDLE_TIMEOUT, NetError.ERR_TIMED_OUT, + NetworkException.ERROR_TIMED_OUT, "TIMED_OUT", true, + /*error_details=*/"RESPONSE_CODE: 400|ERROR_CODE: 0|RESPONSE_FLAGS: 16|PROTOCOL: 1"); + checkSpecificErrorCode( + 0x2000, NetError.ERR_OTHER, NetworkException.ERROR_OTHER, "OTHER", false, + /*error_details=*/"RESPONSE_CODE: 400|ERROR_CODE: 0|RESPONSE_FLAGS: 13|PROTOCOL: 1"); } /* @@ -2095,10 +2099,10 @@ public void testInternetDisconnectedError() throws Exception { androidNetworkMonitor.onReceive(getContext(), intent); // send request and confirm errorcode - checkSpecificErrorCode(EnvoyMobileError.DNS_RESOLUTION_FAILED, - NetError.ERR_INTERNET_DISCONNECTED, - NetworkException.ERROR_INTERNET_DISCONNECTED, "INTERNET_DISCONNECTED", - false, /*error_details=*/"RESPONSE_CODE: 400|ERROR_CODE: 0"); + checkSpecificErrorCode( + EnvoyMobileError.DNS_RESOLUTION_FAILED, NetError.ERR_INTERNET_DISCONNECTED, + NetworkException.ERROR_INTERNET_DISCONNECTED, "INTERNET_DISCONNECTED", false, + /*error_details=*/"RESPONSE_CODE: 400|ERROR_CODE: 0|RESPONSE_FLAGS: 26|PROTOCOL: 1"); // bring back online since the AndroidNetworkMonitor class is a singleton connectivityManager.setActiveNetworkInfo(networkInfo); @@ -2316,10 +2320,10 @@ public void test404Head() throws Exception { NativeTestServer.getFileURL("/notfound.html"), callback, callback.getExecutor()); builder.setHttpMethod("HEAD").build().start(); callback.blockForDone(); - checkSpecificErrorCode(EnvoyMobileError.UPSTREAM_CONNECTION_FAILURE, - NetError.ERR_CONNECTION_REFUSED, - NetworkException.ERROR_CONNECTION_REFUSED, "CONNECTION_REFUSED", false, - /*error_details=*/"RESPONSE_CODE: 400|ERROR_CODE: 0"); + checkSpecificErrorCode( + EnvoyMobileError.UPSTREAM_CONNECTION_FAILURE, NetError.ERR_CONNECTION_REFUSED, + NetworkException.ERROR_CONNECTION_REFUSED, "CONNECTION_REFUSED", false, + /*error_details=*/"RESPONSE_CODE: 400|ERROR_CODE: 0|RESPONSE_FLAGS: 5|PROTOCOL: 1"); } @Test diff --git a/mobile/test/java/org/chromium/net/impl/BUILD b/mobile/test/java/org/chromium/net/impl/BUILD index 8bde99407885..3c34f8e7c9f3 100644 --- a/mobile/test/java/org/chromium/net/impl/BUILD +++ b/mobile/test/java/org/chromium/net/impl/BUILD @@ -1,5 +1,5 @@ load("@envoy//bazel:envoy_build_system.bzl", "envoy_mobile_package") -load("@envoy_mobile//bazel:kotlin_test.bzl", "envoy_mobile_android_test") +load("@envoy_mobile//bazel:envoy_mobile_android_test.bzl", "envoy_mobile_android_test") licenses(["notice"]) # Apache 2 @@ -16,10 +16,6 @@ envoy_mobile_android_test( "ErrorsTest.java", "UrlRequestCallbackTester.java", ], - exec_properties = { - # TODO(lfpino): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", - }, native_deps = [ "//test/jni:libenvoy_jni_with_test_extensions.so", ] + select({ diff --git a/mobile/test/java/org/chromium/net/impl/ErrorsTest.java b/mobile/test/java/org/chromium/net/impl/ErrorsTest.java index 6536beda875c..dd1db3b8ee0d 100644 --- a/mobile/test/java/org/chromium/net/impl/ErrorsTest.java +++ b/mobile/test/java/org/chromium/net/impl/ErrorsTest.java @@ -13,7 +13,6 @@ @RunWith(RobolectricTestRunner.class) public class ErrorsTest { - @Test public void testMapEnvoyMobileErrorToNetErrorHttp3() throws Exception { // 8 corresponds to NoRouteFound in StreamInfo::CoreResponseFlag: @@ -35,6 +34,16 @@ public void testMapEnvoyMobileErrorToNetErrorFoundInMap() throws Exception { assertEquals(NetError.ERR_CONNECTION_RESET, error); } + @Test + public void testMapEnvoyMobileErrorToNetErrorFoundInMapOnHttp3() throws Exception { + // 4 corresponds to UpstreamRemoteReset in StreamInfo::CoreResponseFlag: + // https://github.com/envoyproxy/envoy/blob/410e9a77bd6b74abb3e1545b4fd077e734d0fce3/envoy/stream_info/stream_info.h#L39 + long responseFlags = 1L << 4; + EnvoyFinalStreamIntel intel = constructStreamIntel(responseFlags, UpstreamHttpProtocol.HTTP3); + NetError error = mapEnvoyMobileErrorToNetError(intel); + assertEquals(NetError.ERR_CONNECTION_RESET, error); + } + @Test public void testMapEnvoyMobileErrorToNetErrorMultipleResponseFlags() throws Exception { // 4 corresponds to UpstreamRemoteReset and 16 corresponds to StreamIdleTimeout in diff --git a/mobile/test/java/org/chromium/net/testing/BUILD b/mobile/test/java/org/chromium/net/testing/BUILD index d086f967686e..96b8b41c1724 100644 --- a/mobile/test/java/org/chromium/net/testing/BUILD +++ b/mobile/test/java/org/chromium/net/testing/BUILD @@ -1,6 +1,6 @@ load("@build_bazel_rules_android//android:rules.bzl", "android_library") load("@envoy//bazel:envoy_build_system.bzl", "envoy_mobile_package") -load("@envoy_mobile//bazel:kotlin_test.bzl", "envoy_mobile_android_test") +load("@envoy_mobile//bazel:envoy_mobile_android_test.bzl", "envoy_mobile_android_test") licenses(["notice"]) # Apache 2 @@ -84,10 +84,6 @@ envoy_mobile_android_test( srcs = [ "Http2TestServerTest.java", ], - exec_properties = { - # TODO(lfpino): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", - }, native_deps = [ "//test/jni:libenvoy_jni_with_test_extensions.so", ] + select({ @@ -108,10 +104,6 @@ envoy_mobile_android_test( srcs = [ "AndroidEnvoyExplicitH2FlowTest.java", ], - exec_properties = { - # TODO(lfpino): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", - }, native_deps = [ "//test/jni:libenvoy_jni_with_test_extensions.so", ] + select({ diff --git a/mobile/test/java/org/chromium/net/urlconnection/BUILD b/mobile/test/java/org/chromium/net/urlconnection/BUILD index c5424f48828a..8505f860f917 100644 --- a/mobile/test/java/org/chromium/net/urlconnection/BUILD +++ b/mobile/test/java/org/chromium/net/urlconnection/BUILD @@ -1,5 +1,5 @@ load("@envoy//bazel:envoy_build_system.bzl", "envoy_mobile_package") -load("@envoy_mobile//bazel:kotlin_test.bzl", "envoy_mobile_android_test") +load("@envoy_mobile//bazel:envoy_mobile_android_test.bzl", "envoy_mobile_android_test") licenses(["notice"]) # Apache 2 @@ -17,10 +17,6 @@ envoy_mobile_android_test( "MessageLoopTest.java", "TestUtil.java", ], - exec_properties = { - # TODO(lfpino): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", - }, native_deps = [ "//test/jni:libenvoy_jni_with_test_extensions.so", ] + select({ diff --git a/mobile/test/jni/BUILD b/mobile/test/jni/BUILD index 31b628a3230e..22afd615eef8 100644 --- a/mobile/test/jni/BUILD +++ b/mobile/test/jni/BUILD @@ -1,5 +1,5 @@ load("@envoy//bazel:envoy_build_system.bzl", "envoy_mobile_package") -load("//bazel:kotlin_lib.bzl", "envoy_mobile_so_to_jni_lib") +load("//bazel:envoy_mobile_android_jni.bzl", "envoy_mobile_so_to_jni_lib") licenses(["notice"]) # Apache 2 diff --git a/mobile/test/kotlin/integration/BUILD b/mobile/test/kotlin/integration/BUILD index 9fb1c3f6e28b..2c376e6322aa 100644 --- a/mobile/test/kotlin/integration/BUILD +++ b/mobile/test/kotlin/integration/BUILD @@ -1,5 +1,5 @@ load("@envoy//bazel:envoy_build_system.bzl", "envoy_mobile_package") -load("@envoy_mobile//bazel:kotlin_test.bzl", "envoy_mobile_android_test") +load("@envoy_mobile//bazel:envoy_mobile_android_test.bzl", "envoy_mobile_android_test") load("@io_bazel_rules_kotlin//kotlin:android.bzl", "kt_android_library") licenses(["notice"]) # Apache 2 @@ -332,8 +332,6 @@ envoy_mobile_android_test( "FilterThrowingExceptionTest.kt", ], exec_properties = { - # TODO(lfpino): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", "dockerNetwork": "standard", }, native_deps = [ diff --git a/mobile/test/kotlin/integration/proxying/BUILD b/mobile/test/kotlin/integration/proxying/BUILD index 492baa548d90..0f8f5864cfa1 100644 --- a/mobile/test/kotlin/integration/proxying/BUILD +++ b/mobile/test/kotlin/integration/proxying/BUILD @@ -1,5 +1,5 @@ load("@envoy//bazel:envoy_build_system.bzl", "envoy_mobile_package") -load("@envoy_mobile//bazel:kotlin_test.bzl", "envoy_mobile_android_test") +load("@envoy_mobile//bazel:envoy_mobile_android_test.bzl", "envoy_mobile_android_test") licenses(["notice"]) # Apache 2 @@ -11,8 +11,6 @@ envoy_mobile_android_test( "ProxyInfoIntentPerformHTTPRequestUsingProxyTest.kt", ], exec_properties = { - # TODO(lfpino): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", "dockerNetwork": "standard", }, native_deps = [ @@ -38,8 +36,6 @@ envoy_mobile_android_test( "ProxyInfoIntentPerformHTTPSRequestUsingProxyTest.kt", ], exec_properties = { - # TODO(lfpino): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", "dockerNetwork": "standard", }, native_deps = [ @@ -65,8 +61,6 @@ envoy_mobile_android_test( "ProxyInfoIntentPerformHTTPSRequestUsingAsyncProxyTest.kt", ], exec_properties = { - # TODO(lfpino): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", "dockerNetwork": "standard", }, native_deps = [ @@ -92,8 +86,6 @@ envoy_mobile_android_test( "ProxyInfoIntentPerformHTTPSRequestBadHostnameTest.kt", ], exec_properties = { - # TODO(lfpino): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", "dockerNetwork": "standard", }, native_deps = [ @@ -119,8 +111,6 @@ envoy_mobile_android_test( "ProxyPollPerformHTTPRequestUsingProxyTest.kt", ], exec_properties = { - # TODO(lfpino): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", "dockerNetwork": "standard", }, native_deps = [ @@ -146,8 +136,6 @@ envoy_mobile_android_test( "ProxyPollPerformHTTPRequestWithoutUsingPACProxyTest.kt", ], exec_properties = { - # TODO(lfpino): Remove this once the sandboxNetwork=off works for ipv4 localhost addresses. - "sandboxNetwork": "standard", "dockerNetwork": "standard", }, native_deps = [ diff --git a/mobile/test/kotlin/io/envoyproxy/envoymobile/BUILD b/mobile/test/kotlin/io/envoyproxy/envoymobile/BUILD index 48853e4413c8..bd3c32617858 100644 --- a/mobile/test/kotlin/io/envoyproxy/envoymobile/BUILD +++ b/mobile/test/kotlin/io/envoyproxy/envoymobile/BUILD @@ -1,5 +1,5 @@ load("@envoy//bazel:envoy_build_system.bzl", "envoy_mobile_package") -load("@envoy_mobile//bazel:kotlin_test.bzl", "envoy_mobile_android_test") +load("@envoy_mobile//bazel:envoy_mobile_android_test.bzl", "envoy_mobile_android_test") licenses(["notice"]) # Apache 2 diff --git a/mobile/test/kotlin/io/envoyproxy/envoymobile/stats/BUILD b/mobile/test/kotlin/io/envoyproxy/envoymobile/stats/BUILD index 267a6a3a5c04..1f46f947bb59 100644 --- a/mobile/test/kotlin/io/envoyproxy/envoymobile/stats/BUILD +++ b/mobile/test/kotlin/io/envoyproxy/envoymobile/stats/BUILD @@ -1,5 +1,5 @@ load("@envoy//bazel:envoy_build_system.bzl", "envoy_mobile_package") -load("@envoy_mobile//bazel:kotlin_test.bzl", "envoy_mobile_android_test") +load("@envoy_mobile//bazel:envoy_mobile_android_test.bzl", "envoy_mobile_android_test") licenses(["notice"]) # Apache 2 diff --git a/mobile/test/objective-c/EnvoyTestServer.h b/mobile/test/objective-c/EnvoyTestServer.h index 13b0ea1f48bf..dbc9758d9e64 100644 --- a/mobile/test/objective-c/EnvoyTestServer.h +++ b/mobile/test/objective-c/EnvoyTestServer.h @@ -4,9 +4,10 @@ // Interface for starting and managing a test server. Calls into to test_server.cc // -// NB: Any test that utilizes this class must have a `no-remote-exec` tag in its BUILD target. -// EnvoyTestServer binds to a listening socket on the machine it runs on, and on CI, this -// operation is not permitted in remote execution environments. +// NB: Any test that utilizes this class must have a `"sandboxNetwork": "standard"` +// `exec_properties` in its BUILD target to allow binding a listening socket on +// the EngFlow machines +// (https://docs.engflow.com/re/client/platform-options-reference.html#sandboxallowed). @interface EnvoyTestServer : NSObject // Get the port of the upstream server. diff --git a/mobile/test/swift/BUILD b/mobile/test/swift/BUILD index 80b407888809..7077c25b6368 100644 --- a/mobile/test/swift/BUILD +++ b/mobile/test/swift/BUILD @@ -20,8 +20,10 @@ envoy_mobile_swift_test( "RetryPolicyMapperTests.swift", "RetryPolicyTests.swift", ], + exec_properties = { + "sandboxNetwork": "standard", + }, flaky = True, # TODO(jpsim): Fix timeouts when running these tests on CI - tags = ["no-remote-exec"], # TODO(32551): Re-enable remote exec visibility = ["//visibility:public"], deps = [ "//library/objective-c:envoy_engine_objc_lib", diff --git a/mobile/test/swift/integration/BUILD b/mobile/test/swift/integration/BUILD index 5eeac3b43fdf..fb0f71f20fd0 100644 --- a/mobile/test/swift/integration/BUILD +++ b/mobile/test/swift/integration/BUILD @@ -10,11 +10,9 @@ envoy_mobile_swift_test( srcs = [ "EndToEndNetworkingTest.swift", ], - # The test starts an Envoy server, which binds to a local socket. Binding to a local socket is - # not permitted on remote execution hosts, so we have to add the `no-remote-exec` tag. - tags = [ - "no-remote-exec", - ], + exec_properties = { + "sandboxNetwork": "standard", + }, visibility = ["//visibility:public"], deps = [ ":test_extensions", @@ -28,7 +26,9 @@ envoy_mobile_swift_test( srcs = [ "CancelStreamTest.swift", ], - tags = ["no-remote-exec"], # TODO(32551): Re-enable remote exec + exec_properties = { + "sandboxNetwork": "standard", + }, visibility = ["//visibility:public"], deps = [ ":test_extensions", @@ -42,7 +42,9 @@ envoy_mobile_swift_test( srcs = [ "EngineApiTest.swift", ], - tags = ["no-remote-exec"], # TODO(32551): Re-enable remote exec + exec_properties = { + "sandboxNetwork": "standard", + }, visibility = ["//visibility:public"], deps = [ ":test_extensions", @@ -55,7 +57,9 @@ envoy_mobile_swift_test( srcs = [ "FilterResetIdleTest.swift", ], - tags = ["no-remote-exec"], # TODO(32551): Re-enable remote exec + exec_properties = { + "sandboxNetwork": "standard", + }, visibility = ["//visibility:public"], deps = [ ":test_extensions", @@ -69,7 +73,9 @@ envoy_mobile_swift_test( srcs = [ "GRPCReceiveErrorTest.swift", ], - tags = ["no-remote-exec"], # TODO(32551): Re-enable remote exec + exec_properties = { + "sandboxNetwork": "standard", + }, visibility = ["//visibility:public"], deps = [ ":test_extensions", @@ -83,11 +89,9 @@ envoy_mobile_swift_test( srcs = [ "IdleTimeoutTest.swift", ], - # The test starts an Envoy server, which binds to a local socket. Binding to a local socket is - # not permitted on remote execution hosts, so we have to add the `no-remote-exec` tag. - tags = [ - "no-remote-exec", - ], + exec_properties = { + "sandboxNetwork": "standard", + }, visibility = ["//visibility:public"], deps = [ ":test_extensions", @@ -101,7 +105,9 @@ envoy_mobile_swift_test( srcs = [ "KeyValueStoreTest.swift", ], - tags = ["no-remote-exec"], # TODO(32551): Re-enable remote exec + exec_properties = { + "sandboxNetwork": "standard", + }, visibility = ["//visibility:public"], deps = [ ":test_extensions", @@ -115,7 +121,9 @@ envoy_mobile_swift_test( srcs = [ "ReceiveDataTest.swift", ], - tags = ["no-remote-exec"], # TODO(32551): Re-enable remote exec + exec_properties = { + "sandboxNetwork": "standard", + }, visibility = ["//visibility:public"], deps = [ ":test_extensions", @@ -129,7 +137,9 @@ envoy_mobile_swift_test( srcs = [ "ReceiveErrorTest.swift", ], - tags = ["no-remote-exec"], # TODO(32551): Re-enable remote exec + exec_properties = { + "sandboxNetwork": "standard", + }, visibility = ["//visibility:public"], deps = [ ":test_extensions", @@ -142,7 +152,9 @@ envoy_mobile_swift_test( srcs = [ "ResetConnectivityStateTest.swift", ], - tags = ["no-remote-exec"], # TODO(32551): Re-enable remote exec + exec_properties = { + "sandboxNetwork": "standard", + }, visibility = ["//visibility:public"], deps = [ ":test_extensions", @@ -156,7 +168,9 @@ envoy_mobile_swift_test( srcs = [ "SendDataTest.swift", ], - tags = ["no-remote-exec"], # TODO(32551): Re-enable remote exec + exec_properties = { + "sandboxNetwork": "standard", + }, visibility = ["//visibility:public"], deps = [ ":test_extensions", @@ -170,7 +184,9 @@ envoy_mobile_swift_test( srcs = [ "SendHeadersTest.swift", ], - tags = ["no-remote-exec"], # TODO(32551): Re-enable remote exec + exec_properties = { + "sandboxNetwork": "standard", + }, visibility = ["//visibility:public"], deps = [ ":test_extensions", @@ -184,7 +200,9 @@ envoy_mobile_swift_test( srcs = [ "SendTrailersTest.swift", ], - tags = ["no-remote-exec"], # TODO(32551): Re-enable remote exec + exec_properties = { + "sandboxNetwork": "standard", + }, visibility = ["//visibility:public"], deps = [ ":test_extensions", @@ -198,7 +216,9 @@ envoy_mobile_swift_test( srcs = [ "SetEventTrackerTest.swift", ], - tags = ["no-remote-exec"], # TODO(32551): Re-enable remote exec + exec_properties = { + "sandboxNetwork": "standard", + }, visibility = ["//visibility:public"], deps = [ ":test_extensions", @@ -212,7 +232,9 @@ envoy_mobile_swift_test( srcs = [ "SetEventTrackerTestNoTracker.swift", ], - tags = ["no-remote-exec"], # TODO(32551): Re-enable remote exec + exec_properties = { + "sandboxNetwork": "standard", + }, visibility = ["//visibility:public"], deps = [ ":test_extensions", @@ -226,7 +248,9 @@ envoy_mobile_swift_test( srcs = [ "SetLoggerTest.swift", ], - tags = ["no-remote-exec"], # TODO(32551): Re-enable remote exec + exec_properties = { + "sandboxNetwork": "standard", + }, visibility = ["//visibility:public"], deps = [ ":test_extensions", @@ -240,7 +264,9 @@ envoy_mobile_swift_test( srcs = [ "CancelGRPCStreamTest.swift", ], - tags = ["no-remote-exec"], # TODO(32551): Re-enable remote exec + exec_properties = { + "sandboxNetwork": "standard", + }, visibility = ["//visibility:public"], deps = [ ":test_extensions", diff --git a/mobile/test/swift/integration/proxying/BUILD b/mobile/test/swift/integration/proxying/BUILD index d15f5a6f6526..b891bf3b39c7 100644 --- a/mobile/test/swift/integration/proxying/BUILD +++ b/mobile/test/swift/integration/proxying/BUILD @@ -10,10 +10,9 @@ envoy_mobile_swift_test( srcs = [ "HTTPRequestUsingProxyTest.swift", ], - # The test starts an Envoy test server, which requires the `no-remote-exec` tag. - tags = [ - "no-remote-exec", - ], + exec_properties = { + "sandboxNetwork": "standard", + }, 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 14e1d31bd8d0..bbecf8d2e710 100644 --- a/mobile/test/swift/stats/BUILD +++ b/mobile/test/swift/stats/BUILD @@ -12,8 +12,10 @@ envoy_mobile_swift_test( "ElementTests.swift", "TagsBuilderTests.swift", ], + exec_properties = { + "sandboxNetwork": "standard", + }, flaky = True, # TODO(jpsim): Fix timeouts when running these tests on CI - tags = ["no-remote-exec"], # TODO(32551): Re-enable remote exec visibility = ["//visibility:public"], deps = [ "//library/objective-c:envoy_engine_objc_lib", diff --git a/source/common/config/datasource.cc b/source/common/config/datasource.cc index 95816710b1b2..8cb923f74aeb 100644 --- a/source/common/config/datasource.cc +++ b/source/common/config/datasource.cc @@ -100,29 +100,36 @@ DynamicData::~DynamicData() { } } -absl::string_view DynamicData::data() const { +const std::string& DynamicData::data() const { const auto thread_local_data = slot_->get(); return thread_local_data.has_value() ? *thread_local_data->data_ : EMPTY_STRING; } -absl::string_view DataSourceProvider::data() const { +const std::string& DataSourceProvider::data() const { if (absl::holds_alternative(data_)) { return absl::get(data_); } return absl::get(data_).data(); } -absl::StatusOr DataSourceProvider::create(const ProtoDataSource& source, - Event::Dispatcher& main_dispatcher, - ThreadLocal::SlotAllocator& tls, - Api::Api& api, bool allow_empty, - uint64_t max_size) { +absl::StatusOr DataSourceProvider::create(const ProtoDataSource& source, + Event::Dispatcher& main_dispatcher, + ThreadLocal::SlotAllocator& tls, + Api::Api& api, bool allow_empty, + uint64_t max_size) { auto initial_data_or_error = read(source, allow_empty, api, max_size); RETURN_IF_STATUS_NOT_OK(initial_data_or_error); if (!source.has_watched_directory() || source.specifier_case() != envoy::config::core::v3::DataSource::kFilename) { - return DataSourceProvider(std::move(initial_data_or_error).value()); + if (source.specifier_case() != envoy::config::core::v3::DataSource::kFilename && + initial_data_or_error.value().length() > max_size) { + return absl::InvalidArgumentError(fmt::format("response body size is {} bytes; maximum is {}", + initial_data_or_error.value().length(), + max_size)); + } + return std::unique_ptr( + new DataSourceProvider(std::move(initial_data_or_error).value())); } auto slot = ThreadLocal::TypedSlot::makeUnique(tls); @@ -157,7 +164,8 @@ absl::StatusOr DataSourceProvider::create(const ProtoDataSou }); RETURN_IF_NOT_OK(watcher_status); - return DataSourceProvider(DynamicData(main_dispatcher, std::move(slot), std::move(watcher))); + return std::unique_ptr( + new DataSourceProvider(DynamicData(main_dispatcher, std::move(slot), std::move(watcher)))); } } // namespace DataSource diff --git a/source/common/config/datasource.h b/source/common/config/datasource.h index 1c0f47879392..6dc6131338f9 100644 --- a/source/common/config/datasource.h +++ b/source/common/config/datasource.h @@ -20,8 +20,11 @@ namespace Envoy { namespace Config { namespace DataSource { +class DataSourceProvider; + using ProtoDataSource = envoy::config::core::v3::DataSource; using ProtoWatchedDirectory = envoy::config::core::v3::WatchedDirectory; +using DataSourceProviderPtr = std::unique_ptr; /** * Read contents of the DataSource. @@ -54,7 +57,7 @@ class DynamicData { Filesystem::WatcherPtr watcher); ~DynamicData(); - absl::string_view data() const; + const std::string& data() const; private: Event::Dispatcher& dispatcher_; @@ -85,12 +88,11 @@ class DataSourceProvider { * NOTE: If file watch is enabled and the new file content does not meet the * requirements (allow_empty, max_size), the provider will keep the old content. */ - static absl::StatusOr create(const ProtoDataSource& source, - Event::Dispatcher& main_dispatcher, - ThreadLocal::SlotAllocator& tls, Api::Api& api, - bool allow_empty, uint64_t max_size = 0); + static absl::StatusOr + create(const ProtoDataSource& source, Event::Dispatcher& main_dispatcher, + ThreadLocal::SlotAllocator& tls, Api::Api& api, bool allow_empty, uint64_t max_size = 0); - absl::string_view data() const; + const std::string& data() const; private: DataSourceProvider(std::string&& data) : data_(std::move(data)) {} diff --git a/source/common/config/subscription_factory_impl.cc b/source/common/config/subscription_factory_impl.cc index 4e11b27da8b5..c0f9ce344767 100644 --- a/source/common/config/subscription_factory_impl.cc +++ b/source/common/config/subscription_factory_impl.cc @@ -23,11 +23,11 @@ SubscriptionFactoryImpl::SubscriptionFactoryImpl( validation_visitor_(validation_visitor), api_(api), server_(server), xds_resources_delegate_(xds_resources_delegate), xds_config_tracker_(xds_config_tracker) {} -SubscriptionPtr SubscriptionFactoryImpl::subscriptionFromConfigSource( +absl::StatusOr SubscriptionFactoryImpl::subscriptionFromConfigSource( const envoy::config::core::v3::ConfigSource& config, absl::string_view type_url, Stats::Scope& scope, SubscriptionCallbacks& callbacks, OpaqueResourceDecoderSharedPtr resource_decoder, const SubscriptionOptions& options) { - THROW_IF_NOT_OK(Config::Utility::checkLocalInfo(type_url, local_info_)); + RETURN_IF_NOT_OK(Config::Utility::checkLocalInfo(type_url, local_info_)); SubscriptionStats stats = Utility::generateStats(scope); std::string subscription_type = ""; @@ -50,29 +50,29 @@ SubscriptionPtr SubscriptionFactoryImpl::subscriptionFromConfigSource( switch (config.config_source_specifier_case()) { case envoy::config::core::v3::ConfigSource::ConfigSourceSpecifierCase::kPath: { - THROW_IF_NOT_OK(Utility::checkFilesystemSubscriptionBackingPath(config.path(), api_)); + RETURN_IF_NOT_OK(Utility::checkFilesystemSubscriptionBackingPath(config.path(), api_)); subscription_type = "envoy.config_subscription.filesystem"; break; } case envoy::config::core::v3::ConfigSource::ConfigSourceSpecifierCase::kPathConfigSource: { - THROW_IF_NOT_OK( + RETURN_IF_NOT_OK( Utility::checkFilesystemSubscriptionBackingPath(config.path_config_source().path(), api_)); subscription_type = "envoy.config_subscription.filesystem"; break; } case envoy::config::core::v3::ConfigSource::ConfigSourceSpecifierCase::kApiConfigSource: { const envoy::config::core::v3::ApiConfigSource& api_config_source = config.api_config_source(); - THROW_IF_NOT_OK(Utility::checkApiConfigSourceSubscriptionBackingCluster(cm_.primaryClusters(), - api_config_source)); - THROW_IF_NOT_OK(Utility::checkTransportVersion(api_config_source)); + RETURN_IF_NOT_OK(Utility::checkApiConfigSourceSubscriptionBackingCluster(cm_.primaryClusters(), + api_config_source)); + RETURN_IF_NOT_OK(Utility::checkTransportVersion(api_config_source)); switch (api_config_source.api_type()) { PANIC_ON_PROTO_ENUM_SENTINEL_VALUES; case envoy::config::core::v3::ApiConfigSource::AGGREGATED_GRPC: - throwEnvoyExceptionOrPanic("Unsupported config source AGGREGATED_GRPC"); + return absl::InvalidArgumentError("Unsupported config source AGGREGATED_GRPC"); case envoy::config::core::v3::ApiConfigSource::AGGREGATED_DELTA_GRPC: - throwEnvoyExceptionOrPanic("Unsupported config source AGGREGATED_DELTA_GRPC"); + return absl::InvalidArgumentError("Unsupported config source AGGREGATED_DELTA_GRPC"); case envoy::config::core::v3::ApiConfigSource::DEPRECATED_AND_UNAVAILABLE_DO_NOT_USE: - throwEnvoyExceptionOrPanic( + return absl::InvalidArgumentError( "REST_LEGACY no longer a supported ApiConfigSource. " "Please specify an explicit supported api_type in the following config:\n" + config.DebugString()); @@ -87,7 +87,7 @@ SubscriptionPtr SubscriptionFactoryImpl::subscriptionFromConfigSource( break; } if (subscription_type.empty()) { - throwEnvoyExceptionOrPanic("Invalid API config source API type"); + return absl::InvalidArgumentError("Invalid API config source API type"); } break; } @@ -96,32 +96,32 @@ SubscriptionPtr SubscriptionFactoryImpl::subscriptionFromConfigSource( break; } default: - throwEnvoyExceptionOrPanic( + return absl::InvalidArgumentError( "Missing config source specifier in envoy::config::core::v3::ConfigSource"); } ConfigSubscriptionFactory* factory = Registry::FactoryRegistry::getFactory(subscription_type); if (factory == nullptr) { - throwEnvoyExceptionOrPanic(fmt::format( + return absl::InvalidArgumentError(fmt::format( "Didn't find a registered config subscription factory implementation for name: '{}'", subscription_type)); } return factory->create(data); } -SubscriptionPtr createFromFactoryOrThrow(ConfigSubscriptionFactory::SubscriptionData& data, - absl::string_view subscription_type) { +absl::StatusOr createFromFactory(ConfigSubscriptionFactory::SubscriptionData& data, + absl::string_view subscription_type) { ConfigSubscriptionFactory* factory = Registry::FactoryRegistry::getFactory(subscription_type); if (factory == nullptr) { - throwEnvoyExceptionOrPanic(fmt::format( + return absl::InvalidArgumentError(fmt::format( "Didn't find a registered config subscription factory implementation for name: '{}'", subscription_type)); } return factory->create(data); } -SubscriptionPtr SubscriptionFactoryImpl::collectionSubscriptionFromUrl( +absl::StatusOr SubscriptionFactoryImpl::collectionSubscriptionFromUrl( const xds::core::v3::ResourceLocator& collection_locator, const envoy::config::core::v3::ConfigSource& config, absl::string_view resource_type, Stats::Scope& scope, SubscriptionCallbacks& callbacks, @@ -148,13 +148,15 @@ SubscriptionPtr SubscriptionFactoryImpl::collectionSubscriptionFromUrl( switch (collection_locator.scheme()) { case xds::core::v3::ResourceLocator::FILE: { const std::string path = Http::Utility::localPathFromFilePath(collection_locator.id()); - THROW_IF_NOT_OK(Utility::checkFilesystemSubscriptionBackingPath(path, api_)); + RETURN_IF_NOT_OK(Utility::checkFilesystemSubscriptionBackingPath(path, api_)); factory_config.set_path(path); - return createFromFactoryOrThrow(data, "envoy.config_subscription.filesystem_collection"); + auto ptr_or_error = createFromFactory(data, "envoy.config_subscription.filesystem_collection"); + RETURN_IF_NOT_OK(ptr_or_error.status()); + return std::move(ptr_or_error.value()); } case xds::core::v3::ResourceLocator::XDSTP: { if (resource_type != collection_locator.resource_type()) { - throwEnvoyExceptionOrPanic( + return absl::InvalidArgumentError( fmt::format("xdstp:// type does not match {} in {}", resource_type, Config::XdsResourceIdentifier::encodeUrl(collection_locator))); } @@ -162,8 +164,8 @@ SubscriptionPtr SubscriptionFactoryImpl::collectionSubscriptionFromUrl( case envoy::config::core::v3::ConfigSource::ConfigSourceSpecifierCase::kApiConfigSource: { const envoy::config::core::v3::ApiConfigSource& api_config_source = config.api_config_source(); - THROW_IF_NOT_OK(Utility::checkApiConfigSourceSubscriptionBackingCluster(cm_.primaryClusters(), - api_config_source)); + RETURN_IF_NOT_OK(Utility::checkApiConfigSourceSubscriptionBackingCluster( + cm_.primaryClusters(), api_config_source)); // All Envoy collections currently are xDS resource graph roots and require node context // parameters. options.add_xdstp_node_context_params_ = true; @@ -171,17 +173,22 @@ SubscriptionPtr SubscriptionFactoryImpl::collectionSubscriptionFromUrl( case envoy::config::core::v3::ApiConfigSource::DELTA_GRPC: { std::string type_url = TypeUtil::descriptorFullNameToTypeUrl(resource_type); data.type_url_ = type_url; - return createFromFactoryOrThrow(data, "envoy.config_subscription.delta_grpc_collection"); + auto ptr_or_error = + createFromFactory(data, "envoy.config_subscription.delta_grpc_collection"); + RETURN_IF_NOT_OK(ptr_or_error.status()); + return std::move(ptr_or_error.value()); } case envoy::config::core::v3::ApiConfigSource::AGGREGATED_GRPC: FALLTHRU; case envoy::config::core::v3::ApiConfigSource::AGGREGATED_DELTA_GRPC: { - return createFromFactoryOrThrow(data, - "envoy.config_subscription.aggregated_grpc_collection"); + auto ptr_or_error = + createFromFactory(data, "envoy.config_subscription.aggregated_grpc_collection"); + RETURN_IF_NOT_OK(ptr_or_error.status()); + return std::move(ptr_or_error.value()); } default: - throwEnvoyExceptionOrPanic(fmt::format("Unknown xdstp:// transport API type in {}", - api_config_source.DebugString())); + return absl::InvalidArgumentError(fmt::format("Unknown xdstp:// transport API type in {}", + api_config_source.DebugString())); } } case envoy::config::core::v3::ConfigSource::ConfigSourceSpecifierCase::kAds: { @@ -189,10 +196,12 @@ SubscriptionPtr SubscriptionFactoryImpl::collectionSubscriptionFromUrl( // All Envoy collections currently are xDS resource graph roots and require node context // parameters. options.add_xdstp_node_context_params_ = true; - return createFromFactoryOrThrow(data, "envoy.config_subscription.ads_collection"); + auto ptr_or_error = createFromFactory(data, "envoy.config_subscription.ads_collection"); + RETURN_IF_NOT_OK(ptr_or_error.status()); + return std::move(ptr_or_error.value()); } default: - throwEnvoyExceptionOrPanic( + return absl::InvalidArgumentError( "Missing or not supported config source specifier in " "envoy::config::core::v3::ConfigSource for a collection. Only ADS and " "gRPC in delta-xDS mode are supported."); @@ -200,7 +209,7 @@ SubscriptionPtr SubscriptionFactoryImpl::collectionSubscriptionFromUrl( } default: // TODO(htuch): Implement HTTP semantics for collection ResourceLocators. - throwEnvoyExceptionOrPanic("Unsupported code path"); + return absl::InvalidArgumentError("Unsupported code path"); } } diff --git a/source/common/config/subscription_factory_impl.h b/source/common/config/subscription_factory_impl.h index 2f37e08408e7..aff5a088340a 100644 --- a/source/common/config/subscription_factory_impl.h +++ b/source/common/config/subscription_factory_impl.h @@ -26,12 +26,11 @@ class SubscriptionFactoryImpl : public SubscriptionFactory, Logger::Loggable subscriptionFromConfigSource( + const envoy::config::core::v3::ConfigSource& config, absl::string_view type_url, + Stats::Scope& scope, SubscriptionCallbacks& callbacks, + OpaqueResourceDecoderSharedPtr resource_decoder, const SubscriptionOptions& options) override; + absl::StatusOr collectionSubscriptionFromUrl(const xds::core::v3::ResourceLocator& collection_locator, const envoy::config::core::v3::ConfigSource& config, absl::string_view resource_type, Stats::Scope& scope, diff --git a/source/common/config/well_known_names.cc b/source/common/config/well_known_names.cc index 1cc7913d77f4..c9b73b1ab089 100644 --- a/source/common/config/well_known_names.cc +++ b/source/common/config/well_known_names.cc @@ -27,7 +27,8 @@ std::string expandRegex(const std::string& regex) { // alphanumerics, underscores, and dashes. {"", R"([\w-\./]+)"}, // Match a prefix that is either a listener plus name or cluster plus name - {"", R"((?:listener|cluster)\..*?)"}}); + {"", R"((?:listener|cluster)\..*?)"}, + {"", R"(\d)"}}); } const Regex::CompiledGoogleReMatcher& validTagValueRegex() { @@ -218,8 +219,15 @@ TagNameValues::TagNameValues() { // http..rbac.(.)* but excluding policy addRe2(RBAC_HTTP_PREFIX, R"(^http\.\.rbac\.(()\.).*?)", "", "policy"); - // proxy_proto.(versions.v.)** - addRe2(PROXY_PROTOCOL_VERSION, R"(^proxy_proto\.(versions\.v(\d)\.)\w+)", "proxy_proto.versions"); + // proxy_proto.(.)** + addRe2(PROXY_PROTOCOL_PREFIX, R"(^proxy_proto\.(()\.).+$)", "", "versions"); + + // proxy_proto.([.]versions.v().)(found|disallowed|error) + // + // Strips out: [.]versions.v(). + // Leaving: proxy_proto.(found|disallowed|error) + addRe2(PROXY_PROTOCOL_VERSION, + R"(^proxy_proto\.((?:\.)?versions\.v()\.)\w+$)"); } void TagNameValues::addRe2(const std::string& name, const std::string& regex, diff --git a/source/common/config/well_known_names.h b/source/common/config/well_known_names.h index 86b522b9678f..334e50cc6aa2 100644 --- a/source/common/config/well_known_names.h +++ b/source/common/config/well_known_names.h @@ -165,6 +165,8 @@ class TagNameValues { const std::string REDIS_PREFIX = "envoy.redis_prefix"; // Proxy Protocol version for a connection (Proxy Protocol listener filter). const std::string PROXY_PROTOCOL_VERSION = "envoy.proxy_protocol_version"; + // Stats prefix for the proxy protocol listener filter. + const std::string PROXY_PROTOCOL_PREFIX = "envoy.proxy_protocol_prefix"; // Mapping from the names above to their respective regex strings. const std::vector> name_regex_pairs_; diff --git a/source/common/filter/config_discovery_impl.cc b/source/common/filter/config_discovery_impl.cc index d3cb3e26b70d..5962df8e2345 100644 --- a/source/common/filter/config_discovery_impl.cc +++ b/source/common/filter/config_discovery_impl.cc @@ -78,8 +78,11 @@ FilterConfigSubscription::FilterConfigSubscription( filter_config_provider_manager_(filter_config_provider_manager), subscription_id_(subscription_id) { const auto resource_name = getResourceName(); - subscription_ = cluster_manager.subscriptionFactory().subscriptionFromConfigSource( - config_source, Grpc::Common::typeUrl(resource_name), *scope_, *this, resource_decoder_, {}); + subscription_ = + THROW_OR_RETURN_VALUE(cluster_manager.subscriptionFactory().subscriptionFromConfigSource( + config_source, Grpc::Common::typeUrl(resource_name), *scope_, *this, + resource_decoder_, {}), + Config::SubscriptionPtr); } void FilterConfigSubscription::start() { diff --git a/source/common/grpc/async_client_impl.cc b/source/common/grpc/async_client_impl.cc index ce6fefb67e0e..dd89bc7bec97 100644 --- a/source/common/grpc/async_client_impl.cc +++ b/source/common/grpc/async_client_impl.cc @@ -88,7 +88,7 @@ AsyncStreamImpl::AsyncStreamImpl(AsyncClientImpl& parent, absl::string_view serv // Configure the maximum frame length decoder_.setMaxFrameLength(parent_.max_recv_message_length_); - if (nullptr != options.parent_span_) { + if (options.parent_span_ != nullptr) { const std::string child_span_name = options.child_span_name_.empty() ? absl::StrCat("async ", service_full_name, ".", method_name, " egress") diff --git a/source/common/http/async_client_impl.cc b/source/common/http/async_client_impl.cc index e51d701326d6..a0bec05e0d5a 100644 --- a/source/common/http/async_client_impl.cc +++ b/source/common/http/async_client_impl.cc @@ -16,6 +16,9 @@ namespace Envoy { namespace Http { + +const absl::string_view AsyncClientImpl::ResponseBufferLimit = "http.async_response_buffer_limit"; + AsyncClientImpl::AsyncClientImpl(Upstream::ClusterInfoConstSharedPtr cluster, Stats::Store& stats_store, Event::Dispatcher& dispatcher, Upstream::ClusterManager& cm, @@ -29,7 +32,7 @@ AsyncClientImpl::AsyncClientImpl(Upstream::ClusterInfoConstSharedPtr cluster, factory_context.api().randomGenerator(), std::move(shadow_writer), true, false, false, false, false, false, Protobuf::RepeatedPtrField{}, dispatcher.timeSource(), http_context, router_context)), - dispatcher_(dispatcher) {} + dispatcher_(dispatcher), runtime_(factory_context.runtime()) {} AsyncClientImpl::~AsyncClientImpl() { while (!active_streams_.empty()) { @@ -108,7 +111,8 @@ createRetryPolicy(AsyncClientImpl& parent, const AsyncClient::StreamOptions& opt AsyncStreamImpl::AsyncStreamImpl(AsyncClientImpl& parent, AsyncClient::StreamCallbacks& callbacks, const AsyncClient::StreamOptions& options, absl::Status& creation_status) - : parent_(parent), stream_callbacks_(callbacks), stream_id_(parent.config_->random_.random()), + : parent_(parent), discard_response_body_(options.discard_response_body), + stream_callbacks_(callbacks), stream_id_(parent.config_->random_.random()), router_(options.filter_config_ ? options.filter_config_ : parent.config_, parent.config_->async_stats_), stream_info_(Protocol::Http11, parent.dispatcher().timeSource(), nullptr, @@ -302,11 +306,13 @@ AsyncRequestSharedImpl::AsyncRequestSharedImpl(AsyncClientImpl& parent, AsyncClient::Callbacks& callbacks, const AsyncClient::RequestOptions& options, absl::Status& creation_status) - : AsyncStreamImpl(parent, *this, options, creation_status), callbacks_(callbacks) { + : AsyncStreamImpl(parent, *this, options, creation_status), callbacks_(callbacks), + response_buffer_limit_(parent.runtime_.snapshot().getInteger( + AsyncClientImpl::ResponseBufferLimit, kBufferLimitForResponse)) { if (!creation_status.ok()) { return; } - if (nullptr != options.parent_span_) { + if (options.parent_span_ != nullptr) { const std::string child_span_name = options.child_span_name_.empty() ? absl::StrCat("async ", parent.cluster_->name(), " egress") @@ -367,7 +373,22 @@ void AsyncRequestSharedImpl::onHeaders(ResponseHeaderMapPtr&& headers, bool) { response_ = std::make_unique(std::move(headers)); } -void AsyncRequestSharedImpl::onData(Buffer::Instance& data, bool) { response_->body().move(data); } +void AsyncRequestSharedImpl::onData(Buffer::Instance& data, bool) { + if (discard_response_body_) { + data.drain(data.length()); + return; + } + + if (response_->body().length() + data.length() > response_buffer_limit_) { + ENVOY_LOG_EVERY_POW_2(warn, "the buffer size limit for async client response body " + "has been exceeded, draining data"); + data.drain(data.length()); + response_buffer_overlimit_ = true; + reset(); + } else { + response_->body().move(data); + } +} void AsyncRequestSharedImpl::onTrailers(ResponseTrailerMapPtr&& trailers) { response_->trailers(std::move(trailers)); @@ -393,8 +414,12 @@ void AsyncRequestSharedImpl::onReset() { Tracing::EgressConfig::get()); if (!cancelled_) { - // In this case we don't have a valid response so we do need to raise a failure. - callbacks_.onFailure(*this, AsyncClient::FailureReason::Reset); + if (response_buffer_overlimit_) { + callbacks_.onFailure(*this, AsyncClient::FailureReason::ExceedResponseBufferLimit); + } else { + // In this case we don't have a valid response so we do need to raise a failure. + callbacks_.onFailure(*this, AsyncClient::FailureReason::Reset); + } } } diff --git a/source/common/http/async_client_impl.h b/source/common/http/async_client_impl.h index 544c66ae7a18..af6422203293 100644 --- a/source/common/http/async_client_impl.h +++ b/source/common/http/async_client_impl.h @@ -51,6 +51,8 @@ namespace { // Limit the size of buffer for data used for retries. // This is currently fixed to 64KB. constexpr uint64_t kBufferLimitForRetry = 1 << 16; +// Response buffer limit 32MB. +constexpr uint64_t kBufferLimitForResponse = 32 * 1024 * 1024; } // namespace class AsyncStreamImpl; @@ -74,12 +76,14 @@ class AsyncClientImpl final : public AsyncClient { Server::Configuration::CommonFactoryContext& factory_context_; Upstream::ClusterInfoConstSharedPtr cluster_; Event::Dispatcher& dispatcher() override { return dispatcher_; } + static const absl::string_view ResponseBufferLimit; private: template T* internalStartRequest(T* async_request); const Router::FilterConfigSharedPtr config_; Event::Dispatcher& dispatcher_; std::list> active_streams_; + Runtime::Loader& runtime_; friend class AsyncStreamImpl; friend class AsyncRequestSharedImpl; @@ -92,7 +96,7 @@ class AsyncClientImpl final : public AsyncClient { class AsyncStreamImpl : public virtual AsyncClient::Stream, public StreamDecoderFilterCallbacks, public Event::DeferredDeletable, - Logger::Loggable, + public Logger::Loggable, public LinkedObject, public ScopeTrackedObject { public: @@ -161,6 +165,7 @@ class AsyncStreamImpl : public virtual AsyncClient::Stream, // Callback to listen for low/high/overflow watermark events. absl::optional> watermark_callbacks_; bool complete_{}; + const bool discard_response_body_; private: void cleanup(); @@ -325,6 +330,8 @@ class AsyncRequestSharedImpl : public virtual AsyncClient::Request, Tracing::SpanPtr child_span_; std::unique_ptr response_; bool cancelled_{}; + bool response_buffer_overlimit_{}; + const uint64_t response_buffer_limit_; }; class AsyncOngoingRequestImpl final : public AsyncClient::OngoingRequest, diff --git a/source/common/http/conn_manager_impl.cc b/source/common/http/conn_manager_impl.cc index d8ddacb89e9b..b217c1e2f7b2 100644 --- a/source/common/http/conn_manager_impl.cc +++ b/source/common/http/conn_manager_impl.cc @@ -126,10 +126,8 @@ ConnectionManagerImpl::ConnectionManagerImpl(ConnectionManagerConfigSharedPtr co /*node_id=*/local_info_.node().id(), /*server_name=*/config_->serverName(), /*proxy_status_config=*/config_->proxyStatusConfig())), - max_requests_during_dispatch_( - runtime_.snapshot().getInteger(ConnectionManagerImpl::MaxRequestsPerIoCycle, UINT32_MAX)), - refresh_rtt_after_request_( - Runtime::runtimeFeatureEnabled("envoy.reloadable_features.refresh_rtt_after_request")) { + max_requests_during_dispatch_(runtime_.snapshot().getInteger( + ConnectionManagerImpl::MaxRequestsPerIoCycle, UINT32_MAX)) { ENVOY_LOG_ONCE_IF( trace, accept_new_http_stream_ == nullptr, "LoadShedPoint envoy.load_shed_points.http_connection_manager_decode_headers is not " @@ -337,17 +335,6 @@ void ConnectionManagerImpl::doDeferredStreamDestroy(ActiveStream& stream) { } stream.completeRequest(); - - // If refresh rtt after request is required explicitly, then try to get rtt again set it into - // connection info. - if (refresh_rtt_after_request_) { - // 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(); // For HTTP/3, skip access logging here and add deferred logging info diff --git a/source/common/http/conn_manager_impl.h b/source/common/http/conn_manager_impl.h index 5f546ed9be3d..a325dff0dfb2 100644 --- a/source/common/http/conn_manager_impl.h +++ b/source/common/http/conn_manager_impl.h @@ -638,8 +638,6 @@ class ConnectionManagerImpl : Logger::Loggable, uint32_t requests_during_dispatch_count_{0}; const uint32_t max_requests_during_dispatch_{UINT32_MAX}; Event::SchedulableCallbackPtr deferred_request_processing_callback_; - - const bool refresh_rtt_after_request_{}; }; } // namespace Http diff --git a/source/common/json/json_internal.cc b/source/common/json/json_internal.cc index f7e624ed7881..f5c8cc5c21d5 100644 --- a/source/common/json/json_internal.cc +++ b/source/common/json/json_internal.cc @@ -570,7 +570,9 @@ std::vector Field::asObjectArray() const { std::string Field::asJsonString() const { nlohmann::json j = asJsonDocument(); - return j.dump(); + // Call with defaults except in the case of UTF-8 errors which we replace + // invalid UTF-8 characters instead of throwing an exception. + return j.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace); } bool Field::empty() const { diff --git a/source/common/listener_manager/filter_chain_manager_impl.cc b/source/common/listener_manager/filter_chain_manager_impl.cc index 9c3331ab6f45..ea25462bc6b8 100644 --- a/source/common/listener_manager/filter_chain_manager_impl.cc +++ b/source/common/listener_manager/filter_chain_manager_impl.cc @@ -194,7 +194,8 @@ void FilterChainManagerImpl::addFilterChains( std::vector ips; ips.reserve(prefix_ranges.size()); for (const auto& ip : prefix_ranges) { - const auto& cidr_range = Network::Address::CidrRange::create(ip); + const auto& cidr_range = THROW_OR_RETURN_VALUE(Network::Address::CidrRange::create(ip), + Network::Address::CidrRange); ips.push_back(cidr_range.asString()); } return ips; @@ -457,15 +458,18 @@ std::pair> makeCidrListEntry(const s std::vector subnets; if (cidr == EMPTY_STRING) { if (Network::SocketInterfaceSingleton::get().ipFamilySupported(AF_INET)) { - subnets.push_back( - Network::Address::CidrRange::create(Network::Utility::getIpv4CidrCatchAllAddress())); + subnets.push_back(THROW_OR_RETURN_VALUE( + Network::Address::CidrRange::create(Network::Utility::getIpv4CidrCatchAllAddress()), + Network::Address::CidrRange)); } if (Network::SocketInterfaceSingleton::get().ipFamilySupported(AF_INET6)) { - subnets.push_back( - Network::Address::CidrRange::create(Network::Utility::getIpv6CidrCatchAllAddress())); + subnets.push_back(THROW_OR_RETURN_VALUE( + Network::Address::CidrRange::create(Network::Utility::getIpv6CidrCatchAllAddress()), + Network::Address::CidrRange)); } } else { - subnets.push_back(Network::Address::CidrRange::create(cidr)); + subnets.push_back(THROW_OR_RETURN_VALUE(Network::Address::CidrRange::create(cidr), + Network::Address::CidrRange)); } return std::make_pair>(T(data), std::move(subnets)); } diff --git a/source/common/listener_manager/lds_api.cc b/source/common/listener_manager/lds_api.cc index b3b48a10c916..f77333ea0cce 100644 --- a/source/common/listener_manager/lds_api.cc +++ b/source/common/listener_manager/lds_api.cc @@ -32,11 +32,15 @@ LdsApiImpl::LdsApiImpl(const envoy::config::core::v3::ConfigSource& lds_config, init_target_("LDS", [this]() { subscription_->start({}); }) { const auto resource_name = getResourceName(); if (lds_resources_locator == nullptr) { - subscription_ = cm.subscriptionFactory().subscriptionFromConfigSource( - lds_config, Grpc::Common::typeUrl(resource_name), *scope_, *this, resource_decoder_, {}); + subscription_ = THROW_OR_RETURN_VALUE(cm.subscriptionFactory().subscriptionFromConfigSource( + lds_config, Grpc::Common::typeUrl(resource_name), + *scope_, *this, resource_decoder_, {}), + Config::SubscriptionPtr); } else { - subscription_ = cm.subscriptionFactory().collectionSubscriptionFromUrl( - *lds_resources_locator, lds_config, resource_name, *scope_, *this, resource_decoder_); + subscription_ = THROW_OR_RETURN_VALUE( + cm.subscriptionFactory().collectionSubscriptionFromUrl( + *lds_resources_locator, lds_config, resource_name, *scope_, *this, resource_decoder_), + Config::SubscriptionPtr); } init_manager.add(init_target_); } diff --git a/source/common/network/cidr_range.cc b/source/common/network/cidr_range.cc index 7fc622644d52..8101552d3bbc 100644 --- a/source/common/network/cidr_range.cc +++ b/source/common/network/cidr_range.cc @@ -99,30 +99,42 @@ std::string CidrRange::asString() const { } // static -CidrRange CidrRange::create(InstanceConstSharedPtr address, int length) { +absl::StatusOr +CidrRange::create(InstanceConstSharedPtr address, int length, + absl::optional original_address_str) { InstanceConstSharedPtr ptr = truncateIpAddressAndLength(std::move(address), &length); - return {std::move(ptr), length}; + if (!ptr) { + return absl::InvalidArgumentError(absl::StrCat( + "malformed IP address: ", original_address_str.has_value() ? *original_address_str : "")); + } + CidrRange ret = CidrRange(std::move(ptr), length); + if (ret.isValid()) { + return ret; + } + return absl::InvalidArgumentError("Invalid CIDR range"); } // static -CidrRange CidrRange::create(const std::string& address, int length) { - return create(Utility::parseInternetAddress(address), length); +absl::StatusOr CidrRange::create(const std::string& address, int length) { + return create(Utility::parseInternetAddressNoThrow(address), length, address); } -CidrRange CidrRange::create(const envoy::config::core::v3::CidrRange& cidr) { - return create(Utility::parseInternetAddress(cidr.address_prefix()), cidr.prefix_len().value()); +absl::StatusOr CidrRange::create(const envoy::config::core::v3::CidrRange& cidr) { + return create(Utility::parseInternetAddressNoThrow(cidr.address_prefix()), + cidr.prefix_len().value(), cidr.address_prefix()); } -CidrRange CidrRange::create(const xds::core::v3::CidrRange& cidr) { - return create(Utility::parseInternetAddress(cidr.address_prefix()), cidr.prefix_len().value()); +absl::StatusOr CidrRange::create(const xds::core::v3::CidrRange& cidr) { + return create(Utility::parseInternetAddressNoThrow(cidr.address_prefix()), + cidr.prefix_len().value(), cidr.address_prefix()); } // static -CidrRange CidrRange::create(const std::string& range) { +absl::StatusOr CidrRange::create(const std::string& range) { const auto parts = StringUtil::splitToken(range, "/"); if (parts.size() == 2) { - InstanceConstSharedPtr ptr = Utility::parseInternetAddress(std::string{parts[0]}); - if (ptr->type() == Type::Ip) { + InstanceConstSharedPtr ptr = Utility::parseInternetAddressNoThrow(std::string{parts[0]}); + if (ptr && ptr->type() == Type::Ip) { uint64_t length64; if (absl::SimpleAtoi(parts[1], &length64)) { if ((ptr->ip()->version() == IpVersion::v6 && length64 <= 128) || @@ -132,7 +144,7 @@ CidrRange CidrRange::create(const std::string& range) { } } } - return {nullptr, -1}; + return absl::InvalidArgumentError("Invalid CIDR range"); } // static @@ -202,12 +214,13 @@ IpList::create(const Protobuf::RepeatedPtrField& cidrs) { ip_list_.reserve(cidrs.size()); for (const envoy::config::core::v3::CidrRange& entry : cidrs) { - CidrRange list_entry = CidrRange::create(entry); - if (list_entry.isValid()) { - ip_list_.push_back(std::move(list_entry)); + absl::StatusOr range_or_error = CidrRange::create(entry); + if (range_or_error.status().ok()) { + ip_list_.push_back(std::move(range_or_error.value())); } else { error_ = fmt::format("invalid ip/mask combo '{}/{}' (format is /<# mask bits>)", entry.address_prefix(), entry.prefix_len().value()); diff --git a/source/common/network/cidr_range.h b/source/common/network/cidr_range.h index b78555dc41f6..33f3af9ebf92 100644 --- a/source/common/network/cidr_range.h +++ b/source/common/network/cidr_range.h @@ -68,43 +68,34 @@ class CidrRange { std::string asString() const; /** - * @return true if this instance is valid; address != nullptr && length is appropriate for the - * IP version (these are checked during construction, and reduced down to a check of - * the length). - */ - bool isValid() const { return length_ >= 0; } - - /** - * TODO(ccaraman): Update CidrRange::create to support only constructing valid ranges. * @return a CidrRange instance with the specified address and length, modified so that the only * bits that might be non-zero are in the high-order length bits, and so that length is * in the appropriate range (0 to 32 for IPv4, 0 to 128 for IPv6). If the address or * length is invalid, then the range will be invalid (i.e. length == -1). */ - static CidrRange create(InstanceConstSharedPtr address, int length); - static CidrRange create(const std::string& address, int length); + static absl::StatusOr + create(InstanceConstSharedPtr address, int length, + absl::optional original_address_str = ""); + static absl::StatusOr create(const std::string& address, int length); /** * Constructs an CidrRange from a string with this format (same as returned * by CidrRange::asString above): *
/ e.g. "10.240.0.0/16" or "1234:5678::/64" - * TODO(ccaraman): Update CidrRange::create to support only constructing valid ranges. * @return a CidrRange instance with the specified address and length if parsed successfully, * else with no address and a length of -1. */ - static CidrRange create(const std::string& range); + static absl::StatusOr create(const std::string& range); /** * Constructs a CidrRange from envoy::config::core::v3::CidrRange. - * TODO(ccaraman): Update CidrRange::create to support only constructing valid ranges. */ - static CidrRange create(const envoy::config::core::v3::CidrRange& cidr); + static absl::StatusOr create(const envoy::config::core::v3::CidrRange& cidr); /** * Constructs a CidrRange from xds::core::v3::CidrRange. - * TODO(ccaraman): Update CidrRange::create to support only constructing valid ranges. */ - static CidrRange create(const xds::core::v3::CidrRange& cidr); + static absl::StatusOr create(const xds::core::v3::CidrRange& cidr); /** * Given an IP address and a length of high order bits to keep, returns an address @@ -120,6 +111,13 @@ class CidrRange { int* length_io); private: + /** + * @return true if this instance is valid; address != nullptr && length is appropriate for the + * IP version (these are checked during construction, and reduced down to a check of + * the length). + */ + bool isValid() const { return length_ >= 0; } + CidrRange(InstanceConstSharedPtr address, int length); InstanceConstSharedPtr address_; diff --git a/source/common/quic/envoy_quic_server_stream.cc b/source/common/quic/envoy_quic_server_stream.cc index 94a82cce10d7..5bdf06608546 100644 --- a/source/common/quic/envoy_quic_server_stream.cc +++ b/source/common/quic/envoy_quic_server_stream.cc @@ -112,10 +112,13 @@ void EnvoyQuicServerStream::resetStream(Http::StreamResetReason reason) { // of propagating original reset reason. In QUICHE if a stream stops reading // before FIN or RESET received, it resets the steam with QUIC_STREAM_NO_ERROR. StopReading(); - runResetCallbacks(Http::StreamResetReason::LocalReset); } else { Reset(envoyResetReasonToQuicRstError(reason)); } + // Run reset callbacks once because HCM calls resetStream() without tearing + // down its own ActiveStream. It might be no-op if it has been called already + // in ResetWithError(). + runResetCallbacks(Http::StreamResetReason::LocalReset); } void EnvoyQuicServerStream::switchStreamBlockState() { diff --git a/source/common/quic/quic_stats_gatherer.h b/source/common/quic/quic_stats_gatherer.h index f704eeebc238..8a7b55fdc448 100644 --- a/source/common/quic/quic_stats_gatherer.h +++ b/source/common/quic/quic_stats_gatherer.h @@ -17,6 +17,11 @@ namespace Quic { class QuicStatsGatherer : public quic::QuicAckListenerInterface { public: explicit QuicStatsGatherer(Envoy::TimeSource* time_source) : time_source_(time_source) {} + ~QuicStatsGatherer() override { + if (!logging_done_) { + maybeDoDeferredLog(false); + } + } // QuicAckListenerInterface void OnPacketAcked(int acked_bytes, quic::QuicTime::Delta delta_largest_observed) override; diff --git a/source/common/rds/rds_route_config_subscription.cc b/source/common/rds/rds_route_config_subscription.cc index 4bda9ae928c5..2f8eea06f982 100644 --- a/source/common/rds/rds_route_config_subscription.cc +++ b/source/common/rds/rds_route_config_subscription.cc @@ -31,10 +31,11 @@ RdsRouteConfigSubscription::RdsRouteConfigSubscription( manager_identifier_(manager_identifier), config_update_info_(std::move(config_update)), resource_decoder_(std::move(resource_decoder)) { const auto resource_type = route_config_provider_manager_.protoTraits().resourceType(); - subscription_ = + subscription_ = THROW_OR_RETURN_VALUE( factory_context.clusterManager().subscriptionFactory().subscriptionFromConfigSource( config_source, Envoy::Grpc::Common::typeUrl(resource_type), *scope_, *this, - resource_decoder_, {}); + resource_decoder_, {}), + Envoy::Config::SubscriptionPtr); local_init_manager_.add(local_init_target_); } diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index b8a3ab7b660c..80a213b3ab54 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -587,10 +587,16 @@ RouteEntryImplBase::RouteEntryImplBase(const CommonVirtualHostSharedPtr& vhost, SET_AND_RETURN_IF_NOT_OK(policy_or_error.status(), creation_status); retry_policy_ = std::move(policy_or_error.value()); - auto body_or_error = ConfigUtility::parseDirectResponseBody( - route, factory_context.api(), vhost_->globalRouteConfig().maxDirectResponseBodySizeBytes()); - SET_AND_RETURN_IF_NOT_OK(body_or_error.status(), creation_status); - direct_response_body_ = body_or_error.value(); + if (route.has_direct_response() && route.direct_response().has_body()) { + + auto provider_or_error = Envoy::Config::DataSource::DataSourceProvider::create( + route.direct_response().body(), factory_context.mainThreadDispatcher(), + factory_context.threadLocal(), factory_context.api(), true, + vhost_->globalRouteConfig().maxDirectResponseBodySizeBytes()); + SET_AND_RETURN_IF_NOT_OK(provider_or_error.status(), creation_status); + direct_response_body_provider_ = std::move(provider_or_error.value()); + } + if (!route.request_headers_to_add().empty() || !route.request_headers_to_remove().empty()) { auto parser_or_error = HeaderParser::configure(route.request_headers_to_add(), route.request_headers_to_remove()); diff --git a/source/common/router/config_impl.h b/source/common/router/config_impl.h index a8fcaeae124e..b27fe572645f 100644 --- a/source/common/router/config_impl.h +++ b/source/common/router/config_impl.h @@ -24,6 +24,7 @@ #include "source/common/common/matchers.h" #include "source/common/common/packed_struct.h" +#include "source/common/config/datasource.h" #include "source/common/config/metadata.h" #include "source/common/http/hash_policy.h" #include "source/common/http/header_utility.h" @@ -811,7 +812,10 @@ class RouteEntryImplBase : public RouteEntryAndRoute, std::string newUri(const Http::RequestHeaderMap& headers) const override; void rewritePathHeader(Http::RequestHeaderMap&, bool) const override {} Http::Code responseCode() const override { return direct_response_code_.value(); } - const std::string& responseBody() const override { return direct_response_body_; } + const std::string& responseBody() const override { + return direct_response_body_provider_ != nullptr ? direct_response_body_provider_->data() + : EMPTY_STRING; + } // Router::Route const DirectResponseEntry* directResponseEntry() const override; @@ -1240,7 +1244,7 @@ class RouteEntryImplBase : public RouteEntryAndRoute, const DecoratorConstPtr decorator_; const RouteTracingConstPtr route_tracing_; - std::string direct_response_body_; + Envoy::Config::DataSource::DataSourceProviderPtr direct_response_body_provider_; std::unique_ptr per_filter_configs_; const std::string route_name_; TimeSource& time_source_; diff --git a/source/common/router/router.cc b/source/common/router/router.cc index e29c26cbd711..e37c0f08d2a0 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -789,7 +789,8 @@ Http::FilterHeadersStatus Filter::decodeHeaders(Http::RequestHeaderMap& headers, .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_); + .setBufferLimit(1 > retry_shadow_buffer_limit_ ? 1 : retry_shadow_buffer_limit_) + .setDiscardResponseBody(true); options.setFilterConfig(config_); if (end_stream) { // This is a header-only request, and can be dispatched immediately to the shadow diff --git a/source/common/router/router_ratelimit.cc b/source/common/router/router_ratelimit.cc index e48b93d791a9..db2dcc45e298 100644 --- a/source/common/router/router_ratelimit.cc +++ b/source/common/router/router_ratelimit.cc @@ -170,8 +170,10 @@ bool MaskedRemoteAddressAction::populateDescriptor(RateLimit::DescriptorEntry& d } // TODO: increase the efficiency, avoid string transform back and forth + // Note: we don't do validity checking for CIDR range here because we know + // from addressAsString this is a valid address. Network::Address::CidrRange cidr_entry = - Network::Address::CidrRange::create(remote_address->ip()->addressAsString(), mask_len); + *Network::Address::CidrRange::create(remote_address->ip()->addressAsString(), mask_len); descriptor_entry = {"masked_remote_address", cidr_entry.asString()}; return true; diff --git a/source/common/router/scoped_rds.cc b/source/common/router/scoped_rds.cc index 1ad77bd8aecd..e459d0dc6be0 100644 --- a/source/common/router/scoped_rds.cc +++ b/source/common/router/scoped_rds.cc @@ -144,18 +144,20 @@ ScopedRdsConfigSubscription::ScopedRdsConfigSubscription( route_config_provider_manager_(route_config_provider_manager) { const auto resource_name = getResourceName(); if (scoped_rds.srds_resources_locator().empty()) { - subscription_ = + subscription_ = THROW_OR_RETURN_VALUE( factory_context.clusterManager().subscriptionFactory().subscriptionFromConfigSource( scoped_rds.scoped_rds_config_source(), Grpc::Common::typeUrl(resource_name), *scope_, - *this, resource_decoder_, {}); + *this, resource_decoder_, {}), + Envoy::Config::SubscriptionPtr); } else { const auto srds_resources_locator = THROW_OR_RETURN_VALUE( Envoy::Config::XdsResourceIdentifier::decodeUrl(scoped_rds.srds_resources_locator()), xds::core::v3::ResourceLocator); - subscription_ = + subscription_ = THROW_OR_RETURN_VALUE( factory_context.clusterManager().subscriptionFactory().collectionSubscriptionFromUrl( srds_resources_locator, scoped_rds.scoped_rds_config_source(), resource_name, *scope_, - *this, resource_decoder_); + *this, resource_decoder_), + Envoy::Config::SubscriptionPtr); } // TODO(tony612): consider not using the callback here. diff --git a/source/common/router/upstream_codec_filter.cc b/source/common/router/upstream_codec_filter.cc index 9882fda66eed..56ac395dd40b 100644 --- a/source/common/router/upstream_codec_filter.cc +++ b/source/common/router/upstream_codec_filter.cc @@ -83,6 +83,8 @@ Http::FilterHeadersStatus UpstreamCodecFilter::decodeHeaders(Http::RequestHeader } if (callbacks_->upstreamCallbacks()->pausedForConnect()) { return Http::FilterHeadersStatus::StopAllIterationAndWatermark; + } else if (callbacks_->upstreamCallbacks()->pausedForWebsocketUpgrade()) { + return Http::FilterHeadersStatus::StopAllIterationAndWatermark; } return Http::FilterHeadersStatus::Continue; } @@ -154,6 +156,34 @@ void UpstreamCodecFilter::CodecBridge::decodeHeaders(Http::ResponseHeaderMapPtr& filter_.callbacks_->continueDecoding(); } + if (filter_.callbacks_->upstreamCallbacks()->pausedForWebsocketUpgrade()) { + const uint64_t status = Http::Utility::getResponseStatus(*headers); + const auto protocol = filter_.callbacks_->upstreamCallbacks()->upstreamStreamInfo().protocol(); + if (status == static_cast(Http::Code::SwitchingProtocols) || + (protocol.has_value() && protocol.value() != Envoy::Http::Protocol::Http11)) { + // handshake is finished and continue the data processing. + filter_.callbacks_->upstreamCallbacks()->setPausedForWebsocketUpgrade(false); + filter_.callbacks_->continueDecoding(); + } else { + // Other status, e.g., 426 or 200, indicate a failed handshake, Envoy as a proxy will proxy + // back the response header to downstream and then close the request, since WebSocket + // just needs headers for handshake per RFC-6455. Note: HTTP/2 200 will be normalized to + // 101 before this point in codec and this patch will skip this scenario from the above + // proto check. + filter_.callbacks_->sendLocalReply( + static_cast(status), "", + [&headers](Http::ResponseHeaderMap& local_headers) { + headers->iterate([&local_headers](const Envoy::Http::HeaderEntry& header) { + local_headers.addCopy(Http::LowerCaseString(header.key().getStringView()), + header.value().getStringView()); + return Envoy::Http::HeaderMap::Iterate::Continue; + }); + }, + std::nullopt, StreamInfo::ResponseCodeDetails::get().WebsocketHandshakeUnsuccessful); + return; + } + } + maybeEndDecode(end_stream); filter_.callbacks_->encodeHeaders(std::move(headers), end_stream, StreamInfo::ResponseCodeDetails::get().ViaUpstream); diff --git a/source/common/router/upstream_request.cc b/source/common/router/upstream_request.cc index 41a42290d3c0..2d8c83b5b775 100644 --- a/source/common/router/upstream_request.cc +++ b/source/common/router/upstream_request.cc @@ -90,7 +90,7 @@ UpstreamRequest::UpstreamRequest(RouterFilterInterface& parent, encode_trailers_(false), retried_(false), awaiting_headers_(true), outlier_detection_timeout_recorded_(false), create_per_try_timeout_on_request_complete_(false), paused_for_connect_(false), - reset_stream_(false), + paused_for_websocket_(false), reset_stream_(false), record_timeout_budget_(parent_.cluster()->timeoutBudgetStats().has_value()), cleaned_up_(false), had_upstream_(false), stream_options_({can_send_early_data, can_use_http3}), grpc_rq_success_deferred_(false), @@ -389,6 +389,13 @@ void UpstreamRequest::acceptHeadersFromRouter(bool end_stream) { auto* headers = parent_.downstreamHeaders(); if (headers->getMethodValue() == Http::Headers::get().MethodValues.Connect) { paused_for_connect_ = true; + // If this is a websocket upgrade request, pause the request until the upstream sends + // the 101 Switching Protocols response code. Using the else logic here to obey CONNECT + // method which is expecting 2xx response. + } else if ((Runtime::runtimeFeatureEnabled( + "envoy.reloadable_features.check_switch_protocol_websocket_handshake")) && + Http::Utility::isWebSocketUpgradeRequest(*headers)) { + paused_for_websocket_ = true; } // Kick off creation of the upstream connection immediately upon receiving headers. @@ -602,8 +609,10 @@ void UpstreamRequest::onPoolReady(std::unique_ptr&& upstream, if (protocol) { stream_info_.protocol(protocol.value()); } else { - // We only pause for CONNECT for HTTP upstreams. If this is a TCP upstream, unpause. + // We only pause for CONNECT and WebSocket for HTTP upstreams. If this is a TCP upstream, + // unpause. paused_for_connect_ = false; + paused_for_websocket_ = false; } StreamInfo::UpstreamInfo& upstream_info = *stream_info_.upstreamInfo(); diff --git a/source/common/router/upstream_request.h b/source/common/router/upstream_request.h index c6e7a4ec4c94..eb32a8ec8431 100644 --- a/source/common/router/upstream_request.h +++ b/source/common/router/upstream_request.h @@ -245,6 +245,7 @@ class UpstreamRequest : public Logger::Loggable, // True if the CONNECT headers have been sent but proxying payload is paused // waiting for response headers. bool paused_for_connect_ : 1; + bool paused_for_websocket_ : 1; bool reset_stream_ : 1; // Sentinel to indicate if timeout budget tracking is configured for the cluster, @@ -361,6 +362,14 @@ class UpstreamRequestFilterManagerCallbacks : public Http::FilterManagerCallback } bool pausedForConnect() const override { return upstream_request_.paused_for_connect_; } void setPausedForConnect(bool value) override { upstream_request_.paused_for_connect_ = value; } + + bool pausedForWebsocketUpgrade() const override { + return upstream_request_.paused_for_websocket_; + } + void setPausedForWebsocketUpgrade(bool value) override { + upstream_request_.paused_for_websocket_ = value; + } + const Http::ConnectionPool::Instance::StreamOptions& upstreamStreamOptions() const override { return upstream_request_.upstreamStreamOptions(); } diff --git a/source/common/router/vhds.cc b/source/common/router/vhds.cc index 823ee0b0f86d..b182c5b1f96c 100644 --- a/source/common/router/vhds.cc +++ b/source/common/router/vhds.cc @@ -58,10 +58,11 @@ VhdsSubscription::VhdsSubscription(RouteConfigUpdatePtr& config_update_info, const auto resource_name = getResourceName(); Envoy::Config::SubscriptionOptions options; options.use_namespace_matching_ = true; - subscription_ = + subscription_ = THROW_OR_RETURN_VALUE( factory_context.clusterManager().subscriptionFactory().subscriptionFromConfigSource( config_update_info_->protobufConfigurationCast().vhds().config_source(), - Grpc::Common::typeUrl(resource_name), *scope_, *this, resource_decoder_, options); + Grpc::Common::typeUrl(resource_name), *scope_, *this, resource_decoder_, options), + Envoy::Config::SubscriptionPtr); } void VhdsSubscription::updateOnDemand(const std::string& with_route_config_name_prefix) { diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 8d3a4e7065d4..50bc1655a634 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -32,6 +32,7 @@ RUNTIME_GUARD(envoy_reloadable_features_abort_filter_chain_on_stream_reset); RUNTIME_GUARD(envoy_reloadable_features_avoid_zombie_streams); RUNTIME_GUARD(envoy_reloadable_features_check_mep_on_first_eject); +RUNTIME_GUARD(envoy_reloadable_features_check_switch_protocol_websocket_handshake); RUNTIME_GUARD(envoy_reloadable_features_conn_pool_delete_when_idle); RUNTIME_GUARD(envoy_reloadable_features_consistent_header_validation); RUNTIME_GUARD(envoy_reloadable_features_defer_processing_backedup_streams); @@ -131,8 +132,6 @@ FALSE_RUNTIME_GUARD(envoy_reloadable_features_runtime_initialized); // TODO(mattklein123): Also unit test this if this sticks and this becomes the default for Apple & // Android. FALSE_RUNTIME_GUARD(envoy_reloadable_features_always_use_v6); -// TODO(wbpcode) complete remove this feature is no one use it. -FALSE_RUNTIME_GUARD(envoy_reloadable_features_refresh_rtt_after_request); // TODO(vikaschoudhary16) flip this to true only after all the // TcpProxy::Filter::HttpStreamDecoderFilterCallbacks are implemented or commented as unnecessary FALSE_RUNTIME_GUARD(envoy_restart_features_upstream_http_filters_with_tcp_proxy); @@ -149,7 +148,7 @@ FALSE_RUNTIME_GUARD(envoy_restart_features_use_eds_cache_for_ads); // For more information about Universal Header Validation, please see // https://github.com/envoyproxy/envoy/issues/10646 FALSE_RUNTIME_GUARD(envoy_reloadable_features_enable_universal_header_validator); -// TODO(pksohn): enable after fixing https://github.com/envoyproxy/envoy/issues/29930 +// TODO(pksohn): enable after canarying fix for https://github.com/envoyproxy/envoy/issues/29930 FALSE_RUNTIME_GUARD(envoy_reloadable_features_quic_defer_logging_to_ack_listener); // TODO(panting): flip this to true after some test time. FALSE_RUNTIME_GUARD(envoy_reloadable_features_use_config_in_happy_eyeballs); @@ -159,6 +158,8 @@ FALSE_RUNTIME_GUARD(envoy_reloadable_features_prefer_quic_client_udp_gro); FALSE_RUNTIME_GUARD(envoy_reloadable_features_reresolve_null_addresses); // TODO(alyssar) evaluate and either make this a config knob or remove. FALSE_RUNTIME_GUARD(envoy_reloadable_features_reresolve_if_no_connections); +// TODO(adisuissa): flip to true after this is out of alpha mode. +FALSE_RUNTIME_GUARD(envoy_restart_features_xds_failover_support); // A flag to set the maximum TLS version for google_grpc client to TLS1.2, when needed for // compliance restrictions. diff --git a/source/common/runtime/runtime_impl.cc b/source/common/runtime/runtime_impl.cc index 253ea9710471..2d625947ccee 100644 --- a/source/common/runtime/runtime_impl.cc +++ b/source/common/runtime/runtime_impl.cc @@ -605,12 +605,13 @@ absl::Status LoaderImpl::initLayers(Event::Dispatcher& dispatcher, return loadNewSnapshot(); } -void LoaderImpl::initialize(Upstream::ClusterManager& cm) { +absl::Status LoaderImpl::initialize(Upstream::ClusterManager& cm) { cm_ = &cm; for (const auto& s : subscriptions_) { - s->createSubscription(); + RETURN_IF_NOT_OK(s->createSubscription()); } + return absl::OkStatus(); } void LoaderImpl::startRtdsSubscriptions(ReadyCallback on_done) { @@ -632,11 +633,14 @@ RtdsSubscription::RtdsSubscription( stats_scope_(store_.createScope("runtime")), resource_name_(rtds_layer.name()), init_target_("RTDS " + resource_name_, [this]() { start(); }) {} -void RtdsSubscription::createSubscription() { +absl::Status RtdsSubscription::createSubscription() { const auto resource_name = getResourceName(); - subscription_ = parent_.cm_->subscriptionFactory().subscriptionFromConfigSource( + auto subscription_or_error = parent_.cm_->subscriptionFactory().subscriptionFromConfigSource( config_source_, Grpc::Common::typeUrl(resource_name), *stats_scope_, *this, resource_decoder_, {}); + RETURN_IF_NOT_OK(subscription_or_error.status()); + subscription_ = std::move(*subscription_or_error); + return absl::OkStatus(); } absl::Status diff --git a/source/common/runtime/runtime_impl.h b/source/common/runtime/runtime_impl.h index 9c960a58cbd3..cf47d6f435d9 100644 --- a/source/common/runtime/runtime_impl.h +++ b/source/common/runtime/runtime_impl.h @@ -193,7 +193,7 @@ struct RtdsSubscription : Envoy::Config::SubscriptionBase& removed_resources); - void createSubscription(); + absl::Status createSubscription(); LoaderImpl& parent_; const envoy::config::core::v3::ConfigSource config_source_; @@ -223,7 +223,7 @@ class LoaderImpl : public Loader, Logger::Loggable { Api::Api& api); // Runtime::Loader - void initialize(Upstream::ClusterManager& cm) override; + absl::Status initialize(Upstream::ClusterManager& cm) override; const Snapshot& snapshot() override; SnapshotConstSharedPtr threadsafeSnapshot() override; absl::Status mergeValues(const absl::node_hash_map& values) override; diff --git a/source/common/secret/sds_api.cc b/source/common/secret/sds_api.cc index 1cce6941e2f9..a8ab37af3ee6 100644 --- a/source/common/secret/sds_api.cc +++ b/source/common/secret/sds_api.cc @@ -32,8 +32,10 @@ SdsApi::SdsApi(envoy::config::core::v3::ConfigSource sds_config, absl::string_vi time_source_.systemTime()} { const auto resource_name = getResourceName(); // This has to happen here (rather than in initialize()) as it can throw exceptions. - subscription_ = subscription_factory_.subscriptionFromConfigSource( - sds_config_, Grpc::Common::typeUrl(resource_name), *scope_, *this, resource_decoder_, {}); + subscription_ = THROW_OR_RETURN_VALUE( + subscription_factory_.subscriptionFromConfigSource( + sds_config_, Grpc::Common::typeUrl(resource_name), *scope_, *this, resource_decoder_, {}), + Config::SubscriptionPtr); } void SdsApi::resolveDataSource(const FileContentMap& files, diff --git a/source/common/ssl/tls_certificate_config_impl.cc b/source/common/ssl/tls_certificate_config_impl.cc index d206870a85e6..24393f420a75 100644 --- a/source/common/ssl/tls_certificate_config_impl.cc +++ b/source/common/ssl/tls_certificate_config_impl.cc @@ -17,7 +17,7 @@ namespace { // string. std::string maybeSet(absl::StatusOr to_set, absl::Status error) { if (to_set.status().ok()) { - return to_set.value(); + return std::move(to_set.value()); } error = to_set.status(); return ""; diff --git a/source/common/tracing/null_span_impl.h b/source/common/tracing/null_span_impl.h index d573318e5751..cba151272db4 100644 --- a/source/common/tracing/null_span_impl.h +++ b/source/common/tracing/null_span_impl.h @@ -25,6 +25,7 @@ class NullSpan : public Span { void injectContext(Tracing::TraceContext&, const UpstreamContext&) override {} void setBaggage(absl::string_view, absl::string_view) override {} std::string getBaggage(absl::string_view) override { return EMPTY_STRING; } + std::string getSpanId() const override { return EMPTY_STRING; } std::string getTraceId() const override { return EMPTY_STRING; } SpanPtr spawnChild(const Config&, const std::string&, SystemTime) override { return SpanPtr{new NullSpan()}; diff --git a/source/common/upstream/cds_api_impl.cc b/source/common/upstream/cds_api_impl.cc index cfb0ee3e8b21..caa8c5de399a 100644 --- a/source/common/upstream/cds_api_impl.cc +++ b/source/common/upstream/cds_api_impl.cc @@ -25,11 +25,15 @@ CdsApiImpl::CdsApiImpl(const envoy::config::core::v3::ConfigSource& cds_config, helper_(cm, "cds"), cm_(cm), scope_(scope.createScope("cluster_manager.cds.")) { const auto resource_name = getResourceName(); if (cds_resources_locator == nullptr) { - subscription_ = cm_.subscriptionFactory().subscriptionFromConfigSource( - cds_config, Grpc::Common::typeUrl(resource_name), *scope_, *this, resource_decoder_, {}); + subscription_ = THROW_OR_RETURN_VALUE(cm_.subscriptionFactory().subscriptionFromConfigSource( + cds_config, Grpc::Common::typeUrl(resource_name), + *scope_, *this, resource_decoder_, {}), + Config::SubscriptionPtr); } else { - subscription_ = cm.subscriptionFactory().collectionSubscriptionFromUrl( - *cds_resources_locator, cds_config, resource_name, *scope_, *this, resource_decoder_); + subscription_ = THROW_OR_RETURN_VALUE( + cm.subscriptionFactory().collectionSubscriptionFromUrl( + *cds_resources_locator, cds_config, resource_name, *scope_, *this, resource_decoder_), + Config::SubscriptionPtr); } } diff --git a/source/common/upstream/cluster_manager_impl.cc b/source/common/upstream/cluster_manager_impl.cc index 28c7fb53e3e5..64de15c7c196 100644 --- a/source/common/upstream/cluster_manager_impl.cc +++ b/source/common/upstream/cluster_manager_impl.cc @@ -450,11 +450,11 @@ ClusterManagerImpl::initialize(const envoy::config::bootstrap::v3::Bootstrap& bo auto factory_or_error = Config::Utility::factoryForGrpcApiConfigSource( *async_client_manager_, dyn_resources.ads_config(), *stats_.rootScope(), false); THROW_IF_STATUS_NOT_OK(factory_or_error, throw); - ads_mux_ = - factory->create(factory_or_error.value()->createUncachedRawAsyncClient(), dispatcher_, - random_, *stats_.rootScope(), dyn_resources.ads_config(), local_info_, - std::move(custom_config_validators), std::move(backoff_strategy), - makeOptRefFromPtr(xds_config_tracker_.get()), {}, use_eds_cache); + ads_mux_ = factory->create(factory_or_error.value()->createUncachedRawAsyncClient(), nullptr, + dispatcher_, random_, *stats_.rootScope(), + dyn_resources.ads_config(), local_info_, + std::move(custom_config_validators), std::move(backoff_strategy), + makeOptRefFromPtr(xds_config_tracker_.get()), {}, use_eds_cache); } else { absl::Status status = Config::Utility::checkTransportVersion(dyn_resources.ads_config()); RETURN_IF_NOT_OK(status); @@ -474,7 +474,7 @@ ClusterManagerImpl::initialize(const envoy::config::bootstrap::v3::Bootstrap& bo *async_client_manager_, dyn_resources.ads_config(), *stats_.rootScope(), false); THROW_IF_STATUS_NOT_OK(factory_or_error, throw); ads_mux_ = factory->create( - factory_or_error.value()->createUncachedRawAsyncClient(), dispatcher_, random_, + factory_or_error.value()->createUncachedRawAsyncClient(), nullptr, dispatcher_, random_, *stats_.rootScope(), dyn_resources.ads_config(), local_info_, std::move(custom_config_validators), std::move(backoff_strategy), makeOptRefFromPtr(xds_config_tracker_.get()), xds_delegate_opt_ref, use_eds_cache); diff --git a/source/common/upstream/od_cds_api_impl.cc b/source/common/upstream/od_cds_api_impl.cc index a9ec3c349693..f52fee9f193a 100644 --- a/source/common/upstream/od_cds_api_impl.cc +++ b/source/common/upstream/od_cds_api_impl.cc @@ -30,11 +30,15 @@ OdCdsApiImpl::OdCdsApiImpl(const envoy::config::core::v3::ConfigSource& odcds_co // class for CDS and ODCDS. const auto resource_name = getResourceName(); if (!odcds_resources_locator.has_value()) { - subscription_ = cm_.subscriptionFactory().subscriptionFromConfigSource( - odcds_config, Grpc::Common::typeUrl(resource_name), *scope_, *this, resource_decoder_, {}); + subscription_ = THROW_OR_RETURN_VALUE(cm_.subscriptionFactory().subscriptionFromConfigSource( + odcds_config, Grpc::Common::typeUrl(resource_name), + *scope_, *this, resource_decoder_, {}), + Config::SubscriptionPtr); } else { - subscription_ = cm.subscriptionFactory().collectionSubscriptionFromUrl( - *odcds_resources_locator, odcds_config, resource_name, *scope_, *this, resource_decoder_); + subscription_ = THROW_OR_RETURN_VALUE(cm.subscriptionFactory().collectionSubscriptionFromUrl( + *odcds_resources_locator, odcds_config, resource_name, + *scope_, *this, resource_decoder_), + Config::SubscriptionPtr); } } diff --git a/source/common/websocket/codec.cc b/source/common/websocket/codec.cc index 1c1fc4e40b1a..e95d8ab3d256 100644 --- a/source/common/websocket/codec.cc +++ b/source/common/websocket/codec.cc @@ -128,7 +128,11 @@ uint8_t Decoder::doDecodeExtendedLength(absl::Span& data) { num_remaining_extended_length_bytes_ -= bytes_to_decode; if (num_remaining_extended_length_bytes_ == 0) { +#if ABSL_IS_BIG_ENDIAN + length_ = state_ == State::FrameHeaderExtendedLength16Bit ? htole16(le64toh(length_)) : length_; +#else length_ = state_ == State::FrameHeaderExtendedLength16Bit ? htobe16(length_) : htobe64(length_); +#endif if (num_remaining_masking_key_bytes_ > 0) { state_ = State::FrameHeaderMaskingKey; } else { diff --git a/source/exe/BUILD b/source/exe/BUILD index 905a09ff4627..59246022ad08 100644 --- a/source/exe/BUILD +++ b/source/exe/BUILD @@ -45,9 +45,10 @@ envoy_cc_library( "//source/server:options_base", "//source/server:server_base_lib", ] + select({ - "//bazel:windows_x86_64": envoy_all_extensions(WINDOWS_SKIP_TARGETS), - "//bazel:linux_ppc": envoy_all_extensions(PPC_SKIP_TARGETS), - "//bazel:disable_http3": envoy_all_extensions(NO_HTTP3_SKIP_TARGETS), + "//bazel:enable_http3_on_windows_x86_64": envoy_all_extensions(WINDOWS_SKIP_TARGETS), + "//bazel:enable_http3_on_linux_ppc": envoy_all_extensions(PPC_SKIP_TARGETS), + "//bazel:disable_http3_on_windows_x86_64": envoy_all_extensions(NO_HTTP3_SKIP_TARGETS + WINDOWS_SKIP_TARGETS), + "//bazel:disable_http3_on_linux_ppc": envoy_all_extensions(NO_HTTP3_SKIP_TARGETS + PPC_SKIP_TARGETS), "//conditions:default": envoy_all_extensions(), }), ) diff --git a/source/extensions/access_loggers/open_telemetry/access_log_impl.cc b/source/extensions/access_loggers/open_telemetry/access_log_impl.cc index e3866dfc98e7..f86fda60b50b 100644 --- a/source/extensions/access_loggers/open_telemetry/access_log_impl.cc +++ b/source/extensions/access_loggers/open_telemetry/access_log_impl.cc @@ -9,6 +9,7 @@ #include "source/common/common/assert.h" #include "source/common/config/utility.h" +#include "source/common/formatter/substitution_formatter.h" #include "source/common/http/headers.h" #include "source/common/network/utility.h" #include "source/common/protobuf/message_validator_impl.h" @@ -58,7 +59,8 @@ AccessLog::ThreadLocalLogger::ThreadLocalLogger(GrpcAccessLoggerSharedPtr logger AccessLog::AccessLog( ::Envoy::AccessLog::FilterPtr&& filter, envoy::extensions::access_loggers::open_telemetry::v3::OpenTelemetryAccessLogConfig config, - ThreadLocal::SlotAllocator& tls, GrpcAccessLoggerCacheSharedPtr access_logger_cache) + ThreadLocal::SlotAllocator& tls, GrpcAccessLoggerCacheSharedPtr access_logger_cache, + const std::vector& commands) : Common::ImplBase(std::move(filter)), tls_slot_(tls.allocateSlot()), access_logger_cache_(std::move(access_logger_cache)) { @@ -71,9 +73,9 @@ AccessLog::AccessLog( // Packing the body "AnyValue" to a "KeyValueList" only if it's not empty, otherwise the // formatter would fail to parse it. if (config.body().value_case() != ::opentelemetry::proto::common::v1::AnyValue::VALUE_NOT_SET) { - body_formatter_ = std::make_unique(packBody(config.body())); + body_formatter_ = std::make_unique(packBody(config.body()), commands); } - attributes_formatter_ = std::make_unique(config.attributes()); + attributes_formatter_ = std::make_unique(config.attributes(), commands); } void AccessLog::emitLog(const Formatter::HttpFormatterContext& log_context, @@ -102,6 +104,10 @@ void AccessLog::emitLog(const Formatter::HttpFormatterContext& log_context, auto trace_id = absl::StrCat(Hex::uint64ToHex(0), trace_id_hex); *log_entry.mutable_trace_id() = absl::HexStringToBytes(trace_id); } + std::string span_id_hex = log_context.activeSpan().getSpanId(); + if (!span_id_hex.empty()) { + *log_entry.mutable_span_id() = absl::HexStringToBytes(span_id_hex); + } tls_slot_->getTyped().logger_->log(std::move(log_entry)); } diff --git a/source/extensions/access_loggers/open_telemetry/access_log_impl.h b/source/extensions/access_loggers/open_telemetry/access_log_impl.h index 7d1013488eb4..308ec8ac1bdb 100644 --- a/source/extensions/access_loggers/open_telemetry/access_log_impl.h +++ b/source/extensions/access_loggers/open_telemetry/access_log_impl.h @@ -36,7 +36,8 @@ class AccessLog : public Common::ImplBase { AccessLog( ::Envoy::AccessLog::FilterPtr&& filter, envoy::extensions::access_loggers::open_telemetry::v3::OpenTelemetryAccessLogConfig config, - ThreadLocal::SlotAllocator& tls, GrpcAccessLoggerCacheSharedPtr access_logger_cache); + ThreadLocal::SlotAllocator& tls, GrpcAccessLoggerCacheSharedPtr access_logger_cache, + const std::vector& commands); private: /** diff --git a/source/extensions/access_loggers/open_telemetry/config.cc b/source/extensions/access_loggers/open_telemetry/config.cc index c371b5b724a4..237c5c9fa839 100644 --- a/source/extensions/access_loggers/open_telemetry/config.cc +++ b/source/extensions/access_loggers/open_telemetry/config.cc @@ -7,6 +7,7 @@ #include "source/common/common/assert.h" #include "source/common/common/macros.h" +#include "source/common/formatter/substitution_format_string.h" #include "source/common/grpc/async_client_impl.h" #include "source/common/protobuf/protobuf.h" #include "source/extensions/access_loggers/open_telemetry/access_log_impl.h" @@ -40,9 +41,12 @@ AccessLogFactory::createAccessLogInstance(const Protobuf::Message& config, const envoy::extensions::access_loggers::open_telemetry::v3::OpenTelemetryAccessLogConfig&>( config, context.messageValidationVisitor()); - return std::make_shared(std::move(filter), proto_config, - context.serverFactoryContext().threadLocal(), - getAccessLoggerCacheSingleton(context.serverFactoryContext())); + auto commands = + Formatter::SubstitutionFormatStringUtils::parseFormatters(proto_config.formatters(), context); + + return std::make_shared( + std::move(filter), proto_config, context.serverFactoryContext().threadLocal(), + getAccessLoggerCacheSingleton(context.serverFactoryContext()), commands); } ProtobufTypes::MessagePtr AccessLogFactory::createEmptyConfigProto() { diff --git a/source/extensions/access_loggers/open_telemetry/substitution_formatter.cc b/source/extensions/access_loggers/open_telemetry/substitution_formatter.cc index e48522bc54ef..a99b80878dd2 100644 --- a/source/extensions/access_loggers/open_telemetry/substitution_formatter.cc +++ b/source/extensions/access_loggers/open_telemetry/substitution_formatter.cc @@ -21,8 +21,9 @@ namespace AccessLoggers { namespace OpenTelemetry { OpenTelemetryFormatter::OpenTelemetryFormatter( - const ::opentelemetry::proto::common::v1::KeyValueList& format_mapping) - : kv_list_output_format_(FormatBuilder().toFormatMapValue(format_mapping)) {} + const ::opentelemetry::proto::common::v1::KeyValueList& format_mapping, + const std::vector& commands) + : kv_list_output_format_(FormatBuilder(commands).toFormatMapValue(format_mapping)) {} OpenTelemetryFormatter::OpenTelemetryFormatMapWrapper OpenTelemetryFormatter::FormatBuilder::toFormatMapValue( @@ -77,7 +78,7 @@ OpenTelemetryFormatter::FormatBuilder::toFormatListValue( std::vector OpenTelemetryFormatter::FormatBuilder::toFormatStringValue(const std::string& string_format) const { - return Formatter::SubstitutionFormatParser::parse(string_format, {}); + return Formatter::SubstitutionFormatParser::parse(string_format, commands_); } ::opentelemetry::proto::common::v1::AnyValue OpenTelemetryFormatter::providersCallback( diff --git a/source/extensions/access_loggers/open_telemetry/substitution_formatter.h b/source/extensions/access_loggers/open_telemetry/substitution_formatter.h index ea797fff13e2..bc97fe91d5b9 100644 --- a/source/extensions/access_loggers/open_telemetry/substitution_formatter.h +++ b/source/extensions/access_loggers/open_telemetry/substitution_formatter.h @@ -26,7 +26,8 @@ OpenTelemetryFormatMapVisitorHelper(Ts...) -> OpenTelemetryFormatMapVisitorHelpe */ class OpenTelemetryFormatter { public: - OpenTelemetryFormatter(const ::opentelemetry::proto::common::v1::KeyValueList& format_mapping); + OpenTelemetryFormatter(const ::opentelemetry::proto::common::v1::KeyValueList& format_mapping, + const std::vector& commands); ::opentelemetry::proto::common::v1::KeyValueList format(const Formatter::HttpFormatterContext& context, const StreamInfo::StreamInfo& info) const; @@ -62,12 +63,17 @@ class OpenTelemetryFormatter { // Methods for building the format map. class FormatBuilder { public: + explicit FormatBuilder(const std::vector& commands) + : commands_(commands) {} std::vector toFormatStringValue(const std::string& string_format) const; OpenTelemetryFormatMapWrapper toFormatMapValue(const ::opentelemetry::proto::common::v1::KeyValueList& struct_format) const; OpenTelemetryFormatListWrapper toFormatListValue( const ::opentelemetry::proto::common::v1::ArrayValue& list_value_format) const; + + private: + const std::vector& commands_; }; // Methods for doing the actual formatting. diff --git a/source/extensions/clusters/eds/eds.cc b/source/extensions/clusters/eds/eds.cc index 5ea525cd7b20..02a2b748298d 100644 --- a/source/extensions/clusters/eds/eds.cc +++ b/source/extensions/clusters/eds/eds.cc @@ -34,10 +34,11 @@ EdsClusterImpl::EdsClusterImpl(const envoy::config::cluster::v3::Cluster& cluste initialize_phase_ = InitializePhase::Secondary; } const auto resource_name = getResourceName(); - subscription_ = + subscription_ = THROW_OR_RETURN_VALUE( cluster_context.clusterManager().subscriptionFactory().subscriptionFromConfigSource( eds_config, Grpc::Common::typeUrl(resource_name), info_->statsScope(), *this, - resource_decoder_, {}); + resource_decoder_, {}), + Config::SubscriptionPtr); } EdsClusterImpl::~EdsClusterImpl() { diff --git a/source/extensions/clusters/eds/leds.cc b/source/extensions/clusters/eds/leds.cc index 5cad9d6c8b40..a3e5310b3e5b 100644 --- a/source/extensions/clusters/eds/leds.cc +++ b/source/extensions/clusters/eds/leds.cc @@ -24,10 +24,11 @@ LedsSubscription::LedsSubscription( Config::XdsResourceIdentifier::decodeUrl(leds_config.leds_collection_name()), xds::core::v3::ResourceLocator); const auto resource_name = getResourceName(); - subscription_ = + subscription_ = THROW_OR_RETURN_VALUE( factory_context.clusterManager().subscriptionFactory().collectionSubscriptionFromUrl( leds_resource_locator, leds_config.leds_config(), resource_name, *stats_scope_, *this, - resource_decoder_); + resource_decoder_), + Config::SubscriptionPtr); subscription_->start({}); } diff --git a/source/extensions/common/matcher/trie_matcher.h b/source/extensions/common/matcher/trie_matcher.h index b50ae214eac2..6084d9c82449 100644 --- a/source/extensions/common/matcher/trie_matcher.h +++ b/source/extensions/common/matcher/trie_matcher.h @@ -144,7 +144,9 @@ class TrieMatcherFactoryBase : public ::Envoy::Matcher::CustomMatcherFactory node = {i, range.prefix_len().value(), range_matcher.exclusive(), on_match}; - data.push_back({node, {Network::Address::CidrRange::create(range)}}); + data.push_back({node, + {THROW_OR_RETURN_VALUE(Network::Address::CidrRange::create(range), + Network::Address::CidrRange)}}); } } auto lc_trie = std::make_shared>>(data); diff --git a/source/extensions/common/wasm/context.cc b/source/extensions/common/wasm/context.cc index 9164670de7b2..abf93e7fab73 100644 --- a/source/extensions/common/wasm/context.cc +++ b/source/extensions/common/wasm/context.cc @@ -1879,8 +1879,9 @@ void Context::onHttpCallFailure(uint32_t token, Http::AsyncClient::FailureReason return; } status_code_ = static_cast(WasmResult::BrokenConnection); - // This is the only value currently. - ASSERT(reason == Http::AsyncClient::FailureReason::Reset); + // TODO(botengyao): handle different failure reasons. + ASSERT(reason == Http::AsyncClient::FailureReason::Reset || + reason == Http::AsyncClient::FailureReason::ExceedResponseBufferLimit); status_message_ = "reset"; // Deferred "after VM call" actions are going to be executed upon returning from // ContextBase::*, which might include deleting Context object via proxy_done(). diff --git a/source/extensions/compression/brotli/decompressor/brotli_decompressor_impl.cc b/source/extensions/compression/brotli/decompressor/brotli_decompressor_impl.cc index eb1bb144baa5..d096179d1717 100644 --- a/source/extensions/compression/brotli/decompressor/brotli_decompressor_impl.cc +++ b/source/extensions/compression/brotli/decompressor/brotli_decompressor_impl.cc @@ -60,26 +60,47 @@ void BrotliDecompressorImpl::decompress(const Buffer::Instance& input_buffer, } bool BrotliDecompressorImpl::process(Common::BrotliContext& ctx, Buffer::Instance& output_buffer) { - BrotliDecoderResult result; - result = BrotliDecoderDecompressStream(state_.get(), &ctx.avail_in_, &ctx.next_in_, - &ctx.avail_out_, &ctx.next_out_, nullptr); - if (result == BROTLI_DECODER_RESULT_ERROR) { - // TODO(rojkov): currently the Brotli library doesn't specify possible errors in its API. Add - // more detailed stats when they are documented. - stats_.brotli_error_.inc(); - return false; - } + BrotliDecoderResult result = BrotliDecoderDecompressStream( + state_.get(), &ctx.avail_in_, &ctx.next_in_, &ctx.avail_out_, &ctx.next_out_, nullptr); + + switch (result) { + case BROTLI_DECODER_RESULT_SUCCESS: + // The decompression is done successfully but there is still some input left. + // We treat this as an error and stop the decompression directly to avoid + // possible endless loop. + if (ctx.avail_in_ > 0) { + stats_.brotli_error_.inc(); + stats_.brotli_redundant_input_.inc(); + return false; + } + // The decompression is done successfully and fall through to the next case + // to check if the output buffer is full and flush chunk to the output buffer. + FALLTHRU; + case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT: + ASSERT(ctx.avail_in_ == 0); + FALLTHRU; + case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT: + // Check if the output buffer is full first. If it is full then we treat it + // as an error and stop the decompression directly to avoid possible decompression + // bomb. + if (Runtime::runtimeFeatureEnabled( + "envoy.reloadable_features.enable_compression_bomb_protection") && + (output_buffer.length() > ctx.max_output_size_)) { + stats_.brotli_error_.inc(); + stats_.brotli_output_overflow_.inc(); + return false; + } - if (Runtime::runtimeFeatureEnabled( - "envoy.reloadable_features.enable_compression_bomb_protection") && - (output_buffer.length() > ctx.max_output_size_)) { + // If current chunk is full then flush it to the output buffer and reset + // the chunk or do nothing. + ctx.updateOutput(output_buffer); + return true; + case BROTLI_DECODER_RESULT_ERROR: stats_.brotli_error_.inc(); return false; } - ctx.updateOutput(output_buffer); - - return true; + PANIC("Unexpected BrotliDecoderResult"); } } // namespace Decompressor diff --git a/source/extensions/compression/brotli/decompressor/brotli_decompressor_impl.h b/source/extensions/compression/brotli/decompressor/brotli_decompressor_impl.h index f55f6a7c545a..1f3328807fb3 100644 --- a/source/extensions/compression/brotli/decompressor/brotli_decompressor_impl.h +++ b/source/extensions/compression/brotli/decompressor/brotli_decompressor_impl.h @@ -17,7 +17,10 @@ namespace Decompressor { /** * All brotli decompressor stats. @see stats_macros.h */ -#define ALL_BROTLI_DECOMPRESSOR_STATS(COUNTER) COUNTER(brotli_error) +#define ALL_BROTLI_DECOMPRESSOR_STATS(COUNTER) \ + COUNTER(brotli_error) /*Decompression error of all.*/ \ + COUNTER(brotli_output_overflow) /*Decompression error because of the overflow output.*/ \ + COUNTER(brotli_redundant_input) /*Decompression error because of the redundant input.*/ /** * Struct definition for brotli decompressor stats. @see stats_macros.h diff --git a/source/extensions/config_subscription/grpc/BUILD b/source/extensions/config_subscription/grpc/BUILD index 581be4213417..9114b4a5213f 100644 --- a/source/extensions/config_subscription/grpc/BUILD +++ b/source/extensions/config_subscription/grpc/BUILD @@ -29,6 +29,7 @@ envoy_cc_extension( deps = [ ":eds_resources_cache_lib", ":grpc_mux_context_lib", + ":grpc_mux_failover_lib", ":grpc_stream_lib", ":xds_source_id_lib", "//envoy/config:custom_config_validators_interface", @@ -62,6 +63,7 @@ envoy_cc_library( ":delta_subscription_state_lib", ":eds_resources_cache_lib", ":grpc_mux_context_lib", + ":grpc_mux_failover_lib", ":grpc_stream_lib", ":pausable_ack_queue_lib", ":watch_map_lib", diff --git a/source/extensions/config_subscription/grpc/grpc_collection_subscription_factory.cc b/source/extensions/config_subscription/grpc/grpc_collection_subscription_factory.cc index a356e7a8f6c3..5b8433fd989c 100644 --- a/source/extensions/config_subscription/grpc/grpc_collection_subscription_factory.cc +++ b/source/extensions/config_subscription/grpc/grpc_collection_subscription_factory.cc @@ -31,6 +31,7 @@ SubscriptionPtr DeltaGrpcCollectionConfigSubscriptionFactory::create( THROW_IF_STATUS_NOT_OK(rate_limit_settings_or_error, throw); GrpcMuxContext grpc_mux_context{ factory_or_error.value()->createUncachedRawAsyncClient(), + /*failover_async_client_*/ nullptr, /*dispatcher_=*/data.dispatcher_, /*service_method_=*/deltaGrpcMethod(data.type_url_), /*local_info_=*/data.local_info_, diff --git a/source/extensions/config_subscription/grpc/grpc_mux_context.h b/source/extensions/config_subscription/grpc/grpc_mux_context.h index 254cb18b9534..03f7e4c7d119 100644 --- a/source/extensions/config_subscription/grpc/grpc_mux_context.h +++ b/source/extensions/config_subscription/grpc/grpc_mux_context.h @@ -19,6 +19,7 @@ namespace Config { // These are parameters needed for the creation of all GrpcMux objects. struct GrpcMuxContext { Grpc::RawAsyncClientPtr async_client_; + Grpc::RawAsyncClientPtr failover_async_client_; Event::Dispatcher& dispatcher_; const Protobuf::MethodDescriptor& service_method_; const LocalInfo::LocalInfo& local_info_; diff --git a/source/extensions/config_subscription/grpc/grpc_mux_failover.h b/source/extensions/config_subscription/grpc/grpc_mux_failover.h index 7ab3e944f084..60cba40cbd18 100644 --- a/source/extensions/config_subscription/grpc/grpc_mux_failover.h +++ b/source/extensions/config_subscription/grpc/grpc_mux_failover.h @@ -47,7 +47,8 @@ namespace Config { * defined, and may be converted to a config field in the future. */ template -class GrpcMuxFailover : public Logger::Loggable { +class GrpcMuxFailover : public GrpcStreamInterface, + public Logger::Loggable { public: // A GrpcStream creator function that receives the stream callbacks and returns a // GrpcStream object. This is introduced to facilitate dependency injection for @@ -79,7 +80,7 @@ class GrpcMuxFailover : public Logger::Loggable { virtual ~GrpcMuxFailover() = default; // Attempts to establish a new stream to the either the primary or failover source. - void establishNewStream() { + void establishNewStream() override { // Attempt establishing a connection to the primary source. // This method may be called multiple times, even if the primary stream is already // established or in the process of being established. @@ -111,7 +112,7 @@ class GrpcMuxFailover : public Logger::Loggable { } // Returns the availability of the underlying stream. - bool grpcStreamAvailable() const { + bool grpcStreamAvailable() const override { if (connectingToOrConnectedToFailover()) { return failover_grpc_stream_->grpcStreamAvailable(); } @@ -120,7 +121,7 @@ class GrpcMuxFailover : public Logger::Loggable { } // Sends a message using the underlying stream. - void sendMessage(const RequestType& request) { + void sendMessage(const RequestType& request) override { if (connectingToOrConnectedToFailover()) { failover_grpc_stream_->sendMessage(request); return; @@ -130,7 +131,7 @@ class GrpcMuxFailover : public Logger::Loggable { } // Updates the queue size of the underlying stream. - void maybeUpdateQueueSizeStat(uint64_t size) { + void maybeUpdateQueueSizeStat(uint64_t size) override { if (connectingToOrConnectedToFailover()) { failover_grpc_stream_->maybeUpdateQueueSizeStat(size); return; @@ -140,7 +141,7 @@ class GrpcMuxFailover : public Logger::Loggable { } // Returns true if the rate-limit allows draining. - bool checkRateLimitAllowsDrain() { + bool checkRateLimitAllowsDrain() override { if (connectingToOrConnectedToFailover()) { return failover_grpc_stream_->checkRateLimitAllowsDrain(); } @@ -355,6 +356,20 @@ class GrpcMuxFailover : public Logger::Loggable { } } + // The following method overrides are to allow GrpcMuxFailover to extend the + // GrpcStreamInterface. Once envoy.restart_features.xds_failover_support is deprecated, + // the class will no longer need to extend the interface, and these can be removed. + void onCreateInitialMetadata(Http::RequestHeaderMap&) override { PANIC("not implemented"); } + void onReceiveInitialMetadata(Http::ResponseHeaderMapPtr&&) override { PANIC("not implemented"); } + void onReceiveMessage(std::unique_ptr&&) override { PANIC("not implemented"); } + void onReceiveTrailingMetadata(Http::ResponseTrailerMapPtr&&) override { + PANIC("not implemented"); + } + void onRemoteClose(Grpc::Status::GrpcStatus, const std::string&) override { + PANIC("not implemented"); + } + void closeStream() override { PANIC("not implemented"); } + // The stream callbacks that will be invoked on the GrpcMux object, to notify // about the state of the underlying primary/failover stream. GrpcStreamCallbacks& grpc_mux_callbacks_; diff --git a/source/extensions/config_subscription/grpc/grpc_mux_impl.cc b/source/extensions/config_subscription/grpc/grpc_mux_impl.cc index 5d7faa7012d9..8091835caa6e 100644 --- a/source/extensions/config_subscription/grpc/grpc_mux_impl.cc +++ b/source/extensions/config_subscription/grpc/grpc_mux_impl.cc @@ -60,10 +60,7 @@ std::string convertToWildcard(const std::string& resource_name) { } // namespace GrpcMuxImpl::GrpcMuxImpl(GrpcMuxContext& grpc_mux_context, bool skip_subsequent_node) - : grpc_stream_(this, std::move(grpc_mux_context.async_client_), - grpc_mux_context.service_method_, grpc_mux_context.dispatcher_, - grpc_mux_context.scope_, std::move(grpc_mux_context.backoff_strategy_), - grpc_mux_context.rate_limit_settings_), + : grpc_stream_(createGrpcStreamObject(grpc_mux_context)), local_info_(grpc_mux_context.local_info_), skip_subsequent_node_(skip_subsequent_node), config_validators_(std::move(grpc_mux_context.config_validators_)), xds_config_tracker_(grpc_mux_context.xds_config_tracker_), @@ -81,6 +78,37 @@ GrpcMuxImpl::GrpcMuxImpl(GrpcMuxContext& grpc_mux_context, bool skip_subsequent_ AllMuxes::get().insert(this); } +std::unique_ptr> +GrpcMuxImpl::createGrpcStreamObject(GrpcMuxContext& grpc_mux_context) { + if (Runtime::runtimeFeatureEnabled("envoy.restart_features.xds_failover_support")) { + return std::make_unique>( + /*primary_stream_creator=*/ + [&grpc_mux_context]( + GrpcStreamCallbacks* callbacks) + -> GrpcStreamInterfacePtr { + return std::make_unique>( + callbacks, std::move(grpc_mux_context.async_client_), + grpc_mux_context.service_method_, grpc_mux_context.dispatcher_, + grpc_mux_context.scope_, std::move(grpc_mux_context.backoff_strategy_), + grpc_mux_context.rate_limit_settings_); + }, + /*failover_stream_creator=*/ + // TODO(adisuissa): implement when failover is fully plumbed. + absl::nullopt, + /*grpc_mux_callbacks=*/*this, + /*dispatch=*/grpc_mux_context.dispatcher_); + } + return std::make_unique>( + this, std::move(grpc_mux_context.async_client_), grpc_mux_context.service_method_, + grpc_mux_context.dispatcher_, grpc_mux_context.scope_, + std::move(grpc_mux_context.backoff_strategy_), grpc_mux_context.rate_limit_settings_); +} + GrpcMuxImpl::~GrpcMuxImpl() { AllMuxes::get().erase(this); } void GrpcMuxImpl::shutdownAll() { AllMuxes::get().shutdownAll(); } @@ -100,7 +128,7 @@ void GrpcMuxImpl::start() { return; } started_ = true; - grpc_stream_.establishNewStream(); + grpc_stream_->establishNewStream(); } void GrpcMuxImpl::sendDiscoveryRequest(absl::string_view type_url) { @@ -131,7 +159,7 @@ void GrpcMuxImpl::sendDiscoveryRequest(absl::string_view type_url) { request.clear_node(); } ENVOY_LOG(trace, "Sending DiscoveryRequest for {}: {}", type_url, request.ShortDebugString()); - grpc_stream_.sendMessage(request); + grpc_stream_->sendMessage(request); first_stream_request_ = false; // clear error_detail after the request is sent if it exists. @@ -470,7 +498,7 @@ void GrpcMuxImpl::onWriteable() { drainRequests(); } void GrpcMuxImpl::onStreamEstablished() { first_stream_request_ = true; - grpc_stream_.maybeUpdateQueueSizeStat(0); + grpc_stream_->maybeUpdateQueueSizeStat(0); clearNonce(); request_queue_ = std::make_unique>(); for (const auto& type_url : subscriptions_) { @@ -498,7 +526,7 @@ void GrpcMuxImpl::onEstablishmentFailure() { } void GrpcMuxImpl::queueDiscoveryRequest(absl::string_view queue_item) { - if (!grpc_stream_.grpcStreamAvailable()) { + if (!grpc_stream_->grpcStreamAvailable()) { ENVOY_LOG(debug, "No stream available to queueDiscoveryRequest for {}", queue_item); return; // Drop this request; the reconnect will enqueue a new one. } @@ -551,12 +579,12 @@ GrpcMuxImpl::ApiState& GrpcMuxImpl::apiStateFor(absl::string_view type_url) { } void GrpcMuxImpl::drainRequests() { - while (!request_queue_->empty() && grpc_stream_.checkRateLimitAllowsDrain()) { + while (!request_queue_->empty() && grpc_stream_->checkRateLimitAllowsDrain()) { // Process the request, if rate limiting is not enabled at all or if it is under rate limit. sendDiscoveryRequest(request_queue_->front()); request_queue_->pop(); } - grpc_stream_.maybeUpdateQueueSizeStat(request_queue_->size()); + grpc_stream_->maybeUpdateQueueSizeStat(request_queue_->size()); } // A factory class for creating GrpcMuxImpl so it does not have to be @@ -566,8 +594,8 @@ class GrpcMuxFactory : public MuxFactory { std::string name() const override { return "envoy.config_mux.grpc_mux_factory"; } void shutdownAll() override { return GrpcMuxImpl::shutdownAll(); } std::shared_ptr - create(Grpc::RawAsyncClientPtr&& async_client, Event::Dispatcher& dispatcher, - Random::RandomGenerator&, Stats::Scope& scope, + create(Grpc::RawAsyncClientPtr&& async_client, Grpc::RawAsyncClientPtr&& failover_async_client, + Event::Dispatcher& dispatcher, Random::RandomGenerator&, Stats::Scope& scope, const envoy::config::core::v3::ApiConfigSource& ads_config, const LocalInfo::LocalInfo& local_info, CustomConfigValidatorsPtr&& config_validators, BackOffStrategyPtr&& backoff_strategy, XdsConfigTrackerOptRef xds_config_tracker, @@ -577,6 +605,7 @@ class GrpcMuxFactory : public MuxFactory { THROW_IF_STATUS_NOT_OK(rate_limit_settings_or_error, throw); GrpcMuxContext grpc_mux_context{ /*async_client_=*/std::move(async_client), + /*failover_async_client_=*/std::move(failover_async_client), /*dispatcher_=*/dispatcher, /*service_method_=*/ *Protobuf::DescriptorPool::generated_pool()->FindMethodByName( diff --git a/source/extensions/config_subscription/grpc/grpc_mux_impl.h b/source/extensions/config_subscription/grpc/grpc_mux_impl.h index f2a5b1fb3f9f..8766ce96935a 100644 --- a/source/extensions/config_subscription/grpc/grpc_mux_impl.h +++ b/source/extensions/config_subscription/grpc/grpc_mux_impl.h @@ -27,7 +27,7 @@ #include "source/common/config/xds_context_params.h" #include "source/common/config/xds_resource.h" #include "source/extensions/config_subscription/grpc/grpc_mux_context.h" -#include "source/extensions/config_subscription/grpc/grpc_stream.h" +#include "source/extensions/config_subscription/grpc/grpc_mux_failover.h" #include "absl/container/node_hash_map.h" #include "xds/core/v3/resource_name.pb.h" @@ -84,13 +84,28 @@ class GrpcMuxImpl : public GrpcMux, ControlPlaneStats& control_plane_stats) override; void onWriteable() override; - GrpcStream& + GrpcStreamInterface& grpcStreamForTest() { - return grpc_stream_; + // TODO(adisuissa): Once envoy.restart_features.xds_failover_support is deprecated, + // return grpc_stream_.currentStreamForTest() directly (defined in GrpcMuxFailover). + if (Runtime::runtimeFeatureEnabled("envoy.restart_features.xds_failover_support")) { + return dynamic_cast*>( + grpc_stream_.get()) + ->currentStreamForTest(); + } + return *grpc_stream_.get(); } private: + // Helper function to create the grpc_stream_ object. + // TODO(adisuissa): this should be removed when envoy.restart_features.xds_failover_support + // is deprecated. + std::unique_ptr> + createGrpcStreamObject(GrpcMuxContext& grpc_mux_context); + void drainRequests(); void setRetryTimer(); void sendDiscoveryRequest(absl::string_view type_url); @@ -257,8 +272,11 @@ class GrpcMuxImpl : public GrpcMux, ApiState& api_state, const std::string& type_url, const std::string& version_info, bool call_delegate); - GrpcStream + // Multiplexes the stream to the primary and failover sources. + // TODO(adisuissa): Once envoy.restart_features.xds_failover_support is deprecated, + // convert from unique_ptr to GrpcMuxFailover directly. + std::unique_ptr> grpc_stream_; const LocalInfo::LocalInfo& local_info_; const bool skip_subsequent_node_; diff --git a/source/extensions/config_subscription/grpc/grpc_subscription_factory.cc b/source/extensions/config_subscription/grpc/grpc_subscription_factory.cc index 20efc1d418b4..e3792b49b42f 100644 --- a/source/extensions/config_subscription/grpc/grpc_subscription_factory.cc +++ b/source/extensions/config_subscription/grpc/grpc_subscription_factory.cc @@ -34,6 +34,7 @@ GrpcConfigSubscriptionFactory::create(ConfigSubscriptionFactory::SubscriptionDat THROW_IF_STATUS_NOT_OK(rate_limit_settings_or_error, throw); GrpcMuxContext grpc_mux_context{ /*async_client_=*/factory_or_error.value()->createUncachedRawAsyncClient(), + /*failover_async_client_=*/nullptr, /*dispatcher_=*/data.dispatcher_, /*service_method_=*/sotwGrpcMethod(data.type_url_), /*local_info_=*/data.local_info_, @@ -82,6 +83,7 @@ DeltaGrpcConfigSubscriptionFactory::create(ConfigSubscriptionFactory::Subscripti THROW_IF_STATUS_NOT_OK(rate_limit_settings_or_error, throw); GrpcMuxContext grpc_mux_context{ /*async_client_=*/factory_or_error.value()->createUncachedRawAsyncClient(), + /*failover_async_client_=*/nullptr, /*dispatcher_=*/data.dispatcher_, /*service_method_=*/deltaGrpcMethod(data.type_url_), /*local_info_=*/data.local_info_, diff --git a/source/extensions/config_subscription/grpc/new_grpc_mux_impl.cc b/source/extensions/config_subscription/grpc/new_grpc_mux_impl.cc index 229a4865ed55..c06685f13af6 100644 --- a/source/extensions/config_subscription/grpc/new_grpc_mux_impl.cc +++ b/source/extensions/config_subscription/grpc/new_grpc_mux_impl.cc @@ -36,10 +36,7 @@ using AllMuxes = ThreadSafeSingleton; } // namespace NewGrpcMuxImpl::NewGrpcMuxImpl(GrpcMuxContext& grpc_mux_context) - : grpc_stream_(this, std::move(grpc_mux_context.async_client_), - grpc_mux_context.service_method_, grpc_mux_context.dispatcher_, - grpc_mux_context.scope_, std::move(grpc_mux_context.backoff_strategy_), - grpc_mux_context.rate_limit_settings_), + : grpc_stream_(createGrpcStreamObject(grpc_mux_context)), local_info_(grpc_mux_context.local_info_), config_validators_(std::move(grpc_mux_context.config_validators_)), dynamic_update_callback_handle_( @@ -54,6 +51,38 @@ NewGrpcMuxImpl::NewGrpcMuxImpl(GrpcMuxContext& grpc_mux_context) AllMuxes::get().insert(this); } +std::unique_ptr> +NewGrpcMuxImpl::createGrpcStreamObject(GrpcMuxContext& grpc_mux_context) { + if (Runtime::runtimeFeatureEnabled("envoy.restart_features.xds_failover_support")) { + return std::make_unique>( + /*primary_stream_creator=*/ + [&grpc_mux_context]( + GrpcStreamCallbacks* callbacks) + -> GrpcStreamInterfacePtr { + return std::make_unique< + GrpcStream>( + callbacks, std::move(grpc_mux_context.async_client_), + grpc_mux_context.service_method_, grpc_mux_context.dispatcher_, + grpc_mux_context.scope_, std::move(grpc_mux_context.backoff_strategy_), + grpc_mux_context.rate_limit_settings_); + }, + /*failover_stream_creator=*/ + // TODO(adisuissa): implement when failover is fully plumbed. + absl::nullopt, + /*grpc_mux_callbacks=*/*this, + /*dispatch=*/grpc_mux_context.dispatcher_); + } + return std::make_unique>( + this, std::move(grpc_mux_context.async_client_), grpc_mux_context.service_method_, + grpc_mux_context.dispatcher_, grpc_mux_context.scope_, + std::move(grpc_mux_context.backoff_strategy_), grpc_mux_context.rate_limit_settings_); +} + NewGrpcMuxImpl::~NewGrpcMuxImpl() { AllMuxes::get().erase(this); } void NewGrpcMuxImpl::shutdownAll() { AllMuxes::get().shutdownAll(); } @@ -165,7 +194,7 @@ void NewGrpcMuxImpl::start() { return; } started_ = true; - grpc_stream_.establishNewStream(); + grpc_stream_->establishNewStream(); } GrpcMuxWatchPtr NewGrpcMuxImpl::addWatch(const std::string& type_url, @@ -297,9 +326,9 @@ void NewGrpcMuxImpl::trySendDiscoveryRequests() { } else { request = sub->second->sub_state_.getNextRequestAckless(); } - grpc_stream_.sendMessage(request); + grpc_stream_->sendMessage(request); } - grpc_stream_.maybeUpdateQueueSizeStat(pausable_ack_queue_.size()); + grpc_stream_->maybeUpdateQueueSizeStat(pausable_ack_queue_.size()); } // Checks whether external conditions allow sending a DeltaDiscoveryRequest. (Does not check @@ -311,10 +340,10 @@ bool NewGrpcMuxImpl::canSendDiscoveryRequest(const std::string& type_url) { "supposed to be filtered out by whoWantsToSendDiscoveryRequest(). ", type_url)); - if (!grpc_stream_.grpcStreamAvailable()) { + if (!grpc_stream_->grpcStreamAvailable()) { ENVOY_LOG(trace, "No stream available to send a discovery request for {}.", type_url); return false; - } else if (!grpc_stream_.checkRateLimitAllowsDrain()) { + } else if (!grpc_stream_->checkRateLimitAllowsDrain()) { ENVOY_LOG(trace, "{} discovery request hit rate limit; will try later.", type_url); return false; } @@ -352,8 +381,8 @@ class NewGrpcMuxFactory : public MuxFactory { std::string name() const override { return "envoy.config_mux.new_grpc_mux_factory"; } void shutdownAll() override { return NewGrpcMuxImpl::shutdownAll(); } std::shared_ptr - create(Grpc::RawAsyncClientPtr&& async_client, Event::Dispatcher& dispatcher, - Random::RandomGenerator&, Stats::Scope& scope, + create(Grpc::RawAsyncClientPtr&& async_client, Grpc::RawAsyncClientPtr&& failover_async_client, + Event::Dispatcher& dispatcher, Random::RandomGenerator&, Stats::Scope& scope, const envoy::config::core::v3::ApiConfigSource& ads_config, const LocalInfo::LocalInfo& local_info, CustomConfigValidatorsPtr&& config_validators, BackOffStrategyPtr&& backoff_strategy, XdsConfigTrackerOptRef xds_config_tracker, @@ -363,6 +392,7 @@ class NewGrpcMuxFactory : public MuxFactory { THROW_IF_STATUS_NOT_OK(rate_limit_settings_or_error, throw); GrpcMuxContext grpc_mux_context{ /*async_client_=*/std::move(async_client), + /*failover_async_client_=*/std::move(failover_async_client), /*dispatcher_=*/dispatcher, /*service_method_=*/ *Protobuf::DescriptorPool::generated_pool()->FindMethodByName( diff --git a/source/extensions/config_subscription/grpc/new_grpc_mux_impl.h b/source/extensions/config_subscription/grpc/new_grpc_mux_impl.h index 788879136560..26a949ae4dbb 100644 --- a/source/extensions/config_subscription/grpc/new_grpc_mux_impl.h +++ b/source/extensions/config_subscription/grpc/new_grpc_mux_impl.h @@ -17,7 +17,7 @@ #include "source/common/runtime/runtime_features.h" #include "source/extensions/config_subscription/grpc/delta_subscription_state.h" #include "source/extensions/config_subscription/grpc/grpc_mux_context.h" -#include "source/extensions/config_subscription/grpc/grpc_stream.h" +#include "source/extensions/config_subscription/grpc/grpc_mux_failover.h" #include "source/extensions/config_subscription/grpc/pausable_ack_queue.h" #include "source/extensions/config_subscription/grpc/watch_map.h" @@ -78,10 +78,18 @@ class NewGrpcMuxImpl // TODO(fredlas) remove this from the GrpcMux interface. void start() override; - GrpcStream& + GrpcStreamInterface& grpcStreamForTest() { - return grpc_stream_; + // TODO(adisuissa): Once envoy.restart_features.xds_failover_support is deprecated, + // return grpc_stream_.currentStreamForTest() directly (defined in GrpcMuxFailover). + if (Runtime::runtimeFeatureEnabled("envoy.restart_features.xds_failover_support")) { + return dynamic_cast*>( + grpc_stream_.get()) + ->currentStreamForTest(); + } + return *grpc_stream_.get(); } struct SubscriptionStuff { @@ -140,6 +148,13 @@ class NewGrpcMuxImpl const SubscriptionOptions options_; }; + // Helper function to create the grpc_stream_ object. + // TODO(adisuissa): this should be removed when envoy.restart_features.xds_failover_support + // is deprecated. + std::unique_ptr> + createGrpcStreamObject(GrpcMuxContext& grpc_mux_context); + void removeWatch(const std::string& type_url, Watch* watch); // Updates the list of resource names watched by the given watch. If an added name is new across @@ -181,8 +196,11 @@ class NewGrpcMuxImpl // the order of Envoy's dependency ordering). std::list subscription_ordering_; - GrpcStream + // Multiplexes the stream to the primary and failover sources. + // TODO(adisuissa): Once envoy.restart_features.xds_failover_support is deprecated, + // convert from unique_ptr to GrpcMuxFailover directly. + std::unique_ptr> grpc_stream_; const LocalInfo::LocalInfo& local_info_; diff --git a/source/extensions/config_subscription/grpc/xds_mux/BUILD b/source/extensions/config_subscription/grpc/xds_mux/BUILD index a9e7f75fd70a..a0fa33be7558 100644 --- a/source/extensions/config_subscription/grpc/xds_mux/BUILD +++ b/source/extensions/config_subscription/grpc/xds_mux/BUILD @@ -70,6 +70,7 @@ envoy_cc_extension( "//source/common/memory:utils_lib", "//source/extensions/config_subscription/grpc:eds_resources_cache_lib", "//source/extensions/config_subscription/grpc:grpc_mux_context_lib", + "//source/extensions/config_subscription/grpc:grpc_mux_failover_lib", "//source/extensions/config_subscription/grpc:grpc_stream_lib", "//source/extensions/config_subscription/grpc:pausable_ack_queue_lib", "//source/extensions/config_subscription/grpc:watch_map_lib", diff --git a/source/extensions/config_subscription/grpc/xds_mux/grpc_mux_impl.cc b/source/extensions/config_subscription/grpc/xds_mux/grpc_mux_impl.cc index 7de99ac446fd..b564b0b929db 100644 --- a/source/extensions/config_subscription/grpc/xds_mux/grpc_mux_impl.cc +++ b/source/extensions/config_subscription/grpc/xds_mux/grpc_mux_impl.cc @@ -39,28 +39,49 @@ using AllMuxes = ThreadSafeSingleton; template GrpcMuxImpl::GrpcMuxImpl(std::unique_ptr subscription_state_factory, - GrpcMuxContext& grpc_mux_content, bool skip_subsequent_node) - : grpc_stream_(this, std::move(grpc_mux_content.async_client_), - grpc_mux_content.service_method_, grpc_mux_content.dispatcher_, - grpc_mux_content.scope_, std::move(grpc_mux_content.backoff_strategy_), - grpc_mux_content.rate_limit_settings_), + GrpcMuxContext& grpc_mux_context, bool skip_subsequent_node) + : grpc_stream_(createGrpcStreamObject(grpc_mux_context)), subscription_state_factory_(std::move(subscription_state_factory)), - skip_subsequent_node_(skip_subsequent_node), local_info_(grpc_mux_content.local_info_), + skip_subsequent_node_(skip_subsequent_node), local_info_(grpc_mux_context.local_info_), dynamic_update_callback_handle_( - grpc_mux_content.local_info_.contextProvider().addDynamicContextUpdateCallback( + grpc_mux_context.local_info_.contextProvider().addDynamicContextUpdateCallback( [this](absl::string_view resource_type_url) { onDynamicContextUpdate(resource_type_url); return absl::OkStatus(); })), - config_validators_(std::move(grpc_mux_content.config_validators_)), - xds_config_tracker_(grpc_mux_content.xds_config_tracker_), - xds_resources_delegate_(grpc_mux_content.xds_resources_delegate_), - eds_resources_cache_(std::move(grpc_mux_content.eds_resources_cache_)), - target_xds_authority_(grpc_mux_content.target_xds_authority_) { - THROW_IF_NOT_OK(Config::Utility::checkLocalInfo("ads", grpc_mux_content.local_info_)); + config_validators_(std::move(grpc_mux_context.config_validators_)), + xds_config_tracker_(grpc_mux_context.xds_config_tracker_), + xds_resources_delegate_(grpc_mux_context.xds_resources_delegate_), + eds_resources_cache_(std::move(grpc_mux_context.eds_resources_cache_)), + target_xds_authority_(grpc_mux_context.target_xds_authority_) { + THROW_IF_NOT_OK(Config::Utility::checkLocalInfo("ads", grpc_mux_context.local_info_)); AllMuxes::get().insert(this); } +template +std::unique_ptr> +GrpcMuxImpl::createGrpcStreamObject(GrpcMuxContext& grpc_mux_context) { + if (Runtime::runtimeFeatureEnabled("envoy.restart_features.xds_failover_support")) { + return std::make_unique>( + /*primary_stream_creator=*/ + [&grpc_mux_context](GrpcStreamCallbacks* callbacks) -> GrpcStreamInterfacePtr { + return std::make_unique>( + callbacks, std::move(grpc_mux_context.async_client_), + grpc_mux_context.service_method_, grpc_mux_context.dispatcher_, + grpc_mux_context.scope_, std::move(grpc_mux_context.backoff_strategy_), + grpc_mux_context.rate_limit_settings_); + }, + /*failover_stream_creator=*/ + // TODO(adisuissa): implement when failover is fully plumbed. + absl::nullopt, + /*grpc_mux_callbacks=*/*this, + /*dispatch=*/grpc_mux_context.dispatcher_); + } + return std::make_unique>( + this, std::move(grpc_mux_context.async_client_), grpc_mux_context.service_method_, + grpc_mux_context.dispatcher_, grpc_mux_context.scope_, + std::move(grpc_mux_context.backoff_strategy_), grpc_mux_context.rate_limit_settings_); +} template GrpcMuxImpl::~GrpcMuxImpl() { AllMuxes::get().erase(this); } @@ -231,7 +252,7 @@ template void GrpcMuxImpl:: } started_ = true; ENVOY_LOG(debug, "GrpcMuxImpl now trying to establish a stream"); - grpc_stream_.establishNewStream(); + grpc_stream_->establishNewStream(); } template @@ -410,8 +431,8 @@ class DeltaGrpcMuxFactory : public MuxFactory { std::string name() const override { return "envoy.config_mux.delta_grpc_mux_factory"; } void shutdownAll() override { return GrpcMuxDelta::shutdownAll(); } std::shared_ptr - create(Grpc::RawAsyncClientPtr&& async_client, Event::Dispatcher& dispatcher, - Random::RandomGenerator&, Stats::Scope& scope, + create(Grpc::RawAsyncClientPtr&& async_client, Grpc::RawAsyncClientPtr&& failover_async_client, + Event::Dispatcher& dispatcher, Random::RandomGenerator&, Stats::Scope& scope, const envoy::config::core::v3::ApiConfigSource& ads_config, const LocalInfo::LocalInfo& local_info, CustomConfigValidatorsPtr&& config_validators, BackOffStrategyPtr&& backoff_strategy, XdsConfigTrackerOptRef xds_config_tracker, @@ -421,6 +442,7 @@ class DeltaGrpcMuxFactory : public MuxFactory { THROW_IF_STATUS_NOT_OK(rate_limit_settings_or_error, throw); GrpcMuxContext grpc_mux_context{ /*async_client_=*/std::move(async_client), + /*failover_async_client=*/std::move(failover_async_client), /*dispatcher_=*/dispatcher, /*service_method_=*/ *Protobuf::DescriptorPool::generated_pool()->FindMethodByName( @@ -448,8 +470,8 @@ class SotwGrpcMuxFactory : public MuxFactory { std::string name() const override { return "envoy.config_mux.sotw_grpc_mux_factory"; } void shutdownAll() override { return GrpcMuxSotw::shutdownAll(); } std::shared_ptr - create(Grpc::RawAsyncClientPtr&& async_client, Event::Dispatcher& dispatcher, - Random::RandomGenerator&, Stats::Scope& scope, + create(Grpc::RawAsyncClientPtr&& async_client, Grpc::RawAsyncClientPtr&& failover_async_client, + Event::Dispatcher& dispatcher, Random::RandomGenerator&, Stats::Scope& scope, const envoy::config::core::v3::ApiConfigSource& ads_config, const LocalInfo::LocalInfo& local_info, CustomConfigValidatorsPtr&& config_validators, BackOffStrategyPtr&& backoff_strategy, XdsConfigTrackerOptRef xds_config_tracker, @@ -459,6 +481,7 @@ class SotwGrpcMuxFactory : public MuxFactory { THROW_IF_STATUS_NOT_OK(rate_limit_settings_or_error, throw); GrpcMuxContext grpc_mux_context{ /*async_client_=*/std::move(async_client), + /*failover_async_client_=*/std::move(failover_async_client), /*dispatcher_=*/dispatcher, /*service_method_=*/ *Protobuf::DescriptorPool::generated_pool()->FindMethodByName( diff --git a/source/extensions/config_subscription/grpc/xds_mux/grpc_mux_impl.h b/source/extensions/config_subscription/grpc/xds_mux/grpc_mux_impl.h index 8af1675ba5cb..2e0ce7407862 100644 --- a/source/extensions/config_subscription/grpc/xds_mux/grpc_mux_impl.h +++ b/source/extensions/config_subscription/grpc/xds_mux/grpc_mux_impl.h @@ -22,7 +22,7 @@ #include "source/common/config/api_version.h" #include "source/common/grpc/common.h" #include "source/extensions/config_subscription/grpc/grpc_mux_context.h" -#include "source/extensions/config_subscription/grpc/grpc_stream.h" +#include "source/extensions/config_subscription/grpc/grpc_mux_failover.h" #include "source/extensions/config_subscription/grpc/pausable_ack_queue.h" #include "source/extensions/config_subscription/grpc/watch_map.h" #include "source/extensions/config_subscription/grpc/xds_mux/delta_subscription_state.h" @@ -107,7 +107,14 @@ class GrpcMuxImpl : public GrpcStreamCallbacks, return makeOptRefFromPtr(eds_resources_cache_.get()); } - GrpcStream& grpcStreamForTest() { return grpc_stream_; } + GrpcStreamInterface& grpcStreamForTest() { + // TODO(adisuissa): Once envoy.restart_features.xds_failover_support is deprecated, + // return grpc_stream_.currentStreamForTest() directly (defined in GrpcMuxFailover). + if (Runtime::runtimeFeatureEnabled("envoy.restart_features.xds_failover_support")) { + return dynamic_cast*>(grpc_stream_.get())->currentStreamForTest(); + } + return *grpc_stream_.get(); + } protected: class WatchImpl : public Envoy::Config::GrpcMuxWatch { @@ -137,10 +144,10 @@ class GrpcMuxImpl : public GrpcStreamCallbacks, }; void sendGrpcMessage(RQ& msg_proto, S& sub_state); - void maybeUpdateQueueSizeStat(uint64_t size) { grpc_stream_.maybeUpdateQueueSizeStat(size); } - bool grpcStreamAvailable() { return grpc_stream_.grpcStreamAvailable(); } - bool rateLimitAllowsDrain() { return grpc_stream_.checkRateLimitAllowsDrain(); } - void sendMessage(RQ& msg_proto) { grpc_stream_.sendMessage(msg_proto); } + void maybeUpdateQueueSizeStat(uint64_t size) { grpc_stream_->maybeUpdateQueueSizeStat(size); } + bool grpcStreamAvailable() { return grpc_stream_->grpcStreamAvailable(); } + bool rateLimitAllowsDrain() { return grpc_stream_->checkRateLimitAllowsDrain(); } + void sendMessage(RQ& msg_proto) { grpc_stream_->sendMessage(msg_proto); } S& subscriptionStateFor(const std::string& type_url); WatchMap& watchMapFor(const std::string& type_url); @@ -157,6 +164,12 @@ class GrpcMuxImpl : public GrpcStreamCallbacks, const LocalInfo::LocalInfo& localInfo() const { return local_info_; } private: + // Helper function to create the grpc_stream_ object. + // TODO(adisuissa): this should be removed when envoy.restart_features.xds_failover_support + // is deprecated. + std::unique_ptr> + createGrpcStreamObject(GrpcMuxContext& grpc_mux_context); + // Checks whether external conditions allow sending a DeltaDiscoveryRequest. (Does not check // whether we *want* to send a (Delta)DiscoveryRequest). bool canSendDiscoveryRequest(const std::string& type_url); @@ -172,7 +185,10 @@ class GrpcMuxImpl : public GrpcStreamCallbacks, // Invoked when dynamic context parameters change for a resource type. void onDynamicContextUpdate(absl::string_view resource_type_url); - GrpcStream grpc_stream_; + // Multiplexes the stream to the primary and failover sources. + // TODO(adisuissa): Once envoy.restart_features.xds_failover_support is deprecated, + // convert from unique_ptr to GrpcMuxFailover directly. + std::unique_ptr> grpc_stream_; // Resource (N)ACKs we're waiting to send, stored in the order that they should be sent in. All // of our different resource types' ACKs are mixed together in this queue. See class for diff --git a/source/extensions/config_subscription/rest/rest_api_fetcher.cc b/source/extensions/config_subscription/rest/rest_api_fetcher.cc index 92c06f023d19..6b0d63fe73ef 100644 --- a/source/extensions/config_subscription/rest/rest_api_fetcher.cc +++ b/source/extensions/config_subscription/rest/rest_api_fetcher.cc @@ -50,8 +50,8 @@ void RestApiFetcher::onSuccess(const Http::AsyncClient::Request& request, void RestApiFetcher::onFailure(const Http::AsyncClient::Request&, Http::AsyncClient::FailureReason reason) { - // Currently Http::AsyncClient::FailureReason only has one value: "Reset". - ASSERT(reason == Http::AsyncClient::FailureReason::Reset); + ASSERT(reason == Http::AsyncClient::FailureReason::Reset || + reason == Http::AsyncClient::FailureReason::ExceedResponseBufferLimit); onFetchFailure(Config::ConfigUpdateFailureReason::ConnectionFailure, nullptr); requestComplete(); } diff --git a/source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc b/source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc index 4b14c5fb5bb3..d35ef0840dcb 100644 --- a/source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc +++ b/source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc @@ -292,7 +292,9 @@ void RawHttpClientImpl::onSuccess(const Http::AsyncClient::Request&, void RawHttpClientImpl::onFailure(const Http::AsyncClient::Request&, Http::AsyncClient::FailureReason reason) { - ASSERT(reason == Http::AsyncClient::FailureReason::Reset); + // TODO(botengyao): handle different failure reasons. + ASSERT(reason == Http::AsyncClient::FailureReason::Reset || + reason == Http::AsyncClient::FailureReason::ExceedResponseBufferLimit); callbacks_->onComplete(std::make_unique(errorResponse())); callbacks_ = nullptr; } diff --git a/source/extensions/filters/common/rbac/matchers.h b/source/extensions/filters/common/rbac/matchers.h index 362d2c51c979..1fb0a05e6483 100644 --- a/source/extensions/filters/common/rbac/matchers.h +++ b/source/extensions/filters/common/rbac/matchers.h @@ -159,7 +159,9 @@ class IPMatcher : public Matcher { enum Type { ConnectionRemote = 0, DownstreamLocal, DownstreamDirectRemote, DownstreamRemote }; IPMatcher(const envoy::config::core::v3::CidrRange& range, Type type) - : range_(Network::Address::CidrRange::create(range)), type_(type) {} + : range_(THROW_OR_RETURN_VALUE(Network::Address::CidrRange::create(range), + Network::Address::CidrRange)), + type_(type) {} bool matches(const Network::Connection& connection, const Envoy::Http::RequestHeaderMap& headers, const StreamInfo::StreamInfo& info) const override; diff --git a/source/extensions/filters/common/rbac/matchers/upstream_ip_port.cc b/source/extensions/filters/common/rbac/matchers/upstream_ip_port.cc index 89ffe42eb9ca..a0446d54c85b 100644 --- a/source/extensions/filters/common/rbac/matchers/upstream_ip_port.cc +++ b/source/extensions/filters/common/rbac/matchers/upstream_ip_port.cc @@ -23,7 +23,8 @@ UpstreamIpPortMatcher::UpstreamIpPortMatcher( } if (proto.has_upstream_ip()) { - cidr_ = Network::Address::CidrRange::create(proto.upstream_ip()); + cidr_ = THROW_OR_RETURN_VALUE(Network::Address::CidrRange::create(proto.upstream_ip()), + Network::Address::CidrRange); } if (proto.has_upstream_port_range()) { port_ = proto.upstream_port_range(); diff --git a/source/extensions/filters/http/ext_authz/BUILD b/source/extensions/filters/http/ext_authz/BUILD index 6fbb0b871ed1..02970d791e31 100644 --- a/source/extensions/filters/http/ext_authz/BUILD +++ b/source/extensions/filters/http/ext_authz/BUILD @@ -31,6 +31,7 @@ envoy_cc_library( "//source/common/runtime:runtime_protos_lib", "//source/extensions/filters/common/ext_authz:ext_authz_grpc_lib", "//source/extensions/filters/common/ext_authz:ext_authz_http_lib", + "//source/extensions/filters/common/mutation_rules:mutation_rules_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/http/ext_authz/v3:pkg_cc_proto", "@envoy_api//envoy/service/auth/v3:pkg_cc_proto", diff --git a/source/extensions/filters/http/ext_authz/ext_authz.cc b/source/extensions/filters/http/ext_authz/ext_authz.cc index 824682477bc3..88b27ec663a1 100644 --- a/source/extensions/filters/http/ext_authz/ext_authz.cc +++ b/source/extensions/filters/http/ext_authz/ext_authz.cc @@ -21,6 +21,8 @@ namespace ExtAuthz { namespace { using MetadataProto = ::envoy::config::core::v3::Metadata; +using Filters::Common::MutationRules::CheckOperation; +using Filters::Common::MutationRules::CheckResult; void fillMetadataContext(const std::vector& source_metadata, const std::vector& metadata_context_namespaces, @@ -289,6 +291,17 @@ void Filter::onDestroy() { } } +CheckResult Filter::validateAndCheckDecoderHeaderMutation( + Filters::Common::MutationRules::CheckOperation operation, absl::string_view key, + absl::string_view value) const { + if (config_->validateMutations() && (!Http::HeaderUtility::headerNameIsValid(key) || + !Http::HeaderUtility::headerValueIsValid(value))) { + return CheckResult::FAIL; + } + // Check header mutation is valid according to configured decoder mutation rules. + return config_->checkDecoderHeaderMutation(operation, Http::LowerCaseString(key), value); +} + void Filter::onComplete(Filters::Common::ExtAuthz::ResponsePtr&& response) { state_ = State::Complete; using Filters::Common::ExtAuthz::CheckStatus; @@ -325,69 +338,116 @@ void Filter::onComplete(Filters::Common::ExtAuthz::ResponsePtr&& response) { ENVOY_STREAM_LOG(trace, "ext_authz filter added header(s) to the request:", *decoder_callbacks_); for (const auto& [key, value] : response->headers_to_set) { - if (config_->validateMutations() && (!Http::HeaderUtility::headerNameIsValid(key) || - !Http::HeaderUtility::headerValueIsValid(value))) { - ENVOY_STREAM_LOG(trace, "Rejected invalid header '{}':'{}'.", *decoder_callbacks_, key, - value); + CheckResult check_result = validateAndCheckDecoderHeaderMutation( + Filters::Common::MutationRules::CheckOperation::SET, key, value); + switch (check_result) { + case CheckResult::OK: + ENVOY_STREAM_LOG(trace, "'{}':'{}'", *decoder_callbacks_, key, value); + request_headers_->setCopy(Http::LowerCaseString(key), value); + break; + case CheckResult::IGNORE: + ENVOY_STREAM_LOG(trace, "Ignoring invalid header to set '{}':'{}'.", *decoder_callbacks_, + key, value); + break; + case CheckResult::FAIL: + ENVOY_STREAM_LOG(trace, "Rejecting invalid header to set '{}':'{}'.", *decoder_callbacks_, + key, value); rejectResponse(); return; } - ENVOY_STREAM_LOG(trace, "'{}':'{}'", *decoder_callbacks_, key, value); - request_headers_->setCopy(Http::LowerCaseString(key), value); } for (const auto& [key, value] : response->headers_to_add) { - if (config_->validateMutations() && (!Http::HeaderUtility::headerNameIsValid(key) || - !Http::HeaderUtility::headerValueIsValid(value))) { - ENVOY_STREAM_LOG(trace, "Rejected invalid header '{}':'{}'.", *decoder_callbacks_, key, - value); + CheckResult check_result = validateAndCheckDecoderHeaderMutation( + Filters::Common::MutationRules::CheckOperation::SET, key, value); + switch (check_result) { + case CheckResult::OK: + ENVOY_STREAM_LOG(trace, "'{}':'{}'", *decoder_callbacks_, key, value); + request_headers_->addCopy(Http::LowerCaseString(key), value); + break; + case CheckResult::IGNORE: + ENVOY_STREAM_LOG(trace, "Ignoring invalid header to add '{}':'{}'.", *decoder_callbacks_, + key, value); + break; + case CheckResult::FAIL: + ENVOY_STREAM_LOG(trace, "Rejecting invalid header to add '{}':'{}'.", *decoder_callbacks_, + key, value); rejectResponse(); return; } - ENVOY_STREAM_LOG(trace, "'{}':'{}'", *decoder_callbacks_, key, value); - request_headers_->addCopy(Http::LowerCaseString(key), value); } for (const auto& [key, value] : response->headers_to_append) { - if (config_->validateMutations() && (!Http::HeaderUtility::headerNameIsValid(key) || - !Http::HeaderUtility::headerValueIsValid(value))) { - ENVOY_STREAM_LOG(trace, "Rejected invalid header '{}':'{}'.", *decoder_callbacks_, key, - value); + CheckResult check_result = validateAndCheckDecoderHeaderMutation( + Filters::Common::MutationRules::CheckOperation::APPEND, key, value); + switch (check_result) { + case CheckResult::OK: { + ENVOY_STREAM_LOG(trace, "'{}':'{}'", *decoder_callbacks_, key, value); + Http::LowerCaseString lowercase_key(key); + const auto header_to_modify = request_headers_->get(lowercase_key); + // TODO(dio): Add a flag to allow appending non-existent headers, without setting it + // first (via `headers_to_add`). For example, given: + // 1. Original headers {"original": "true"} + // 2. Response headers from the authorization servers {{"append": "1"}, {"append": + // "2"}} + // + // Currently it is not possible to add {{"append": "1"}, {"append": "2"}} (the + // intended combined headers: {{"original": "true"}, {"append": "1"}, {"append": + // "2"}}) to the request to upstream server by only sets `headers_to_append`. + if (!header_to_modify.empty()) { + ENVOY_STREAM_LOG(trace, "'{}':'{}'", *decoder_callbacks_, key, value); + // The current behavior of appending is by combining entries with the same key, + // into one entry. The value of that combined entry is separated by ",". + // TODO(dio): Consider to use addCopy instead. + request_headers_->appendCopy(lowercase_key, value); + } + break; + } + case CheckResult::IGNORE: + ENVOY_STREAM_LOG(trace, "Ignoring invalid header to append '{}':'{}'.", *decoder_callbacks_, + key, value); + break; + case CheckResult::FAIL: + ENVOY_STREAM_LOG(trace, "Rejecting invalid header to append '{}':'{}'.", + *decoder_callbacks_, key, value); rejectResponse(); return; } - Http::LowerCaseString lowercase_key(key); - const auto header_to_modify = request_headers_->get(lowercase_key); - // TODO(dio): Add a flag to allow appending non-existent headers, without setting it first - // (via `headers_to_add`). For example, given: - // 1. Original headers {"original": "true"} - // 2. Response headers from the authorization servers {{"append": "1"}, {"append": "2"}} - // - // Currently it is not possible to add {{"append": "1"}, {"append": "2"}} (the intended - // combined headers: {{"original": "true"}, {"append": "1"}, {"append": "2"}}) to the request - // to upstream server by only sets `headers_to_append`. - if (!header_to_modify.empty()) { - ENVOY_STREAM_LOG(trace, "'{}':'{}'", *decoder_callbacks_, key, value); - // The current behavior of appending is by combining entries with the same key, into one - // entry. The value of that combined entry is separated by ",". - // TODO(dio): Consider to use addCopy instead. - request_headers_->appendCopy(lowercase_key, value); - } } ENVOY_STREAM_LOG(trace, "ext_authz filter removed header(s) from the request:", *decoder_callbacks_); for (const auto& key : response->headers_to_remove) { - // We don't allow removing any :-prefixed headers, nor Host, as removing them would make the - // request malformed. - // // If the response contains an invalid header to remove, it's the same as trying to remove a // header that doesn't exist, so just ignore it. - if (!Http::HeaderUtility::isRemovableHeader(key) || - (config_->validateMutations() && !Http::HeaderUtility::headerNameIsValid(key))) { - ENVOY_STREAM_LOG(trace, "Not removing header '{}'.", *decoder_callbacks_, key); + if (config_->validateMutations() && !Http::HeaderUtility::headerNameIsValid(key)) { + ENVOY_STREAM_LOG(trace, "Ignoring invalid header removal '{}'.", *decoder_callbacks_, key); + continue; + } + // We don't allow removing any :-prefixed headers, nor Host, as removing them would make the + // request malformed. checkDecoderHeaderMutation also performs this check, however, so only + // perform this check explicitly if decoder header mutation rules is empty. + if (!config_->hasDecoderHeaderMutationRules() && + !Http::HeaderUtility::isRemovableHeader(key)) { + ENVOY_STREAM_LOG(trace, "Ignoring invalid header removal '{}'.", *decoder_callbacks_, key); continue; } - ENVOY_STREAM_LOG(trace, "'{}'", *decoder_callbacks_, key); - request_headers_->remove(Http::LowerCaseString(key)); + // Check header mutation is valid according to configured decoder header mutation rules. + Http::LowerCaseString lowercase_key(key); + switch (config_->checkDecoderHeaderMutation(CheckOperation::REMOVE, lowercase_key, + EMPTY_STRING)) { + case CheckResult::OK: + ENVOY_STREAM_LOG(trace, "'{}'", *decoder_callbacks_, key); + request_headers_->remove(lowercase_key); + break; + case CheckResult::IGNORE: + ENVOY_STREAM_LOG(trace, "Ignoring disallowed header removal '{}'.", *decoder_callbacks_, + key); + break; + case CheckResult::FAIL: + ENVOY_STREAM_LOG(trace, "Rejecting disallowed header removal '{}'.", *decoder_callbacks_, + key); + rejectResponse(); + return; + } } if (!response->response_headers_to_add.empty()) { diff --git a/source/extensions/filters/http/ext_authz/ext_authz.h b/source/extensions/filters/http/ext_authz/ext_authz.h index ad2b14f92408..81e6cee23d81 100644 --- a/source/extensions/filters/http/ext_authz/ext_authz.h +++ b/source/extensions/filters/http/ext_authz/ext_authz.h @@ -24,6 +24,7 @@ #include "source/extensions/filters/common/ext_authz/ext_authz.h" #include "source/extensions/filters/common/ext_authz/ext_authz_grpc_impl.h" #include "source/extensions/filters/common/ext_authz/ext_authz_http_impl.h" +#include "source/extensions/filters/common/mutation_rules/mutation_rules.h" namespace Envoy { namespace Extensions { @@ -74,6 +75,12 @@ class FilterConfig { status_on_error_(toErrorCode(config.status_on_error().code())), validate_mutations_(config.validate_mutations()), scope_(scope), + decoder_header_mutation_checker_( + config.has_decoder_header_mutation_rules() + ? absl::optional( + Filters::Common::MutationRules::Checker( + config.decoder_header_mutation_rules(), factory_context.regexEngine())) + : absl::nullopt), runtime_(factory_context.runtime()), http_context_(factory_context.httpContext()), filter_enabled_(config.has_filter_enabled() ? absl::optional( @@ -162,6 +169,20 @@ class FilterConfig { bool headersAsBytes() const { return encode_raw_headers_; } + Filters::Common::MutationRules::CheckResult + checkDecoderHeaderMutation(const Filters::Common::MutationRules::CheckOperation& operation, + const Http::LowerCaseString& key, absl::string_view value) const { + if (!decoder_header_mutation_checker_.has_value()) { + return Filters::Common::MutationRules::CheckResult::OK; + } + return decoder_header_mutation_checker_->check(operation, key, value); + } + + // Used for headers_to_remove to avoid a redundant pseudo header check. + bool hasDecoderHeaderMutationRules() const { + return decoder_header_mutation_checker_.has_value(); + } + Http::Code statusOnError() const { return status_on_error_; } bool validateMutations() const { return validate_mutations_; } @@ -252,6 +273,7 @@ class FilterConfig { const Http::Code status_on_error_; const bool validate_mutations_; Stats::Scope& scope_; + const absl::optional decoder_header_mutation_checker_; Runtime::Loader& runtime_; Http::Context& http_context_; LabelsMap destination_labels_; @@ -372,6 +394,13 @@ class Filter : public Logger::Loggable, void onComplete(Filters::Common::ExtAuthz::ResponsePtr&&) override; private: + // Convenience function for the following: + // 1. If `validate_mutations` is set to true, validate header key and value. + // 2. If `decoder_header_mutation_rules` is set, check that mutation is allowed. + Filters::Common::MutationRules::CheckResult + validateAndCheckDecoderHeaderMutation(Filters::Common::MutationRules::CheckOperation operation, + absl::string_view key, absl::string_view value) const; + // Called when the filter is configured to reject invalid responses & the authz response contains // invalid header or query parameters. Sends a local response with the configured rejection status // code. diff --git a/source/extensions/filters/http/gcp_authn/gcp_authn_impl.cc b/source/extensions/filters/http/gcp_authn/gcp_authn_impl.cc index ed16283f308c..05f108f48781 100644 --- a/source/extensions/filters/http/gcp_authn/gcp_authn_impl.cc +++ b/source/extensions/filters/http/gcp_authn/gcp_authn_impl.cc @@ -91,8 +91,9 @@ void GcpAuthnClient::onSuccess(const Http::AsyncClient::Request&, void GcpAuthnClient::onFailure(const Http::AsyncClient::Request&, Http::AsyncClient::FailureReason reason) { - // Http::AsyncClient::FailureReason only has one value: "Reset". - ASSERT(reason == Http::AsyncClient::FailureReason::Reset); + // TODO(botengyao): handle different failure reasons. + ASSERT(reason == Http::AsyncClient::FailureReason::Reset || + reason == Http::AsyncClient::FailureReason::ExceedResponseBufferLimit); ENVOY_LOG(error, "Request failed: stream has been reset"); active_request_ = nullptr; onError(); diff --git a/source/extensions/filters/http/ip_tagging/ip_tagging_filter.cc b/source/extensions/filters/http/ip_tagging/ip_tagging_filter.cc index 52a6ad00c87e..a8a8d3b8bcbc 100644 --- a/source/extensions/filters/http/ip_tagging/ip_tagging_filter.cc +++ b/source/extensions/filters/http/ip_tagging/ip_tagging_filter.cc @@ -38,10 +38,10 @@ IpTaggingFilterConfig::IpTaggingFilterConfig( cidr_set.reserve(ip_tag.ip_list().size()); for (const envoy::config::core::v3::CidrRange& entry : ip_tag.ip_list()) { - // Currently, CidrRange::create doesn't guarantee that the CidrRanges are valid. - Network::Address::CidrRange cidr_entry = Network::Address::CidrRange::create(entry); - if (cidr_entry.isValid()) { - cidr_set.emplace_back(std::move(cidr_entry)); + absl::StatusOr cidr_or_error = + Network::Address::CidrRange::create(entry); + if (cidr_or_error.status().ok()) { + cidr_set.emplace_back(std::move(cidr_or_error.value())); } else { throw EnvoyException( fmt::format("invalid ip/mask combo '{}/{}' (format is /<# mask bits>)", diff --git a/source/extensions/filters/http/oauth2/config.cc b/source/extensions/filters/http/oauth2/config.cc index 771b4d307457..322c6e2907db 100644 --- a/source/extensions/filters/http/oauth2/config.cc +++ b/source/extensions/filters/http/oauth2/config.cc @@ -64,6 +64,13 @@ Http::FilterFactoryCb OAuth2Config::createFilterFactoryFromProtoTyped( throw EnvoyException("invalid HMAC secret configuration"); } + if (proto_config.preserve_authorization_header() && proto_config.forward_bearer_token()) { + throw EnvoyException( + "invalid combination of forward_bearer_token and preserve_authorization_header " + "configuration. If forward_bearer_token is set to true, then " + "preserve_authorization_header must be false"); + } + auto secret_reader = std::make_shared( std::move(secret_provider_token_secret), std::move(secret_provider_hmac_secret), context.serverFactoryContext().threadLocal(), context.serverFactoryContext().api()); diff --git a/source/extensions/filters/http/oauth2/filter.cc b/source/extensions/filters/http/oauth2/filter.cc index 8ccf894bfa9e..d5d97279cf7c 100644 --- a/source/extensions/filters/http/oauth2/filter.cc +++ b/source/extensions/filters/http/oauth2/filter.cc @@ -198,6 +198,7 @@ FilterConfig::FilterConfig( stats_(FilterConfig::generateStats(stats_prefix, scope)), encoded_resource_query_params_(encodeResourceList(proto_config.resources())), forward_bearer_token_(proto_config.forward_bearer_token()), + preserve_authorization_header_(proto_config.preserve_authorization_header()), pass_through_header_matchers_(headerMatchers(proto_config.pass_through_matcher(), context)), deny_redirect_header_matchers_(headerMatchers(proto_config.deny_redirect_matcher(), context)), cookie_names_(proto_config.credentials().cookie_names()), @@ -289,10 +290,13 @@ Http::FilterHeadersStatus OAuth2Filter::decodeHeaders(Http::RequestHeaderMap& he } } - // Sanitize the Authorization header, since we have no way to validate its content. Also, - // if token forwarding is enabled, this header will be set based on what is on the HMAC cookie - // before forwarding the request upstream. - headers.removeInline(authorization_handle.handle()); + // Only sanitize the Authorization header if preserveAuthorizationHeader is false + if (!config_->preserveAuthorizationHeader()) { + // Sanitize the Authorization header, since we have no way to validate its content. Also, + // if token forwarding is enabled, this header will be set based on what is on the HMAC cookie + // before forwarding the request upstream. + headers.removeInline(authorization_handle.handle()); + } // The following 2 headers are guaranteed for regular requests. The asserts are helpful when // writing test code to not forget these important variables in mock requests diff --git a/source/extensions/filters/http/oauth2/filter.h b/source/extensions/filters/http/oauth2/filter.h index 69b1acfad215..f1ed914a931b 100644 --- a/source/extensions/filters/http/oauth2/filter.h +++ b/source/extensions/filters/http/oauth2/filter.h @@ -119,6 +119,7 @@ class FilterConfig { const std::string& clusterName() const { return oauth_token_endpoint_.cluster(); } const std::string& clientId() const { return client_id_; } bool forwardBearerToken() const { return forward_bearer_token_; } + bool preserveAuthorizationHeader() const { return preserve_authorization_header_; } const std::vector& passThroughMatchers() const { return pass_through_header_matchers_; } @@ -164,6 +165,7 @@ class FilterConfig { const std::string encoded_auth_scopes_; const std::string encoded_resource_query_params_; const bool forward_bearer_token_ : 1; + const bool preserve_authorization_header_ : 1; const std::vector pass_through_header_matchers_; const std::vector deny_redirect_header_matchers_; const CookieNames cookie_names_; diff --git a/source/extensions/filters/http/rbac/rbac_filter.h b/source/extensions/filters/http/rbac/rbac_filter.h index e20568432fcd..6de163c11369 100644 --- a/source/extensions/filters/http/rbac/rbac_filter.h +++ b/source/extensions/filters/http/rbac/rbac_filter.h @@ -98,7 +98,7 @@ class RoleBasedAccessControlFilter : public Http::StreamDecoderFilter, public Logger::Loggable { public: RoleBasedAccessControlFilter(RoleBasedAccessControlFilterConfigSharedPtr config) - : config_(config) {} + : config_(std::move(config)) {} // Http::StreamDecoderFilter Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap& headers, diff --git a/source/extensions/filters/listener/proxy_protocol/proxy_protocol.cc b/source/extensions/filters/listener/proxy_protocol/proxy_protocol.cc index 847ce6d45e7b..5af1c2ca4094 100644 --- a/source/extensions/filters/listener/proxy_protocol/proxy_protocol.cc +++ b/source/extensions/filters/listener/proxy_protocol/proxy_protocol.cc @@ -55,17 +55,22 @@ namespace ProxyProtocol { constexpr absl::string_view kProxyProtoStatsPrefix = "proxy_proto."; constexpr absl::string_view kVersionStatsPrefix = "versions."; -ProxyProtocolStats ProxyProtocolStats::create(Stats::Scope& scope) { +ProxyProtocolStats ProxyProtocolStats::create(Stats::Scope& scope, absl::string_view stat_prefix) { + std::string filter_stat_prefix = std::string(kProxyProtoStatsPrefix); + if (!stat_prefix.empty()) { + filter_stat_prefix = absl::StrCat(kProxyProtoStatsPrefix, stat_prefix, "."); + } + return { /*legacy_=*/{LEGACY_PROXY_PROTOCOL_STATS(POOL_COUNTER(scope))}, /*general_=*/ - {GENERAL_PROXY_PROTOCOL_STATS(POOL_COUNTER_PREFIX(scope, kProxyProtoStatsPrefix))}, + {GENERAL_PROXY_PROTOCOL_STATS(POOL_COUNTER_PREFIX(scope, filter_stat_prefix))}, /*v1_=*/ {VERSIONED_PROXY_PROTOCOL_STATS(POOL_COUNTER_PREFIX( - scope, absl::StrCat(kProxyProtoStatsPrefix, kVersionStatsPrefix, "v1.")))}, + scope, absl::StrCat(filter_stat_prefix, kVersionStatsPrefix, "v1.")))}, /*v2_=*/ {VERSIONED_PROXY_PROTOCOL_STATS(POOL_COUNTER_PREFIX( - scope, absl::StrCat(kProxyProtoStatsPrefix, kVersionStatsPrefix, "v2.")))}, + scope, absl::StrCat(filter_stat_prefix, kVersionStatsPrefix, "v2.")))}, }; } @@ -105,7 +110,7 @@ void VersionedProxyProtocolStats::increment(ReadOrParseState decision) { Config::Config( Stats::Scope& scope, const envoy::extensions::filters::listener::proxy_protocol::v3::ProxyProtocol& proto_config) - : stats_(ProxyProtocolStats::create(scope)), + : stats_(ProxyProtocolStats::create(scope, proto_config.stat_prefix())), 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() == diff --git a/source/extensions/filters/listener/proxy_protocol/proxy_protocol.h b/source/extensions/filters/listener/proxy_protocol/proxy_protocol.h index b3507e1a6071..3a508813891a 100644 --- a/source/extensions/filters/listener/proxy_protocol/proxy_protocol.h +++ b/source/extensions/filters/listener/proxy_protocol/proxy_protocol.h @@ -96,7 +96,7 @@ struct ProxyProtocolStats { * For backwards compatibility, the legacy stats are rooted under their own scope. * The general and versioned stats are correctly rooted at this filter's own scope. */ - static ProxyProtocolStats create(Stats::Scope& scope); + static ProxyProtocolStats create(Stats::Scope& scope, absl::string_view stat_prefix); }; /** diff --git a/source/extensions/filters/network/mongo_proxy/bson_impl.cc b/source/extensions/filters/network/mongo_proxy/bson_impl.cc index 8ea6cf028376..669f26e08917 100644 --- a/source/extensions/filters/network/mongo_proxy/bson_impl.cc +++ b/source/extensions/filters/network/mongo_proxy/bson_impl.cc @@ -23,7 +23,11 @@ int32_t BufferHelper::peekInt32(Buffer::Instance& data) { int32_t val; val = data.peekLEInt(); +#ifdef ABSL_IS_BIG_ENDIAN + return val; +#else return le32toh(val); +#endif } uint8_t BufferHelper::removeByte(Buffer::Instance& data) { @@ -88,7 +92,11 @@ int64_t BufferHelper::removeInt64(Buffer::Instance& data) { int64_t val; val = data.drainLEInt(); +#ifdef ABSL_IS_BIG_ENDIAN + return val; +#else return le64toh(val); +#endif } std::string BufferHelper::removeString(Buffer::Instance& data) { diff --git a/source/extensions/filters/udp/dns_filter/dns_parser.cc b/source/extensions/filters/udp/dns_filter/dns_parser.cc index b63f69278d89..ed4b850a5c8b 100644 --- a/source/extensions/filters/udp/dns_filter/dns_parser.cc +++ b/source/extensions/filters/udp/dns_filter/dns_parser.cc @@ -91,8 +91,13 @@ bool DnsAnswerRecord::serialize(Buffer::OwnedImpl& output) { // Store the 128bit address with 2 64 bit writes const absl::uint128 addr6 = ip_address->ipv6()->address(); output.writeBEInt(sizeof(addr6)); +#ifdef ABSL_IS_BIG_ENDIAN + output.writeBEInt(absl::Uint128High64(addr6)); + output.writeBEInt(absl::Uint128Low64(addr6)); +#else output.writeLEInt(absl::Uint128Low64(addr6)); output.writeLEInt(absl::Uint128High64(addr6)); +#endif } else if (ip_address->ipv4() != nullptr) { output.writeBEInt(4); output.writeLEInt(ip_address->ipv4()->address()); diff --git a/source/extensions/filters/udp/dns_filter/dns_parser.h b/source/extensions/filters/udp/dns_filter/dns_parser.h index 80ebabd398ae..3449c19d6478 100644 --- a/source/extensions/filters/udp/dns_filter/dns_parser.h +++ b/source/extensions/filters/udp/dns_filter/dns_parser.h @@ -137,9 +137,23 @@ struct DnsParserCounters { queries_with_ans_or_authority_rrs(queries_with_ans_or_authority_rrs) {} }; -// The flags have been verified with dig and this structure should not be modified. The flag -// order here does not match the RFC, but takes byte ordering into account so that serialization -// does not bitwise operations. +// The flags have been verified with dig and this structure should not be modified. On little +// endian platforms, the flag order here does not match the RFC, but takes byte ordering into +// account so that serialization does not bitwise operations. +#if ABSL_IS_BIG_ENDIAN +PACKED_STRUCT(struct DnsHeaderFlags { + unsigned qr : 1; // query or response + unsigned opcode : 4; // operation code + unsigned aa : 1; // authoritative answer + unsigned tc : 1; // truncated response + unsigned rd : 1; // recursion desired + unsigned ra : 1; // recursion available + unsigned z : 1; // z - bit (must be zero in queries per RFC1035) + unsigned ad : 1; // authenticated data + unsigned cd : 1; // checking disabled + unsigned rcode : 4; // return code +}); +#else PACKED_STRUCT(struct DnsHeaderFlags { unsigned rcode : 4; // return code unsigned cd : 1; // checking disabled @@ -152,6 +166,7 @@ PACKED_STRUCT(struct DnsHeaderFlags { unsigned opcode : 4; // operation code unsigned qr : 1; // query or response }); +#endif /** * Structure representing the DNS header as it appears in a packet diff --git a/source/extensions/matching/input_matchers/ip/config.cc b/source/extensions/matching/input_matchers/ip/config.cc index 811a279ae5e9..6ae3ea720033 100644 --- a/source/extensions/matching/input_matchers/ip/config.cc +++ b/source/extensions/matching/input_matchers/ip/config.cc @@ -19,13 +19,8 @@ Config::createInputMatcherFactoryCb(const Protobuf::Message& config, for (const auto& cidr_range : cidr_ranges) { const std::string& address = cidr_range.address_prefix(); const uint32_t prefix_len = cidr_range.prefix_len().value(); - const auto range = Network::Address::CidrRange::create(address, prefix_len); - // We only assert that the range is valid because: - // * if "address" can't be parsed, it will throw an EnvoyException - // * prefix_len can't be < 0 as per the protobuf definition as an uint32_t - // * if prefix_len is too big, CidrRange::create clamps it to a valid value - // => it is thus not possible to create an invalid range. - ASSERT(range.isValid(), "address range should be valid!"); + const auto range = THROW_OR_RETURN_VALUE( + Network::Address::CidrRange::create(address, prefix_len), Network::Address::CidrRange); ranges.emplace_back(std::move(range)); } diff --git a/source/extensions/tracers/common/ot/opentracing_driver_impl.h b/source/extensions/tracers/common/ot/opentracing_driver_impl.h index 5fd46bfe96a4..b06323043f8d 100644 --- a/source/extensions/tracers/common/ot/opentracing_driver_impl.h +++ b/source/extensions/tracers/common/ot/opentracing_driver_impl.h @@ -45,10 +45,11 @@ class OpenTracingSpan : public Tracing::Span, Logger::Loggableid())); } +std::string Span::getSpanId() const { + // TODO(#34412): This method is not yet implemented for Datadog. + return EMPTY_STRING; +} + } // namespace Datadog } // namespace Tracers } // namespace Extensions diff --git a/source/extensions/tracers/datadog/span.h b/source/extensions/tracers/datadog/span.h index b4c14245cc04..0e41272ef49b 100644 --- a/source/extensions/tracers/datadog/span.h +++ b/source/extensions/tracers/datadog/span.h @@ -48,6 +48,7 @@ class Span : public Tracing::Span { std::string getBaggage(absl::string_view key) override; void setBaggage(absl::string_view key, absl::string_view value) override; std::string getTraceId() const override; + std::string getSpanId() const override; private: datadog::tracing::Optional span_; diff --git a/source/extensions/tracers/opencensus/opencensus_tracer_impl.cc b/source/extensions/tracers/opencensus/opencensus_tracer_impl.cc index 026f9e70f4cf..476c3681d2c6 100644 --- a/source/extensions/tracers/opencensus/opencensus_tracer_impl.cc +++ b/source/extensions/tracers/opencensus/opencensus_tracer_impl.cc @@ -81,6 +81,7 @@ class Span : public Tracing::Span { std::string getBaggage(absl::string_view) override { return EMPTY_STRING; }; std::string getTraceId() const override; + std::string getSpanId() const override; private: ::opencensus::trace::Span span_; @@ -236,6 +237,8 @@ void Span::injectContext(Tracing::TraceContext& trace_context, const Tracing::Up } } +std::string Span::getSpanId() const { return EMPTY_STRING; } + std::string Span::getTraceId() const { const auto& ctx = span_.context(); return ctx.trace_id().ToHex(); diff --git a/source/extensions/tracers/opentelemetry/tracer.h b/source/extensions/tracers/opentelemetry/tracer.h index 057814365b9a..392bf7825420 100644 --- a/source/extensions/tracers/opentelemetry/tracer.h +++ b/source/extensions/tracers/opentelemetry/tracer.h @@ -119,6 +119,8 @@ class Span : Logger::Loggable, public Tracing::Span { std::string getTraceId() const override { return absl::BytesToHexString(span_.trace_id()); }; + std::string getSpanId() const override { return absl::BytesToHexString(span_.span_id()); }; + OTelSpanKind spankind() const { return span_.kind(); } /** diff --git a/source/extensions/tracers/skywalking/tracer.h b/source/extensions/tracers/skywalking/tracer.h index d5bb1f65f329..70f03f9e50b5 100644 --- a/source/extensions/tracers/skywalking/tracer.h +++ b/source/extensions/tracers/skywalking/tracer.h @@ -89,6 +89,7 @@ class Span : public Tracing::Span { std::string getBaggage(absl::string_view) override { return EMPTY_STRING; } void setBaggage(absl::string_view, absl::string_view) override {} std::string getTraceId() const override { return tracing_context_->traceId(); } + std::string getSpanId() const override { return EMPTY_STRING; } const TracingContextPtr tracingContext() { return tracing_context_; } const TracingSpanPtr spanEntity() { return span_entity_; } diff --git a/source/extensions/tracers/xray/tracer.h b/source/extensions/tracers/xray/tracer.h index bf9d696c0836..6d21b5999a94 100644 --- a/source/extensions/tracers/xray/tracer.h +++ b/source/extensions/tracers/xray/tracer.h @@ -230,9 +230,11 @@ class Span : public Tracing::Span, Logger::Loggable { void setBaggage(absl::string_view, absl::string_view) override {} std::string getBaggage(absl::string_view) override { return EMPTY_STRING; } - // TODO: This method is unimplemented for X-Ray. std::string getTraceId() const override { return trace_id_; }; + // TODO(#34412): This method is unimplemented for X-Ray. + std::string getSpanId() const override { return EMPTY_STRING; }; + /** * Creates a child span. * In X-Ray terms this creates a sub-segment and sets its parent ID to the current span's ID. diff --git a/source/extensions/tracers/zipkin/zipkin_tracer_impl.h b/source/extensions/tracers/zipkin/zipkin_tracer_impl.h index cfa3c95a4fc3..2739bc61010b 100644 --- a/source/extensions/tracers/zipkin/zipkin_tracer_impl.h +++ b/source/extensions/tracers/zipkin/zipkin_tracer_impl.h @@ -85,6 +85,9 @@ class ZipkinSpan : public Tracing::Span { std::string getTraceId() const override { return span_.traceIdAsHexString(); }; + // TODO(#34412): This method is unimplemented for Zipkin. + std::string getSpanId() const override { return EMPTY_STRING; }; + /** * @return a reference to the Zipkin::Span object. */ diff --git a/source/server/config_validation/server.cc b/source/server/config_validation/server.cc index 7aeb4e1d41a1..344bc8b5cd5a 100644 --- a/source/server/config_validation/server.cc +++ b/source/server/config_validation/server.cc @@ -144,7 +144,7 @@ void ValidationInstance::initialize(const Options& options, [this]() -> Network::DnsResolverSharedPtr { return this->dnsResolver(); }, sslContextManager(), *secret_manager_, quic_stat_names_, *this); THROW_IF_NOT_OK(config_.initialize(bootstrap_, *this, *cluster_manager_factory_)); - runtime().initialize(clusterManager()); + THROW_IF_NOT_OK(runtime().initialize(clusterManager())); clusterManager().setInitializedCb([this]() -> void { init_manager_.initialize(init_watcher_); }); } diff --git a/source/server/server.cc b/source/server/server.cc index ee3ab436b23e..0c02614b833e 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -768,7 +768,7 @@ absl::Status InstanceBase::initializeOrThrow(Network::Address::InstanceConstShar // We have to defer RTDS initialization until after the cluster manager is // instantiated (which in turn relies on runtime...). - runtime().initialize(clusterManager()); + RETURN_IF_NOT_OK(runtime().initialize(clusterManager())); clusterManager().setPrimaryClustersInitializedCb( [this]() { onClusterManagerPrimaryInitializationComplete(); }); diff --git a/test/common/config/datasource_test.cc b/test/common/config/datasource_test.cc index 015613eaa695..9c4ee31aa56c 100644 --- a/test/common/config/datasource_test.cc +++ b/test/common/config/datasource_test.cc @@ -176,8 +176,8 @@ TEST(DataSourceProviderTest, NonFileDataSourceTest) { NiceMock tls; auto provider_or_error = - DataSource::DataSourceProvider::create(config, *dispatcher, tls, *api, false, 0); - EXPECT_EQ(provider_or_error.value().data(), "Hello, world!"); + DataSource::DataSourceProvider::create(config, *dispatcher, tls, *api, false, 15); + EXPECT_EQ(provider_or_error.value()->data(), "Hello, world!"); } TEST(DataSourceProviderTest, FileDataSourceButNoWatch) { @@ -218,7 +218,7 @@ TEST(DataSourceProviderTest, FileDataSourceButNoWatch) { auto provider_or_error = DataSource::DataSourceProvider::create(config, *dispatcher, tls, *api, false, 0); - EXPECT_EQ(provider_or_error.value().data(), "Hello, world!"); + EXPECT_EQ(provider_or_error.value()->data(), "Hello, world!"); // Update the symlink to point to the new file. TestEnvironment::renameFile(TestEnvironment::temporaryPath("envoy_test/watcher_new_link"), @@ -227,7 +227,7 @@ TEST(DataSourceProviderTest, FileDataSourceButNoWatch) { dispatcher->run(Event::Dispatcher::RunType::NonBlock); // The provider should still return the old content. - EXPECT_EQ(provider_or_error.value().data(), "Hello, world!"); + EXPECT_EQ(provider_or_error.value()->data(), "Hello, world!"); // Remove the file. unlink(TestEnvironment::temporaryPath("envoy_test/watcher_target").c_str()); @@ -278,7 +278,7 @@ TEST(DataSourceProviderTest, FileDataSourceAndWithWatch) { // Create a provider with watch. auto provider_or_error = DataSource::DataSourceProvider::create(config, *dispatcher, tls, *api, false, 0); - EXPECT_EQ(provider_or_error.value().data(), "Hello, world!"); + EXPECT_EQ(provider_or_error.value()->data(), "Hello, world!"); // Update the symlink to point to the new file. TestEnvironment::renameFile(TestEnvironment::temporaryPath("envoy_test/watcher_new_link"), @@ -287,7 +287,7 @@ TEST(DataSourceProviderTest, FileDataSourceAndWithWatch) { dispatcher->run(Event::Dispatcher::RunType::NonBlock); // The provider should return the updated content. - EXPECT_EQ(provider_or_error.value().data(), "Hello, world! Updated!"); + EXPECT_EQ(provider_or_error.value()->data(), "Hello, world! Updated!"); // Remove the file. unlink(TestEnvironment::temporaryPath("envoy_test/watcher_target").c_str()); @@ -339,7 +339,7 @@ TEST(DataSourceProviderTest, FileDataSourceAndWithWatchButUpdateError) { // ignored. auto provider_or_error = DataSource::DataSourceProvider::create(config, *dispatcher, tls, *api, false, 15); - EXPECT_EQ(provider_or_error.value().data(), "Hello, world!"); + EXPECT_EQ(provider_or_error.value()->data(), "Hello, world!"); // Update the symlink to point to the new file. TestEnvironment::renameFile(TestEnvironment::temporaryPath("envoy_test/watcher_new_link"), @@ -348,7 +348,7 @@ TEST(DataSourceProviderTest, FileDataSourceAndWithWatchButUpdateError) { dispatcher->run(Event::Dispatcher::RunType::NonBlock); // The provider should return the old content because the updated content is ignored. - EXPECT_EQ(provider_or_error.value().data(), "Hello, world!"); + EXPECT_EQ(provider_or_error.value()->data(), "Hello, world!"); // Remove the file. unlink(TestEnvironment::temporaryPath("envoy_test/watcher_target").c_str()); diff --git a/test/common/config/grpc_subscription_test_harness.h b/test/common/config/grpc_subscription_test_harness.h index 6467981a8510..13c02090c0f2 100644 --- a/test/common/config/grpc_subscription_test_harness.h +++ b/test/common/config/grpc_subscription_test_harness.h @@ -61,6 +61,7 @@ class GrpcSubscriptionTestHarness : public SubscriptionTestHarness { GrpcMuxContext grpc_mux_context{ /*async_client_=*/std::unique_ptr(async_client_), + /*failover_async_client_=*/nullptr, /*dispatcher_=*/dispatcher_, /*service_method_=*/*method_descriptor_, /*local_info_=*/local_info_, diff --git a/test/common/http/BUILD b/test/common/http/BUILD index 20782fb16392..acde87d4daa2 100644 --- a/test/common/http/BUILD +++ b/test/common/http/BUILD @@ -33,6 +33,7 @@ envoy_cc_test( "//test/mocks/server:server_factory_context_mocks", "//test/mocks/stats:stats_mocks", "//test/mocks/upstream:cluster_manager_mocks", + "//test/test_common:test_runtime_lib", "//test/test_common:test_time_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", "@envoy_api//envoy/config/route/v3:pkg_cc_proto", diff --git a/test/common/http/async_client_impl_test.cc b/test/common/http/async_client_impl_test.cc index ae074cd026e2..28272c2a31ae 100644 --- a/test/common/http/async_client_impl_test.cc +++ b/test/common/http/async_client_impl_test.cc @@ -269,6 +269,129 @@ TEST_F(AsyncClientImplTest, Basic) { .value()); } +TEST_F(AsyncClientImplTest, NoResponseBodyBuffering) { + message_->body().add("test body"); + Buffer::Instance& data = message_->body(); + + 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; + })); + + TestRequestHeaderMapImpl copy(message_->headers()); + copy.addCopy("x-envoy-internal", "true"); + copy.addCopy("x-forwarded-for", "127.0.0.1"); + copy.addCopy(":scheme", "http"); + + EXPECT_CALL(stream_encoder_, encodeHeaders(HeaderMapEqualRef(©), false)); + EXPECT_CALL(stream_encoder_, encodeData(BufferEqual(&data), true)); + + auto* request = client_.send(std::move(message_), callbacks_, + AsyncClient::RequestOptions().setDiscardResponseBody(true)); + EXPECT_NE(request, nullptr); + + EXPECT_CALL(callbacks_, onBeforeFinalizeUpstreamSpan(_, _)); + EXPECT_CALL(callbacks_, onSuccess_(_, _)) + .WillOnce(Invoke([](const AsyncClient::Request&, ResponseMessage* response) -> void { + // Verify that there is zero response body. + EXPECT_EQ(response->body().length(), 0); + })); + ResponseHeaderMapPtr response_headers(new TestResponseHeaderMapImpl{{":status", "200"}}); + response_decoder_->decodeHeaders(std::move(response_headers), false); + response_decoder_->decodeData(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, LargeResponseBody) { + message_->body().add("test body"); + Buffer::Instance& data = message_->body(); + + 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; + })); + + TestRequestHeaderMapImpl copy(message_->headers()); + copy.addCopy("x-envoy-internal", "true"); + copy.addCopy("x-forwarded-for", "127.0.0.1"); + copy.addCopy(":scheme", "http"); + + EXPECT_CALL(stream_encoder_, encodeHeaders(HeaderMapEqualRef(©), false)); + EXPECT_CALL(stream_encoder_, encodeData(BufferEqual(&data), true)); + ON_CALL(factory_context_.runtime_loader_.snapshot_, + getInteger(AsyncClientImpl::ResponseBufferLimit, kBufferLimitForResponse)) + .WillByDefault(Return(100)); + + auto* request = client_.send(std::move(message_), callbacks_, AsyncClient::RequestOptions()); + EXPECT_NE(request, nullptr); + + EXPECT_CALL(callbacks_, onBeforeFinalizeUpstreamSpan(_, _)); + EXPECT_CALL(callbacks_, onFailure(_, AsyncClient::FailureReason::ExceedResponseBufferLimit)); + + Buffer::InstancePtr large_body{new Buffer::OwnedImpl(std::string(100 + 1, 'a'))}; + ResponseHeaderMapPtr response_headers(new TestResponseHeaderMapImpl{{":status", "200"}}); + response_decoder_->decodeHeaders(std::move(response_headers), false); + response_decoder_->decodeData(*large_body, true); + EXPECT_EQ(large_body->length(), 0); +} + +TEST_F(AsyncClientImplTest, LargeResponseBodyMultipleRead) { + message_->body().add("test body"); + Buffer::Instance& data = message_->body(); + + 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; + })); + + TestRequestHeaderMapImpl copy(message_->headers()); + copy.addCopy("x-envoy-internal", "true"); + copy.addCopy("x-forwarded-for", "127.0.0.1"); + copy.addCopy(":scheme", "http"); + + EXPECT_CALL(stream_encoder_, encodeHeaders(HeaderMapEqualRef(©), false)); + EXPECT_CALL(stream_encoder_, encodeData(BufferEqual(&data), true)); + ON_CALL(factory_context_.runtime_loader_.snapshot_, + getInteger(AsyncClientImpl::ResponseBufferLimit, kBufferLimitForResponse)) + .WillByDefault(Return(100)); + + auto* request = client_.send(std::move(message_), callbacks_, AsyncClient::RequestOptions()); + EXPECT_NE(request, nullptr); + + EXPECT_CALL(callbacks_, onBeforeFinalizeUpstreamSpan(_, _)); + EXPECT_CALL(callbacks_, onFailure(_, AsyncClient::FailureReason::ExceedResponseBufferLimit)); + + Buffer::InstancePtr large_body{new Buffer::OwnedImpl(std::string(50, 'a'))}; + Buffer::InstancePtr large_body_second{new Buffer::OwnedImpl(std::string(50, 'a'))}; + Buffer::InstancePtr large_body_third{new Buffer::OwnedImpl(std::string(2, 'a'))}; + ResponseHeaderMapPtr response_headers(new TestResponseHeaderMapImpl{{":status", "200"}}); + response_decoder_->decodeHeaders(std::move(response_headers), false); + response_decoder_->decodeData(*large_body, false); + response_decoder_->decodeData(*large_body_second, false); + response_decoder_->decodeData(*large_body_third, true); +} + TEST_F(AsyncClientImplTest, BasicOngoingRequest) { auto headers = std::make_unique(); HttpTestUtility::addDefaultHeaders(*headers); diff --git a/test/common/http/conn_manager_impl_test.cc b/test/common/http/conn_manager_impl_test.cc index 27e876a43669..51cc25624285 100644 --- a/test/common/http/conn_manager_impl_test.cc +++ b/test/common/http/conn_manager_impl_test.cc @@ -3570,44 +3570,6 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutAfterBidiData) { EXPECT_EQ("world", response_body); } -TEST_F(HttpConnectionManagerImplTest, RoundTripTimeHasValue) { - TestScopedRuntime scoped_runtime; - scoped_runtime.mergeValues({{"envoy.reloadable_features.refresh_rtt_after_request", "true"}}); - - 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) { - TestScopedRuntime scoped_runtime; - scoped_runtime.mergeValues({{"envoy.reloadable_features.refresh_rtt_after_request", "true"}}); - - 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, ""); diff --git a/test/common/network/cidr_range_test.cc b/test/common/network/cidr_range_test.cc index 2938159c38ee..b8060738c605 100644 --- a/test/common/network/cidr_range_test.cc +++ b/test/common/network/cidr_range_test.cc @@ -85,15 +85,10 @@ TEST(TruncateIpAddressAndLength, Various) { } TEST(IsInRange, Various) { - { - CidrRange rng = CidrRange::create("foo"); - EXPECT_FALSE(rng.isValid()); - EXPECT_FALSE(rng.isInRange(Ipv4Instance("0.0.0.0"))); - } + { EXPECT_FALSE(CidrRange::create("foo").status().ok()); } { - CidrRange rng = CidrRange::create("10.255.255.255/0"); - EXPECT_TRUE(rng.isValid()); + CidrRange rng = *CidrRange::create("10.255.255.255/0"); EXPECT_EQ(rng.asString(), "0.0.0.0/0"); EXPECT_EQ(rng.length(), 0); EXPECT_EQ(rng.ip()->version(), IpVersion::v4); @@ -105,8 +100,7 @@ TEST(IsInRange, Various) { } { - CidrRange rng = CidrRange::create("10.255.255.255/10"); - EXPECT_TRUE(rng.isValid()); + CidrRange rng = *CidrRange::create("10.255.255.255/10"); EXPECT_EQ(rng.asString(), "10.192.0.0/10"); EXPECT_EQ(rng.length(), 10); EXPECT_EQ(rng.ip()->version(), IpVersion::v4); @@ -117,8 +111,7 @@ TEST(IsInRange, Various) { } { - CidrRange rng = CidrRange::create("::/0"); - EXPECT_TRUE(rng.isValid()); + CidrRange rng = *CidrRange::create("::/0"); EXPECT_EQ(rng.asString(), "::/0"); EXPECT_EQ(rng.length(), 0); EXPECT_EQ(rng.ip()->version(), IpVersion::v6); @@ -130,8 +123,7 @@ TEST(IsInRange, Various) { } { - CidrRange rng = CidrRange::create("::1/128"); - EXPECT_TRUE(rng.isValid()); + CidrRange rng = *CidrRange::create("::1/128"); EXPECT_EQ(rng.asString(), "::1/128"); EXPECT_EQ(rng.length(), 128); EXPECT_EQ(rng.ip()->version(), IpVersion::v6); @@ -142,8 +134,7 @@ TEST(IsInRange, Various) { } { - CidrRange rng = CidrRange::create("2001:abcd:ef01:2345:6789:abcd:ef01:234/64"); - EXPECT_TRUE(rng.isValid()); + CidrRange rng = *CidrRange::create("2001:abcd:ef01:2345:6789:abcd:ef01:234/64"); EXPECT_EQ(rng.asString(), "2001:abcd:ef01:2345::/64"); EXPECT_EQ(rng.length(), 64); EXPECT_EQ(rng.ip()->version(), IpVersion::v6); @@ -156,8 +147,7 @@ TEST(IsInRange, Various) { } { - CidrRange rng = CidrRange::create("2001:abcd:ef01:2345:6789:abcd:ef01:234/60"); - EXPECT_TRUE(rng.isValid()); + CidrRange rng = *CidrRange::create("2001:abcd:ef01:2345:6789:abcd:ef01:234/60"); EXPECT_EQ(rng.asString(), "2001:abcd:ef01:2340::/60"); EXPECT_EQ(rng.length(), 60); EXPECT_EQ(rng.ip()->version(), IpVersion::v6); @@ -173,57 +163,47 @@ TEST(IsInRange, Various) { TEST(CidrRangeTest, OperatorIsEqual) { { - CidrRange rng1 = CidrRange::create("192.0.0.0/8"); - CidrRange rng2 = CidrRange::create("192.168.0.0/16"); + CidrRange rng1 = *CidrRange::create("192.0.0.0/8"); + CidrRange rng2 = *CidrRange::create("192.168.0.0/16"); EXPECT_FALSE(rng1 == rng2); } { - CidrRange rng1 = CidrRange::create("192.0.0.0/8"); - CidrRange rng2 = CidrRange::create("192.168.0.0/8"); + CidrRange rng1 = *CidrRange::create("192.0.0.0/8"); + CidrRange rng2 = *CidrRange::create("192.168.0.0/8"); EXPECT_TRUE(rng1 == rng2); } { - CidrRange rng1 = CidrRange::create("192.0.0.0/8"); - CidrRange rng2 = CidrRange::create("2001::/8"); + CidrRange rng1 = *CidrRange::create("192.0.0.0/8"); + CidrRange rng2 = *CidrRange::create("2001::/8"); EXPECT_FALSE(rng1 == rng2); } { - CidrRange rng1 = CidrRange::create("2002::/16"); - CidrRange rng2 = CidrRange::create("2001::/16"); + CidrRange rng1 = *CidrRange::create("2002::/16"); + CidrRange rng2 = *CidrRange::create("2001::/16"); EXPECT_FALSE(rng1 == rng2); } { - CidrRange rng1 = CidrRange::create("2002::/16"); - CidrRange rng2 = CidrRange::create("192.168.0.1/16"); + CidrRange rng1 = *CidrRange::create("2002::/16"); + CidrRange rng2 = *CidrRange::create("192.168.0.1/16"); EXPECT_FALSE(rng1 == rng2); } { - CidrRange rng1 = CidrRange::create("2002::/16"); - CidrRange rng2 = CidrRange::create("2002::1/16"); + CidrRange rng1 = *CidrRange::create("2002::/16"); + CidrRange rng2 = *CidrRange::create("2002::1/16"); EXPECT_TRUE(rng1 == rng2); } } -TEST(CidrRangeTest, InvalidCidrRange) { - CidrRange rng1 = CidrRange::create("foo"); - EXPECT_EQ(nullptr, rng1.ip()); - EXPECT_EQ("/-1", rng1.asString()); - // Not equal due to invalid CidrRange. - EXPECT_FALSE(rng1 == rng1); - - CidrRange rng2 = CidrRange::create("192.0.0.0/8"); - EXPECT_FALSE(rng1 == rng2); -} +TEST(CidrRangeTest, InvalidCidrRange) { EXPECT_FALSE(CidrRange::create("foo").status().ok()); } TEST(Ipv4CidrRangeTest, InstanceConstSharedPtrAndLengthCtor) { InstanceConstSharedPtr ptr = Utility::parseInternetAddress("1.2.3.5"); - CidrRange rng(CidrRange::create(ptr, 31)); // Copy ctor. - EXPECT_TRUE(rng.isValid()); + CidrRange rng(*CidrRange::create(ptr, 31)); // Copy ctor. EXPECT_EQ(rng.length(), 31); EXPECT_EQ(rng.ip()->version(), IpVersion::v4); EXPECT_EQ(rng.asString(), "1.2.3.4/31"); @@ -232,18 +212,15 @@ TEST(Ipv4CidrRangeTest, InstanceConstSharedPtrAndLengthCtor) { EXPECT_TRUE(rng.isInRange(Ipv4Instance("1.2.3.5"))); EXPECT_FALSE(rng.isInRange(Ipv4Instance("1.2.3.6"))); - CidrRange rng2(CidrRange::create(ptr, -1)); // Invalid length. - EXPECT_FALSE(rng2.isValid()); + EXPECT_FALSE(CidrRange::create(ptr, -1).status().ok()); // Invalid length. ptr.reset(); - CidrRange rng3(CidrRange::create(ptr, 10)); // Invalid address. - EXPECT_FALSE(rng3.isValid()); + EXPECT_FALSE(CidrRange::create(ptr, 10).status().ok()); // Invalid address. } TEST(Ipv4CidrRangeTest, StringAndLengthCtor) { CidrRange rng; - rng = CidrRange::create("1.2.3.4", 31); // Assignment operator. - EXPECT_TRUE(rng.isValid()); + rng = *CidrRange::create("1.2.3.4", 31); // Assignment operator. EXPECT_EQ(rng.asString(), "1.2.3.4/31"); EXPECT_EQ(rng.length(), 31); EXPECT_EQ(rng.ip()->version(), IpVersion::v4); @@ -252,15 +229,13 @@ TEST(Ipv4CidrRangeTest, StringAndLengthCtor) { EXPECT_TRUE(rng.isInRange(Ipv4Instance("1.2.3.5"))); EXPECT_FALSE(rng.isInRange(Ipv4Instance("1.2.3.6"))); - rng = CidrRange::create("1.2.3.4", -10); // Invalid length. - EXPECT_FALSE(rng.isValid()); + EXPECT_FALSE(CidrRange::create("1.2.3.4", -10).status().ok()); // Invalid length. - EXPECT_THROW(CidrRange::create("bogus", 31), EnvoyException); // Invalid address. + EXPECT_FALSE(CidrRange::create("bogus", 31).status().ok()); // Invalid address. } TEST(Ipv4CidrRangeTest, StringCtor) { - CidrRange rng = CidrRange::create("1.2.3.4/31"); - EXPECT_TRUE(rng.isValid()); + CidrRange rng = *CidrRange::create("1.2.3.4/31"); EXPECT_EQ(rng.asString(), "1.2.3.4/31"); EXPECT_EQ(rng.length(), 31); EXPECT_EQ(rng.ip()->version(), IpVersion::v4); @@ -269,21 +244,14 @@ TEST(Ipv4CidrRangeTest, StringCtor) { EXPECT_TRUE(rng.isInRange(Ipv4Instance("1.2.3.5"))); EXPECT_FALSE(rng.isInRange(Ipv4Instance("1.2.3.6"))); - CidrRange rng2 = CidrRange::create("1.2.3.4/-10"); // Invalid length. - EXPECT_FALSE(rng2.isValid()); - - EXPECT_THROW(CidrRange::create("bogus/31"), EnvoyException); // Invalid address. - - CidrRange rng4 = CidrRange::create("/31"); // Missing address. - EXPECT_FALSE(rng4.isValid()); - - CidrRange rng5 = CidrRange::create("1.2.3.4/"); // Missing length. - EXPECT_FALSE(rng5.isValid()); + EXPECT_FALSE(CidrRange::create("1.2.3.4/-10").status().ok()); // Invalid length. + EXPECT_FALSE(CidrRange::create("bogus/31").status().ok()); // Invalid address. + EXPECT_FALSE(CidrRange::create("/31").status().ok()); // Missing address. + EXPECT_FALSE(CidrRange::create("1.2.3.4/").status().ok()); // Missing length. } TEST(Ipv4CidrRangeTest, BigRange) { - CidrRange rng = CidrRange::create("10.255.255.255/8"); - EXPECT_TRUE(rng.isValid()); + CidrRange rng = *CidrRange::create("10.255.255.255/8"); EXPECT_EQ(rng.asString(), "10.0.0.0/8"); EXPECT_EQ(rng.length(), 8); EXPECT_EQ(rng.ip()->version(), IpVersion::v4); @@ -300,8 +268,7 @@ TEST(Ipv4CidrRangeTest, BigRange) { TEST(Ipv6CidrRange, InstanceConstSharedPtrAndLengthCtor) { InstanceConstSharedPtr ptr = Utility::parseInternetAddress("abcd::0345"); - CidrRange rng(CidrRange::create(ptr, 127)); // Copy ctor. - EXPECT_TRUE(rng.isValid()); + CidrRange rng(*CidrRange::create(ptr, 127)); // Copy ctor. EXPECT_EQ(rng.length(), 127); EXPECT_EQ(rng.ip()->version(), IpVersion::v6); EXPECT_EQ(rng.asString(), "abcd::344/127"); @@ -310,18 +277,14 @@ TEST(Ipv6CidrRange, InstanceConstSharedPtrAndLengthCtor) { EXPECT_TRUE(rng.isInRange(Ipv6Instance("abcd::345"))); EXPECT_FALSE(rng.isInRange(Ipv6Instance("abcd::346"))); - CidrRange rng2(CidrRange::create(ptr, -1)); // Invalid length. - EXPECT_FALSE(rng2.isValid()); - + EXPECT_FALSE(CidrRange::create(ptr, -1).status().ok()); // Invalid length. ptr.reset(); - CidrRange rng3(CidrRange::create(ptr, 127)); // Invalid address. - EXPECT_FALSE(rng3.isValid()); + EXPECT_FALSE(CidrRange::create(ptr, 127).status().ok()); // Invalid address. } TEST(Ipv6CidrRange, StringAndLengthCtor) { CidrRange rng; - rng = CidrRange::create("ff::ffff", 122); // Assignment operator. - EXPECT_TRUE(rng.isValid()); + rng = *CidrRange::create("ff::ffff", 122); // Assignment operator. EXPECT_EQ(rng.asString(), "ff::ffc0/122"); EXPECT_EQ(rng.length(), 122); EXPECT_EQ(rng.ip()->version(), IpVersion::v6); @@ -330,15 +293,12 @@ TEST(Ipv6CidrRange, StringAndLengthCtor) { EXPECT_TRUE(rng.isInRange(Ipv6Instance("ff::ffff"))); EXPECT_FALSE(rng.isInRange(Ipv6Instance("::1:0"))); - rng = CidrRange::create("::ffff", -2); // Invalid length. - EXPECT_FALSE(rng.isValid()); - - EXPECT_THROW(CidrRange::create("bogus", 122), EnvoyException); // Invalid address. + EXPECT_FALSE(CidrRange::create("::ffff", -2).status().ok()); // Invalid length. + EXPECT_FALSE(CidrRange::create("bogus", 122).status().ok()); // Invalid address. } TEST(Ipv6CidrRange, StringCtor) { - CidrRange rng = CidrRange::create("ff::fc1f/118"); - EXPECT_TRUE(rng.isValid()); + CidrRange rng = *CidrRange::create("ff::fc1f/118"); EXPECT_EQ(rng.asString(), "ff::fc00/118"); EXPECT_EQ(rng.length(), 118); EXPECT_EQ(rng.ip()->version(), IpVersion::v6); @@ -347,22 +307,15 @@ TEST(Ipv6CidrRange, StringCtor) { EXPECT_TRUE(rng.isInRange(Ipv6Instance("ff::ffff"))); EXPECT_FALSE(rng.isInRange(Ipv6Instance("::1:00"))); - CidrRange rng2 = CidrRange::create("::fc1f/-10"); // Invalid length. - EXPECT_FALSE(rng2.isValid()); - - EXPECT_THROW(CidrRange::create("::fc1f00/118"), EnvoyException); // Invalid address. - - CidrRange rng4 = CidrRange::create("/118"); // Missing address. - EXPECT_FALSE(rng4.isValid()); - - CidrRange rng5 = CidrRange::create("::fc1f/"); // Missing length. - EXPECT_FALSE(rng5.isValid()); + EXPECT_FALSE(CidrRange::create("::fc1f/-10").status().ok()); // Invalid length. + EXPECT_FALSE(CidrRange::create("::fc1f00/118").status().ok()); // Invalid address. + EXPECT_FALSE(CidrRange::create("/118").status().ok()); // Missing address. + EXPECT_FALSE(CidrRange::create("::fc1f/").status().ok()); // Missing length. } TEST(Ipv6CidrRange, BigRange) { std::string prefix = "2001:0db8:85a3:0000"; - CidrRange rng = CidrRange::create(prefix + "::/64"); - EXPECT_TRUE(rng.isValid()); + CidrRange rng = *CidrRange::create(prefix + "::/64"); EXPECT_EQ(rng.asString(), "2001:db8:85a3::/64"); EXPECT_EQ(rng.length(), 64); EXPECT_EQ(rng.ip()->version(), IpVersion::v6); @@ -389,7 +342,7 @@ makeCidrRangeList(const std::vector>& ranges) { } TEST(IpListTest, Errors) { - { EXPECT_THROW(IpList::create(makeCidrRangeList({{"foo", 0}})).IgnoreError(), EnvoyException); } + { EXPECT_FALSE(IpList::create(makeCidrRangeList({{"foo", 0}})).status().ok()); } } TEST(IpListTest, SpecificAddressAllowed) { diff --git a/test/common/network/lc_trie_speed_test.cc b/test/common/network/lc_trie_speed_test.cc index 99eb7e6aede1..3a59d87cc820 100644 --- a/test/common/network/lc_trie_speed_test.cc +++ b/test/common/network/lc_trie_speed_test.cc @@ -30,17 +30,17 @@ struct CidrInputs { tag_data_.emplace_back( std::pair>( {"tag_1", - {Envoy::Network::Address::CidrRange::create( + {*Envoy::Network::Address::CidrRange::create( fmt::format("192.0.{}.{}/32", i, j))}})); } } tag_data_nested_prefixes_ = tag_data_; tag_data_nested_prefixes_.emplace_back( std::pair>( - {"tag_0", {Envoy::Network::Address::CidrRange::create("0.0.0.0/0")}})); + {"tag_0", {*Envoy::Network::Address::CidrRange::create("0.0.0.0/0")}})); tag_data_minimal_.emplace_back( std::pair>( - {"tag_1", {Envoy::Network::Address::CidrRange::create("0.0.0.0/0")}})); + {"tag_1", {*Envoy::Network::Address::CidrRange::create("0.0.0.0/0")}})); } std::vector>> tag_data_; diff --git a/test/common/network/lc_trie_test.cc b/test/common/network/lc_trie_test.cc index c2da7417c96d..965d58141685 100644 --- a/test/common/network/lc_trie_test.cc +++ b/test/common/network/lc_trie_test.cc @@ -22,7 +22,7 @@ class LcTrieTest : public testing::Test { std::pair> ip_tags; ip_tags.first = fmt::format("tag_{0}", i); for (const auto& j : cidr_range_strings[i]) { - ip_tags.second.push_back(Address::CidrRange::create(j)); + ip_tags.second.push_back(*Address::CidrRange::create(j)); } output.push_back(ip_tags); } @@ -329,7 +329,7 @@ TEST_F(LcTrieTest, ExclusiveNestedPrefixesWithCatchAll) { // when using the default fill factor. TEST_F(LcTrieTest, MaximumEntriesExceptionDefault) { static const size_t num_prefixes = 1 << 19; - Address::CidrRange address = Address::CidrRange::create("10.0.0.1/8"); + Address::CidrRange address = *Address::CidrRange::create("10.0.0.1/8"); std::vector prefixes; prefixes.reserve(num_prefixes); for (size_t i = 0; i < num_prefixes; i++) { @@ -355,7 +355,7 @@ TEST_F(LcTrieTest, MaximumEntriesExceptionOverride) { for (size_t i = 0; i < 16; i++) { for (size_t j = 0; j < 16; j++) { for (size_t k = 0; k < 32; k++) { - prefixes.emplace_back(Address::CidrRange::create(fmt::format("10.{}.{}.{}/8", i, j, k))); + prefixes.emplace_back(*Address::CidrRange::create(fmt::format("10.{}.{}.{}/8", i, j, k))); } } } diff --git a/test/common/network/multi_connection_base_impl_test.cc b/test/common/network/multi_connection_base_impl_test.cc index a5cf8cccab50..673eafe91831 100644 --- a/test/common/network/multi_connection_base_impl_test.cc +++ b/test/common/network/multi_connection_base_impl_test.cc @@ -297,7 +297,8 @@ TEST_F(MultiConnectionBaseImplTest, HashKey) { startConnect(); std::vector hash_key = {'A', 'B', 'C'}; - uint8_t* id_array = reinterpret_cast(&id); + uint8_t id_array[sizeof(id)]; + absl::little_endian::Store64(id_array, id); impl_->hashKey(hash_key); EXPECT_EQ(3 + sizeof(id), hash_key.size()); EXPECT_EQ('A', hash_key[0]); diff --git a/test/common/quic/envoy_quic_server_session_test.cc b/test/common/quic/envoy_quic_server_session_test.cc index 11f43607b402..4fba3ac27b03 100644 --- a/test/common/quic/envoy_quic_server_session_test.cc +++ b/test/common/quic/envoy_quic_server_session_test.cc @@ -1019,11 +1019,11 @@ TEST_F(EnvoyQuicServerSessionTest, SendBufferWatermark) { EXPECT_TRUE(stream2->IsFlowControlBlocked()); // Resetting stream3 should lower the buffered bytes, but callbacks will not - // be triggered because end stream is already encoded. + // be triggered because end stream is already decoded and encoded. EXPECT_CALL(stream_callbacks3, onResetStream(Http::StreamResetReason::LocalReset, "")).Times(0); // Connection buffered data book keeping should also be updated. EXPECT_CALL(network_connection_callbacks_, onBelowWriteBufferLowWatermark()); - stream3->resetStream(Http::StreamResetReason::LocalReset); + stream3->Reset(quic::QUIC_STREAM_CANCELLED); // Update flow control window for stream1. quic::QuicWindowUpdateFrame window_update3(quic::kInvalidControlFrameId, stream1->id(), diff --git a/test/common/runtime/runtime_impl_test.cc b/test/common/runtime/runtime_impl_test.cc index fa6415809cde..9d90992b54ab 100644 --- a/test/common/runtime/runtime_impl_test.cc +++ b/test/common/runtime/runtime_impl_test.cc @@ -962,7 +962,7 @@ class RtdsLoaderImplTest : public LoaderImplTest { dispatcher_, tls_, config, local_info_, store_, generator_, validation_visitor_, *api_); THROW_IF_NOT_OK(loader.status()); loader_ = std::move(loader.value()); - loader_->initialize(cm_); + THROW_IF_NOT_OK(loader_->initialize(cm_)); for (auto* sub : rtds_subscriptions_) { EXPECT_CALL(*sub, start(_)); } @@ -1298,7 +1298,8 @@ TEST_F(RtdsLoaderImplTest, BadConfigSource) { absl::StatusOr> loader = Runtime::LoaderImpl::create( dispatcher_, tls_, config, local_info_, store_, generator_, validation_visitor_, *api_); - EXPECT_THROW_WITH_MESSAGE(loader.value()->initialize(cm_), EnvoyException, "bad config"); + EXPECT_THROW_WITH_MESSAGE(loader.value()->initialize(cm_).IgnoreError(), EnvoyException, + "bad config"); } } // namespace diff --git a/test/common/secret/sds_api_test.cc b/test/common/secret/sds_api_test.cc index 4e1fab3c1042..1f73cacb7e39 100644 --- a/test/common/secret/sds_api_test.cc +++ b/test/common/secret/sds_api_test.cc @@ -112,7 +112,7 @@ TEST_F(SdsApiTest, InitManagerInitialised) { &stats](const envoy::config::core::v3::ConfigSource&, absl::string_view, Stats::Scope&, Config::SubscriptionCallbacks& cbs, Config::OpaqueResourceDecoderSharedPtr, - const Config::SubscriptionOptions&) -> Config::SubscriptionPtr { + const Config::SubscriptionOptions&) { return std::make_unique( *dispatcher_, Config::makePathConfigSource(sds_config_path), cbs, resource_decoder, stats, validation_visitor_, *api_); @@ -138,7 +138,7 @@ TEST_F(SdsApiTest, BadConfigSource) { ::testing::InSequence s; envoy::config::core::v3::ConfigSource config_source; EXPECT_CALL(subscription_factory_, subscriptionFromConfigSource(_, _, _, _, _, _)) - .WillOnce(InvokeWithoutArgs([]() -> Config::SubscriptionPtr { + .WillOnce(InvokeWithoutArgs([]() { throw EnvoyException("bad config"); return nullptr; })); diff --git a/test/common/stats/tag_extractor_impl_test.cc b/test/common/stats/tag_extractor_impl_test.cc index e7ed185c9b1b..6bb2e8443b1e 100644 --- a/test/common/stats/tag_extractor_impl_test.cc +++ b/test/common/stats/tag_extractor_impl_test.cc @@ -485,12 +485,23 @@ TEST(TagExtractorTest, DefaultTagExtractors) { "http.rbac.policy.shadow_denied", {rbac_http_hcm_prefix, rbac_http_prefix, rbac_policy_name}); + // Proxy Protocol stat prefix + Tag proxy_protocol_prefix; + proxy_protocol_prefix.name_ = tag_names.PROXY_PROTOCOL_PREFIX; + proxy_protocol_prefix.value_ = "test_stat_prefix"; + regex_tester.testRegex("proxy_proto.not_found_disallowed", "proxy_proto.not_found_disallowed", + {}); + regex_tester.testRegex("proxy_proto.test_stat_prefix.not_found_disallowed", + "proxy_proto.not_found_disallowed", {proxy_protocol_prefix}); + // Proxy Protocol version prefix Tag proxy_protocol_version; proxy_protocol_version.name_ = tag_names.PROXY_PROTOCOL_VERSION; proxy_protocol_version.value_ = "2"; regex_tester.testRegex("proxy_proto.versions.v2.error", "proxy_proto.error", {proxy_protocol_version}); + regex_tester.testRegex("proxy_proto.test_stat_prefix.versions.v2.error", "proxy_proto.error", + {proxy_protocol_prefix, proxy_protocol_version}); } TEST(TagExtractorTest, ExtAuthzTagExtractors) { diff --git a/test/common/tracing/tracer_impl_test.cc b/test/common/tracing/tracer_impl_test.cc index 851795817631..a6c70556b0a4 100644 --- a/test/common/tracing/tracer_impl_test.cc +++ b/test/common/tracing/tracer_impl_test.cc @@ -265,6 +265,7 @@ TEST(NullTracerTest, BasicFunctionality) { span_ptr->setBaggage("key", "value"); ASSERT_EQ("", span_ptr->getBaggage("baggage_key")); ASSERT_EQ(span_ptr->getTraceId(), ""); + ASSERT_EQ(span_ptr->getSpanId(), ""); span_ptr->injectContext(trace_context, upstream_context); span_ptr->log(SystemTime(), "fake_event"); diff --git a/test/common/upstream/cluster_manager_impl_test.cc b/test/common/upstream/cluster_manager_impl_test.cc index 03e59f08245d..2b9206cbe056 100644 --- a/test/common/upstream/cluster_manager_impl_test.cc +++ b/test/common/upstream/cluster_manager_impl_test.cc @@ -144,11 +144,11 @@ class MockGrpcMuxFactory : public Config::MuxFactory { std::string name() const override { return "envoy.config_mux.grpc_mux_factory"; } void shutdownAll() override {} std::shared_ptr - create(std::unique_ptr&&, Event::Dispatcher&, Random::RandomGenerator&, - Stats::Scope&, const envoy::config::core::v3::ApiConfigSource&, - const LocalInfo::LocalInfo&, std::unique_ptr&&, - BackOffStrategyPtr&&, OptRef, - OptRef, bool) override { + create(std::unique_ptr&&, std::unique_ptr&&, + Event::Dispatcher&, Random::RandomGenerator&, Stats::Scope&, + const envoy::config::core::v3::ApiConfigSource&, const LocalInfo::LocalInfo&, + std::unique_ptr&&, BackOffStrategyPtr&&, + OptRef, OptRef, bool) override { return std::make_shared>(); } }; diff --git a/test/config_test/BUILD b/test/config_test/BUILD index b0b664f2bece..43561cd54f84 100644 --- a/test/config_test/BUILD +++ b/test/config_test/BUILD @@ -63,9 +63,10 @@ envoy_cc_test_library( "//test/test_common:simulated_time_system_lib", "//test/test_common:threadsafe_singleton_injector_lib", ] + select({ - "//bazel:windows_x86_64": envoy_all_extensions(WINDOWS_SKIP_TARGETS), - "//bazel:linux_ppc": envoy_all_extensions(PPC_SKIP_TARGETS), - "//bazel:disable_http3": envoy_all_extensions(NO_HTTP3_SKIP_TARGETS), + "//bazel:enable_http3_on_windows_x86_64": envoy_all_extensions(WINDOWS_SKIP_TARGETS), + "//bazel:enable_http3_on_linux_ppc": envoy_all_extensions(PPC_SKIP_TARGETS), + "//bazel:disable_http3_on_windows_x86_64": envoy_all_extensions(NO_HTTP3_SKIP_TARGETS + WINDOWS_SKIP_TARGETS), + "//bazel:disable_http3_on_linux_ppc": envoy_all_extensions(NO_HTTP3_SKIP_TARGETS + PPC_SKIP_TARGETS), "//conditions:default": envoy_all_extensions(), }), ) diff --git a/test/extensions/access_loggers/open_telemetry/BUILD b/test/extensions/access_loggers/open_telemetry/BUILD index 659be55579cf..9764ee5ecd18 100644 --- a/test/extensions/access_loggers/open_telemetry/BUILD +++ b/test/extensions/access_loggers/open_telemetry/BUILD @@ -43,9 +43,11 @@ envoy_extension_cc_test( "//test/mocks/access_log:access_log_mocks", "//test/mocks/grpc:grpc_mocks", "//test/mocks/local_info:local_info_mocks", + "//test/mocks/server:server_mocks", "//test/mocks/ssl:ssl_mocks", "//test/mocks/stream_info:stream_info_mocks", "//test/mocks/thread_local:thread_local_mocks", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", "@envoy_api//envoy/data/accesslog/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/access_loggers/grpc/v3:pkg_cc_proto", "@opentelemetry_proto//:logs_cc_proto", @@ -89,18 +91,36 @@ envoy_extension_cc_test( envoy_extension_cc_test( name = "substitution_formatter_test", srcs = ["substitution_formatter_test.cc"], + copts = select({ + "//bazel:windows_x86_64": [], # TODO: fix the windows ANTLR build + "//conditions:default": [ + "-DUSE_CEL_PARSER", + ], + }), extension_names = ["envoy.access_loggers.open_telemetry"], + tags = ["skip_on_windows"], deps = [ "//source/common/formatter:substitution_formatter_lib", "//source/common/http:exception_lib", "//source/common/http:header_map_lib", "//source/common/router:string_accessor_lib", + "//source/extensions/access_loggers/open_telemetry:config", "//source/extensions/access_loggers/open_telemetry:substitution_formatter_lib", + "//source/extensions/formatter/cel:cel_lib", + "//source/extensions/formatter/cel:config", + "//test/mocks/server:factory_context_mocks", "//test/mocks/stream_info:stream_info_mocks", "//test/mocks/upstream:cluster_info_mocks", "//test/test_common:utility_lib", "@opentelemetry_proto//:logs_cc_proto", - ], + ] + select( + { + "//bazel:windows_x86_64": [], + "//conditions:default": [ + "@com_google_cel_cpp//parser", + ], + }, + ), ) envoy_cc_benchmark_binary( diff --git a/test/extensions/access_loggers/open_telemetry/access_log_impl_test.cc b/test/extensions/access_loggers/open_telemetry/access_log_impl_test.cc index b16d47d135c2..532d2b288eef 100644 --- a/test/extensions/access_loggers/open_telemetry/access_log_impl_test.cc +++ b/test/extensions/access_loggers/open_telemetry/access_log_impl_test.cc @@ -2,10 +2,12 @@ #include #include "envoy/common/time.h" +#include "envoy/config/core/v3/extension.pb.h" #include "envoy/data/accesslog/v3/accesslog.pb.h" #include "envoy/extensions/access_loggers/grpc/v3/als.pb.h" #include "source/common/buffer/zero_copy_input_stream_impl.h" +#include "source/common/formatter/substitution_format_string.h" #include "source/common/network/address_impl.h" #include "source/common/protobuf/protobuf.h" #include "source/common/router/string_accessor_impl.h" @@ -15,6 +17,7 @@ #include "test/mocks/common.h" #include "test/mocks/grpc/mocks.h" #include "test/mocks/local_info/mocks.h" +#include "test/mocks/server/mocks.h" #include "test/mocks/ssl/mocks.h" #include "test/mocks/stream_info/mocks.h" #include "test/mocks/thread_local/mocks.h" @@ -80,7 +83,10 @@ class AccessLogTest : public testing::Test { EXPECT_EQ(Common::GrpcAccessLoggerType::HTTP, logger_type); return logger_; }); - return std::make_unique(FilterPtr{filter_}, config_, tls_, logger_cache_); + auto commands = + Formatter::SubstitutionFormatStringUtils::parseFormatters(config_.formatters(), context_); + + return std::make_unique(FilterPtr{filter_}, config_, tls_, logger_cache_, commands); } void expectLog(const std::string& expected_log_entry_yaml) { @@ -94,6 +100,7 @@ class AccessLogTest : public testing::Test { MockFilter* filter_{new NiceMock()}; NiceMock tls_; + NiceMock context_; envoy::extensions::access_loggers::open_telemetry::v3::OpenTelemetryAccessLogConfig config_; std::shared_ptr logger_{new MockGrpcAccessLogger()}; std::shared_ptr logger_cache_{new MockGrpcAccessLoggerCache()}; @@ -176,7 +183,9 @@ TEST_F(AccessLogTest, TraceId) { NiceMock active_span; EXPECT_CALL(active_span, getTraceId()).WillOnce(Return("404142434445464748494a4b4c4d4e4f")); + EXPECT_CALL(active_span, getSpanId()).WillOnce(Return("4041424344454647")); expectLog(R"EOF( + span_id: "QEFCQ0RFRkc=" trace_id: "QEFCQ0RFRkdISUpLTE1OTw==" time_unix_nano: 3600000000000 )EOF"); 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 11557702df2b..31975e697e2b 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 @@ -53,7 +53,8 @@ std::unique_ptr makeOpenTelemetryFormatter() { string_value: '%REQ(USER-AGENT)%' )EOF"; TestUtility::loadFromYaml(format_yaml, otel_log_format); - return std::make_unique(otel_log_format); + std::vector commands = {}; + return std::make_unique(otel_log_format, commands); } std::unique_ptr makeStreamInfo() { diff --git a/test/extensions/access_loggers/open_telemetry/substitution_formatter_test.cc b/test/extensions/access_loggers/open_telemetry/substitution_formatter_test.cc index 2adedb958afc..54fe710a47f3 100644 --- a/test/extensions/access_loggers/open_telemetry/substitution_formatter_test.cc +++ b/test/extensions/access_loggers/open_telemetry/substitution_formatter_test.cc @@ -5,11 +5,14 @@ #include "envoy/common/exception.h" #include "envoy/stream_info/stream_info.h" +#include "source/common/formatter/substitution_format_string.h" #include "source/common/formatter/substitution_formatter.h" #include "source/common/http/header_map_impl.h" #include "source/common/router/string_accessor_impl.h" +#include "source/extensions/access_loggers/open_telemetry/grpc_access_log_impl.h" #include "source/extensions/access_loggers/open_telemetry/substitution_formatter.h" +#include "test/mocks/server/factory_context.h" #include "test/mocks/stream_info/mocks.h" #include "test/mocks/upstream/cluster_info.h" #include "test/test_common/utility.h" @@ -127,7 +130,7 @@ TEST(SubstitutionFormatterTest, OpenTelemetryFormatterPlainStringTest) { string_value: "plain_string_value" )EOF", key_mapping); - OpenTelemetryFormatter formatter(key_mapping); + OpenTelemetryFormatter formatter(key_mapping, {}); verifyOpenTelemetryOutput(formatter.format({}, stream_info), expected); } @@ -162,7 +165,7 @@ TEST(SubstitutionFormatterTest, OpenTelemetryFormatterTypesTest) { - string_value: "%PROTOCOL%" )EOF", key_mapping); - OpenTelemetryFormatter formatter(key_mapping); + OpenTelemetryFormatter formatter(key_mapping, {}); KeyValueList expected; TestUtility::loadFromYaml(R"EOF( @@ -298,7 +301,7 @@ TEST(SubstitutionFormatterTest, OpenTelemetryFormatterNestedObjectsTest) { - string_value: "%PROTOCOL%" )EOF", key_mapping); - OpenTelemetryFormatter formatter(key_mapping); + OpenTelemetryFormatter formatter(key_mapping, {}); KeyValueList expected; TestUtility::loadFromYaml(R"EOF( values: @@ -416,7 +419,7 @@ TEST(SubstitutionFormatterTest, OpenTelemetryFormatterSingleOperatorTest) { string_value: "%PROTOCOL%" )EOF", key_mapping); - OpenTelemetryFormatter formatter(key_mapping); + OpenTelemetryFormatter formatter(key_mapping, {}); verifyOpenTelemetryOutput(formatter.format({}, stream_info), expected); } @@ -437,7 +440,7 @@ TEST(SubstitutionFormatterTest, EmptyOpenTelemetryFormatterTest) { string_value: "" )EOF", key_mapping); - OpenTelemetryFormatter formatter(key_mapping); + OpenTelemetryFormatter formatter(key_mapping, {}); verifyOpenTelemetryOutput(formatter.format({}, stream_info), expected); } @@ -469,7 +472,7 @@ TEST(SubstitutionFormatterTest, OpenTelemetryFormatterNonExistentHeaderTest) { string_value: "%RESP(some_response_header)%" )EOF", key_mapping); - OpenTelemetryFormatter formatter(key_mapping); + OpenTelemetryFormatter formatter(key_mapping, {}); absl::optional protocol = Http::Protocol::Http11; EXPECT_CALL(stream_info, protocol()).WillRepeatedly(Return(protocol)); @@ -508,7 +511,7 @@ TEST(SubstitutionFormatterTest, OpenTelemetryFormatterAlternateHeaderTest) { string_value: "%RESP(response_present_header?response_absent_header)%" )EOF", key_mapping); - OpenTelemetryFormatter formatter(key_mapping); + OpenTelemetryFormatter formatter(key_mapping, {}); absl::optional protocol = Http::Protocol::Http11; EXPECT_CALL(stream_info, protocol()).WillRepeatedly(Return(protocol)); @@ -546,7 +549,7 @@ TEST(SubstitutionFormatterTest, OpenTelemetryFormatterDynamicMetadataTest) { string_value: "%DYNAMIC_METADATA(com.test:test_obj:inner_key)%" )EOF", key_mapping); - OpenTelemetryFormatter formatter(key_mapping); + OpenTelemetryFormatter formatter(key_mapping, {}); verifyOpenTelemetryOutput( formatter.format({&request_header, &response_header, &response_trailer}, stream_info), @@ -591,7 +594,7 @@ TEST(SubstitutionFormatterTest, OpenTelemetryFormatterClusterMetadataTest) { string_value: "%CLUSTER_METADATA(com.test:test_obj:non_existing_key)%" )EOF", key_mapping); - OpenTelemetryFormatter formatter(key_mapping); + OpenTelemetryFormatter formatter(key_mapping, {}); verifyOpenTelemetryOutput( formatter.format({&request_header, &response_header, &response_trailer}, stream_info), @@ -614,7 +617,7 @@ TEST(SubstitutionFormatterTest, OpenTelemetryFormatterClusterMetadataNoClusterIn string_value: "%CLUSTER_METADATA(com.test:test_key)%" )EOF", key_mapping); - OpenTelemetryFormatter formatter(key_mapping); + OpenTelemetryFormatter formatter(key_mapping, {}); // Empty optional (absl::nullopt) { @@ -656,7 +659,7 @@ TEST(SubstitutionFormatterTest, OpenTelemetryFormatterFilterStateTest) { string_value: "%FILTER_STATE(test_obj)%" )EOF", key_mapping); - OpenTelemetryFormatter formatter(key_mapping); + OpenTelemetryFormatter formatter(key_mapping, {}); verifyOpenTelemetryOutput(formatter.format({}, stream_info), expected); } @@ -696,7 +699,7 @@ TEST(SubstitutionFormatterTest, OpenTelemetryFormatterUpstreamFilterStateTest) { string_value: "%UPSTREAM_FILTER_STATE(test_obj)%" )EOF", key_mapping); - OpenTelemetryFormatter formatter(key_mapping); + OpenTelemetryFormatter formatter(key_mapping, {}); verifyOpenTelemetryOutput(formatter.format({}, stream_info), expected); } @@ -726,7 +729,7 @@ TEST(SubstitutionFormatterTest, OpenTelemetryFormatterFilterStateSpeciferTest) { string_value: "%FILTER_STATE(test_key:TYPED)%" )EOF", key_mapping); - OpenTelemetryFormatter formatter(key_mapping); + OpenTelemetryFormatter formatter(key_mapping, {}); verifyOpenTelemetryOutput(formatter.format({}, stream_info), expected); } @@ -762,7 +765,7 @@ TEST(SubstitutionFormatterTest, OpenTelemetryFormatterUpstreamFilterStateSpecife string_value: "%UPSTREAM_FILTER_STATE(test_key:TYPED)%" )EOF", key_mapping); - OpenTelemetryFormatter formatter(key_mapping); + OpenTelemetryFormatter formatter(key_mapping, {}); verifyOpenTelemetryOutput(formatter.format({}, stream_info), expected); } @@ -787,7 +790,7 @@ TEST(SubstitutionFormatterTest, OpenTelemetryFormatterFilterStateErrorSpeciferTe string_value: "%FILTER_STATE(test_key:TYPED)%" )EOF", key_mapping); - EXPECT_THROW_WITH_MESSAGE(OpenTelemetryFormatter formatter(key_mapping), EnvoyException, + EXPECT_THROW_WITH_MESSAGE(OpenTelemetryFormatter formatter(key_mapping, {}), EnvoyException, "Invalid filter state serialize type, only support PLAIN/TYPED/FIELD."); } @@ -818,7 +821,7 @@ TEST(SubstitutionFormatterTest, OpenTelemetryFormatterUpstreamFilterStateErrorSp string_value: "%UPSTREAM_FILTER_STATE(test_key:TYPED)%" )EOF", key_mapping); - EXPECT_THROW_WITH_MESSAGE(OpenTelemetryFormatter formatter(key_mapping), EnvoyException, + EXPECT_THROW_WITH_MESSAGE(OpenTelemetryFormatter formatter(key_mapping, {}), EnvoyException, "Invalid filter state serialize type, only support PLAIN/TYPED/FIELD."); } @@ -855,7 +858,7 @@ TEST(SubstitutionFormatterTest, OpenTelemetryFormatterStartTimeTest) { string_value: "%START_TIME(%f.%1f.%2f.%3f)%" )EOF", key_mapping); - OpenTelemetryFormatter formatter(key_mapping); + OpenTelemetryFormatter formatter(key_mapping, {}); verifyOpenTelemetryOutput(formatter.format({}, stream_info), expected); } @@ -878,7 +881,7 @@ TEST(SubstitutionFormatterTest, OpenTelemetryFormatterMultiTokenTest) { string_value: "%PROTOCOL% plainstring %REQ(some_request_header)% %RESP(some_response_header)%" )EOF", key_mapping); - OpenTelemetryFormatter formatter(key_mapping); + OpenTelemetryFormatter formatter(key_mapping, {}); absl::optional protocol = Http::Protocol::Http11; EXPECT_CALL(stream_info, protocol()).WillRepeatedly(Return(protocol)); @@ -888,6 +891,41 @@ TEST(SubstitutionFormatterTest, OpenTelemetryFormatterMultiTokenTest) { } } +#ifdef USE_CEL_PARSER +TEST(SubstitutionFormatterTest, CELFormatterTest) { + { + NiceMock context; + StreamInfo::MockStreamInfo stream_info; + Http::TestRequestHeaderMapImpl request_header{{"some_request_header", "SOME_REQUEST_HEADER"}}; + Http::TestResponseHeaderMapImpl response_header{ + {"some_response_header", "SOME_RESPONSE_HEADER"}}; + + OpenTelemetryFormatMap expected = {{"cel_field", "SOME_REQUEST_HEADER SOME_RESPONSE_HEADER"}}; + + envoy::extensions::access_loggers::open_telemetry::v3::OpenTelemetryAccessLogConfig otel_config; + TestUtility::loadFromYaml(R"EOF( + resource_attributes: + values: + - key: "cel_field" + value: + string_value: "%CEL(request.headers['some_request_header'])% %CEL(response.headers['some_response_header'])%" + formatters: + - name: envoy.formatter.cel + typed_config: + "@type": type.googleapis.com/envoy.extensions.formatter.cel.v3.Cel + )EOF", + otel_config); + auto commands = Formatter::SubstitutionFormatStringUtils::parseFormatters( + otel_config.formatters(), context); + + OpenTelemetryFormatter formatter(otel_config.resource_attributes(), commands); + + verifyOpenTelemetryOutput(formatter.format({&request_header, &response_header}, stream_info), + expected); + } +} +#endif + } // namespace } // namespace OpenTelemetry } // namespace AccessLoggers diff --git a/test/extensions/clusters/eds/eds_speed_test.cc b/test/extensions/clusters/eds/eds_speed_test.cc index d87d22a5bfaf..bfdd7d51173a 100644 --- a/test/extensions/clusters/eds/eds_speed_test.cc +++ b/test/extensions/clusters/eds/eds_speed_test.cc @@ -54,6 +54,7 @@ class EdsSpeedTest { Config::SubscriptionFactory::RetryMaxDelayMs, random_); Config::GrpcMuxContext grpc_mux_context{ /*async_client_=*/std::unique_ptr(async_client_), + /*failover_async_client_=*/nullptr, /*dispatcher_=*/server_context_.dispatcher_, /*service_method_=*/ *Protobuf::DescriptorPool::generated_pool()->FindMethodByName( diff --git a/test/extensions/compression/brotli/decompressor/brotli_decompressor_impl_test.cc b/test/extensions/compression/brotli/decompressor/brotli_decompressor_impl_test.cc index 514a5e0fd2b5..4b2451c04d49 100644 --- a/test/extensions/compression/brotli/decompressor/brotli_decompressor_impl_test.cc +++ b/test/extensions/compression/brotli/decompressor/brotli_decompressor_impl_test.cc @@ -106,6 +106,51 @@ TEST_F(BrotliDecompressorImplTest, CompressAndDecompress) { EXPECT_EQ(original_text, decompressed_text); } +TEST_F(BrotliDecompressorImplTest, CompressAndDecompressWithRedundantInput) { + Buffer::OwnedImpl buffer; + Buffer::OwnedImpl accumulation_buffer; + + Brotli::Compressor::BrotliCompressorImpl compressor{ + default_quality, + default_window_bits, + default_input_block_bits, + false, + Brotli::Compressor::BrotliCompressorImpl::EncoderMode::Default, + 4096}; + + std::string original_text{}; + for (uint64_t i = 0; i < 20; ++i) { + TestUtility::feedBufferWithRandomCharacters(buffer, default_input_size * i, i); + original_text.append(buffer.toString()); + compressor.compress(buffer, Envoy::Compression::Compressor::State::Flush); + accumulation_buffer.add(buffer); + drainBuffer(buffer); + } + + ASSERT_EQ(0, buffer.length()); + + compressor.compress(buffer, Envoy::Compression::Compressor::State::Finish); + ASSERT_GE(10, buffer.length()); + + accumulation_buffer.add(buffer); + accumulation_buffer.add("redundant_input_here"); // Add some redundant input. + + drainBuffer(buffer); + ASSERT_EQ(0, buffer.length()); + + Stats::IsolatedStoreImpl stats_store{}; + BrotliDecompressorImpl decompressor{*stats_store.rootScope(), "test.", 16, false}; + decompressor.decompress(accumulation_buffer, buffer); + std::string decompressed_text{buffer.toString()}; + ASSERT_EQ(original_text.length(), decompressed_text.length()); + EXPECT_EQ(original_text, decompressed_text); + + // Although we finally get the original text, we still have some redundant input and + // the decompression is considered as failed. + EXPECT_EQ(1, stats_store.counterFromString("test.brotli_error").value()); + EXPECT_EQ(1, stats_store.counterFromString("test.brotli_redundant_input").value()); +} + // Exercises decompression with a very small output buffer. TEST_F(BrotliDecompressorImplTest, DecompressWithSmallOutputBuffer) { Buffer::OwnedImpl buffer; diff --git a/test/extensions/config_subscription/common/subscription_factory_impl_test.cc b/test/extensions/config_subscription/common/subscription_factory_impl_test.cc index 04ff5951273b..ed4470656532 100644 --- a/test/extensions/config_subscription/common/subscription_factory_impl_test.cc +++ b/test/extensions/config_subscription/common/subscription_factory_impl_test.cc @@ -53,18 +53,21 @@ class SubscriptionFactoryTest : public testing::Test { SubscriptionPtr subscriptionFromConfigSource(const envoy::config::core::v3::ConfigSource& config) { - return subscription_factory_.subscriptionFromConfigSource( - config, Config::TypeUrl::get().ClusterLoadAssignment, *stats_store_.rootScope(), callbacks_, - resource_decoder_, {}); + return THROW_OR_RETURN_VALUE(subscription_factory_.subscriptionFromConfigSource( + config, Config::TypeUrl::get().ClusterLoadAssignment, + *stats_store_.rootScope(), callbacks_, resource_decoder_, {}), + SubscriptionPtr); } SubscriptionPtr collectionSubscriptionFromUrl(const std::string& xds_url, const envoy::config::core::v3::ConfigSource& config) { const auto resource_locator = XdsResourceIdentifier::decodeUrl(xds_url).value(); - return subscription_factory_.collectionSubscriptionFromUrl( - resource_locator, config, "envoy.config.endpoint.v3.ClusterLoadAssignment", - *stats_store_.rootScope(), callbacks_, resource_decoder_); + return THROW_OR_RETURN_VALUE(subscription_factory_.collectionSubscriptionFromUrl( + resource_locator, config, + "envoy.config.endpoint.v3.ClusterLoadAssignment", + *stats_store_.rootScope(), callbacks_, resource_decoder_), + SubscriptionPtr); } Upstream::MockClusterManager cm_; @@ -114,7 +117,7 @@ TEST_F(SubscriptionFactoryTest, RestClusterEmpty) { config.mutable_api_config_source()->set_api_type(envoy::config::core::v3::ApiConfigSource::REST); - EXPECT_CALL(cm_, primaryClusters()).WillOnce(ReturnRef(primary_clusters)); + EXPECT_CALL(cm_, primaryClusters()).WillRepeatedly(ReturnRef(primary_clusters)); EXPECT_THROW_WITH_REGEX(subscriptionFromConfigSource(config), EnvoyException, "API configs must have either a gRPC service or a cluster name defined:"); } @@ -125,7 +128,7 @@ TEST_P(SubscriptionFactoryTestUnifiedOrLegacyMux, GrpcClusterEmpty) { config.mutable_api_config_source()->set_api_type(envoy::config::core::v3::ApiConfigSource::GRPC); - EXPECT_CALL(cm_, primaryClusters()).WillOnce(ReturnRef(primary_clusters)); + EXPECT_CALL(cm_, primaryClusters()).WillRepeatedly(ReturnRef(primary_clusters)); EXPECT_THROW_WITH_REGEX(subscriptionFromConfigSource(config), EnvoyException, "API configs must have either a gRPC service or a cluster name defined:"); } @@ -187,7 +190,7 @@ TEST_F(SubscriptionFactoryTest, RestClusterMultiton) { config.mutable_api_config_source()->add_cluster_names("static_cluster_bar"); primary_clusters.insert("static_cluster_bar"); - EXPECT_CALL(cm_, primaryClusters()).WillOnce(ReturnRef(primary_clusters)); + EXPECT_CALL(cm_, primaryClusters()).WillRepeatedly(ReturnRef(primary_clusters)); EXPECT_THROW_WITH_REGEX(subscriptionFromConfigSource(config), EnvoyException, fmt::format("{} must have a singleton cluster name specified:", config.mutable_api_config_source()->GetTypeName())); @@ -207,7 +210,7 @@ TEST_P(SubscriptionFactoryTestUnifiedOrLegacyMux, GrpcClusterMultiton) { primary_clusters.insert("static_cluster_bar"); EXPECT_CALL(cm_, grpcAsyncClientManager()).WillRepeatedly(ReturnRef(cm_.async_client_manager_)); - EXPECT_CALL(cm_, primaryClusters()).WillOnce(ReturnRef(primary_clusters)); + EXPECT_CALL(cm_, primaryClusters()).WillRepeatedly(ReturnRef(primary_clusters)); EXPECT_THROW_WITH_REGEX(subscriptionFromConfigSource(config), EnvoyException, fmt::format("{}::.DELTA_.GRPC must have a " @@ -486,10 +489,13 @@ TEST_F(SubscriptionFactoryTest, LogWarningOnDeprecatedV2Transport) { primary_clusters.insert("static_cluster"); EXPECT_CALL(cm_, primaryClusters()).WillOnce(ReturnRef(primary_clusters)); - EXPECT_THROW_WITH_REGEX(subscription_factory_.subscriptionFromConfigSource( - config, Config::TypeUrl::get().ClusterLoadAssignment, - *stats_store_.rootScope(), callbacks_, resource_decoder_, {}), - EnvoyException, "V2 xDS transport protocol version is deprecated in"); + EXPECT_THAT(subscription_factory_ + .subscriptionFromConfigSource( + config, Config::TypeUrl::get().ClusterLoadAssignment, + *stats_store_.rootScope(), callbacks_, resource_decoder_, {}) + .status() + .message(), + testing::HasSubstr("V2 xDS transport protocol version is deprecated in")); } // Use of AUTO transport fails by default. This will encourage folks to upgrade to explicit V3. @@ -506,6 +512,7 @@ TEST_F(SubscriptionFactoryTest, AutoTransportIsAllowed) { Upstream::ClusterManager::ClusterSet primary_clusters; primary_clusters.insert("static_cluster"); EXPECT_CALL(cm_, primaryClusters()).WillOnce(ReturnRef(primary_clusters)); + EXPECT_CALL(cm_, grpcAsyncClientManager()).WillOnce(ReturnRef(cm_.async_client_manager_)); EXPECT_CALL(cm_.async_client_manager_, factoryForGrpcService(ProtoEq(expected_grpc_service), _, _)) @@ -539,7 +546,7 @@ TEST_P(SubscriptionFactoryTestApiConfigSource, NonExistentCluster) { api_config_source->add_cluster_names("static_cluster"); } Upstream::ClusterManager::ClusterSet primary_clusters; - EXPECT_CALL(cm_, primaryClusters()).WillOnce(ReturnRef(primary_clusters)); + EXPECT_CALL(cm_, primaryClusters()).WillRepeatedly(ReturnRef(primary_clusters)); EXPECT_THROW_WITH_MESSAGE(subscriptionFromConfigSource(config)->start({"static_cluster"}), EnvoyException, fmt::format("{} must have a statically defined " diff --git a/test/extensions/config_subscription/grpc/delta_subscription_impl_test.cc b/test/extensions/config_subscription/grpc/delta_subscription_impl_test.cc index c74e53cd5b27..694f44cf0e44 100644 --- a/test/extensions/config_subscription/grpc/delta_subscription_impl_test.cc +++ b/test/extensions/config_subscription/grpc/delta_subscription_impl_test.cc @@ -156,6 +156,7 @@ TEST_P(DeltaSubscriptionNoGrpcStreamTest, NoGrpcStream) { GrpcMuxContext grpc_mux_context{ /*async_client_=*/std::unique_ptr(async_client), + /*failover_async_client_=*/nullptr, /*dispatcher_=*/dispatcher, /*service_method_=*/*method_descriptor, /*local_info_=*/local_info, diff --git a/test/extensions/config_subscription/grpc/delta_subscription_test_harness.h b/test/extensions/config_subscription/grpc/delta_subscription_test_harness.h index 95ae79c6f042..57196a1ff8c1 100644 --- a/test/extensions/config_subscription/grpc/delta_subscription_test_harness.h +++ b/test/extensions/config_subscription/grpc/delta_subscription_test_harness.h @@ -52,6 +52,7 @@ class DeltaSubscriptionTestHarness : public SubscriptionTestHarness { SubscriptionFactory::RetryInitialDelayMs, SubscriptionFactory::RetryMaxDelayMs, random_); GrpcMuxContext grpc_mux_context{ /*async_client_=*/std::unique_ptr(async_client_), + /*failover_async_client_=*/nullptr, /*dispatcher_=*/dispatcher_, /*service_method_=*/*method_descriptor_, /*local_info_=*/local_info_, diff --git a/test/extensions/config_subscription/grpc/grpc_mux_impl_test.cc b/test/extensions/config_subscription/grpc/grpc_mux_impl_test.cc index 418eab7506be..93e5d13c5457 100644 --- a/test/extensions/config_subscription/grpc/grpc_mux_impl_test.cc +++ b/test/extensions/config_subscription/grpc/grpc_mux_impl_test.cc @@ -27,6 +27,7 @@ #include "test/test_common/logging.h" #include "test/test_common/resources.h" #include "test/test_common/simulated_time_system.h" +#include "test/test_common/test_runtime.h" #include "test/test_common/test_time.h" #include "test/test_common/utility.h" @@ -49,7 +50,7 @@ namespace { // We test some mux specific stuff below, other unit test coverage for singleton use of GrpcMuxImpl // is provided in [grpc_]subscription_impl_test.cc. -class GrpcMuxImplTestBase : public testing::Test { +class GrpcMuxImplTestBase : public testing::TestWithParam { public: GrpcMuxImplTestBase() : async_client_(new Grpc::MockAsyncClient()), @@ -58,15 +59,19 @@ class GrpcMuxImplTestBase : public testing::Test { control_plane_connected_state_( stats_.gauge("control_plane.connected_state", Stats::Gauge::ImportMode::NeverImport)), control_plane_pending_requests_( - stats_.gauge("control_plane.pending_requests", Stats::Gauge::ImportMode::NeverImport)) - - {} + stats_.gauge("control_plane.pending_requests", Stats::Gauge::ImportMode::NeverImport)) { + // Once "envoy.restart_features.xds_failover_support" is deprecated, the + // test should no longer be parameterized. + scoped_runtime_.mergeValues( + {{"envoy.restart_features.xds_failover_support", GetParam() ? "true" : "false"}}); + } void setup() { setup(rate_limit_settings_); } void setup(const RateLimitSettings& custom_rate_limit_settings) { GrpcMuxContext grpc_mux_context{ /*async_client_=*/std::unique_ptr(async_client_), + /*failover_async_client_=*/nullptr, /*dispatcher_=*/dispatcher_, /*service_method_=*/ *Protobuf::DescriptorPool::generated_pool()->FindMethodByName( @@ -111,6 +116,7 @@ class GrpcMuxImplTestBase : public testing::Test { EXPECT_CALL(async_stream_, sendMessageRaw_(Grpc::ProtoBufferEq(expected_request), false)); } + TestScopedRuntime scoped_runtime_; NiceMock dispatcher_; NiceMock random_; NiceMock local_info_; @@ -132,9 +138,11 @@ class GrpcMuxImplTest : public GrpcMuxImplTestBase { Event::SimulatedTimeSystem time_system_; }; +INSTANTIATE_TEST_SUITE_P(GrpcMuxImpl, GrpcMuxImplTest, ::testing::Bool()); + // Validate behavior when multiple type URL watches are maintained, watches are created/destroyed // (via RAII). -TEST_F(GrpcMuxImplTest, MultipleTypeUrlStreams) { +TEST_P(GrpcMuxImplTest, MultipleTypeUrlStreams) { setup(); InSequence s; auto foo_sub = grpc_mux_->addWatch("foo", {"x", "y"}, callbacks_, resource_decoder_, {}); @@ -154,7 +162,7 @@ TEST_F(GrpcMuxImplTest, MultipleTypeUrlStreams) { } // Validate behavior when dynamic context parameters are updated. -TEST_F(GrpcMuxImplTest, DynamicContextParameters) { +TEST_P(GrpcMuxImplTest, DynamicContextParameters) { setup(); InSequence s; auto foo_sub = grpc_mux_->addWatch("foo", {"x", "y"}, callbacks_, resource_decoder_, {}); @@ -179,7 +187,7 @@ TEST_F(GrpcMuxImplTest, DynamicContextParameters) { } // Validate behavior when xdstp naming is used. -TEST_F(GrpcMuxImplTest, Xdstp) { +TEST_P(GrpcMuxImplTest, Xdstp) { setup(); InSequence s; @@ -203,7 +211,7 @@ TEST_F(GrpcMuxImplTest, Xdstp) { } // Validate behavior when xdstp naming is used with context parameters in the URI. -TEST_F(GrpcMuxImplTest, XdstpWithContextParams) { +TEST_P(GrpcMuxImplTest, XdstpWithContextParams) { setup(); InSequence s; @@ -234,7 +242,7 @@ TEST_F(GrpcMuxImplTest, XdstpWithContextParams) { } // Validate behavior when multiple type URL watches are maintained and the stream is reset. -TEST_F(GrpcMuxImplTest, ResetStream) { +TEST_P(GrpcMuxImplTest, ResetStream) { InSequence s; auto* timer = new Event::MockTimer(&dispatcher_); @@ -278,7 +286,7 @@ TEST_F(GrpcMuxImplTest, ResetStream) { } // Validate cached nonces are cleared on reconnection. -TEST_F(GrpcMuxImplTest, ReconnectionResetsNonceAndAcks) { +TEST_P(GrpcMuxImplTest, ReconnectionResetsNonceAndAcks) { OpaqueResourceDecoderSharedPtr resource_decoder( std::make_shared>("cluster_name")); @@ -334,7 +342,7 @@ TEST_F(GrpcMuxImplTest, ReconnectionResetsNonceAndAcks) { } // Validate pause-resume behavior. -TEST_F(GrpcMuxImplTest, PauseResume) { +TEST_P(GrpcMuxImplTest, PauseResume) { setup(); InSequence s; GrpcMuxWatchPtr foo_sub; @@ -368,7 +376,7 @@ TEST_F(GrpcMuxImplTest, PauseResume) { } // Validate behavior when type URL mismatches occur. -TEST_F(GrpcMuxImplTest, TypeUrlMismatch) { +TEST_P(GrpcMuxImplTest, TypeUrlMismatch) { setup(); auto invalid_response = std::make_unique(); @@ -406,7 +414,7 @@ TEST_F(GrpcMuxImplTest, TypeUrlMismatch) { expectSendMessage("foo", {}, ""); } -TEST_F(GrpcMuxImplTest, RpcErrorMessageTruncated) { +TEST_P(GrpcMuxImplTest, RpcErrorMessageTruncated) { setup(); auto invalid_response = std::make_unique(); InSequence s; @@ -459,7 +467,7 @@ resourceWithTtl(std::chrono::milliseconds ttl, return resource; } // Validates the behavior when the TTL timer expires. -TEST_F(GrpcMuxImplTest, ResourceTTL) { +TEST_P(GrpcMuxImplTest, ResourceTTL) { setup(); time_system_.setSystemTime(std::chrono::seconds(0)); @@ -590,7 +598,7 @@ TEST_F(GrpcMuxImplTest, ResourceTTL) { } // Checks that the control plane identifier is logged -TEST_F(GrpcMuxImplTest, LogsControlPlaneIndentifier) { +TEST_P(GrpcMuxImplTest, LogsControlPlaneIndentifier) { setup(); std::string type_url = "foo"; auto foo_sub = grpc_mux_->addWatch(type_url, {}, callbacks_, resource_decoder_, {}); @@ -624,7 +632,7 @@ TEST_F(GrpcMuxImplTest, LogsControlPlaneIndentifier) { } // Validate behavior when watches has an unknown resource name. -TEST_F(GrpcMuxImplTest, WildcardWatch) { +TEST_P(GrpcMuxImplTest, WildcardWatch) { setup(); InSequence s; @@ -660,7 +668,7 @@ TEST_F(GrpcMuxImplTest, WildcardWatch) { } // Validate behavior when watches specify resources (potentially overlapping). -TEST_F(GrpcMuxImplTest, WatchDemux) { +TEST_P(GrpcMuxImplTest, WatchDemux) { setup(); InSequence s; OpaqueResourceDecoderSharedPtr resource_decoder( @@ -748,7 +756,7 @@ TEST_F(GrpcMuxImplTest, WatchDemux) { } // Validate behavior when we have multiple watchers that send empty updates. -TEST_F(GrpcMuxImplTest, MultipleWatcherWithEmptyUpdates) { +TEST_P(GrpcMuxImplTest, MultipleWatcherWithEmptyUpdates) { setup(); InSequence s; const std::string& type_url = Config::TypeUrl::get().ClusterLoadAssignment; @@ -771,7 +779,7 @@ TEST_F(GrpcMuxImplTest, MultipleWatcherWithEmptyUpdates) { } // Validate behavior when we have Single Watcher that sends Empty updates. -TEST_F(GrpcMuxImplTest, SingleWatcherWithEmptyUpdates) { +TEST_P(GrpcMuxImplTest, SingleWatcherWithEmptyUpdates) { setup(); const std::string& type_url = Config::TypeUrl::get().Cluster; NiceMock foo_callbacks; @@ -801,8 +809,11 @@ class GrpcMuxImplTestWithMockTimeSystem : public GrpcMuxImplTestBase { Event::DelegatingTestTimeSystem mock_time_system_; }; +INSTANTIATE_TEST_SUITE_P(GrpcMuxImplTestWithMockTimeSystem, GrpcMuxImplTestWithMockTimeSystem, + ::testing::Bool()); + // Verifies that rate limiting is not enforced with defaults. -TEST_F(GrpcMuxImplTestWithMockTimeSystem, TooManyRequestsWithDefaultSettings) { +TEST_P(GrpcMuxImplTestWithMockTimeSystem, TooManyRequestsWithDefaultSettings) { auto ttl_timer = new Event::MockTimer(&dispatcher_); // Retry timer, @@ -840,7 +851,7 @@ TEST_F(GrpcMuxImplTestWithMockTimeSystem, TooManyRequestsWithDefaultSettings) { } // Verifies that default rate limiting is enforced with empty RateLimitSettings. -TEST_F(GrpcMuxImplTest, TooManyRequestsWithEmptyRateLimitSettings) { +TEST_P(GrpcMuxImplTest, TooManyRequestsWithEmptyRateLimitSettings) { // Validate that request drain timer is created. auto ttl_timer = new Event::MockTimer(&dispatcher_); @@ -897,7 +908,7 @@ TEST_F(GrpcMuxImplTest, TooManyRequestsWithEmptyRateLimitSettings) { } // Verifies that rate limiting is enforced with custom RateLimitSettings. -TEST_F(GrpcMuxImplTest, TooManyRequestsWithCustomRateLimitSettings) { +TEST_P(GrpcMuxImplTest, TooManyRequestsWithCustomRateLimitSettings) { // Validate that request drain timer is created. // TTL timer. @@ -950,7 +961,7 @@ TEST_F(GrpcMuxImplTest, TooManyRequestsWithCustomRateLimitSettings) { } // Verifies that a message with no resources is accepted. -TEST_F(GrpcMuxImplTest, UnwatchedTypeAcceptsEmptyResources) { +TEST_P(GrpcMuxImplTest, UnwatchedTypeAcceptsEmptyResources) { setup(); EXPECT_CALL(*async_client_, startRaw(_, _, _, _)).WillOnce(Return(&async_stream_)); @@ -986,7 +997,7 @@ TEST_F(GrpcMuxImplTest, UnwatchedTypeAcceptsEmptyResources) { } // Verifies that a message with some resources is rejected when there are no watches. -TEST_F(GrpcMuxImplTest, UnwatchedTypeRejectsResources) { +TEST_P(GrpcMuxImplTest, UnwatchedTypeRejectsResources) { setup(); EXPECT_CALL(*async_client_, startRaw(_, _, _, _)).WillOnce(Return(&async_stream_)); @@ -1016,10 +1027,11 @@ TEST_F(GrpcMuxImplTest, UnwatchedTypeRejectsResources) { grpc_mux_->grpcStreamForTest().onReceiveMessage(std::move(response))); } -TEST_F(GrpcMuxImplTest, BadLocalInfoEmptyClusterName) { +TEST_P(GrpcMuxImplTest, BadLocalInfoEmptyClusterName) { EXPECT_CALL(local_info_, clusterName()).WillOnce(ReturnRef(EMPTY_STRING)); GrpcMuxContext grpc_mux_context{ /*async_client_=*/std::unique_ptr(async_client_), + /*failover_async_client_=*/nullptr, /*dispatcher_=*/dispatcher_, /*service_method_=*/ *Protobuf::DescriptorPool::generated_pool()->FindMethodByName( @@ -1041,10 +1053,11 @@ TEST_F(GrpcMuxImplTest, BadLocalInfoEmptyClusterName) { "--service-node and --service-cluster options."); } -TEST_F(GrpcMuxImplTest, BadLocalInfoEmptyNodeName) { +TEST_P(GrpcMuxImplTest, BadLocalInfoEmptyNodeName) { EXPECT_CALL(local_info_, nodeName()).WillOnce(ReturnRef(EMPTY_STRING)); GrpcMuxContext grpc_mux_context{ /*async_client_=*/std::unique_ptr(async_client_), + /*failover_async_client_=*/nullptr, /*dispatcher_=*/dispatcher_, /*service_method_=*/ *Protobuf::DescriptorPool::generated_pool()->FindMethodByName( @@ -1067,20 +1080,20 @@ TEST_F(GrpcMuxImplTest, BadLocalInfoEmptyNodeName) { } // Validates that the EDS cache getter returns the cache. -TEST_F(GrpcMuxImplTest, EdsResourcesCacheForEds) { +TEST_P(GrpcMuxImplTest, EdsResourcesCacheForEds) { eds_resources_cache_ = new NiceMock(); setup(); EXPECT_TRUE(grpc_mux_->edsResourcesCache().has_value()); } // Validates that the EDS cache getter returns empty if there is no cache. -TEST_F(GrpcMuxImplTest, EdsResourcesCacheForEdsNoCache) { +TEST_P(GrpcMuxImplTest, EdsResourcesCacheForEdsNoCache) { setup(); EXPECT_FALSE(grpc_mux_->edsResourcesCache().has_value()); } // Validate that an EDS resource is cached if there's a cache. -TEST_F(GrpcMuxImplTest, CacheEdsResource) { +TEST_P(GrpcMuxImplTest, CacheEdsResource) { // Create the cache that will also be passed to the GrpcMux object via setup(). eds_resources_cache_ = new NiceMock(); setup(); @@ -1122,7 +1135,7 @@ TEST_F(GrpcMuxImplTest, CacheEdsResource) { // Validate that an update to an EDS resource watcher is reflected in the cache, // if there's a cache. -TEST_F(GrpcMuxImplTest, UpdateCacheEdsResource) { +TEST_P(GrpcMuxImplTest, UpdateCacheEdsResource) { // Create the cache that will also be passed to the GrpcMux object via setup(). eds_resources_cache_ = new NiceMock(); setup(); @@ -1170,7 +1183,7 @@ TEST_F(GrpcMuxImplTest, UpdateCacheEdsResource) { // Validate that adding and removing watchers reflects on the cache changes, // if there's a cache. -TEST_F(GrpcMuxImplTest, AddRemoveSubscriptions) { +TEST_P(GrpcMuxImplTest, AddRemoveSubscriptions) { // Create the cache that will also be passed to the GrpcMux object via setup(). eds_resources_cache_ = new NiceMock(); setup(); @@ -1248,7 +1261,7 @@ TEST_F(GrpcMuxImplTest, AddRemoveSubscriptions) { // Validate that a cached resource is removed only after the last subscription // (watch) that needs it is removed. -TEST_F(GrpcMuxImplTest, RemoveCachedResourceOnLastSubscription) { +TEST_P(GrpcMuxImplTest, RemoveCachedResourceOnLastSubscription) { // Create the cache that will also be passed to the GrpcMux object via setup(). eds_resources_cache_ = new NiceMock(); setup(); @@ -1398,9 +1411,9 @@ TEST(GrpcMuxFactoryTest, InvalidRateLimit) { ads_config.mutable_rate_limit_settings()->mutable_max_tokens()->set_value(100); ads_config.mutable_rate_limit_settings()->mutable_fill_rate()->set_value( std::numeric_limits::quiet_NaN()); - EXPECT_THROW(factory->create(std::make_unique(), dispatcher, random, scope, - ads_config, local_info, nullptr, nullptr, absl::nullopt, - absl::nullopt, false), + EXPECT_THROW(factory->create(std::make_unique(), nullptr, dispatcher, + random, scope, ads_config, local_info, nullptr, nullptr, + absl::nullopt, absl::nullopt, false), EnvoyException); } diff --git a/test/extensions/config_subscription/grpc/new_grpc_mux_impl_test.cc b/test/extensions/config_subscription/grpc/new_grpc_mux_impl_test.cc index dec6a2542512..71f33b93118b 100644 --- a/test/extensions/config_subscription/grpc/new_grpc_mux_impl_test.cc +++ b/test/extensions/config_subscription/grpc/new_grpc_mux_impl_test.cc @@ -28,6 +28,7 @@ #include "test/test_common/logging.h" #include "test/test_common/resources.h" #include "test/test_common/simulated_time_system.h" +#include "test/test_common/test_runtime.h" #include "test/test_common/test_time.h" #include "test/test_common/utility.h" @@ -50,7 +51,7 @@ enum class LegacyOrUnified { Legacy, Unified }; // We test some mux specific stuff below, other unit test coverage for singleton use of // NewGrpcMuxImpl is provided in [grpc_]subscription_impl_test.cc. -class NewGrpcMuxImplTestBase : public testing::TestWithParam { +class NewGrpcMuxImplTestBase : public testing::TestWithParam> { public: NewGrpcMuxImplTestBase(LegacyOrUnified legacy_or_unified) : async_client_(new Grpc::MockAsyncClient()), @@ -60,13 +61,19 @@ class NewGrpcMuxImplTestBase : public testing::TestWithParam { control_plane_stats_(Utility::generateControlPlaneStats(*stats_.rootScope())), control_plane_connected_state_( stats_.gauge("control_plane.connected_state", Stats::Gauge::ImportMode::NeverImport)), - should_use_unified_(legacy_or_unified == LegacyOrUnified::Unified) {} + should_use_unified_(legacy_or_unified == LegacyOrUnified::Unified) { + // Once "envoy.restart_features.xds_failover_support" is deprecated, the + // test should no longer be parameterized on the bool value. + scoped_runtime_.mergeValues({{"envoy.restart_features.xds_failover_support", + std::get<1>(GetParam()) ? "true" : "false"}}); + } void setup() { auto backoff_strategy = std::make_unique( SubscriptionFactory::RetryInitialDelayMs, SubscriptionFactory::RetryMaxDelayMs, random_); GrpcMuxContext grpc_mux_context{ /*async_client_=*/std::unique_ptr(async_client_), + /*failover_async_client_=*/nullptr, /*dispatcher_=*/dispatcher_, /*service_method_=*/ *Protobuf::DescriptorPool::generated_pool()->FindMethodByName( @@ -163,6 +170,7 @@ class NewGrpcMuxImplTestBase : public testing::TestWithParam { bool isUnifiedMuxTest() const { return should_use_unified_; } + TestScopedRuntime scoped_runtime_; NiceMock dispatcher_; NiceMock random_; Grpc::MockAsyncClient* async_client_; @@ -182,12 +190,14 @@ class NewGrpcMuxImplTestBase : public testing::TestWithParam { class NewGrpcMuxImplTest : public NewGrpcMuxImplTestBase { public: - NewGrpcMuxImplTest() : NewGrpcMuxImplTestBase(GetParam()) {} + NewGrpcMuxImplTest() : NewGrpcMuxImplTestBase(std::get<0>(GetParam())) {} Event::SimulatedTimeSystem time_system_; }; INSTANTIATE_TEST_SUITE_P(NewGrpcMuxImplTest, NewGrpcMuxImplTest, - testing::ValuesIn({LegacyOrUnified::Legacy, LegacyOrUnified::Unified})); + testing::Combine(testing::ValuesIn({LegacyOrUnified::Legacy, + LegacyOrUnified::Unified}), + testing::Bool())); // Validate behavior when dynamic context parameters are updated. TEST_P(NewGrpcMuxImplTest, DynamicContextParameters) { @@ -777,9 +787,9 @@ TEST(NewGrpcMuxFactoryTest, InvalidRateLimit) { ads_config.mutable_rate_limit_settings()->mutable_max_tokens()->set_value(100); ads_config.mutable_rate_limit_settings()->mutable_fill_rate()->set_value( std::numeric_limits::quiet_NaN()); - EXPECT_THROW(factory->create(std::make_unique(), dispatcher, random, scope, - ads_config, local_info, nullptr, nullptr, absl::nullopt, - absl::nullopt, false), + EXPECT_THROW(factory->create(std::make_unique(), nullptr, dispatcher, + random, scope, ads_config, local_info, nullptr, nullptr, + absl::nullopt, absl::nullopt, false), EnvoyException); } diff --git a/test/extensions/config_subscription/grpc/xds_grpc_mux_impl_test.cc b/test/extensions/config_subscription/grpc/xds_grpc_mux_impl_test.cc index b36e2c6f958c..123ffd52a56d 100644 --- a/test/extensions/config_subscription/grpc/xds_grpc_mux_impl_test.cc +++ b/test/extensions/config_subscription/grpc/xds_grpc_mux_impl_test.cc @@ -26,6 +26,7 @@ #include "test/test_common/logging.h" #include "test/test_common/resources.h" #include "test/test_common/simulated_time_system.h" +#include "test/test_common/test_runtime.h" #include "test/test_common/test_time.h" #include "test/test_common/utility.h" @@ -48,7 +49,7 @@ namespace { // We test some mux specific stuff below, other unit test coverage for singleton use of GrpcMuxImpl // is provided in [grpc_]subscription_impl_test.cc. -class GrpcMuxImplTestBase : public testing::Test { +class GrpcMuxImplTestBase : public testing::TestWithParam { public: GrpcMuxImplTestBase() : async_client_(new Grpc::MockAsyncClient()), @@ -56,14 +57,20 @@ class GrpcMuxImplTestBase : public testing::Test { control_plane_stats_(Utility::generateControlPlaneStats(*stats_.rootScope())), control_plane_connected_state_( stats_.gauge("control_plane.connected_state", Stats::Gauge::ImportMode::NeverImport)), - control_plane_pending_requests_(stats_.gauge("control_plane.pending_requests", - Stats::Gauge::ImportMode::NeverImport)) {} + control_plane_pending_requests_( + stats_.gauge("control_plane.pending_requests", Stats::Gauge::ImportMode::NeverImport)) { + // Once "envoy.restart_features.xds_failover_support" is deprecated, the + // test should no longer be parameterized. + scoped_runtime_.mergeValues( + {{"envoy.restart_features.xds_failover_support", GetParam() ? "true" : "false"}}); + } void setup() { setup(rate_limit_settings_); } void setup(const RateLimitSettings& custom_rate_limit_settings) { GrpcMuxContext grpc_mux_context{ /*async_client_=*/std::unique_ptr(async_client_), + /*failover_async_client_=*/nullptr, /*dispatcher_=*/dispatcher_, /*service_method_=*/ *Protobuf::DescriptorPool::generated_pool()->FindMethodByName( @@ -122,6 +129,7 @@ class GrpcMuxImplTestBase : public testing::Test { return grpc_mux_->addWatch(type_url, resources, callbacks, resource_decoder, {}); } + TestScopedRuntime scoped_runtime_; NiceMock dispatcher_; NiceMock random_; Grpc::MockAsyncClient* async_client_; @@ -146,8 +154,10 @@ class GrpcMuxImplTest : public GrpcMuxImplTestBase { Event::SimulatedTimeSystem time_system_; }; +INSTANTIATE_TEST_SUITE_P(GrpcMuxImpl, GrpcMuxImplTest, ::testing::Bool()); + // Validate behavior when multiple type URL watches are maintained, watches are created/destroyed. -TEST_F(GrpcMuxImplTest, MultipleTypeUrlStreams) { +TEST_P(GrpcMuxImplTest, MultipleTypeUrlStreams) { setup(); InSequence s; @@ -168,7 +178,7 @@ TEST_F(GrpcMuxImplTest, MultipleTypeUrlStreams) { } // Validate behavior when multiple type URL watches are maintained and the stream is reset. -TEST_F(GrpcMuxImplTest, ResetStream) { +TEST_P(GrpcMuxImplTest, ResetStream) { InSequence s; auto* timer = new Event::MockTimer(&dispatcher_); @@ -212,7 +222,7 @@ TEST_F(GrpcMuxImplTest, ResetStream) { } // Validate pause-resume behavior. -TEST_F(GrpcMuxImplTest, PauseResume) { +TEST_P(GrpcMuxImplTest, PauseResume) { setup(); InSequence s; GrpcMuxWatchPtr foo1; @@ -247,7 +257,7 @@ TEST_F(GrpcMuxImplTest, PauseResume) { } // Validate behavior when type URL mismatches occur. -TEST_F(GrpcMuxImplTest, TypeUrlMismatch) { +TEST_P(GrpcMuxImplTest, TypeUrlMismatch) { setup(); auto invalid_response = std::make_unique(); @@ -288,7 +298,7 @@ TEST_F(GrpcMuxImplTest, TypeUrlMismatch) { expectSendMessage("type_url_foo", {}, ""); } -TEST_F(GrpcMuxImplTest, RpcErrorMessageTruncated) { +TEST_P(GrpcMuxImplTest, RpcErrorMessageTruncated) { setup(); auto invalid_response = std::make_unique(); InSequence s; @@ -350,7 +360,7 @@ resourceWithEmptyTtl(envoy::config::endpoint::v3::ClusterLoadAssignment& cla) { return resource; } // Validates the behavior when the TTL timer expires. -TEST_F(GrpcMuxImplTest, ResourceTTL) { +TEST_P(GrpcMuxImplTest, ResourceTTL) { setup(); time_system_.setSystemTime(std::chrono::seconds(0)); @@ -486,7 +496,7 @@ TEST_F(GrpcMuxImplTest, ResourceTTL) { } // Checks that the control plane identifier is logged -TEST_F(GrpcMuxImplTest, LogsControlPlaneIndentifier) { +TEST_P(GrpcMuxImplTest, LogsControlPlaneIndentifier) { setup(); std::string type_url = "foo"; @@ -520,7 +530,7 @@ TEST_F(GrpcMuxImplTest, LogsControlPlaneIndentifier) { } // Validate behavior when watches has an unknown resource name. -TEST_F(GrpcMuxImplTest, WildcardWatch) { +TEST_P(GrpcMuxImplTest, WildcardWatch) { setup(); const std::string& type_url = Config::TypeUrl::get().ClusterLoadAssignment; @@ -552,7 +562,7 @@ TEST_F(GrpcMuxImplTest, WildcardWatch) { } // Validate behavior when watches specify resources (potentially overlapping). -TEST_F(GrpcMuxImplTest, WatchDemux) { +TEST_P(GrpcMuxImplTest, WatchDemux) { setup(); // We will not require InSequence here: an update that causes multiple onConfigUpdates // causes them in an indeterminate order, based on the whims of the hash map. @@ -642,7 +652,7 @@ TEST_F(GrpcMuxImplTest, WatchDemux) { } // Validate behavior when we have multiple watchers that send empty updates. -TEST_F(GrpcMuxImplTest, MultipleWatcherWithEmptyUpdates) { +TEST_P(GrpcMuxImplTest, MultipleWatcherWithEmptyUpdates) { setup(); InSequence s; const std::string& type_url = Config::TypeUrl::get().ClusterLoadAssignment; @@ -665,7 +675,7 @@ TEST_F(GrpcMuxImplTest, MultipleWatcherWithEmptyUpdates) { } // Validate behavior when we have Single Watcher that sends Empty updates. -TEST_F(GrpcMuxImplTest, SingleWatcherWithEmptyUpdates) { +TEST_P(GrpcMuxImplTest, SingleWatcherWithEmptyUpdates) { setup(); const std::string& type_url = Config::TypeUrl::get().Cluster; NiceMock foo_callbacks; @@ -695,8 +705,11 @@ class GrpcMuxImplTestWithMockTimeSystem : public GrpcMuxImplTestBase { Event::DelegatingTestTimeSystem mock_time_system_; }; +INSTANTIATE_TEST_SUITE_P(GrpcMuxImplTestWithMockTimeSystem, GrpcMuxImplTestWithMockTimeSystem, + ::testing::Bool()); + // Verifies that rate limiting is not enforced with defaults. -TEST_F(GrpcMuxImplTestWithMockTimeSystem, TooManyRequestsWithDefaultSettings) { +TEST_P(GrpcMuxImplTestWithMockTimeSystem, TooManyRequestsWithDefaultSettings) { auto ttl_timer = new Event::MockTimer(&dispatcher_); // Retry timer, @@ -734,7 +747,7 @@ TEST_F(GrpcMuxImplTestWithMockTimeSystem, TooManyRequestsWithDefaultSettings) { } // Verifies that default rate limiting is enforced with empty RateLimitSettings. -TEST_F(GrpcMuxImplTest, TooManyRequestsWithEmptyRateLimitSettings) { +TEST_P(GrpcMuxImplTest, TooManyRequestsWithEmptyRateLimitSettings) { // Validate that request drain timer is created. auto ttl_timer = new Event::MockTimer(&dispatcher_); @@ -791,7 +804,7 @@ TEST_F(GrpcMuxImplTest, TooManyRequestsWithEmptyRateLimitSettings) { } // Verifies that rate limiting is enforced with custom RateLimitSettings. -TEST_F(GrpcMuxImplTest, TooManyRequestsWithCustomRateLimitSettings) { +TEST_P(GrpcMuxImplTest, TooManyRequestsWithCustomRateLimitSettings) { // Validate that request drain timer is created. // TTL timer. @@ -844,7 +857,7 @@ TEST_F(GrpcMuxImplTest, TooManyRequestsWithCustomRateLimitSettings) { } // Verifies that a message with no resources is accepted. -TEST_F(GrpcMuxImplTest, UnwatchedTypeAcceptsEmptyResources) { +TEST_P(GrpcMuxImplTest, UnwatchedTypeAcceptsEmptyResources) { setup(); EXPECT_CALL(*async_client_, startRaw(_, _, _, _)).WillOnce(Return(&async_stream_)); @@ -884,7 +897,7 @@ TEST_F(GrpcMuxImplTest, UnwatchedTypeAcceptsEmptyResources) { // are accompanied by interesting ones. // Note: this was previously "rejects", not "accepts". See // https://github.com/envoyproxy/envoy/pull/8350#discussion_r328218220 for discussion. -TEST_F(GrpcMuxImplTest, UnwatchedTypeAcceptsResources) { +TEST_P(GrpcMuxImplTest, UnwatchedTypeAcceptsResources) { setup(); EXPECT_CALL(*async_client_, startRaw(_, _, _, _)).WillOnce(Return(&async_stream_)); const std::string& type_url = Config::TypeUrl::get().ClusterLoadAssignment; @@ -907,10 +920,11 @@ TEST_F(GrpcMuxImplTest, UnwatchedTypeAcceptsResources) { grpc_mux_->onDiscoveryResponse(std::move(response), control_plane_stats_); } -TEST_F(GrpcMuxImplTest, BadLocalInfoEmptyClusterName) { +TEST_P(GrpcMuxImplTest, BadLocalInfoEmptyClusterName) { EXPECT_CALL(local_info_, clusterName()).WillOnce(ReturnRef(EMPTY_STRING)); GrpcMuxContext grpc_mux_context{ /*async_client_=*/std::unique_ptr(async_client_), + /*failover_async_client_=*/nullptr, /*dispatcher_=*/dispatcher_, /*service_method_=*/ *Protobuf::DescriptorPool::generated_pool()->FindMethodByName( @@ -932,10 +946,11 @@ TEST_F(GrpcMuxImplTest, BadLocalInfoEmptyClusterName) { "--service-node and --service-cluster options."); } -TEST_F(GrpcMuxImplTest, BadLocalInfoEmptyNodeName) { +TEST_P(GrpcMuxImplTest, BadLocalInfoEmptyNodeName) { EXPECT_CALL(local_info_, nodeName()).WillOnce(ReturnRef(EMPTY_STRING)); GrpcMuxContext grpc_mux_context{ /*async_client_=*/std::unique_ptr(async_client_), + /*failover_async_client_=*/nullptr, /*dispatcher_=*/dispatcher_, /*service_method_=*/ *Protobuf::DescriptorPool::generated_pool()->FindMethodByName( @@ -958,7 +973,7 @@ TEST_F(GrpcMuxImplTest, BadLocalInfoEmptyNodeName) { } // Validate that a valid resource decoder is used after removing a subscription. -TEST_F(GrpcMuxImplTest, ValidResourceDecoderAfterRemoval) { +TEST_P(GrpcMuxImplTest, ValidResourceDecoderAfterRemoval) { setup(); const std::string& type_url = Config::TypeUrl::get().ClusterLoadAssignment; @@ -1036,7 +1051,7 @@ TEST_F(GrpcMuxImplTest, ValidResourceDecoderAfterRemoval) { } // Validate behavior when dynamic context parameters are updated. -TEST_F(GrpcMuxImplTest, DynamicContextParameters) { +TEST_P(GrpcMuxImplTest, DynamicContextParameters) { setup(); InSequence s; auto foo = grpc_mux_->addWatch("foo", {"x", "y"}, callbacks_, resource_decoder_, {}); @@ -1058,10 +1073,11 @@ TEST_F(GrpcMuxImplTest, DynamicContextParameters) { expectSendMessage("foo", {}, "", false); } -TEST_F(GrpcMuxImplTest, AllMuxesStateTest) { +TEST_P(GrpcMuxImplTest, AllMuxesStateTest) { setup(); GrpcMuxContext grpc_mux_context{ /*async_client_=*/std::unique_ptr(), + /*failover_async_client_=*/nullptr, /*dispatcher_=*/dispatcher_, /*service_method_=*/ *Protobuf::DescriptorPool::generated_pool()->FindMethodByName( @@ -1085,20 +1101,20 @@ TEST_F(GrpcMuxImplTest, AllMuxesStateTest) { } // Validates that the EDS cache getter returns the cache. -TEST_F(GrpcMuxImplTest, EdsResourcesCacheForEds) { +TEST_P(GrpcMuxImplTest, EdsResourcesCacheForEds) { eds_resources_cache_ = new NiceMock(); setup(); EXPECT_TRUE(grpc_mux_->edsResourcesCache().has_value()); } // Validates that the EDS cache getter returns empty if there is no cache. -TEST_F(GrpcMuxImplTest, EdsResourcesCacheForEdsNoCache) { +TEST_P(GrpcMuxImplTest, EdsResourcesCacheForEdsNoCache) { setup(); EXPECT_FALSE(grpc_mux_->edsResourcesCache().has_value()); } // Validate that an EDS resource is cached if there's a cache. -TEST_F(GrpcMuxImplTest, CacheEdsResource) { +TEST_P(GrpcMuxImplTest, CacheEdsResource) { // Create the cache that will also be passed to the GrpcMux object via setup(). eds_resources_cache_ = new NiceMock(); setup(); @@ -1140,7 +1156,7 @@ TEST_F(GrpcMuxImplTest, CacheEdsResource) { // Validate that an update to an EDS resource watcher is reflected in the cache, // if there's a cache. -TEST_F(GrpcMuxImplTest, UpdateCacheEdsResource) { +TEST_P(GrpcMuxImplTest, UpdateCacheEdsResource) { // Create the cache that will also be passed to the GrpcMux object via setup(). eds_resources_cache_ = new NiceMock(); setup(); @@ -1187,7 +1203,7 @@ TEST_F(GrpcMuxImplTest, UpdateCacheEdsResource) { // Validate that adding and removing watchers reflects on the cache changes, // if there's a cache. -TEST_F(GrpcMuxImplTest, AddRemoveSubscriptions) { +TEST_P(GrpcMuxImplTest, AddRemoveSubscriptions) { // Create the cache that will also be passed to the GrpcMux object via setup(). eds_resources_cache_ = new NiceMock(); setup(); @@ -1312,9 +1328,9 @@ TEST(UnifiedSotwGrpcMuxFactoryTest, InvalidRateLimit) { ads_config.mutable_rate_limit_settings()->mutable_max_tokens()->set_value(100); ads_config.mutable_rate_limit_settings()->mutable_fill_rate()->set_value( std::numeric_limits::quiet_NaN()); - EXPECT_THROW(factory->create(std::make_unique(), dispatcher, random, scope, - ads_config, local_info, nullptr, nullptr, absl::nullopt, - absl::nullopt, false), + EXPECT_THROW(factory->create(std::make_unique(), nullptr, dispatcher, + random, scope, ads_config, local_info, nullptr, nullptr, + absl::nullopt, absl::nullopt, false), EnvoyException); } @@ -1330,9 +1346,9 @@ TEST(UnifiedDeltaGrpcMuxFactoryTest, InvalidRateLimit) { ads_config.mutable_rate_limit_settings()->mutable_max_tokens()->set_value(100); ads_config.mutable_rate_limit_settings()->mutable_fill_rate()->set_value( std::numeric_limits::quiet_NaN()); - EXPECT_THROW(factory->create(std::make_unique(), dispatcher, random, scope, - ads_config, local_info, nullptr, nullptr, absl::nullopt, - absl::nullopt, false), + EXPECT_THROW(factory->create(std::make_unique(), nullptr, dispatcher, + random, scope, ads_config, local_info, nullptr, nullptr, + absl::nullopt, absl::nullopt, false), EnvoyException); } diff --git a/test/extensions/filters/http/ext_authz/ext_authz_integration_test.cc b/test/extensions/filters/http/ext_authz/ext_authz_integration_test.cc index e4fa04e707b8..a31789636957 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_integration_test.cc +++ b/test/extensions/filters/http/ext_authz/ext_authz_integration_test.cc @@ -176,6 +176,7 @@ class ExtAuthzGrpcIntegrationTest {"not-allowed", "nope"}, {"regex-food", "food"}, {"regex-fool", "fool"}, + {"disallow-mutation-downstream-req", "authz resp cannot set or append to this header"}, // If the below header exists in the downstream request, it is NOT copied in authz request. {Envoy::Extensions::Filters::Common::ExtAuthz::Headers::get().EnvoyAuthPartialBody.get(), "shouldn't be visible in authz request"}, @@ -330,6 +331,11 @@ class ExtAuthzGrpcIntegrationTest EXPECT_THAT(upstream_request_->headers(), Http::HeaderValueOf("x-envoy-auth-failure-mode-allowed", "true")); } + // Check that ext_authz didn't remove this downstream header which should be immune to + // mutations. + EXPECT_THAT(upstream_request_->headers(), + Http::HeaderValueOf("disallow-mutation-downstream-req", + "authz resp cannot set or append to this header")); for (const auto& header_to_add : opts.headers_to_add) { EXPECT_THAT(upstream_request_->headers(), @@ -616,6 +622,10 @@ class ExtAuthzGrpcIntegrationTest patterns: - prefix: allowed-prefix-denied + decoder_header_mutation_rules: + disallow_expression: + regex: ^disallow-mutation.* + with_request_body: max_request_bytes: 1024 allow_partial_message: true @@ -645,26 +655,27 @@ class ExtAuthzHttpIntegrationTest void initiateClientConnection() { auto conn = makeClientConnection(lookupPort("http")); codec_client_ = makeHttpConnection(std::move(conn)); - const auto headers = - Http::TestRequestHeaderMapImpl{{":method", "GET"}, - {":path", "/"}, - {":scheme", "http"}, - {":authority", "host"}, - {"x-case-sensitive-header", case_sensitive_header_value_}, - {"baz", "foo"}, - {"bat", "foo"}, - {"remove-me", "upstream-should-not-see-me"}, - {"x-duplicate", "one"}, - {"x-duplicate", "two"}, - {"x-duplicate", "three"}, - {"allowed-prefix-one", "one"}, - {"allowed-prefix-two", "two"}, - {"allowed-prefix-denied", "blah"}, - {"not-allowed", "nope"}, - {"authorization", "legit"}, - {"regex-food", "food"}, - {"regex-fool", "fool"}, - {"x-forwarded-for", "1.2.3.4"}}; + const auto headers = Http::TestRequestHeaderMapImpl{ + {":method", "GET"}, + {":path", "/"}, + {":scheme", "http"}, + {":authority", "host"}, + {"x-case-sensitive-header", case_sensitive_header_value_}, + {"baz", "foo"}, + {"bat", "foo"}, + {"remove-me", "upstream-should-not-see-me"}, + {"x-duplicate", "one"}, + {"x-duplicate", "two"}, + {"x-duplicate", "three"}, + {"allowed-prefix-one", "one"}, + {"allowed-prefix-two", "two"}, + {"allowed-prefix-denied", "blah"}, + {"not-allowed", "nope"}, + {"authorization", "legit"}, + {"regex-food", "food"}, + {"regex-fool", "fool"}, + {"disallow-mutation-downstream-req", "authz resp cannot set or append to this header"}, + {"x-forwarded-for", "1.2.3.4"}}; if (client_request_body_.empty()) { response_ = codec_client_->makeHeaderOnlyRequest(headers); } else { @@ -681,32 +692,19 @@ class ExtAuthzHttpIntegrationTest result = ext_authz_request_->waitForEndStream(*dispatcher_); RELEASE_ASSERT(result, result.message()); - EXPECT_EQ("one", ext_authz_request_->headers() - .get(Http::LowerCaseString(std::string("allowed-prefix-one")))[0] - ->value() - .getStringView()); - EXPECT_EQ("two", ext_authz_request_->headers() - .get(Http::LowerCaseString(std::string("allowed-prefix-two")))[0] - ->value() - .getStringView()); - EXPECT_EQ("legit", ext_authz_request_->headers() - .get(Http::LowerCaseString(std::string("authorization")))[0] - ->value() - .getStringView()); + EXPECT_THAT(ext_authz_request_->headers(), Http::HeaderValueOf("allowed-prefix-one", "one")); + EXPECT_THAT(ext_authz_request_->headers(), Http::HeaderValueOf("allowed-prefix-two", "two")); + EXPECT_THAT(ext_authz_request_->headers(), Http::HeaderValueOf("authorization", "legit")); + EXPECT_THAT(ext_authz_request_->headers(), Http::HeaderValueOf("regex-food", "food")); + EXPECT_THAT(ext_authz_request_->headers(), Http::HeaderValueOf("regex-fool", "fool")); + EXPECT_TRUE(ext_authz_request_->headers() .get(Http::LowerCaseString(std::string("not-allowed"))) .empty()); EXPECT_TRUE(ext_authz_request_->headers() .get(Http::LowerCaseString(std::string("allowed-prefix-denied"))) .empty()); - EXPECT_EQ("food", ext_authz_request_->headers() - .get(Http::LowerCaseString(std::string("regex-food")))[0] - ->value() - .getStringView()); - EXPECT_EQ("fool", ext_authz_request_->headers() - .get(Http::LowerCaseString(std::string("regex-fool")))[0] - ->value() - .getStringView()); + if (encodeRawHeaders()) { // Duplicate headers should NOT be merged. const auto duplicate = @@ -740,9 +738,12 @@ class ExtAuthzHttpIntegrationTest {":status", "200"}, {"baz", "baz"}, {"bat", "bar"}, + {"authz-add-disallow-mutation", "this should not be allowed due to disallow_expression"}, {"x-append-bat", "append-foo"}, {"x-append-bat", "append-bar"}, {"x-envoy-auth-headers-to-remove", "remove-me"}, + // Try to remove this header that should not be able to be removed. + {"x-envoy-auth-headers-to-remove", "disallow-mutation-downstream-req"}, }; ext_authz_request_->encodeHeaders(response_headers, true); } @@ -831,6 +832,11 @@ class ExtAuthzHttpIntegrationTest EXPECT_TRUE(upstream_request_->headers() .get(Http::LowerCaseString{"x-envoy-auth-headers-to-remove"}) .empty()); + // The side stream tried to add this header that violates the disallow_expression header + // mutation rule. Make sure it did not get added. + EXPECT_TRUE(upstream_request_->headers() + .get(Http::LowerCaseString{"authz-add-disallow-mutation"}) + .empty()); upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}}, true); ASSERT_TRUE(response_->waitForEndStream()); @@ -847,10 +853,16 @@ class ExtAuthzHttpIntegrationTest std::string client_request_body_; const Http::LowerCaseString case_sensitive_header_name_{"x-case-sensitive-header"}; const std::string case_sensitive_header_value_{"Case-Sensitive"}; + // TODO: mutation rule const std::string legacy_default_config_ = R"EOF( disallowed_headers: patterns: - prefix: allowed-prefix-denied + + decoder_header_mutation_rules: + disallow_expression: + regex: disallow-mutation.* + http_service: server_uri: uri: "ext_authz:9000" @@ -889,6 +901,10 @@ class ExtAuthzHttpIntegrationTest patterns: - prefix: allowed-prefix-denied + decoder_header_mutation_rules: + disallow_expression: + regex: disallow-mutation.* + http_service: server_uri: uri: "ext_authz:9000" @@ -977,13 +993,20 @@ TEST_P(ExtAuthzGrpcIntegrationTest, CheckAfterBufferingComplete) { // Start a client connection and start request. Http::TestRequestHeaderMapImpl headers{ - {":method", "POST"}, {":path", "/test"}, - {":scheme", "http"}, {":authority", "host"}, - {"x-duplicate", "one"}, {"x-duplicate", "two"}, - {"x-duplicate", "three"}, {"allowed-prefix-one", "one"}, - {"allowed-prefix-two", "two"}, {"allowed-prefix-denied", "will not be sent"}, - {"not-allowed", "nope"}, {"regex-food", "food"}, - {"regex-fool", "fool"}}; + {":method", "POST"}, + {":path", "/test"}, + {":scheme", "http"}, + {":authority", "host"}, + {"x-duplicate", "one"}, + {"x-duplicate", "two"}, + {"x-duplicate", "three"}, + {"allowed-prefix-one", "one"}, + {"allowed-prefix-two", "two"}, + {"allowed-prefix-denied", "will not be sent"}, + {"not-allowed", "nope"}, + {"regex-food", "food"}, + {"regex-fool", "fool"}, + {"disallow-mutation-downstream-req", "authz resp cannot set or append to this header"}}; auto conn = makeClientConnection(lookupPort("http")); codec_client_ = makeHttpConnection(std::move(conn)); @@ -1156,13 +1179,21 @@ TEST_P(ExtAuthzGrpcIntegrationTest, FailureModeAllowNonUtf8) { invalid_unicode.append(1, char(0x28)); invalid_unicode.append("valid_suffix"); Http::TestRequestHeaderMapImpl headers{ - {":method", "POST"}, {":path", "/test"}, - {":scheme", "http"}, {":authority", "host"}, - {"x-bypass", invalid_unicode}, {"allowed-prefix-one", "one"}, - {"allowed-prefix-two", "two"}, {"allowed-prefix-denied", "denied"}, - {"not-allowed", "nope"}, {"x-duplicate", "one"}, - {"x-duplicate", "two"}, {"x-duplicate", "three"}, - {"regex-food", "food"}, {"regex-fool", "fool"}}; + {":method", "POST"}, + {":path", "/test"}, + {":scheme", "http"}, + {":authority", "host"}, + {"x-bypass", invalid_unicode}, + {"allowed-prefix-one", "one"}, + {"allowed-prefix-two", "two"}, + {"allowed-prefix-denied", "denied"}, + {"not-allowed", "nope"}, + {"x-duplicate", "one"}, + {"x-duplicate", "two"}, + {"x-duplicate", "three"}, + {"regex-food", "food"}, + {"regex-fool", "fool"}, + {"disallow-mutation-downstream-req", "authz resp cannot set or append to this header"}}; response_ = codec_client_->makeRequestWithBody(headers, {}); @@ -1321,6 +1352,32 @@ TEST_P(ExtAuthzHttpIntegrationTest, DirectReponse) { EXPECT_EQ("204", response_->headers().Status()->value().getStringView()); } +// Test exceeding the async client buffer limit. +TEST_P(ExtAuthzHttpIntegrationTest, ErrorReponseWithDefultBufferLimit) { + initializeConfig(false, /*failure_mode_allow=*/false); + config_helper_.addRuntimeOverride("http.async_response_buffer_limit", "1024"); + + HttpIntegrationTest::initialize(); + initiateClientConnection(); + waitForExtAuthzRequest(); + + Http::TestResponseHeaderMapImpl response_headers{ + {":status", "200"}, + {"baz", "baz"}, + {"bat", "bar"}, + {"x-append-bat", "append-foo"}, + {"x-append-bat", "append-bar"}, + {"x-envoy-auth-headers-to-remove", "remove-me"}, + }; + ext_authz_request_->encodeHeaders(response_headers, false); + ext_authz_request_->encodeData(2048, true); + + ASSERT_TRUE(response_->waitForEndStream()); + EXPECT_TRUE(response_->complete()); + // A forbidden response since the onFailure is called due to the async client buffer limit. + EXPECT_EQ("403", response_->headers().Status()->value().getStringView()); +} + // (uses new config for allowed_headers). TEST_P(ExtAuthzHttpIntegrationTest, RedirectResponse) { config_helper_.addConfigModifier( diff --git a/test/extensions/filters/http/ext_authz/ext_authz_test.cc b/test/extensions/filters/http/ext_authz/ext_authz_test.cc index 042d640bcd8c..53f2fdf33583 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_test.cc +++ b/test/extensions/filters/http/ext_authz/ext_authz_test.cc @@ -388,6 +388,329 @@ TEST_F(InvalidMutationTest, QueryParametersToSetValue) { testResponse(response); } +struct DecoderHeaderMutationRulesTestOpts { + absl::optional rules; + bool expect_reject_response = false; + Filters::Common::ExtAuthz::UnsafeHeaderVector allowed_headers_to_add; + Filters::Common::ExtAuthz::UnsafeHeaderVector disallowed_headers_to_add; + Filters::Common::ExtAuthz::UnsafeHeaderVector allowed_headers_to_append; + Filters::Common::ExtAuthz::UnsafeHeaderVector disallowed_headers_to_append; + Filters::Common::ExtAuthz::UnsafeHeaderVector allowed_headers_to_set; + Filters::Common::ExtAuthz::UnsafeHeaderVector disallowed_headers_to_set; + std::vector allowed_headers_to_remove; + std::vector disallowed_headers_to_remove; +}; +class DecoderHeaderMutationRulesTest + : public HttpFilterTestBase> { +public: + void runTest(DecoderHeaderMutationRulesTestOpts opts) { + InSequence s; + + envoy::extensions::filters::http::ext_authz::v3::ExtAuthz proto_config{}; + TestUtility::loadFromYaml(R"( + transport_api_version: V3 + grpc_service: + envoy_grpc: + cluster_name: "ext_authz_server" + failure_mode_allow: false + )", + proto_config); + if (opts.rules != std::nullopt) { + *(proto_config.mutable_decoder_header_mutation_rules()) = *opts.rules; + } + + initialize(proto_config); + + // Simulate a downstream request. + populateRequestHeadersFromOpts(opts); + + ON_CALL(decoder_filter_callbacks_, connection()) + .WillByDefault(Return(OptRef{connection_})); + connection_.stream_info_.downstream_connection_info_provider_->setRemoteAddress(addr_); + connection_.stream_info_.downstream_connection_info_provider_->setLocalAddress(addr_); + EXPECT_CALL(*client_, check(_, _, _, _)) + .WillOnce(Invoke( + [&](Filters::Common::ExtAuthz::RequestCallbacks& callbacks, + const envoy::service::auth::v3::CheckRequest&, Tracing::Span&, + const StreamInfo::StreamInfo&) -> void { request_callbacks_ = &callbacks; })); + + EXPECT_EQ(Http::FilterHeadersStatus::StopAllIterationAndWatermark, + filter_->decodeHeaders(request_headers_, false)); + if (opts.expect_reject_response) { + EXPECT_CALL(decoder_filter_callbacks_, encodeHeaders_(_, true)) + .WillOnce(Invoke([&](const Http::ResponseHeaderMap& headers, bool) -> void { + EXPECT_EQ(headers.getStatusValue(), + std::to_string(enumToInt(Http::Code::InternalServerError))); + })); + } else { + EXPECT_CALL(decoder_filter_callbacks_, continueDecoding()); + } + + // Construct authz response from opts. + Filters::Common::ExtAuthz::Response response = getResponseFromOpts(opts); + + request_callbacks_->onComplete(std::make_unique(response)); + + if (!opts.expect_reject_response) { + // Now make sure the downstream header is / is not there depending on the test. + checkRequestHeadersFromOpts(opts); + } + } + + void populateRequestHeadersFromOpts(const DecoderHeaderMutationRulesTestOpts& opts) { + for (const auto& [key, _] : opts.allowed_headers_to_set) { + request_headers_.addCopy(Http::LowerCaseString(key), "will be overridden"); + } + for (const auto& [key, _] : opts.disallowed_headers_to_set) { + request_headers_.addCopy(Http::LowerCaseString(key), "will not be overridden"); + } + + for (const auto& [key, _] : opts.allowed_headers_to_append) { + request_headers_.addCopy(Http::LowerCaseString(key), "will be appended to"); + } + for (const auto& [key, _] : opts.disallowed_headers_to_append) { + request_headers_.addCopy(Http::LowerCaseString(key), "will not be appended to"); + } + + for (const auto& key : opts.allowed_headers_to_remove) { + request_headers_.addCopy(Http::LowerCaseString(key), "will be removed"); + } + for (const auto& key : opts.disallowed_headers_to_remove) { + request_headers_.addCopy(Http::LowerCaseString(key), "will not be removed"); + } + } + + void checkRequestHeadersFromOpts(const DecoderHeaderMutationRulesTestOpts& opts) { + for (const auto& [key, value] : opts.allowed_headers_to_add) { + EXPECT_EQ(request_headers_.get_(Http::LowerCaseString(key)), value) + << "(key: '" << key << "')"; + } + for (const auto& [key, _] : opts.disallowed_headers_to_add) { + EXPECT_FALSE(request_headers_.has(Http::LowerCaseString(key))) << "(key: '" << key << "')"; + } + + for (const auto& [key, value] : opts.allowed_headers_to_set) { + EXPECT_EQ(request_headers_.get_(Http::LowerCaseString(key)), value) + << "(key: '" << key << "')"; + } + for (const auto& [key, _] : opts.disallowed_headers_to_set) { + EXPECT_EQ(request_headers_.get_(Http::LowerCaseString(key)), "will not be overridden") + << "(key: '" << key << "')"; + } + + for (const auto& [key, value] : opts.allowed_headers_to_append) { + EXPECT_EQ(request_headers_.get_(Http::LowerCaseString(key)), + absl::StrCat("will be appended to,", value)) + << "(key: '" << key << "')"; + } + for (const auto& [key, value] : opts.disallowed_headers_to_append) { + EXPECT_EQ(request_headers_.get_(Http::LowerCaseString(key)), "will not be appended to") + << "(key: '" << key << "')"; + } + + for (const auto& key : opts.allowed_headers_to_remove) { + EXPECT_FALSE(request_headers_.has(Http::LowerCaseString(key))) << "(key: '" << key << "')"; + } + for (const auto& key : opts.disallowed_headers_to_remove) { + EXPECT_EQ(request_headers_.get_(Http::LowerCaseString(key)), "will not be removed") + << "(key: '" << key << "')"; + } + } + + static Filters::Common::ExtAuthz::Response + getResponseFromOpts(const DecoderHeaderMutationRulesTestOpts& opts) { + Filters::Common::ExtAuthz::Response response; + response.status = Filters::Common::ExtAuthz::CheckStatus::OK; + + for (const auto& vec : {opts.allowed_headers_to_add, opts.disallowed_headers_to_add}) { + for (const auto& [key, value] : vec) { + response.headers_to_add.emplace_back(key, value); + } + } + + for (const auto& vec : {opts.allowed_headers_to_set, opts.disallowed_headers_to_set}) { + for (const auto& [key, value] : vec) { + response.headers_to_set.emplace_back(key, value); + } + } + + for (const auto& vec : {opts.allowed_headers_to_append, opts.disallowed_headers_to_append}) { + for (const auto& [key, value] : vec) { + response.headers_to_append.emplace_back(key, value); + } + } + + for (const auto& vec : {opts.allowed_headers_to_remove, opts.disallowed_headers_to_remove}) { + for (const auto& key : vec) { + response.headers_to_remove.emplace_back(key); + } + } + + return response; + } +}; + +// If decoder_header_mutation_rules is empty, there should be no additional restrictions to +// ext_authz header mutations. +TEST_F(DecoderHeaderMutationRulesTest, EmptyConfig) { + DecoderHeaderMutationRulesTestOpts opts; + opts.allowed_headers_to_add = {{":authority", "google"}}; + opts.allowed_headers_to_append = {{"normal", "one"}, {":fake-pseudo-header", "append me"}}; + opts.allowed_headers_to_set = {{"x-envoy-whatever", "override"}}; + opts.allowed_headers_to_remove = {"delete-me"}; + // These are not allowed not because of the header mutation rules config, but because ext_authz + // doesn't allow the removable of headers starting with `:` anyway. + opts.disallowed_headers_to_remove = {":method", ":fake-pseudoheader"}; + runTest(opts); +} + +// Test behavior when the rules field exists but all sub-fields are their default values (should be +// exactly the same as above) +TEST_F(DecoderHeaderMutationRulesTest, ExplicitDefaultConfig) { + DecoderHeaderMutationRulesTestOpts opts; + opts.rules = envoy::config::common::mutation_rules::v3::HeaderMutationRules(); + opts.disallowed_headers_to_add = {{":authority", "google"}}; + opts.allowed_headers_to_append = {{"normal", "one"}}; + opts.disallowed_headers_to_append = {{":fake-pseudo-header", "append me"}}; + opts.disallowed_headers_to_set = {{"x-envoy-whatever", "override"}}; + opts.allowed_headers_to_remove = {"delete-me"}; + // These are not allowed not because of the header mutation rules config, but because ext_authz + // doesn't allow the removable of headers starting with `:` anyway. + opts.disallowed_headers_to_remove = {":method", ":fake-pseudoheader"}; + runTest(opts); +} + +TEST_F(DecoderHeaderMutationRulesTest, DisallowAll) { + DecoderHeaderMutationRulesTestOpts opts; + opts.rules = envoy::config::common::mutation_rules::v3::HeaderMutationRules(); + opts.rules->mutable_disallow_all()->set_value(true); + + opts.disallowed_headers_to_add = {{"cant-add-me", "sad"}}; + opts.disallowed_headers_to_append = {{"cant-append-to-me", "fail"}}; + opts.disallowed_headers_to_set = {{"cant-override-me", "nope"}}; + opts.disallowed_headers_to_remove = {"cant-delete-me"}; + runTest(opts); +} + +TEST_F(DecoderHeaderMutationRulesTest, RejectResponseAdd) { + DecoderHeaderMutationRulesTestOpts opts; + opts.rules = envoy::config::common::mutation_rules::v3::HeaderMutationRules(); + opts.rules->mutable_disallow_all()->set_value(true); + opts.rules->mutable_disallow_is_error()->set_value(true); + opts.expect_reject_response = true; + + opts.disallowed_headers_to_add = {{"cant-add-me", "sad"}}; + runTest(opts); +} + +TEST_F(DecoderHeaderMutationRulesTest, RejectResponseAppend) { + DecoderHeaderMutationRulesTestOpts opts; + opts.rules = envoy::config::common::mutation_rules::v3::HeaderMutationRules(); + opts.rules->mutable_disallow_all()->set_value(true); + opts.rules->mutable_disallow_is_error()->set_value(true); + opts.expect_reject_response = true; + + opts.disallowed_headers_to_append = {{"cant-append-to-me", "fail"}}; + runTest(opts); +} + +TEST_F(DecoderHeaderMutationRulesTest, RejectResponseAppendPseudoheader) { + DecoderHeaderMutationRulesTestOpts opts; + opts.rules = envoy::config::common::mutation_rules::v3::HeaderMutationRules(); + opts.rules->mutable_disallow_is_error()->set_value(true); + opts.expect_reject_response = true; + + opts.disallowed_headers_to_append = {{":fake-pseudo-header", "fail"}}; + runTest(opts); +} + +TEST_F(DecoderHeaderMutationRulesTest, RejectResponseSet) { + DecoderHeaderMutationRulesTestOpts opts; + opts.rules = envoy::config::common::mutation_rules::v3::HeaderMutationRules(); + opts.rules->mutable_disallow_all()->set_value(true); + opts.rules->mutable_disallow_is_error()->set_value(true); + opts.expect_reject_response = true; + + opts.disallowed_headers_to_set = {{"cant-override-me", "nope"}}; + runTest(opts); +} + +TEST_F(DecoderHeaderMutationRulesTest, RejectResponseRemove) { + DecoderHeaderMutationRulesTestOpts opts; + opts.rules = envoy::config::common::mutation_rules::v3::HeaderMutationRules(); + opts.rules->mutable_disallow_all()->set_value(true); + opts.rules->mutable_disallow_is_error()->set_value(true); + opts.expect_reject_response = true; + + opts.disallowed_headers_to_remove = {"cant-delete-me"}; + runTest(opts); +} + +TEST_F(DecoderHeaderMutationRulesTest, RejectResponseRemovePseudoHeader) { + DecoderHeaderMutationRulesTestOpts opts; + opts.rules = envoy::config::common::mutation_rules::v3::HeaderMutationRules(); + opts.rules->mutable_disallow_is_error()->set_value(true); + opts.expect_reject_response = true; + + opts.disallowed_headers_to_remove = {":fake-pseudo-header"}; + runTest(opts); +} + +TEST_F(DecoderHeaderMutationRulesTest, DisallowExpression) { + DecoderHeaderMutationRulesTestOpts opts; + opts.rules = envoy::config::common::mutation_rules::v3::HeaderMutationRules(); + opts.rules->mutable_disallow_expression()->set_regex("^x-example-.*"); + + opts.allowed_headers_to_add = {{"add-me-one", "one"}, {"add-me-two", "two"}}; + opts.disallowed_headers_to_add = {{"x-example-add", "nope"}}; + opts.allowed_headers_to_append = {{"append-to-me", "appended value"}}; + opts.disallowed_headers_to_append = {{"x-example-append", "no sir"}}; + opts.allowed_headers_to_set = {{"override-me", "new value"}}; + opts.disallowed_headers_to_set = {{"x-example-set", "no can do"}}; + opts.allowed_headers_to_remove = {"delete-me"}; + opts.disallowed_headers_to_remove = {"x-example-remove"}; + runTest(opts); +} + +// Tests that allow_expression overrides other settings (except disallow, which is tested +// elsewhere). +TEST_F(DecoderHeaderMutationRulesTest, AllowExpression) { + DecoderHeaderMutationRulesTestOpts opts; + opts.rules = envoy::config::common::mutation_rules::v3::HeaderMutationRules(); + opts.rules->mutable_disallow_all()->set_value(true); + opts.rules->mutable_allow_expression()->set_regex("^x-allow-.*"); + + opts.allowed_headers_to_add = {{"x-allow-add-me-one", "one"}, {"x-allow-add-me-two", "two"}}; + opts.disallowed_headers_to_add = {{"not-allowed", "nope"}}; + opts.allowed_headers_to_append = {{"x-allow-append-to-me", "appended value"}}; + opts.disallowed_headers_to_append = {{"xx-allow-wrong-prefix", "no sir"}}; + opts.allowed_headers_to_set = {{"x-allow-override-me", "new value"}}; + opts.disallowed_headers_to_set = {{"cant-set-me", "no can do"}}; + opts.allowed_headers_to_remove = {"x-allow-delete-me"}; + opts.disallowed_headers_to_remove = {"cannot-remove"}; + runTest(opts); +} + +// Tests that disallow_expression overrides allow_expression. +TEST_F(DecoderHeaderMutationRulesTest, OverlappingAllowAndDisallowExpressions) { + DecoderHeaderMutationRulesTestOpts opts; + opts.rules = envoy::config::common::mutation_rules::v3::HeaderMutationRules(); + opts.rules->mutable_disallow_all()->set_value(true); + // Note the disallow expression's matches are a subset of the allow expression's matches. + opts.rules->mutable_allow_expression()->set_regex(".*allowed.*"); + opts.rules->mutable_disallow_expression()->set_regex(".*disallowed.*"); + + opts.allowed_headers_to_add = {{"allowed-add", "yes"}}; + opts.disallowed_headers_to_add = {{"disallowed-add", "nope"}}; + opts.allowed_headers_to_append = {{"allowed-append", "appended value"}}; + opts.disallowed_headers_to_append = {{"disallowed-append", "no sir"}}; + opts.allowed_headers_to_set = {{"allowed-set", "new value"}}; + opts.disallowed_headers_to_set = {{"disallowed-set", "no can do"}}; + opts.allowed_headers_to_remove = {"allowed-remove"}; + opts.disallowed_headers_to_remove = {"disallowed-remove"}; + runTest(opts); +} + // Test that the per route config is properly merged: more specific keys override previous keys. TEST_F(HttpFilterTest, MergeConfig) { envoy::extensions::filters::http::ext_authz::v3::ExtAuthzPerRoute settings; diff --git a/test/extensions/filters/http/ext_proc/tracer_test_filter.cc b/test/extensions/filters/http/ext_proc/tracer_test_filter.cc index d58c97372160..15c7b1b348ea 100644 --- a/test/extensions/filters/http/ext_proc/tracer_test_filter.cc +++ b/test/extensions/filters/http/ext_proc/tracer_test_filter.cc @@ -73,6 +73,10 @@ class Span : public Tracing::Span { /* not implemented */ return EMPTY_STRING; }; + std::string getSpanId() const { + /* not implemented */ + return EMPTY_STRING; + }; Tracing::SpanPtr spawnChild(const Tracing::Config&, const std::string& operation_name, SystemTime) { diff --git a/test/extensions/filters/http/oauth2/config_test.cc b/test/extensions/filters/http/oauth2/config_test.cc index 8abe960cd904..e16fb0ca9b9d 100644 --- a/test/extensions/filters/http/oauth2/config_test.cc +++ b/test/extensions/filters/http/oauth2/config_test.cc @@ -201,6 +201,64 @@ TEST(ConfigTest, WrongCookieName) { EnvoyException, "value does not match regex pattern"); } +TEST(ConfigTest, WrongCombinationOfPreserveAuthorizationAndForwardBearer) { + const std::string yaml = R"EOF( +config: + forward_bearer_token: true + preserve_authorization_header: true + token_endpoint: + cluster: foo + uri: oauth.com/token + timeout: 3s + credentials: + client_id: "secret" + token_secret: + name: token + hmac_secret: + name: hmac + cookie_names: + bearer_token: BearerToken + oauth_hmac: OauthHMAC + oauth_expires: OauthExpires + id_token: IdToken + refresh_token: RefreshToken + authorization_endpoint: https://oauth.com/oauth/authorize/ + redirect_uri: "%REQ(x-forwarded-proto)%://%REQ(:authority)%/callback" + redirect_path_matcher: + path: + exact: /callback + signout_path: + path: + exact: /signout + auth_scopes: + - user + - openid + - email + resources: + - oauth2-resource + - http://example.com + - https://example.com + )EOF"; + + OAuth2Config factory; + ProtobufTypes::MessagePtr proto_config = factory.createEmptyConfigProto(); + TestUtility::loadFromYaml(yaml, *proto_config); + NiceMock context; + context.server_factory_context_.cluster_manager_.initializeClusters({"foo"}, {}); + + // This returns non-nullptr for token_secret and hmac_secret. + auto& secret_manager = + context.server_factory_context_.cluster_manager_.cluster_manager_factory_.secretManager(); + ON_CALL(secret_manager, findStaticGenericSecretProvider(_)) + .WillByDefault(Return(std::make_shared( + envoy::extensions::transport_sockets::tls::v3::GenericSecret()))); + + EXPECT_THROW_WITH_REGEX( + factory.createFilterFactoryFromProto(*proto_config, "stats", context).status().IgnoreError(), + EnvoyException, + "invalid combination of forward_bearer_token and preserve_authorization_header"); +} + } // namespace Oauth2 } // namespace HttpFilters } // namespace Extensions diff --git a/test/extensions/filters/http/oauth2/filter_test.cc b/test/extensions/filters/http/oauth2/filter_test.cc index 99ee26e79585..78a49df1e710 100644 --- a/test/extensions/filters/http/oauth2/filter_test.cc +++ b/test/extensions/filters/http/oauth2/filter_test.cc @@ -112,7 +112,7 @@ class OAuth2Test : public testing::TestWithParam { ::envoy::extensions::filters::http::oauth2::v3::OAuth2Config_AuthType auth_type = ::envoy::extensions::filters::http::oauth2::v3::OAuth2Config_AuthType:: OAuth2Config_AuthType_URL_ENCODED_BODY, - int default_refresh_token_expires_in = 0) { + int default_refresh_token_expires_in = 0, bool preserve_authorization_header = false) { envoy::extensions::filters::http::oauth2::v3::OAuth2Config p; auto* endpoint = p.mutable_token_endpoint(); endpoint->set_cluster("auth.example.com"); @@ -123,6 +123,7 @@ class OAuth2Test : public testing::TestWithParam { p.set_authorization_endpoint("https://auth.example.com/oauth/authorize/"); p.mutable_signout_path()->mutable_path()->set_exact("/_signout"); p.set_forward_bearer_token(forward_bearer_token); + p.set_preserve_authorization_header(preserve_authorization_header); auto* useRefreshToken = p.mutable_use_refresh_token(); useRefreshToken->set_value(use_refresh_token); @@ -618,6 +619,54 @@ TEST_F(OAuth2Test, OAuthOkPassButInvalidToken) { EXPECT_EQ(scope_.counterFromString("test.oauth_success").value(), 1); } +/** + * Scenario: The OAuth filter receives a request with a foreign token in the Authorization + * header. This header should be forwarded when preserve authorization header is enabled + * and forwarding bearer token is disabled. + * + * Expected behavior: the filter should forward the foreign token and let the request proceed. + */ +TEST_F(OAuth2Test, OAuthOkPreserveForeignAuthHeader) { + init(getConfig(false /* forward_bearer_token */, true /* use_refresh_token */, + ::envoy::extensions::filters::http::oauth2::v3::OAuth2Config_AuthType:: + OAuth2Config_AuthType_URL_ENCODED_BODY /* encoded_body_type */, + 1200 /* default_refresh_token_expires_in */, + true /* preserve_authorization_header */)); + + Http::TestRequestHeaderMapImpl mock_request_headers{ + {Http::Headers::get().Path.get(), "/anypath"}, + {Http::Headers::get().Host.get(), "traffic.example.com"}, + {Http::Headers::get().Method.get(), Http::Headers::get().MethodValues.Get}, + {Http::Headers::get().Scheme.get(), "https"}, + {Http::CustomHeaders::get().Authorization.get(), "Bearer ValidAuthorizationHeader"}, + }; + + Http::TestRequestHeaderMapImpl expected_headers{ + {Http::Headers::get().Path.get(), "/anypath"}, + {Http::Headers::get().Host.get(), "traffic.example.com"}, + {Http::Headers::get().Method.get(), Http::Headers::get().MethodValues.Get}, + {Http::Headers::get().Scheme.get(), "https"}, + {Http::CustomHeaders::get().Authorization.get(), "Bearer ValidAuthorizationHeader"}, + }; + + // cookie-validation mocking + EXPECT_CALL(*validator_, setParams(_, _)); + EXPECT_CALL(*validator_, isValid()).WillOnce(Return(true)); + + // Sanitized return reference mocking + std::string legit_token{"legit_token"}; + EXPECT_CALL(*validator_, token()).WillRepeatedly(ReturnRef(legit_token)); + + EXPECT_EQ(Http::FilterHeadersStatus::Continue, + filter_->decodeHeaders(mock_request_headers, false)); + + // Ensure that existing OAuth forwarded headers got sanitized. + EXPECT_EQ(mock_request_headers, expected_headers); + + EXPECT_EQ(scope_.counterFromString("test.oauth_failure").value(), 0); + EXPECT_EQ(scope_.counterFromString("test.oauth_success").value(), 1); +} + /** * Scenario: The OAuth filter receives a request without valid OAuth cookies to a non-callback URL * (indicating that the user needs to re-validate cookies or get 401'd). diff --git a/test/extensions/filters/http/rbac/BUILD b/test/extensions/filters/http/rbac/BUILD index 4c397d1e0389..92e1d8ab3bbc 100644 --- a/test/extensions/filters/http/rbac/BUILD +++ b/test/extensions/filters/http/rbac/BUILD @@ -1,6 +1,8 @@ load( "//bazel:envoy_build_system.bzl", + "envoy_cc_fuzz_test", "envoy_package", + "envoy_proto_library", ) load( "//test/extensions:extensions_build_system.bzl", @@ -89,3 +91,28 @@ envoy_extension_cc_mock( "@envoy_api//envoy/extensions/filters/http/rbac/v3:pkg_cc_proto", ], ) + +envoy_proto_library( + name = "rbac_filter_fuzz_proto", + srcs = ["rbac_filter_fuzz.proto"], + deps = [ + "//test/fuzz:common_proto", + "@envoy_api//envoy/extensions/filters/http/rbac/v3:pkg", + ], +) + +envoy_cc_fuzz_test( + name = "rbac_filter_fuzz_test", + srcs = ["rbac_filter_fuzz_test.cc"], + corpus = "rbac_filter_corpus", + deps = [ + ":rbac_filter_fuzz_proto_cc_proto", + "//source/common/buffer:buffer_lib", + "//source/extensions/filters/http/rbac:rbac_filter_lib", + "//test/extensions/filters/http/common/fuzz:http_filter_fuzzer_lib", + "//test/mocks/http:http_mocks", + "//test/mocks/runtime:runtime_mocks", + "//test/mocks/server:server_factory_context_mocks", + "@envoy_api//envoy/extensions/filters/http/rbac/v3:pkg_cc_proto", + ], +) diff --git a/test/extensions/filters/http/rbac/rbac_filter_corpus/example b/test/extensions/filters/http/rbac/rbac_filter_corpus/example new file mode 100644 index 000000000000..7da000f76a2f --- /dev/null +++ b/test/extensions/filters/http/rbac/rbac_filter_corpus/example @@ -0,0 +1,7 @@ +config { +} +request_data { + http_body { + data: "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%" + } +} diff --git a/test/extensions/filters/http/rbac/rbac_filter_corpus/no_registered_implementation b/test/extensions/filters/http/rbac/rbac_filter_corpus/no_registered_implementation new file mode 100644 index 000000000000..fc6e9dc504cc --- /dev/null +++ b/test/extensions/filters/http/rbac/rbac_filter_corpus/no_registered_implementation @@ -0,0 +1,14 @@ +config { + matcher { + on_no_match { + action { + name: "8888888888888888888888888888888888888888888888" + typed_config { + value: "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" + } + } + } + } +} +request_data { +} diff --git a/test/extensions/filters/http/rbac/rbac_filter_corpus/track_per_rule_stats b/test/extensions/filters/http/rbac/rbac_filter_corpus/track_per_rule_stats new file mode 100644 index 000000000000..7146655025b9 --- /dev/null +++ b/test/extensions/filters/http/rbac/rbac_filter_corpus/track_per_rule_stats @@ -0,0 +1,5 @@ +config { + track_per_rule_stats: true +} +request_data { +} diff --git a/test/extensions/filters/http/rbac/rbac_filter_fuzz.proto b/test/extensions/filters/http/rbac/rbac_filter_fuzz.proto new file mode 100644 index 000000000000..0a87f76dd07b --- /dev/null +++ b/test/extensions/filters/http/rbac/rbac_filter_fuzz.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; +package envoy.extensions.filters.http.rbac; + +import "envoy/extensions/filters/http/rbac/v3/rbac.proto"; +import "test/fuzz/common.proto"; +import "validate/validate.proto"; + +// We only fuzz a single request per iteration. +message RbacTestCase { + envoy.extensions.filters.http.rbac.v3.RBAC config = 1 + [(validate.rules).message = {required: true}]; + test.fuzz.HttpData request_data = 2 [(validate.rules).message = {required: true}]; +} diff --git a/test/extensions/filters/http/rbac/rbac_filter_fuzz_test.cc b/test/extensions/filters/http/rbac/rbac_filter_fuzz_test.cc new file mode 100644 index 000000000000..c8e7660278ee --- /dev/null +++ b/test/extensions/filters/http/rbac/rbac_filter_fuzz_test.cc @@ -0,0 +1,88 @@ +#include "envoy/extensions/filters/http/rbac/v3/rbac.pb.h" + +#include "source/common/buffer/buffer_impl.h" +#include "source/common/network/address_impl.h" +#include "source/extensions/filters/http/rbac/rbac_filter.h" + +#include "test/extensions/filters/http/common/fuzz/http_filter_fuzzer.h" +#include "test/extensions/filters/http/rbac/rbac_filter_fuzz.pb.validate.h" +#include "test/fuzz/fuzz_runner.h" +#include "test/mocks/http/mocks.h" +#include "test/mocks/runtime/mocks.h" +#include "test/mocks/server/server_factory_context.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using Envoy::Extensions::HttpFilters::RBACFilter::RoleBasedAccessControlFilter; +using Envoy::Extensions::HttpFilters::RBACFilter::RoleBasedAccessControlFilterConfig; +using Envoy::Extensions::HttpFilters::RBACFilter::RoleBasedAccessControlFilterConfigSharedPtr; +using testing::WithArgs; + +namespace Envoy { +namespace Extensions { +namespace Http { +namespace Rbac { + +class ReusableFilterFactory { +public: + ReusableFilterFactory() + : addr_(std::make_shared("/test/test.sock")) { + connection_.stream_info_.downstream_connection_info_provider_->setRemoteAddress(addr_); + connection_.stream_info_.downstream_connection_info_provider_->setLocalAddress(addr_); + + ON_CALL(callbacks_, connection()) + .WillByDefault(testing::Return(OptRef{connection_})); + } + + absl::StatusOr> + newFilter(const envoy::extensions::filters::http::rbac::v3::RBAC& proto_config) { + RoleBasedAccessControlFilterConfigSharedPtr config; + try { + config = make_shared( + proto_config, "stats_prefix", *stats_store_.rootScope(), context_, + ProtobufMessage::getStrictValidationVisitor()); + } catch (const EnvoyException& e) { + return absl::InvalidArgumentError( + absl::StrCat("EnvoyException during validation: ", e.what())); + } + auto filter = std::make_unique(std::move(config)); + filter->setDecoderFilterCallbacks(callbacks_); + return filter; + } + +private: + Network::Address::InstanceConstSharedPtr addr_; + NiceMock callbacks_; + NiceMock connection_; + NiceMock context_; + Stats::TestUtil::TestStore stats_store_; +}; + +DEFINE_PROTO_FUZZER(const envoy::extensions::filters::http::rbac::RbacTestCase& input) { + try { + TestUtility::validate(input); + } catch (const EnvoyException& e) { + ENVOY_LOG_MISC(debug, "EnvoyException during validation: {}", e.what()); + return; + } + + // This is static to avoid recreating all the mocks between each fuzz test. The class is + // stateless; it just stores mocks and uses them when you call newFilter(). + static ReusableFilterFactory filter_factory; + absl::StatusOr> filter = + filter_factory.newFilter(input.config()); + if (!filter.ok()) { + ENVOY_LOG_MISC(debug, "Failed to create filter: {}", filter.status()); + return; + } + + static Envoy::Extensions::HttpFilters::HttpFilterFuzzer fuzzer; + fuzzer.runData(static_cast(filter->get()), + input.request_data()); +} + +} // namespace Rbac +} // namespace Http +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/filters/listener/proxy_protocol/proxy_proto_integration_test.cc b/test/extensions/filters/listener/proxy_protocol/proxy_proto_integration_test.cc index 8ad20b803384..4ca41df0b729 100644 --- a/test/extensions/filters/listener/proxy_protocol/proxy_proto_integration_test.cc +++ b/test/extensions/filters/listener/proxy_protocol/proxy_proto_integration_test.cc @@ -116,8 +116,9 @@ TEST_P(ProxyProtoIntegrationTest, V2RouterRequestAndResponseWithBodyNoBufferV6) testRouterRequestAndResponseWithBody(1024, 512, false, false, &creator); - // Verify stats (with tags for proxy protocol version). + // Verify stats (with tags for proxy protocol version, but no stat prefix). const auto found_counter = test_server_->counter("proxy_proto.versions.v2.found"); + ASSERT_NE(found_counter.get(), nullptr); EXPECT_EQ(found_counter->value(), 1UL); EXPECT_EQ(found_counter->tagExtractedName(), "proxy_proto.found"); EXPECT_THAT(found_counter->tags(), IsSupersetOf(Stats::TagVector{ @@ -378,6 +379,7 @@ ProxyProtoDisallowedVersionsIntegrationTest::ProxyProtoDisallowedVersionsIntegra // V1 is disallowed. ::envoy::extensions::filters::listener::proxy_protocol::v3::ProxyProtocol proxy_protocol; proxy_protocol.add_disallowed_versions(::envoy::config::core::v3::ProxyProtocolConfig::V1); + proxy_protocol.set_stat_prefix("test_stat_prefix"); auto* listener = bootstrap.mutable_static_resources()->mutable_listeners(0); auto* ppv_filter = listener->mutable_listener_filters(0); @@ -400,19 +402,25 @@ TEST_P(ProxyProtoDisallowedVersionsIntegrationTest, V1Disallowed) { /*end_stream=*/false, /*verify=*/false)); tcp_client->waitForDisconnect(); - // Verify stats (with tags for proxy protocol version). - const auto found_counter = test_server_->counter("proxy_proto.versions.v1.found"); + // Verify stats (with tags for proxy protocol version and stat prefix). + const auto found_counter = + test_server_->counter("proxy_proto.test_stat_prefix.versions.v1.found"); + ASSERT_NE(found_counter.get(), nullptr); EXPECT_EQ(found_counter->value(), 1UL); EXPECT_EQ(found_counter->tagExtractedName(), "proxy_proto.found"); EXPECT_THAT(found_counter->tags(), IsSupersetOf(Stats::TagVector{ {"envoy.proxy_protocol_version", "1"}, + {"envoy.proxy_protocol_prefix", "test_stat_prefix"}, })); - const auto disallowed_counter = test_server_->counter("proxy_proto.versions.v1.disallowed"); + const auto disallowed_counter = + test_server_->counter("proxy_proto.test_stat_prefix.versions.v1.disallowed"); + ASSERT_NE(disallowed_counter.get(), nullptr); EXPECT_EQ(disallowed_counter->value(), 1UL); EXPECT_EQ(disallowed_counter->tagExtractedName(), "proxy_proto.disallowed"); EXPECT_THAT(disallowed_counter->tags(), IsSupersetOf(Stats::TagVector{ {"envoy.proxy_protocol_version", "1"}, + {"envoy.proxy_protocol_prefix", "test_stat_prefix"}, })); } @@ -430,12 +438,15 @@ TEST_P(ProxyProtoDisallowedVersionsIntegrationTest, V2Error) { ASSERT_TRUE(tcp_client->write(buf.toString(), /*end_stream=*/false, /*verify=*/false)); tcp_client->waitForDisconnect(); - // Verify stats (with tags for proxy protocol version). - const auto found_counter = test_server_->counter("proxy_proto.versions.v2.error"); + // Verify stats (with tags for proxy protocol version and stat prefix). + const auto found_counter = + test_server_->counter("proxy_proto.test_stat_prefix.versions.v2.error"); + ASSERT_NE(found_counter.get(), nullptr); EXPECT_EQ(found_counter->value(), 1UL); EXPECT_EQ(found_counter->tagExtractedName(), "proxy_proto.error"); EXPECT_THAT(found_counter->tags(), IsSupersetOf(Stats::TagVector{ {"envoy.proxy_protocol_version", "2"}, + {"envoy.proxy_protocol_prefix", "test_stat_prefix"}, })); } diff --git a/test/extensions/filters/udp/dns_filter/dns_filter_test.cc b/test/extensions/filters/udp/dns_filter/dns_filter_test.cc index 58620fc5191a..ba7a0e61aa86 100644 --- a/test/extensions/filters/udp/dns_filter/dns_filter_test.cc +++ b/test/extensions/filters/udp/dns_filter/dns_filter_test.cc @@ -1910,6 +1910,12 @@ TEST_F(DnsFilterTest, InvalidShortBufferTest) { } TEST_F(DnsFilterTest, RandomizeFirstAnswerTest) { +#if defined(__linux__) && defined(__s390x__) + // Skip on s390x because this test incorrectly depends on the ordering of + // addresses that happens to work on other platforms. + // See https://github.com/envoyproxy/envoy/pull/24330 + GTEST_SKIP() << "Skipping RandomizeFirstAnswerTest on s390x"; +#endif InSequence s; setup(forward_query_off_config); diff --git a/test/extensions/matching/input_matchers/ip/matcher_test.cc b/test/extensions/matching/input_matchers/ip/matcher_test.cc index 68842784e819..287343adedd1 100644 --- a/test/extensions/matching/input_matchers/ip/matcher_test.cc +++ b/test/extensions/matching/input_matchers/ip/matcher_test.cc @@ -27,8 +27,8 @@ class MatcherTest : public testing::Test { TEST_F(MatcherTest, TestV4) { std::vector ranges; - ranges.emplace_back(Network::Address::CidrRange::create("192.0.2.0", 24)); - ranges.emplace_back(Network::Address::CidrRange::create("10.0.0.0", 24)); + ranges.emplace_back(*Network::Address::CidrRange::create("192.0.2.0", 24)); + ranges.emplace_back(*Network::Address::CidrRange::create("10.0.0.0", 24)); initialize(std::move(ranges)); EXPECT_FALSE(m_->match("192.0.1.255")); EXPECT_TRUE(m_->match("192.0.2.0")); @@ -42,9 +42,9 @@ TEST_F(MatcherTest, TestV4) { TEST_F(MatcherTest, TestV6) { std::vector ranges; - ranges.emplace_back(Network::Address::CidrRange::create("::1/128")); - ranges.emplace_back(Network::Address::CidrRange::create("2001::/16")); - ranges.emplace_back(Network::Address::CidrRange::create("2002::/16")); + ranges.emplace_back(*Network::Address::CidrRange::create("::1/128")); + ranges.emplace_back(*Network::Address::CidrRange::create("2001::/16")); + ranges.emplace_back(*Network::Address::CidrRange::create("2002::/16")); initialize(std::move(ranges)); EXPECT_FALSE(m_->match("::")); @@ -66,7 +66,7 @@ TEST_F(MatcherTest, EmptyRanges) { TEST_F(MatcherTest, EmptyIP) { std::vector ranges; - ranges.emplace_back(Network::Address::CidrRange::create("192.0.2.0", 24)); + ranges.emplace_back(*Network::Address::CidrRange::create("192.0.2.0", 24)); initialize(std::move(ranges)); EXPECT_FALSE(m_->match("")); EXPECT_FALSE(m_->match(absl::monostate())); @@ -74,7 +74,7 @@ TEST_F(MatcherTest, EmptyIP) { TEST_F(MatcherTest, InvalidIP) { std::vector ranges; - ranges.emplace_back(Network::Address::CidrRange::create("192.0.2.0", 24)); + ranges.emplace_back(*Network::Address::CidrRange::create("192.0.2.0", 24)); initialize(std::move(ranges)); EXPECT_EQ(m_->stats()->ip_parsing_failed_.value(), 0); EXPECT_FALSE(m_->match("foo")); diff --git a/test/extensions/network/dns_resolver/getaddrinfo/getaddrinfo_test.cc b/test/extensions/network/dns_resolver/getaddrinfo/getaddrinfo_test.cc index 2968fb828953..4c260d16501a 100644 --- a/test/extensions/network/dns_resolver/getaddrinfo/getaddrinfo_test.cc +++ b/test/extensions/network/dns_resolver/getaddrinfo/getaddrinfo_test.cc @@ -216,7 +216,6 @@ TEST_F(GetAddrInfoDnsImplTest, TryAgainThenCancel) { EXPECT_CALL(os_sys_calls_, getaddrinfo(_, _, _, _)) .Times(testing::AnyNumber()) .WillOnce(Invoke([&](const char*, const char*, const addrinfo*, addrinfo**) { - query.load()->cancel(ActiveDnsQuery::CancelReason::QueryAbandoned); dispatcher_->exit(); return Api::SysCallIntResult{EAI_AGAIN, 0}; })); diff --git a/test/extensions/tracers/common/ot/opentracing_driver_impl_test.cc b/test/extensions/tracers/common/ot/opentracing_driver_impl_test.cc index 99c2ef75712e..352dfd8f2c14 100644 --- a/test/extensions/tracers/common/ot/opentracing_driver_impl_test.cc +++ b/test/extensions/tracers/common/ot/opentracing_driver_impl_test.cc @@ -353,6 +353,8 @@ TEST_F(OpenTracingDriverTest, GetTraceId) { // This method is unimplemented and a noop. ASSERT_EQ(first_span->getTraceId(), ""); + // This method is unimplemented and a noop. + ASSERT_EQ(first_span->getSpanId(), ""); } TEST_F(OpenTracingDriverTest, ExtractUsingForeach) { diff --git a/test/extensions/tracers/datadog/agent_http_client_test.cc b/test/extensions/tracers/datadog/agent_http_client_test.cc index 57e4bcceb4d3..9b367cca6067 100644 --- a/test/extensions/tracers/datadog/agent_http_client_test.cc +++ b/test/extensions/tracers/datadog/agent_http_client_test.cc @@ -352,6 +352,43 @@ TEST_F(DatadogAgentHttpClientTest, OnErrorStreamReset) { callbacks_->onFailure(request_, Http::AsyncClient::FailureReason::Reset); } +TEST_F(DatadogAgentHttpClientTest, OnErrorExceedResponseBufferLimit) { + // When `onFailure` is invoked on the `Http::AsyncClient::Callbacks` with + // `FailureReason::ExceedResponseBufferLimit`, the associated `on_error` callback is invoked + // with a corresponding `datadog::tracing::Error`. + + EXPECT_CALL(cluster_manager_.instance_.thread_local_cluster_.async_client_, send_(_, _, _)) + .WillOnce( + Invoke([this](Http::RequestMessagePtr&, Http::AsyncClient::Callbacks& callbacks_arg, + const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { + callbacks_ = &callbacks_arg; + return &request_; + })); + + // `callbacks_->onFailure(...)` will cause `on_error_` to be called. + // `on_response_` will not be called. + EXPECT_CALL(on_error_, Call(_)).WillOnce(Invoke([](datadog::tracing::Error error) { + EXPECT_EQ(error.code, datadog::tracing::Error::ENVOY_HTTP_CLIENT_FAILURE); + })); + EXPECT_CALL(on_response_, Call(_, _, _)).Times(0); + + // The request will not be canceled; neither explicitly nor in + // `~AgentHTTPClient`, because it will have been fulfilled. + EXPECT_CALL(request_, cancel()).Times(0); + + const auto ignore = [](auto&&...) {}; + datadog::tracing::Expected result = + client_.post(url_, ignore, "{}", on_response_.AsStdFunction(), on_error_.AsStdFunction(), + time_.monotonicTime() + std::chrono::seconds(1)); + EXPECT_TRUE(result) << result.error(); + + Http::ResponseMessagePtr msg(new Http::ResponseMessageImpl( + Http::ResponseHeaderMapPtr{new Http::TestResponseHeaderMapImpl{{":status", "200"}}})); + msg->body().add("{}"); + + callbacks_->onFailure(request_, Http::AsyncClient::FailureReason::ExceedResponseBufferLimit); +} + TEST_F(DatadogAgentHttpClientTest, OnErrorOther) { // When `onFailure` is invoked on the `Http::AsyncClient::Callbacks` with any // value other than `FailureReason::Reset`, the associated `on_error` callback diff --git a/test/extensions/tracers/datadog/span_test.cc b/test/extensions/tracers/datadog/span_test.cc index 2dccafbea424..6104989bd7c9 100644 --- a/test/extensions/tracers/datadog/span_test.cc +++ b/test/extensions/tracers/datadog/span_test.cc @@ -389,6 +389,7 @@ TEST_F(DatadogTracerSpanTest, Baggage) { TEST_F(DatadogTracerSpanTest, GetTraceId) { Span span{std::move(span_)}; EXPECT_EQ("cafebabe", span.getTraceId()); + EXPECT_EQ("", span.getSpanId()); } TEST_F(DatadogTracerSpanTest, NoOpMode) { @@ -430,6 +431,7 @@ TEST_F(DatadogTracerSpanTest, NoOpMode) { EXPECT_EQ("", span.getBaggage("foo")); span.setBaggage("foo", "bar"); EXPECT_EQ("", span.getTraceId()); + EXPECT_EQ("", span.getSpanId()); } } // namespace diff --git a/test/extensions/tracers/opencensus/tracer_test.cc b/test/extensions/tracers/opencensus/tracer_test.cc index 65a1632596f6..cd4adebe4af7 100644 --- a/test/extensions/tracers/opencensus/tracer_test.cc +++ b/test/extensions/tracers/opencensus/tracer_test.cc @@ -130,6 +130,9 @@ TEST(OpenCensusTracerTest, Span) { // Trace id is automatically created when no parent context exists. ASSERT_NE(span->getTraceId(), ""); + + // Span id should be empty since this is not yet supported. + ASSERT_EQ(span->getSpanId(), ""); } // Retrieve SpanData from the OpenCensus trace exporter. @@ -222,6 +225,8 @@ void testIncomingHeaders( // Check contents via public API. // Trace id is set via context propagation headers. EXPECT_EQ(span->getTraceId(), "404142434445464748494a4b4c4d4e4f"); + // TODO(#34412) This method is unimplemented. + EXPECT_EQ(span->getSpanId(), ""); } // Retrieve SpanData from the OpenCensus trace exporter. diff --git a/test/extensions/tracers/skywalking/tracer_test.cc b/test/extensions/tracers/skywalking/tracer_test.cc index cfa2564a5816..60a12a42ed15 100644 --- a/test/extensions/tracers/skywalking/tracer_test.cc +++ b/test/extensions/tracers/skywalking/tracer_test.cc @@ -90,6 +90,8 @@ TEST_F(TracerTest, TracerTestCreateNewSpanWithNoPropagationHeaders) { span->setOperation("FakeStringAndNothingToDo"); span->setBaggage("FakeStringAndNothingToDo", "FakeStringAndNothingToDo"); ASSERT_EQ(span->getTraceId(), segment_context->traceId()); + // This method is unimplemented and a noop. + ASSERT_EQ(span->getSpanId(), ""); // Test whether the basic functions of Span are normal. EXPECT_FALSE(span->spanEntity()->skipAnalysis()); span->setSampled(false); diff --git a/test/extensions/tracers/xray/tracer_test.cc b/test/extensions/tracers/xray/tracer_test.cc index 12b7c50238f9..c451fb993f1f 100644 --- a/test/extensions/tracers/xray/tracer_test.cc +++ b/test/extensions/tracers/xray/tracer_test.cc @@ -388,6 +388,9 @@ TEST_F(XRayTracerTest, GetTraceId) { // Trace ID is always generated EXPECT_NE(span->getTraceId(), ""); + + // This method is unimplemented and a noop. + EXPECT_EQ(span->getSpanId(), ""); } TEST_F(XRayTracerTest, ChildSpanHasParentInfo) { diff --git a/test/extensions/tracers/zipkin/span_buffer_test.cc b/test/extensions/tracers/zipkin/span_buffer_test.cc index 85ae80edd300..cd5cd5f5ac85 100644 --- a/test/extensions/tracers/zipkin/span_buffer_test.cc +++ b/test/extensions/tracers/zipkin/span_buffer_test.cc @@ -327,7 +327,11 @@ TEST(ZipkinSpanBufferTest, SerializeSpan) { EXPECT_EQ(withDefaultTimestampAndDuration("{" R"("spans":[{)" R"("traceId":"AAAAAAAAAAE=",)" +#ifdef ABSL_IS_BIG_ENDIAN + R"("id":"AAAAAAAAAAE=",)" +#else R"("id":"AQAAAAAAAAA=",)" +#endif R"("kind":"CLIENT",)" R"("timestamp":"ANNOTATION_TEST_TIMESTAMP",)" R"("duration":"DEFAULT_TEST_DURATION",)" @@ -346,7 +350,11 @@ TEST(ZipkinSpanBufferTest, SerializeSpan) { "{" R"("spans":[{)" R"("traceId":"AAAAAAAAAAE=",)" +#ifdef ABSL_IS_BIG_ENDIAN + R"("id":"AAAAAAAAAAE=",)" +#else R"("id":"AQAAAAAAAAA=",)" +#endif R"("kind":"CLIENT",)" R"("timestamp":"ANNOTATION_TEST_TIMESTAMP",)" R"("duration":"DEFAULT_TEST_DURATION",)" @@ -365,7 +373,11 @@ TEST(ZipkinSpanBufferTest, SerializeSpan) { EXPECT_EQ(withDefaultTimestampAndDuration("{" R"("spans":[{)" R"("traceId":"AAAAAAAAAAE=",)" +#ifdef ABSL_IS_BIG_ENDIAN + R"("id":"AAAAAAAAAAE=",)" +#else R"("id":"AQAAAAAAAAA=",)" +#endif R"("kind":"CLIENT",)" R"("timestamp":"ANNOTATION_TEST_TIMESTAMP",)" R"("duration":"DEFAULT_TEST_DURATION",)" @@ -377,7 +389,11 @@ TEST(ZipkinSpanBufferTest, SerializeSpan) { R"("response_size":"DEFAULT_TEST_DURATION"}},)" R"({)" R"("traceId":"AAAAAAAAAAE=",)" +#ifdef ABSL_IS_BIG_ENDIAN + R"("id":"AAAAAAAAAAE=",)" +#else R"("id":"AQAAAAAAAAA=",)" +#endif R"("kind":"SERVER",)" R"("timestamp":"ANNOTATION_TEST_TIMESTAMP",)" R"("duration":"DEFAULT_TEST_DURATION",)" @@ -396,7 +412,11 @@ TEST(ZipkinSpanBufferTest, SerializeSpan) { EXPECT_EQ(withDefaultTimestampAndDuration("{" R"("spans":[{)" R"("traceId":"AAAAAAAAAAE=",)" +#ifdef ABSL_IS_BIG_ENDIAN + R"("id":"AAAAAAAAAAE=",)" +#else R"("id":"AQAAAAAAAAA=",)" +#endif R"("kind":"CLIENT",)" R"("timestamp":"ANNOTATION_TEST_TIMESTAMP",)" R"("duration":"DEFAULT_TEST_DURATION",)" @@ -408,7 +428,11 @@ TEST(ZipkinSpanBufferTest, SerializeSpan) { R"("response_size":"DEFAULT_TEST_DURATION"}},)" R"({)" R"("traceId":"AAAAAAAAAAE=",)" +#ifdef ABSL_IS_BIG_ENDIAN + R"("id":"AAAAAAAAAAE=",)" +#else R"("id":"AQAAAAAAAAA=",)" +#endif R"("kind":"SERVER",)" R"("timestamp":"ANNOTATION_TEST_TIMESTAMP",)" R"("duration":"DEFAULT_TEST_DURATION",)" diff --git a/test/extensions/tracers/zipkin/zipkin_tracer_impl_test.cc b/test/extensions/tracers/zipkin/zipkin_tracer_impl_test.cc index 384d53bee290..e4292aa90452 100644 --- a/test/extensions/tracers/zipkin/zipkin_tracer_impl_test.cc +++ b/test/extensions/tracers/zipkin/zipkin_tracer_impl_test.cc @@ -726,6 +726,7 @@ TEST_F(ZipkinDriverTest, ZipkinSpanTest) { Tracing::SpanPtr span6 = driver_->startSpan(config_, request_headers_, stream_info_, operation_name_, {Tracing::Reason::Sampling, true}); EXPECT_EQ(span6->getTraceId(), "0000000000000000"); + EXPECT_EQ(span6->getSpanId(), ""); } TEST_F(ZipkinDriverTest, ZipkinSpanContextFromB3HeadersTest) { @@ -799,6 +800,7 @@ TEST_F(ZipkinDriverTest, ZipkinSpanContextFromB3Headers128TraceIdTest) { EXPECT_EQ(parent_id, zipkin_span->span().parentIdAsHexString()); EXPECT_TRUE(zipkin_span->span().sampled()); EXPECT_EQ(trace_id, zipkin_span->getTraceId()); + EXPECT_EQ("", zipkin_span->getSpanId()); } TEST_F(ZipkinDriverTest, ZipkinSpanContextFromInvalidTraceIdB3HeadersTest) { diff --git a/test/integration/BUILD b/test/integration/BUILD index 56fb0148d4f4..1635e574593e 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -1407,6 +1407,7 @@ envoy_cc_test( "//source/common/http:header_map_lib", "//source/extensions/access_loggers/file:config", "//source/extensions/filters/http/buffer:config", + "//test/test_common:test_runtime_lib", "//test/test_common:utility_lib", "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", @@ -2482,6 +2483,7 @@ envoy_cc_test( deps = [ ":http_integration_lib", "//test/test_common:utility_lib", + "@envoy_api//envoy/extensions/access_loggers/file/v3:pkg_cc_proto", ], ) diff --git a/test/integration/access_log_integration_test.cc b/test/integration/access_log_integration_test.cc index 6a181c95df76..c62d220b189f 100644 --- a/test/integration/access_log_integration_test.cc +++ b/test/integration/access_log_integration_test.cc @@ -1,3 +1,7 @@ +#include "envoy/extensions/access_loggers/file/v3/file.pb.h" + +#include "source/common/protobuf/protobuf.h" + #include "test/integration/http_integration.h" #include "test/test_common/registry.h" #include "test/test_common/utility.h" @@ -24,4 +28,32 @@ TEST_P(AccessLogIntegrationTest, DownstreamDisconnectBeforeHeadersResponseCode) std::string log = waitForAccessLog(access_log_name_); EXPECT_THAT(log, HasSubstr("RESPONSE_CODE=0")); } + +TEST_P(AccessLogIntegrationTest, ShouldReplaceInvalidUtf8) { + // Add incomplete UTF-8 strings. + default_request_headers_.setForwardedFor("\xec"); + + // Update access logs to use json format sorted. + access_log_name_ = TestEnvironment::temporaryPath(TestUtility::uniqueFilename()); + config_helper_.addConfigModifier( + [this]( + envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) { + envoy::extensions::access_loggers::file::v3::FileAccessLog access_log_config; + auto* access_log_config_to_clobber = hcm.add_access_log(); + access_log_config.set_path(access_log_name_); + + auto* log_format = access_log_config.mutable_log_format(); + auto* json = log_format->mutable_json_format(); + Envoy::ProtobufWkt::Value v; + v.set_string_value("%REQ(X-FORWARDED-FOR)%"); + auto fields = json->mutable_fields(); + (*fields)["x_forwarded_for"] = v; + log_format->mutable_json_format_options()->set_sort_properties(true); + access_log_config_to_clobber->mutable_typed_config()->PackFrom(access_log_config); + }); + testRouterDownstreamDisconnectBeforeRequestComplete(); + const std::string log = waitForAccessLog(access_log_name_); + EXPECT_THAT(log, HasSubstr("x_forwarded_for\":\"\xEF\xBF\xBD")); +} } // namespace Envoy diff --git a/test/integration/direct_response_integration_test.cc b/test/integration/direct_response_integration_test.cc index bc79926acaff..7000502d0b74 100644 --- a/test/integration/direct_response_integration_test.cc +++ b/test/integration/direct_response_integration_test.cc @@ -44,6 +44,62 @@ class DirectResponseIntegrationTest : public testing::TestWithParambody().size()); EXPECT_EQ(body_content, response->body()); } + + // Test direct response with a file as the body. + void testDirectResponseFile() { + TestEnvironment::writeStringToFileForTest("file_direct.txt", "dummy"); + const std::string filename = TestEnvironment::temporaryPath("file_direct.txt"); + config_helper_.addConfigModifier( + [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) -> void { + auto* route_config = hcm.mutable_route_config(); + auto* route = route_config->mutable_virtual_hosts(0)->mutable_routes(0); + + route->mutable_match()->set_prefix("/direct"); + + auto* direct_response = route->mutable_direct_response(); + direct_response->set_status(200); + direct_response->mutable_body()->set_filename(filename); + direct_response->mutable_body()->mutable_watched_directory()->set_path( + TestEnvironment::temporaryDirectory()); + }); + + initialize(); + codec_client_ = makeHttpConnection(lookupPort("http")); + + auto encoder_decoder = codec_client_->startRequest(Http::TestRequestHeaderMapImpl{ + {":method", "POST"}, + {":path", "/direct"}, + {":scheme", "http"}, + {":authority", "host"}, + }); + auto response = std::move(encoder_decoder.second); + ASSERT_TRUE(response->waitForEndStream()); + ASSERT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().getStatusValue()); + EXPECT_EQ("dummy", response->body()); + + codec_client_->close(); + + // Update the file and validate that the response is updated. + TestEnvironment::writeStringToFileForTest("file_direct_updated.txt", "dummy-updated"); + TestEnvironment::renameFile(TestEnvironment::temporaryPath("file_direct_updated.txt"), + TestEnvironment::temporaryPath("file_direct.txt")); + + codec_client_ = makeHttpConnection(lookupPort("http")); + auto encoder_decoder_updated = codec_client_->startRequest(Http::TestRequestHeaderMapImpl{ + {":method", "POST"}, + {":path", "/direct"}, + {":scheme", "http"}, + {":authority", "host"}, + }); + auto updated_response = std::move(encoder_decoder_updated.second); + ASSERT_TRUE(updated_response->waitForEndStream()); + ASSERT_TRUE(updated_response->complete()); + EXPECT_EQ("200", updated_response->headers().getStatusValue()); + EXPECT_EQ("dummy-updated", updated_response->body()); + codec_client_->close(); + } }; INSTANTIATE_TEST_SUITE_P(IpVersions, DirectResponseIntegrationTest, @@ -65,4 +121,6 @@ TEST_P(DirectResponseIntegrationTest, DirectResponseBodySizeSmall) { testDirectResponseBodySize(1); } +TEST_P(DirectResponseIntegrationTest, DefaultDirectResponseFile) { testDirectResponseFile(); } + } // namespace Envoy diff --git a/test/integration/filter_integration_test.cc b/test/integration/filter_integration_test.cc index dad67eb89edd..3d1af43a8c7e 100644 --- a/test/integration/filter_integration_test.cc +++ b/test/integration/filter_integration_test.cc @@ -224,51 +224,6 @@ TEST_P(FilterIntegrationTest, MissingHeadersLocalReplyDownstreamBytesCount) { } } -TEST_P(FilterIntegrationTest, RoundTripTimeForDownstreamConnection) { - config_helper_.addRuntimeOverride("envoy.reloadable_features.refresh_rtt_after_request", "true"); - - config_helper_.prependFilter(R"EOF( - name: stream-info-to-headers-filter - )EOF"); - initialize(); - - codec_client_ = makeHttpConnection(lookupPort("http")); - - // Send first request. - { - // 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()); - } - - // Send second request. - { - // 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/quic_http_integration_test.cc b/test/integration/quic_http_integration_test.cc index b196ee0771c0..ce687bb7a122 100644 --- a/test/integration/quic_http_integration_test.cc +++ b/test/integration/quic_http_integration_test.cc @@ -1359,6 +1359,61 @@ TEST_P(QuicHttpIntegrationTest, DeferredLogging) { EXPECT_EQ(/* request headers */ metrics.at(19), metrics.at(20)); } +TEST_P(QuicHttpIntegrationTest, DeferredLoggingWithBlackholedClient) { + config_helper_.addRuntimeOverride("envoy.reloadable_features.quic_defer_logging_to_ack_listener", + "true"); + 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(); + + // Make a header-only request and delay the response by 1ms to ensure that the ROUNDTRIP_DURATION + // metric is > 0 if exists. + codec_client_ = makeHttpConnection(makeClientConnection(lookupPort("http"))); + IntegrationStreamDecoderPtr response = + codec_client_->makeHeaderOnlyRequest(default_request_headers_); + absl::SleepFor(absl::Milliseconds(1)); + waitForNextUpstreamRequest(0, TestUtility::DefaultTimeout); + upstream_request_->encodeHeaders(default_response_headers_, true); + + // Prevent the client's dispatcher from running by not calling waitForEndStream or closing the + // client's connection, then wait for server to tear down connection due to too many + // retransmissions. + int iterations = 0; + std::string contents = TestEnvironment::readFileToStringForTest(access_log_name_); + while (iterations < 20) { + timeSystem().advanceTimeWait(std::chrono::seconds(1)); + // check for deferred logs from connection teardown. + contents = TestEnvironment::readFileToStringForTest(access_log_name_); + if (!contents.empty()) { + break; + } + iterations++; + } + + std::vector entries = absl::StrSplit(contents, '\n', absl::SkipEmpty()); + EXPECT_EQ(entries.size(), 1); + std::string log = entries[0]; + + 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"); + // 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)); + + codec_client_->close(); +} + TEST_P(QuicHttpIntegrationTest, DeferredLoggingDisabled) { config_helper_.addRuntimeOverride("envoy.reloadable_features.quic_defer_logging_to_ack_listener", "false"); @@ -2359,5 +2414,31 @@ TEST_P(QuicHttpIntegrationTest, ConnectionDebugVisitor) { }); } +TEST_P(QuicHttpIntegrationTest, StreamTimeoutWithHalfClose) { + // Tighten the stream idle timeout to 400ms. + config_helper_.addConfigModifier( + [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) -> void { + hcm.mutable_stream_idle_timeout()->set_seconds(0); + hcm.mutable_stream_idle_timeout()->set_nanos(400 * 1000 * 1000); + }); + initialize(); + codec_client_ = makeHttpConnection(makeClientConnection(lookupPort("http"))); + IntegrationStreamDecoderPtr response = + codec_client_->makeRequestWithBody(default_request_headers_, "partial body", false); + EnvoyQuicClientSession* quic_session = + static_cast(codec_client_->connection()); + quic::QuicStream* stream = quic_session->GetActiveStream(0); + // Only send RESET_STREAM to close write side of this stream. + stream->ResetWriteSide(quic::QuicResetStreamError::FromInternal(quic::QUIC_STREAM_NO_ERROR)); + + // Wait for the server to timeout this request and the local reply. + EXPECT_TRUE(response->waitForEndStream()); + ASSERT_TRUE(response->complete()); + + EXPECT_EQ(1, test_server_->counter("http.config_test.downstream_rq_idle_timeout")->value()); + codec_client_->close(); +} + } // namespace Quic } // namespace Envoy diff --git a/test/integration/websocket_integration_test.cc b/test/integration/websocket_integration_test.cc index 66818889e2db..5853814e3e71 100644 --- a/test/integration/websocket_integration_test.cc +++ b/test/integration/websocket_integration_test.cc @@ -11,6 +11,7 @@ #include "test/integration/utility.h" #include "test/test_common/network_utility.h" #include "test/test_common/printers.h" +#include "test/test_common/test_runtime.h" #include "test/test_common/utility.h" #include "absl/strings/str_cat.h" @@ -158,7 +159,7 @@ void WebsocketIntegrationTest::initialize() { void WebsocketIntegrationTest::performUpgrade( const Http::TestRequestHeaderMapImpl& upgrade_request_headers, - const Http::TestResponseHeaderMapImpl& upgrade_response_headers) { + const Http::TestResponseHeaderMapImpl& upgrade_response_headers, bool upgrade_should_fail) { // Establish the initial connection. codec_client_ = makeHttpConnection(lookupPort("http")); @@ -180,7 +181,9 @@ void WebsocketIntegrationTest::performUpgrade( // Verify the upgrade response was received downstream. response_->waitForHeaders(); - validateUpgradeResponseHeaders(response_->headers(), upgrade_response_headers); + if (!upgrade_should_fail) { + validateUpgradeResponseHeaders(response_->headers(), upgrade_response_headers); + } } void WebsocketIntegrationTest::sendBidirectionalData() { @@ -242,6 +245,10 @@ TEST_P(WebsocketIntegrationTest, EarlyData) { upstreamProtocol() != Http::CodecType::HTTP1) { return; } + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.check_switch_protocol_websocket_handshake", "false"}}); + config_helper_.addConfigModifier(setRouteUsingWebsocket()); initialize(); @@ -630,4 +637,150 @@ TEST_P(WebsocketIntegrationTest, BidirectionalConnectNoContentLengthNoTransferEn ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); } +// Test Websocket Upgrade in HTTP1 with 200 response code. +TEST_P(WebsocketIntegrationTest, Http1UpgradeStatusCodeOK) { + if (downstreamProtocol() != Http::CodecType::HTTP1 || + upstreamProtocol() != Http::CodecType::HTTP1) { + return; + } + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.check_switch_protocol_websocket_handshake", "true"}}); + + config_helper_.addConfigModifier(setRouteUsingWebsocket()); + initialize(); + + auto in_correct_status_response_headers = upgradeResponseHeaders(); + in_correct_status_response_headers.setStatus(200); + + // The upgrade should be paused, but the response header is proxied back to downstream. + performUpgrade(upgradeRequestHeaders(), in_correct_status_response_headers, true); + EXPECT_EQ("200", response_->headers().Status()->value().getStringView()); + EXPECT_EQ("upgrade", response_->headers().Connection()->value().getStringView()); + EXPECT_EQ("websocket", response_->headers().Upgrade()->value().getStringView()); + + test_server_->waitForCounterEq("cluster.cluster_0.upstream_cx_destroy", 1); + test_server_->waitForGaugeEq("http.config_test.downstream_cx_upgrades_active", 0); + ASSERT_TRUE(codec_client_->waitForDisconnect()); + ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); +} + +// Test Websocket Upgrade with 200 response code from no HTTP1 upstream and downstream. +TEST_P(WebsocketIntegrationTest, NonHttp1UpgradeStatusCodeOK) { + if (upstreamProtocol() == Http::CodecType::HTTP1 || + downstreamProtocol() == Http::CodecType::HTTP1) { + return; + } + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.check_switch_protocol_websocket_handshake", "true"}}); + + config_helper_.addConfigModifier(setRouteUsingWebsocket()); + initialize(); + + auto correct_status_response_headers = upgradeResponseHeaders(); + correct_status_response_headers.setStatus(200); + performUpgrade(upgradeRequestHeaders(), correct_status_response_headers, true); + + // HTTP2 upstream response 200 is converted to 101. + EXPECT_EQ("101", response_->headers().Status()->value().getStringView()); + test_server_->waitForGaugeEq("http.config_test.downstream_cx_upgrades_active", 1); + codec_client_->close(); +} + +// Test Websocket Upgrade with 201 response code from no HTTP1 upstream and downstream. +// This patch will not impact no H/1 behaviors. +TEST_P(WebsocketIntegrationTest, NoHttp1UpstreamUpgradeStatus201) { + if (upstreamProtocol() == Http::CodecType::HTTP1 || + downstreamProtocol() == Http::CodecType::HTTP1) { + return; + } + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.check_switch_protocol_websocket_handshake", "true"}}); + + config_helper_.addConfigModifier(setRouteUsingWebsocket()); + initialize(); + + auto correct_status_response_headers = upgradeResponseHeaders(); + correct_status_response_headers.setStatus(201); + performUpgrade(upgradeRequestHeaders(), correct_status_response_headers, true); + + EXPECT_EQ("201", response_->headers().Status()->value().getStringView()); + test_server_->waitForGaugeEq("http.config_test.downstream_cx_upgrades_active", 1); + codec_client_->close(); +} + +// Test Websocket Upgrade in HTTP1 with 426 response code. +// Upgrade is a HTTP1 header. +TEST_P(WebsocketIntegrationTest, Http1UpgradeStatusCodeUpgradeRequired) { + if (downstreamProtocol() != Http::CodecType::HTTP1 || + upstreamProtocol() != Http::CodecType::HTTP1) { + return; + } + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.check_switch_protocol_websocket_handshake", "true"}}); + + useAccessLog("%RESPONSE_CODE_DETAILS%"); + config_helper_.addConfigModifier(setRouteUsingWebsocket()); + initialize(); + + auto in_correct_status_response_headers = upgradeResponseHeaders(); + in_correct_status_response_headers.setStatus(426); + + // The upgrade should be paused, but the response header is proxied back to downstream. + performUpgrade(upgradeRequestHeaders(), in_correct_status_response_headers, true); + EXPECT_EQ("426", response_->headers().Status()->value().getStringView()); + EXPECT_EQ("upgrade", response_->headers().Connection()->value().getStringView()); + EXPECT_EQ("websocket", response_->headers().Upgrade()->value().getStringView()); + + test_server_->waitForCounterEq("cluster.cluster_0.upstream_cx_destroy", 1); + test_server_->waitForGaugeEq("http.config_test.downstream_cx_upgrades_active", 0); + ASSERT_TRUE(codec_client_->waitForDisconnect()); + ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); +} + +// Test data flow when websocket handshake failed. +TEST_P(WebsocketIntegrationTest, BidirectionalUpgradeFailedWithPrePayload) { + if (downstreamProtocol() != Http::CodecType::HTTP1 || + upstreamProtocol() != Http::CodecType::HTTP1) { + return; + } + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.check_switch_protocol_websocket_handshake", "true"}}); + + config_helper_.addConfigModifier(setRouteUsingWebsocket()); + initialize(); + + IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("http")); + + // Send upgrade request with additional data. + ASSERT_TRUE(tcp_client->write( + "GET / HTTP/1.1\r\nHost: host\r\nconnection: upgrade\r\nupgrade: websocket\r\n\r\nfoo boo", + false, false)); + + FakeRawConnectionPtr fake_upstream_connection; + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); + ASSERT(fake_upstream_connection != nullptr); + std::string received_data; + ASSERT_TRUE(fake_upstream_connection->waitForData( + FakeRawConnection::waitForInexactMatch("\r\n\r\n"), &received_data)); + // Make sure Envoy did not add TE or CL headers + ASSERT_FALSE(absl::StrContains(received_data, "content-length")); + ASSERT_FALSE(absl::StrContains(received_data, "transfer-encoding")); + ASSERT_TRUE(fake_upstream_connection->write( + "HTTP/1.1 426 Upgrade Required\r\nconnection: upgrade\r\nupgrade: websocket\r\n\r\n", false)); + + tcp_client->waitForData("\r\n\r\n", false); + + // Should not receive any data before handshake is finished. + std::string received_data_prepayload; + ASSERT_FALSE(fake_upstream_connection->waitForData( + FakeRawConnection::waitForInexactMatch("foo boo"), nullptr, std::chrono::milliseconds(10))); + + tcp_client->waitForDisconnect(); + ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); +} } // namespace Envoy diff --git a/test/integration/websocket_integration_test.h b/test/integration/websocket_integration_test.h index 4e03ca44bd87..066c8b3ba123 100644 --- a/test/integration/websocket_integration_test.h +++ b/test/integration/websocket_integration_test.h @@ -20,7 +20,8 @@ class WebsocketIntegrationTest : public HttpProtocolIntegrationTest { protected: void performUpgrade(const Http::TestRequestHeaderMapImpl& upgrade_request_headers, - const Http::TestResponseHeaderMapImpl& upgrade_response_headers); + const Http::TestResponseHeaderMapImpl& upgrade_response_headers, + bool upgrade_should_fail = false); void sendBidirectionalData(); void validateUpgradeRequestHeaders(const Http::RequestHeaderMap& proxied_request_headers, diff --git a/test/mocks/config/mocks.h b/test/mocks/config/mocks.h index ef9897f9cb2b..556ce0f7c0ff 100644 --- a/test/mocks/config/mocks.h +++ b/test/mocks/config/mocks.h @@ -79,12 +79,12 @@ class MockSubscriptionFactory : public SubscriptionFactory { MockSubscriptionFactory(); ~MockSubscriptionFactory() override; - MOCK_METHOD(SubscriptionPtr, subscriptionFromConfigSource, + MOCK_METHOD(absl::StatusOr, subscriptionFromConfigSource, (const envoy::config::core::v3::ConfigSource& config, absl::string_view type_url, Stats::Scope& scope, SubscriptionCallbacks& callbacks, OpaqueResourceDecoderSharedPtr resource_decoder, const SubscriptionOptions& options)); - MOCK_METHOD(SubscriptionPtr, collectionSubscriptionFromUrl, + MOCK_METHOD(absl::StatusOr, collectionSubscriptionFromUrl, (const xds::core::v3::ResourceLocator& collection_locator, const envoy::config::core::v3::ConfigSource& config, absl::string_view type_url, Stats::Scope& scope, SubscriptionCallbacks& callbacks, diff --git a/test/mocks/runtime/mocks.h b/test/mocks/runtime/mocks.h index 0329bf8154ca..9bb7e6c09c89 100644 --- a/test/mocks/runtime/mocks.h +++ b/test/mocks/runtime/mocks.h @@ -61,7 +61,7 @@ class MockLoader : public Loader { MockLoader(); ~MockLoader() override; - MOCK_METHOD(void, initialize, (Upstream::ClusterManager & cm)); + MOCK_METHOD(absl::Status, initialize, (Upstream::ClusterManager & cm)); MOCK_METHOD(const Snapshot&, snapshot, ()); MOCK_METHOD(SnapshotConstSharedPtr, threadsafeSnapshot, ()); MOCK_METHOD(absl::Status, mergeValues, ((const absl::node_hash_map&))); diff --git a/test/mocks/tracing/mocks.h b/test/mocks/tracing/mocks.h index 063902b3aadf..51b0fbc6c83e 100644 --- a/test/mocks/tracing/mocks.h +++ b/test/mocks/tracing/mocks.h @@ -44,6 +44,7 @@ class MockSpan : public Span { MOCK_METHOD(void, setBaggage, (absl::string_view key, absl::string_view value)); MOCK_METHOD(std::string, getBaggage, (absl::string_view key)); MOCK_METHOD(std::string, getTraceId, (), (const)); + MOCK_METHOD(std::string, getSpanId, (), (const)); SpanPtr spawnChild(const Config& config, const std::string& name, SystemTime start_time) override { diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index 412d81692d14..d36bef92a33d 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -420,39 +420,39 @@ coloredlogs==15.0.1 \ crcmod==1.7 \ --hash=sha256:dc7051a0db5f2bd48665a990d3ec1cc305a466a77358ca4492826f41f283601e # via gsutil -cryptography==42.0.7 \ - --hash=sha256:02c0eee2d7133bdbbc5e24441258d5d2244beb31da5ed19fbb80315f4bbbff55 \ - --hash=sha256:0d563795db98b4cd57742a78a288cdbdc9daedac29f2239793071fe114f13785 \ - --hash=sha256:16268d46086bb8ad5bf0a2b5544d8a9ed87a0e33f5e77dd3c3301e63d941a83b \ - --hash=sha256:1a58839984d9cb34c855197043eaae2c187d930ca6d644612843b4fe8513c886 \ - --hash=sha256:2954fccea107026512b15afb4aa664a5640cd0af630e2ee3962f2602693f0c82 \ - --hash=sha256:2e47577f9b18723fa294b0ea9a17d5e53a227867a0a4904a1a076d1646d45ca1 \ - --hash=sha256:31adb7d06fe4383226c3e963471f6837742889b3c4caa55aac20ad951bc8ffda \ - --hash=sha256:3577d029bc3f4827dd5bf8bf7710cac13527b470bbf1820a3f394adb38ed7d5f \ - --hash=sha256:36017400817987670037fbb0324d71489b6ead6231c9604f8fc1f7d008087c68 \ - --hash=sha256:362e7197754c231797ec45ee081f3088a27a47c6c01eff2ac83f60f85a50fe60 \ - --hash=sha256:3de9a45d3b2b7d8088c3fbf1ed4395dfeff79d07842217b38df14ef09ce1d8d7 \ - --hash=sha256:4f698edacf9c9e0371112792558d2f705b5645076cc0aaae02f816a0171770fd \ - --hash=sha256:5482e789294854c28237bba77c4c83be698be740e31a3ae5e879ee5444166582 \ - --hash=sha256:5e44507bf8d14b36b8389b226665d597bc0f18ea035d75b4e53c7b1ea84583cc \ - --hash=sha256:779245e13b9a6638df14641d029add5dc17edbef6ec915688f3acb9e720a5858 \ - --hash=sha256:789caea816c6704f63f6241a519bfa347f72fbd67ba28d04636b7c6b7da94b0b \ - --hash=sha256:7f8b25fa616d8b846aef64b15c606bb0828dbc35faf90566eb139aa9cff67af2 \ - --hash=sha256:8cb8ce7c3347fcf9446f201dc30e2d5a3c898d009126010cbd1f443f28b52678 \ - --hash=sha256:93a3209f6bb2b33e725ed08ee0991b92976dfdcf4e8b38646540674fc7508e13 \ - --hash=sha256:a3a5ac8b56fe37f3125e5b72b61dcde43283e5370827f5233893d461b7360cd4 \ - --hash=sha256:a47787a5e3649008a1102d3df55424e86606c9bae6fb77ac59afe06d234605f8 \ - --hash=sha256:a79165431551042cc9d1d90e6145d5d0d3ab0f2d66326c201d9b0e7f5bf43604 \ - --hash=sha256:a987f840718078212fdf4504d0fd4c6effe34a7e4740378e59d47696e8dfb477 \ - --hash=sha256:a9bc127cdc4ecf87a5ea22a2556cab6c7eda2923f84e4f3cc588e8470ce4e42e \ - --hash=sha256:bd13b5e9b543532453de08bcdc3cc7cebec6f9883e886fd20a92f26940fd3e7a \ - --hash=sha256:c65f96dad14f8528a447414125e1fc8feb2ad5a272b8f68477abbcc1ea7d94b9 \ - --hash=sha256:d8e3098721b84392ee45af2dd554c947c32cc52f862b6a3ae982dbb90f577f14 \ - --hash=sha256:e6b79d0adb01aae87e8a44c2b64bc3f3fe59515280e00fb6d57a7267a2583cda \ - --hash=sha256:e6b8f1881dac458c34778d0a424ae5769de30544fc678eac51c1c8bb2183e9da \ - --hash=sha256:e9b2a6309f14c0497f348d08a065d52f3020656f675819fc405fb63bbcd26562 \ - --hash=sha256:ecbfbc00bf55888edda9868a4cf927205de8499e7fabe6c050322298382953f2 \ - --hash=sha256:efd0bf5205240182e0f13bcaea41be4fdf5c22c5129fc7ced4a0282ac86998c9 +cryptography==42.0.8 \ + --hash=sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad \ + --hash=sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583 \ + --hash=sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b \ + --hash=sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c \ + --hash=sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1 \ + --hash=sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648 \ + --hash=sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949 \ + --hash=sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba \ + --hash=sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c \ + --hash=sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9 \ + --hash=sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d \ + --hash=sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c \ + --hash=sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e \ + --hash=sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2 \ + --hash=sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d \ + --hash=sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7 \ + --hash=sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70 \ + --hash=sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2 \ + --hash=sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7 \ + --hash=sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14 \ + --hash=sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe \ + --hash=sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e \ + --hash=sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71 \ + --hash=sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961 \ + --hash=sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7 \ + --hash=sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c \ + --hash=sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28 \ + --hash=sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842 \ + --hash=sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902 \ + --hash=sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801 \ + --hash=sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a \ + --hash=sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e # via # -r requirements.in # aioquic diff --git a/tools/code_format/check_format.py b/tools/code_format/check_format.py index e0ae1fdf1a35..99c15b031ddf 100755 --- a/tools/code_format/check_format.py +++ b/tools/code_format/check_format.py @@ -806,7 +806,9 @@ def has_non_comment_throw(line): return False if self.deny_listed_for_exceptions(file_path): - if has_non_comment_throw(line) or "THROW" in line or "throwExceptionOrPanic" in line: + if has_non_comment_throw( + line) or "THROW" in line or "throwEnvoyExceptionOrPanic" in line: + report_error( "Don't introduce throws into exception-free files, use error " "statuses instead.") diff --git a/tools/code_format/config.yaml b/tools/code_format/config.yaml index feafe3a27a09..29b48cdf294d 100644 --- a/tools/code_format/config.yaml +++ b/tools/code_format/config.yaml @@ -118,7 +118,11 @@ paths: - source/common/quic/quic_server_transport_socket_factory.cc - source/common/grpc/google_grpc_utils.cc - source/common/tcp_proxy/tcp_proxy.cc - - source/common/config/subscription_factory_impl.cc + - source/common/listener_manager/lds_api.cc + - source/common/upstream/od_cds_api_impl.cc + - source/common/upstream/cds_api_impl.cc + - source/common/router/vhds.cc + - source/common/rds/rds_route_config_subscription.cc - source/common/filter/config_discovery_impl.cc - source/common/json/json_internal.cc - source/common/router/scoped_rds.cc @@ -158,6 +162,7 @@ paths: - source/common/event/file_event_impl.cc - source/common/http/async_client_impl.cc - source/common/grpc/google_async_client_impl.cc + - source/common/formatter/substitution_format_utility.cc # Extensions can exempt entire directories but new extensions # points should ideally use StatusOr - source/extensions/access_loggers