diff --git a/.azure-pipelines/pipelines.yml b/.azure-pipelines/pipelines.yml index e3ef1a164a850..527305ef41d83 100644 --- a/.azure-pipelines/pipelines.yml +++ b/.azure-pipelines/pipelines.yml @@ -18,6 +18,12 @@ stages: pool: vmImage: "ubuntu-18.04" steps: + - task: Cache@2 + inputs: + key: "format_pre | ./WORKSPACE | **/*.bzl" + path: $(Build.StagingDirectory)/repository_cache + continueOnError: true + - script: ci/run_envoy_docker.sh 'ci/do_ci.sh format_pre' workingDirectory: $(Build.SourcesDirectory) env: @@ -38,6 +44,12 @@ stages: pool: vmImage: "ubuntu-18.04" steps: + - task: Cache@2 + inputs: + key: "tooling | ./WORKSPACE | **/*.bzl" + path: $(Build.StagingDirectory)/repository_cache + continueOnError: true + - script: ci/run_envoy_docker.sh 'ci/do_ci.sh tooling' workingDirectory: $(Build.SourcesDirectory) env: diff --git a/.github/actions/pr_notifier/requirements.txt b/.github/actions/pr_notifier/requirements.txt index e9d1ec1845062..67f910aa56516 100644 --- a/.github/actions/pr_notifier/requirements.txt +++ b/.github/actions/pr_notifier/requirements.txt @@ -115,9 +115,9 @@ slack-sdk==3.7.0 \ --hash=sha256:50b9fd6d8f83af7e8ad6d8e76882d04931842241f85ccfd30da09b4a7b9b1516 \ --hash=sha256:f0bf3e38ac393eba7fe1a99191b0e72f710860c6d2edc1271606fcfc08bea2e1 # via -r .github/actions/pr_notifier/requirements.txt -urllib3==1.26.5 \ - --hash=sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c \ - --hash=sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098 +urllib3==1.26.6 \ + --hash=sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4 \ + --hash=sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f # via requests wrapt==1.12.1 \ --hash=sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7 diff --git a/CODEOWNERS b/CODEOWNERS index 1f4bc2abbfde6..f84700c4bc384 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -198,3 +198,5 @@ extensions/filters/http/oauth2 @rgs1 @derekargueta @snowp /*/extensions/formatter/req_without_query @dio @tsaarni # IP address input matcher /*/extensions/matching/input_matchers/ip @aguinet @snowp +# Kafka +/*/extensions/filters/network/kafka @mattklein123 @adamkotwasinski diff --git a/api/envoy/config/core/v3/protocol.proto b/api/envoy/config/core/v3/protocol.proto index cf98e537261a8..d3b56a5ed7680 100644 --- a/api/envoy/config/core/v3/protocol.proto +++ b/api/envoy/config/core/v3/protocol.proto @@ -478,3 +478,11 @@ message Http3ProtocolOptions { // `. google.protobuf.BoolValue override_stream_error_on_invalid_http_message = 2; } + +// A message to control transformations to the :scheme header +message SchemeHeaderTransformation { + oneof transformation { + // Overwrite any Scheme header with the contents of this string. + string scheme_to_overwrite = 1 [(validate.rules).string = {in: "http" in: "https"}]; + } +} diff --git a/api/envoy/config/core/v4alpha/protocol.proto b/api/envoy/config/core/v4alpha/protocol.proto index abfd14447c7bc..50c47f006938f 100644 --- a/api/envoy/config/core/v4alpha/protocol.proto +++ b/api/envoy/config/core/v4alpha/protocol.proto @@ -478,3 +478,14 @@ message Http3ProtocolOptions { // `. google.protobuf.BoolValue override_stream_error_on_invalid_http_message = 2; } + +// A message to control transformations to the :scheme header +message SchemeHeaderTransformation { + option (udpa.annotations.versioning).previous_message_type = + "envoy.config.core.v3.SchemeHeaderTransformation"; + + oneof transformation { + // Overwrite any Scheme header with the contents of this string. + string scheme_to_overwrite = 1 [(validate.rules).string = {in: "http" in: "https"}]; + } +} diff --git a/api/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto b/api/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto index 5c35e80d591fd..b5925637a8e5a 100644 --- a/api/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto +++ b/api/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto @@ -30,7 +30,7 @@ message DnsCacheCircuitBreakers { // Configuration for the dynamic forward proxy DNS cache. See the :ref:`architecture overview // ` for more information. -// [#next-free-field: 11] +// [#next-free-field: 12] message DnsCacheConfig { option (udpa.annotations.versioning).previous_message_type = "envoy.config.common.dynamic_forward_proxy.v2alpha.DnsCacheConfig"; @@ -114,4 +114,10 @@ message DnsCacheConfig { // performance improvement, in the form of cache hits, for hostnames that are going to be // resolved during steady state and are known at config load time. repeated config.core.v3.SocketAddress preresolve_hostnames = 10; + + // The timeout used for DNS queries. This timeout is independent of any timeout and retry policy + // used by the underlying DNS implementation (e.g., c-areas and Apple DNS) which are opaque. + // Setting this timeout will ensure that queries succeed or fail within the specified time frame + // and are then retried using the standard refresh rates. Defaults to 5s if not set. + google.protobuf.Duration dns_query_timeout = 11 [(validate.rules).duration = {gt {}}]; } diff --git a/api/envoy/extensions/common/dynamic_forward_proxy/v4alpha/dns_cache.proto b/api/envoy/extensions/common/dynamic_forward_proxy/v4alpha/dns_cache.proto index 08b78d3fa45ca..fd5a296bc0600 100644 --- a/api/envoy/extensions/common/dynamic_forward_proxy/v4alpha/dns_cache.proto +++ b/api/envoy/extensions/common/dynamic_forward_proxy/v4alpha/dns_cache.proto @@ -32,7 +32,7 @@ message DnsCacheCircuitBreakers { // Configuration for the dynamic forward proxy DNS cache. See the :ref:`architecture overview // ` for more information. -// [#next-free-field: 11] +// [#next-free-field: 12] message DnsCacheConfig { option (udpa.annotations.versioning).previous_message_type = "envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig"; @@ -111,4 +111,10 @@ message DnsCacheConfig { // performance improvement, in the form of cache hits, for hostnames that are going to be // resolved during steady state and are known at config load time. repeated config.core.v4alpha.SocketAddress preresolve_hostnames = 10; + + // The timeout used for DNS queries. This timeout is independent of any timeout and retry policy + // used by the underlying DNS implementation (e.g., c-areas and Apple DNS) which are opaque. + // Setting this timeout will ensure that queries succeed or fail within the specified time frame + // and are then retried using the standard refresh rates. Defaults to 5s if not set. + google.protobuf.Duration dns_query_timeout = 11 [(validate.rules).duration = {gt {}}]; } diff --git a/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto b/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto index 856249c2a25ac..97e7f4200567c 100644 --- a/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto +++ b/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto @@ -35,7 +35,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // HTTP connection manager :ref:`configuration overview `. // [#extension: envoy.filters.network.http_connection_manager] -// [#next-free-field: 48] +// [#next-free-field: 49] message HttpConnectionManager { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager"; @@ -371,6 +371,11 @@ message HttpConnectionManager { ServerHeaderTransformation server_header_transformation = 34 [(validate.rules).enum = {defined_only: true}]; + // Allows for explicit transformation of the :scheme header on the request path. + // If not set, Envoy's default :ref:`scheme ` + // handling applies. + config.core.v3.SchemeHeaderTransformation scheme_header_transformation = 48; + // The maximum request headers size for incoming connections. // If unconfigured, the default max request headers allowed is 60 KiB. // Requests that exceed this limit will receive a 431 response. diff --git a/api/envoy/extensions/filters/network/http_connection_manager/v4alpha/http_connection_manager.proto b/api/envoy/extensions/filters/network/http_connection_manager/v4alpha/http_connection_manager.proto index c9f4333f3c7cb..70746c3b11231 100644 --- a/api/envoy/extensions/filters/network/http_connection_manager/v4alpha/http_connection_manager.proto +++ b/api/envoy/extensions/filters/network/http_connection_manager/v4alpha/http_connection_manager.proto @@ -33,7 +33,7 @@ option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSIO // HTTP connection manager :ref:`configuration overview `. // [#extension: envoy.filters.network.http_connection_manager] -// [#next-free-field: 48] +// [#next-free-field: 49] message HttpConnectionManager { option (udpa.annotations.versioning).previous_message_type = "envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager"; @@ -373,6 +373,11 @@ message HttpConnectionManager { ServerHeaderTransformation server_header_transformation = 34 [(validate.rules).enum = {defined_only: true}]; + // Allows for explicit transformation of the :scheme header on the request path. + // If not set, Envoy's default :ref:`scheme ` + // handling applies. + config.core.v4alpha.SchemeHeaderTransformation scheme_header_transformation = 48; + // The maximum request headers size for incoming connections. // If unconfigured, the default max request headers allowed is 60 KiB. // Requests that exceed this limit will receive a 431 response. diff --git a/api/envoy/extensions/request_id/uuid/v3/uuid.proto b/api/envoy/extensions/request_id/uuid/v3/uuid.proto index 9b8b7a45f5607..5c3f00da28d71 100644 --- a/api/envoy/extensions/request_id/uuid/v3/uuid.proto +++ b/api/envoy/extensions/request_id/uuid/v3/uuid.proto @@ -40,4 +40,9 @@ message UuidRequestIdConfig { // stable sampling of traces, access logs, etc. will no longer work and only random sampling will // be possible. google.protobuf.BoolValue pack_trace_reason = 1; + + // Set whether to use :ref:`x-request-id` for sampling or not. + // This defaults to true. See the :ref:`context propagation ` + // overview for more information. + google.protobuf.BoolValue use_request_id_for_trace_sampling = 2; } diff --git a/bazel/README.md b/bazel/README.md index 5276c629951b6..790c69e70d965 100644 --- a/bazel/README.md +++ b/bazel/README.md @@ -863,7 +863,7 @@ TEST_TMPDIR=/tmp tools/gen_compilation_database.py ``` -# Running clang-format without docker +# Running format linting without docker The easiest way to run the clang-format check/fix commands is to run them via docker, which helps ensure the right toolchain is set up. However you may prefer @@ -876,6 +876,8 @@ To run the tools directly, you must install the correct version of clang. This may change over time, check the version of clang in the docker image. You must also have 'buildifier' installed from the bazel distribution. +Note that if you run the `check_spelling.py` script you will need to have `aspell` installed. + Edit the paths shown here to reflect the installation locations on your system: ```shell diff --git a/bazel/foreign_cc/BUILD b/bazel/foreign_cc/BUILD index 7566d7126153e..b24046ea99c57 100644 --- a/bazel/foreign_cc/BUILD +++ b/bazel/foreign_cc/BUILD @@ -315,9 +315,9 @@ envoy_cmake_external( name = "wamr", cache_entries = { "LLVM_DIR": "$EXT_BUILD_DEPS/copy_llvm/llvm/lib/cmake/llvm", - "WAMR_BUILD_INTERP": "1", - "WAMR_BUILD_JIT": "0", - "WAMR_BUILD_AOT": "0", + "WAMR_BUILD_INTERP": "0", + "WAMR_BUILD_JIT": "1", + "WAMR_BUILD_AOT": "1", "WAMR_BUILD_SIMD": "0", "WAMR_BUILD_MULTI_MODULE": "1", "WAMR_BUILD_LIBC_WASI": "0", diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 357c387c6ec98..654df5fb7b034 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -708,11 +708,11 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "Webassembly Micro Runtime", project_desc = "A standalone runtime with a small footprint for WebAssembly", project_url = "https://github.com/bytecodealliance/wasm-micro-runtime", - version = "a14a4487bb8b493bf6c68d83b03f12028d16f58a", - sha256 = "d68668e129f16a9ddd7a1a0da22b17905a25001ae2de398726d37880b61fee9e", + version = "b554a9d05d89bb4ef28068b4ae4d0ee6c99bc9db", + sha256 = "de6b68118c5d4b0d37c9049fa08fae6a850304522ec307f087f0eca4ad8fff57", strip_prefix = "wasm-micro-runtime-{version}", urls = ["https://github.com/bytecodealliance/wasm-micro-runtime/archive/{version}.tar.gz"], - release_date = "2021-05-14", + release_date = "2021-07-06", use_category = ["dataplane_ext"], extensions = ["envoy.wasm.runtime.wamr"], cpe = "N/A", diff --git a/configs/envoyproxy_io_proxy.yaml b/configs/envoyproxy_io_proxy.yaml index 5593c4e2971d0..7b0e345fc9f35 100644 --- a/configs/envoyproxy_io_proxy.yaml +++ b/configs/envoyproxy_io_proxy.yaml @@ -17,6 +17,8 @@ static_resources: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + scheme_header_transformation: + scheme_to_overwrite: https stat_prefix: ingress_http route_config: name: local_route diff --git a/docs/root/configuration/http/http_conn_man/headers.rst b/docs/root/configuration/http/http_conn_man/headers.rst index 57c34ec2e7f6e..9fd0e2fa0c0b9 100644 --- a/docs/root/configuration/http/http_conn_man/headers.rst +++ b/docs/root/configuration/http/http_conn_man/headers.rst @@ -9,6 +9,25 @@ is being received) as well as during encoding (when the response is being sent). .. contents:: :local: +.. _config_http_conn_man_headers_scheme: + +:scheme +------- + +Envoy will always set the *:scheme* header while processing a request. It should always be available to filters, and should be forwarded upstream for HTTP/2 and HTTP/3, where :ref:`config_http_conn_man_headers_x-forwarded-proto` will be sent for HTTP/1.1. + +For HTTP/2, and HTTP/3, incoming *:scheme* headers are trusted and propogated through upstream. +For HTTP/1, the *:scheme* header will be set +1) From the absolute URL if present and valid. An invalid (not "http" or "https") scheme, or an https scheme over an unencrypted connection will result in Envoy rejecting the request. This is the only scheme validation Envoy performs as it avoids a HTTP/1.1-specific privledge escalation attack for edge Envoys [1]_ which doesn't have a comparable vector for HTTP/2 and above [2]_. +2) From the value of the :ref:`config_http_conn_man_headers_x-forwarded-proto` header after sanitization (to valid *x-forwarded-proto* from trusted downstreams, otherwise based on downstream encryption level). + +This default behavior can be overridden via the :ref:`scheme_header_transformation +` +configuration option. + +.. [1] Edge Envoys often have plaintext HTTP/1.1 listeners. If Envoy trusts absolute URL scheme from fully qualfied URLs, a MiTM can adjust relative URLs to https absolute URLs, and inadvertantly cause the Envoy's upstream to send PII or other sensitive data over what it then believes is a secure connection. +.. [2] Unlike HTTP/1.1, HTTP/2 is in practice always served over TLS via ALPN for edge Envoys. In mesh networks using insecure HTTP/2, if the downstream is not trusted to set scheme, the :ref:`scheme_header_transformation ` should be used. + .. _config_http_conn_man_headers_user-agent: user-agent @@ -341,6 +360,14 @@ It is a common case where a service wants to know what the originating protocol of the connection terminated by front/edge Envoy. *x-forwarded-proto* contains this information. It will be set to either *http* or *https*. +Downstream *x-forwarded-proto* headers will only be trusted if *xff_num_trusted_hops* is non-zero. +If *xff_num_trusted_hops* is zero, downstream *x-forwarded-proto* headers and *:scheme* headers +will be set to http or https based on if the downstream connection is TLS or not. + +If the scheme is changed via the :ref:`scheme_header_transformation +` +configuration option, *x-forwarded-proto* will be updated as well. + .. _config_http_conn_man_headers_x-request-id: x-request-id diff --git a/docs/root/configuration/http/http_filters/dynamic_forward_proxy_filter.rst b/docs/root/configuration/http/http_filters/dynamic_forward_proxy_filter.rst index 3699fb96d3efc..d2c9530207ee5 100644 --- a/docs/root/configuration/http/http_filters/dynamic_forward_proxy_filter.rst +++ b/docs/root/configuration/http/http_filters/dynamic_forward_proxy_filter.rst @@ -49,6 +49,7 @@ namespace. dns_query_attempt, Counter, Number of DNS query attempts. dns_query_success, Counter, Number of DNS query successes. dns_query_failure, Counter, Number of DNS query failures. + dns_query_timeout, Counter, Number of DNS query :ref:`timeouts `. host_address_changed, Counter, Number of DNS queries that resulted in a host address change. host_added, Counter, Number of hosts that have been added to the cache. host_removed, Counter, Number of hosts that have been removed from the cache. diff --git a/docs/root/configuration/http/http_filters/rate_limit_filter.rst b/docs/root/configuration/http/http_filters/rate_limit_filter.rst index 1c321fbaf6c64..8e41021d30083 100644 --- a/docs/root/configuration/http/http_filters/rate_limit_filter.rst +++ b/docs/root/configuration/http/http_filters/rate_limit_filter.rst @@ -88,9 +88,9 @@ will be appended to the descriptor produced by the action and sent to the rateli overriding the static service configuration. The override can be configured to be taken from the :ref:`Dynamic Metadata -` under a specified :ref: `key -`. If the value is misconfigured -or key does not exist, the override configuration is ignored. +` under a specified +:ref:`key `. +If the value is misconfigured or key does not exist, the override configuration is ignored. Example 3 ^^^^^^^^^ diff --git a/docs/root/intro/arch_overview/advanced/attributes.rst b/docs/root/intro/arch_overview/advanced/attributes.rst index 3ef7070f03fae..45cdc5bc38d88 100644 --- a/docs/root/intro/arch_overview/advanced/attributes.rst +++ b/docs/root/intro/arch_overview/advanced/attributes.rst @@ -40,7 +40,9 @@ Request attributes ------------------ The following request attributes are generally available upon initial request -processing, which makes them suitable for RBAC policies: +processing, which makes them suitable for RBAC policies. + +``request.*`` attributes are only available in http filters. .. csv-table:: :header: Attribute, Type, Description @@ -76,6 +78,8 @@ Response attributes Response attributes are only available after the request completes. +``response.*`` attributes are only available in http filters. + .. csv-table:: :header: Attribute, Type, Description :widths: 1, 1, 4 diff --git a/docs/root/intro/arch_overview/http/upgrades.rst b/docs/root/intro/arch_overview/http/upgrades.rst index c2f8278c8417f..cefa72e67b807 100644 --- a/docs/root/intro/arch_overview/http/upgrades.rst +++ b/docs/root/intro/arch_overview/http/upgrades.rst @@ -96,7 +96,7 @@ will synthesize 200 response headers, and then forward the TCP data as the HTTP will be forwarded *unsanitized* headers if they are in the body payload. Please use with caution For an example of proxying connect, please see :repo:`configs/proxy_connect.yaml ` -For an example of terminating connect, please see :repo:`configs/terminate_connect.yaml ` +For an example of terminating connect, please see :repo:`configs/terminate_http1_connect.yaml ` and :repo:`configs/terminate_http2_connect.yaml ` Note that for CONNECT-over-tls, Envoy can not currently be configured to do the CONNECT request in the clear and encrypt previously unencrypted payload in one hop. To send CONNECT in plaintext and encrypt the payload, diff --git a/docs/root/intro/arch_overview/listeners/network_filter_chain.rst b/docs/root/intro/arch_overview/listeners/network_filter_chain.rst index 468fc16ea1e1b..447caddc19062 100644 --- a/docs/root/intro/arch_overview/listeners/network_filter_chain.rst +++ b/docs/root/intro/arch_overview/listeners/network_filter_chain.rst @@ -16,7 +16,7 @@ chosen to serve the request. If the default filter chain is not supplied, the co Filter chain only update ------------------------ -:ref:`Filter chains ` can be updated indepedently. Upon listener config +:ref:`Filter chains ` can be updated independently. Upon listener config update, if the listener manager determines that the listener update is a filter chain only update, the listener update will be executed by adding, updating and removing filter chains. The connections owned by these destroying filter chains will be drained as described in listener drain. diff --git a/docs/root/intro/arch_overview/observability/tracing.rst b/docs/root/intro/arch_overview/observability/tracing.rst index ed717e077522a..ece8d4e8b807c 100644 --- a/docs/root/intro/arch_overview/observability/tracing.rst +++ b/docs/root/intro/arch_overview/observability/tracing.rst @@ -69,6 +69,16 @@ to be correlated. field can be used to disable this behavior at the expense of also disabling stable trace reason propagation and associated features within a deployment. +.. attention:: + + The sampling policy for Envoy is determined by the value of :ref:`x-request-id ` by default. + However, such a sampling policy is only valid for a fleet of Envoys. If a service proxy + that is not Envoy is present in the fleet, sampling is performed without considering the policy of that proxy. + For meshes consisting of multiple service proxies such as this, it is more effective to + bypass Envoy's sampling policy and sample based on the trace provider's sampling policy. This can be achieved by setting + :ref:`use_request_id_for_trace_sampling ` + to false. + The tracing providers also require additional context, to enable the parent/child relationships between the spans (logical units of work) to be understood. This can be achieved by using the LightStep (via OpenTracing API) or Zipkin tracer directly within the service itself, to extract the diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 21531ab791649..faa7719719f79 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -23,6 +23,7 @@ Minor Behavior Changes be now be disabled in favor of using unsigned payloads with compatible services via the new ``use_unsigned_payload`` filter option (default false). * cluster: added default value of 5 seconds for :ref:`connect_timeout `. +* dns cache: the new :ref:`dns_query_timeout ` option has a default of 5s. See below for more information. * http: disable the integration between :ref:`ExtensionWithMatcher ` and HTTP filters by default to reflects its experimental status. This feature can be enabled by seting ``envoy.reloadable_features.experimental_matching_api`` to true. @@ -36,9 +37,11 @@ Minor Behavior Changes ``envoy.reloadable_features.no_chunked_encoding_header_for_304`` to false. * http: the behavior of the ``present_match`` in route header matcher changed. The value of ``present_match`` is ignored in the past. The new behavior is ``present_match`` performed when value is true. absent match performed when the value is false. Please reference :ref:`present_match `. +* listener: added an option when balancing across active listeners and wildcard matching is used to return the listener that matches the IP family type associated with the listener's socket address. Any unexpected behavioral changes can be reverted by setting runtime guard ``envoy.reloadable_features.listener_wildcard_match_ip_family`` to false. * listener: respect the :ref:`connection balance config ` defined within the listener where the sockets are redirected to. Clear that field to restore the previous behavior. * tcp: switched to the new connection pool by default. Any unexpected behavioral changes can be reverted by setting runtime guard ``envoy.reloadable_features.new_tcp_connection_pool`` to false. +* tracing: add option :ref:`use_request_id_for_trace_sampling ` whether to use sampling policy based on :ref:`x-request-id` or not. Bug Fixes --------- @@ -54,6 +57,7 @@ Bug Fixes reverted by setting the ``envoy.reloadable_features.http2_consume_stream_refused_errors`` runtime guard to false. * http: port stripping now works for CONNECT requests, though the port will be restored if the CONNECT request is sent upstream. This behavior can be temporarily reverted by setting ``envoy.reloadable_features.strip_port_from_connect`` to false. * http: raise max configurable max_request_headers_kb limit to 8192 KiB (8MiB) from 96 KiB in http connection manager. +* jwt_authn: unauthorized responses now correctly include a `www-authenticate` header. * listener: fix the crash which could happen when the ongoing filter chain only listener update is followed by the listener removal or full listener update. * udp: limit each UDP listener to read maxmium 6000 packets per event loop. This behavior can be temporarily reverted by setting ``envoy.reloadable_features.udp_per_event_loop_read_limit`` to false. * validation: fix an issue that causes TAP sockets to panic during config validation mode. @@ -90,6 +94,7 @@ New Features * connection_limit: added new :ref:`Network connection limit filter `. * crash support: restore crash context when continuing to processing requests or responses as a result of an asynchronous callback that invokes a filter directly. This is unlike the call stacks that go through the various network layers, to eventually reach the filter. For a concrete example see: ``Envoy::Extensions::HttpFilters::Cache::CacheFilter::getHeaders`` which posts a callback on the dispatcher that will invoke the filter directly. * dns cache: added :ref:`preresolve_hostnames ` option to the DNS cache config. This option allows hostnames to be preresolved into the cache upon cache creation. This might provide performance improvement, in the form of cache hits, for hostnames that are going to be resolved during steady state and are known at config load time. +* dns cache: added :ref:`dns_query_timeout ` option to the DNS cache config. This option allows explicitly controlling the timeout of underlying queries independently of the underlying DNS platform implementation. Coupled with success and failure retry policies the use of this timeout will lead to more deterministic DNS resolution times. * dns resolver: added ``DnsResolverOptions`` protobuf message to reconcile all of the DNS lookup option flags. By setting the configuration option :ref:`use_tcp_for_dns_lookups ` as true we can make the underlying dns resolver library to make only TCP queries to the DNS servers and by setting the configuration option :ref:`no_default_search_domain ` as true the DNS resolver library will not use the default search domains. * dns resolver: added ``DnsResolutionConfig`` to combine :ref:`dns_resolver_options ` and :ref:`resolvers ` in a single protobuf message. The field ``resolvers`` can be specified with a list of DNS resolver addresses. If specified, DNS client library will perform resolution via the underlying DNS resolvers. Otherwise, the default system resolvers (e.g., /etc/resolv.conf) will be used. * dns_filter: added :ref:`dns_resolution_config ` to aggregate all of the DNS resolver configuration in a single message. By setting the configuration option ``use_tcp_for_dns_lookups`` to true we can make dns filter's external resolvers to answer queries using TCP only, by setting the configuration option ``no_default_search_domain`` as true the DNS resolver will not use the default search domains. And by setting the configuration ``resolvers`` we can specify the external DNS servers to be used for external DNS query which replaces the pre-existing alpha api field ``upstream_resolvers``. @@ -98,6 +103,7 @@ New Features value is ``true``, the unsupported http filter will be ignored by envoy. This is also same with unsupported http filter in the typed per filter config. For more information, please reference :ref:`HttpFilter `. +* http: added :ref``scheme options ` for adding or overwriting scheme. * http: added :ref:`stripping trailing host dot from host header ` support. * http: added support for :ref:`original IP detection extensions `. Two initial extensions were added, the :ref:`custom header ` extension and the diff --git a/envoy/http/request_id_extension.h b/envoy/http/request_id_extension.h index 8c2faf9e3a027..efdfd1b8cbec2 100644 --- a/envoy/http/request_id_extension.h +++ b/envoy/http/request_id_extension.h @@ -63,6 +63,12 @@ class RequestIDExtension : public RequestIdStreamInfoProvider { * @param status the trace reason that should be set for this request. */ virtual void setTraceReason(Http::RequestHeaderMap& request_headers, Tracing::Reason reason) PURE; + + /** + * Get whether to use request_id based sampling policy or not. + * @return whether to use request_id based sampling policy or not. + */ + virtual bool useRequestIdForTraceSampling() const PURE; }; using RequestIDExtensionSharedPtr = std::shared_ptr; diff --git a/envoy/matcher/matcher.h b/envoy/matcher/matcher.h index 9606d841c1fa9..f101d432e92fe 100644 --- a/envoy/matcher/matcher.h +++ b/envoy/matcher/matcher.h @@ -16,7 +16,7 @@ namespace Envoy { namespace Server { namespace Configuration { -class FactoryContext; +class ServerFactoryContext; } } // namespace Server @@ -76,11 +76,12 @@ class Action { using ActionPtr = std::unique_ptr; using ActionFactoryCb = std::function; -class ActionFactory : public Config::TypedFactory { +template class ActionFactory : public Config::TypedFactory { public: virtual ActionFactoryCb - createActionFactoryCb(const Protobuf::Message& config, const std::string& stats_prefix, - Server::Configuration::FactoryContext& context) PURE; + createActionFactoryCb(const Protobuf::Message& config, + ActionFactoryContext& action_factory_context, + ProtobufMessage::ValidationVisitor& validation_visitor) PURE; std::string category() const override { return "envoy.matching.action"; } }; @@ -155,7 +156,7 @@ class InputMatcherFactory : public Config::TypedFactory { public: virtual InputMatcherFactoryCb createInputMatcherFactoryCb(const Protobuf::Message& config, - Server::Configuration::FactoryContext& factory_context) PURE; + Server::Configuration::ServerFactoryContext& factory_context) PURE; std::string category() const override { return "envoy.matching.input_matchers"; } }; @@ -226,7 +227,7 @@ template class DataInputFactory : public Config::TypedFactory { */ virtual DataInputFactoryCb createDataInputFactoryCb(const Protobuf::Message& config, - Server::Configuration::FactoryContext& factory_context) PURE; + ProtobufMessage::ValidationVisitor& validation_visitor) PURE; /** * The category of this factory depends on the DataType, so we require a name() function to exist @@ -262,7 +263,7 @@ class CommonProtocolInputFactory : public Config::TypedFactory { */ virtual CommonProtocolInputFactoryCb createCommonProtocolInputFactoryCb(const Protobuf::Message& config, - Server::Configuration::FactoryContext& factory_context) PURE; + ProtobufMessage::ValidationVisitor& validation_visitor) PURE; std::string category() const override { return "envoy.matching.common_inputs"; } }; diff --git a/envoy/network/dns.h b/envoy/network/dns.h index 29cef98b104fa..d2f7e23f5bae9 100644 --- a/envoy/network/dns.h +++ b/envoy/network/dns.h @@ -19,10 +19,20 @@ class ActiveDnsQuery { public: virtual ~ActiveDnsQuery() = default; + enum class CancelReason { + // The caller no longer needs the answer to the query. + QueryAbandoned, + // The query timed out from the perspective of the caller. The DNS implementation may take + // a different action in this case (e.g., destroying existing DNS connections) in an effort + // to get an answer to future queries. + Timeout + }; + /** * Cancel an outstanding DNS request. + * @param reason supplies the cancel reason. */ - virtual void cancel() PURE; + virtual void cancel(CancelReason reason) PURE; }; /** diff --git a/envoy/network/socket.h b/envoy/network/socket.h index ed37195de7088..79940b26f9226 100644 --- a/envoy/network/socket.h +++ b/envoy/network/socket.h @@ -80,6 +80,11 @@ class SocketAddressProvider { */ virtual absl::string_view requestedServerName() const PURE; + /** + * @return Connection ID of the downstream connection, or unset if not available. + **/ + virtual absl::optional connectionID() const PURE; + /** * Dumps the state of the SocketAddressProvider to the given ostream. * @@ -121,6 +126,11 @@ class SocketAddressSetter : public SocketAddressProvider { * @param SNI value requested. */ virtual void setRequestedServerName(const absl::string_view requested_server_name) PURE; + + /** + * @param id Connection ID of the downstream connection. + **/ + virtual void setConnectionID(uint64_t id) PURE; }; using SocketAddressSetterSharedPtr = std::shared_ptr; diff --git a/envoy/stats/stats_matcher.h b/envoy/stats/stats_matcher.h index fc0c7c1cc84e3..767e57605f8fc 100644 --- a/envoy/stats/stats_matcher.h +++ b/envoy/stats/stats_matcher.h @@ -9,16 +9,58 @@ namespace Envoy { namespace Stats { +class StatName; + class StatsMatcher { public: + // Holds the result of fastRejects(). This contains state that must be then + // passed to slowRejects() for the final 2-phase determination of whether a + // stat can be rejected. + enum class FastResult { + NoMatch, + Rejects, + Matches, + }; + virtual ~StatsMatcher() = default; /** * Take a metric name and report whether or not it should be instantiated. - * @param the name of a Stats::Metric. + * This may need to convert the StatName to a string. This is equivalent to + * calling fastRejects() and then calling slowResults() if necessary. + * + * @param name the name of a Stats::Metric. + * @return bool true if that stat should not be instantiated. + */ + virtual bool rejects(StatName name) const PURE; + + /** + * Takes a metric name and quickly determines whether it can be rejected based + * purely on the StatName. If fastResults(stat_name).rejects() is 'false', we + * will need to check slowRejects as well. It should not be necessary to cache + * the result of fastRejects() -- it's cheap enough to recompute. However we + * should protect slowRejects() by a cache due to its speed and the potential + * need to take a symbol table lock. + * + * @param name the name of a Stats::Metric. + * @return A result indicating whether the stat can be quickly rejected, as + * well as state that is then passed to slowRejects if rejection + * cannot be quickly determined. + */ + virtual FastResult fastRejects(StatName name) const PURE; + + /** + * Takes a metric name and converts it to a string, if needed, to determine + * whether it needs to be rejected. This is intended to be used if + * fastRejects() cannot determine an early rejection. It is a good idea to + * cache the results of this, to avoid the stringification overhead, potential + * regex overhead, plus a global symbol table lock. + * + * @param fast_result the result of fastRejects(), which must be called first. + * @param name the name of a Stats::Metric. * @return bool true if that stat should not be instantiated. */ - virtual bool rejects(const std::string& name) const PURE; + virtual bool slowRejects(FastResult fast_result, StatName name) const PURE; /** * Helps determine whether the matcher needs to be called. This can be used diff --git a/envoy/stream_info/stream_info.h b/envoy/stream_info/stream_info.h index 002f0ce8bdf63..7a67ff946f180 100644 --- a/envoy/stream_info/stream_info.h +++ b/envoy/stream_info/stream_info.h @@ -584,16 +584,6 @@ class StreamInfo { */ virtual Tracing::Reason traceReason() const PURE; - /** - * @return Connection ID of the downstream connection, or unset if not available. - **/ - virtual absl::optional connectionID() const PURE; - - /** - * @param id Connection ID of the downstream connection. - **/ - virtual void setConnectionID(uint64_t id) PURE; - /** * @param filter_chain_name Network filter chain name of the downstream connection. */ diff --git a/envoy/tracing/BUILD b/envoy/tracing/BUILD index e19f71626a133..688d4218fc33b 100644 --- a/envoy/tracing/BUILD +++ b/envoy/tracing/BUILD @@ -36,6 +36,7 @@ envoy_cc_library( name = "trace_driver_interface", hdrs = ["trace_driver.h"], deps = [ + ":trace_context_interface", ":trace_reason_interface", "//envoy/stream_info:stream_info_interface", ], diff --git a/envoy/tracing/trace_context.h b/envoy/tracing/trace_context.h index 4bb88f0f59cf7..12bd04fb8303b 100644 --- a/envoy/tracing/trace_context.h +++ b/envoy/tracing/trace_context.h @@ -14,6 +14,10 @@ namespace Tracing { * Protocol-independent abstraction for traceable stream. It hides the differences between different * protocol and provides tracer driver with common methods for obtaining and setting the tracing * context. + * + * TODO(wbpcode): A new interface should be added to obtain general traceable stream information, + * such as host, RPC method, protocol identification, etc. At the same time, a new interface needs + * to be added to support traversal of all trace contexts. */ class TraceContext { public: diff --git a/envoy/tracing/trace_driver.h b/envoy/tracing/trace_driver.h index 7adb2222cacd4..b544b3a3cd91b 100644 --- a/envoy/tracing/trace_driver.h +++ b/envoy/tracing/trace_driver.h @@ -6,6 +6,7 @@ #include "envoy/common/pure.h" #include "envoy/stream_info/stream_info.h" +#include "envoy/tracing/trace_context.h" #include "envoy/tracing/trace_reason.h" namespace Envoy { @@ -22,7 +23,7 @@ enum class OperationName { Ingress, Egress }; * The context for the custom tag to obtain the tag value. */ struct CustomTagContext { - const Http::RequestHeaderMap* request_headers; + const TraceContext* trace_context; const StreamInfo::StreamInfo& stream_info; }; @@ -117,7 +118,7 @@ class Span { * (implementation-specific) trace. * @param request_headers the headers to which propagation context will be added */ - virtual void injectContext(Http::RequestHeaderMap& request_headers) PURE; + virtual void injectContext(TraceContext& trace_conext) PURE; /** * Create and start a child Span, with this Span as its parent in the trace. @@ -171,7 +172,7 @@ class Driver { /** * Start driver specific span. */ - virtual SpanPtr startSpan(const Config& config, Http::RequestHeaderMap& request_headers, + virtual SpanPtr startSpan(const Config& config, TraceContext& trace_conext, const std::string& operation_name, SystemTime start_time, const Tracing::Decision tracing_decision) PURE; }; diff --git a/generated_api_shadow/envoy/config/core/v3/protocol.proto b/generated_api_shadow/envoy/config/core/v3/protocol.proto index cf98e537261a8..d3b56a5ed7680 100644 --- a/generated_api_shadow/envoy/config/core/v3/protocol.proto +++ b/generated_api_shadow/envoy/config/core/v3/protocol.proto @@ -478,3 +478,11 @@ message Http3ProtocolOptions { // `. google.protobuf.BoolValue override_stream_error_on_invalid_http_message = 2; } + +// A message to control transformations to the :scheme header +message SchemeHeaderTransformation { + oneof transformation { + // Overwrite any Scheme header with the contents of this string. + string scheme_to_overwrite = 1 [(validate.rules).string = {in: "http" in: "https"}]; + } +} diff --git a/generated_api_shadow/envoy/config/core/v4alpha/protocol.proto b/generated_api_shadow/envoy/config/core/v4alpha/protocol.proto index 857bbef739f16..2017020b3d946 100644 --- a/generated_api_shadow/envoy/config/core/v4alpha/protocol.proto +++ b/generated_api_shadow/envoy/config/core/v4alpha/protocol.proto @@ -490,3 +490,14 @@ message Http3ProtocolOptions { // `. google.protobuf.BoolValue override_stream_error_on_invalid_http_message = 2; } + +// A message to control transformations to the :scheme header +message SchemeHeaderTransformation { + option (udpa.annotations.versioning).previous_message_type = + "envoy.config.core.v3.SchemeHeaderTransformation"; + + oneof transformation { + // Overwrite any Scheme header with the contents of this string. + string scheme_to_overwrite = 1 [(validate.rules).string = {in: "http" in: "https"}]; + } +} diff --git a/generated_api_shadow/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto b/generated_api_shadow/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto index 5c35e80d591fd..b5925637a8e5a 100644 --- a/generated_api_shadow/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto +++ b/generated_api_shadow/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto @@ -30,7 +30,7 @@ message DnsCacheCircuitBreakers { // Configuration for the dynamic forward proxy DNS cache. See the :ref:`architecture overview // ` for more information. -// [#next-free-field: 11] +// [#next-free-field: 12] message DnsCacheConfig { option (udpa.annotations.versioning).previous_message_type = "envoy.config.common.dynamic_forward_proxy.v2alpha.DnsCacheConfig"; @@ -114,4 +114,10 @@ message DnsCacheConfig { // performance improvement, in the form of cache hits, for hostnames that are going to be // resolved during steady state and are known at config load time. repeated config.core.v3.SocketAddress preresolve_hostnames = 10; + + // The timeout used for DNS queries. This timeout is independent of any timeout and retry policy + // used by the underlying DNS implementation (e.g., c-areas and Apple DNS) which are opaque. + // Setting this timeout will ensure that queries succeed or fail within the specified time frame + // and are then retried using the standard refresh rates. Defaults to 5s if not set. + google.protobuf.Duration dns_query_timeout = 11 [(validate.rules).duration = {gt {}}]; } diff --git a/generated_api_shadow/envoy/extensions/common/dynamic_forward_proxy/v4alpha/dns_cache.proto b/generated_api_shadow/envoy/extensions/common/dynamic_forward_proxy/v4alpha/dns_cache.proto index dde756a1608a2..ec3bad19d7510 100644 --- a/generated_api_shadow/envoy/extensions/common/dynamic_forward_proxy/v4alpha/dns_cache.proto +++ b/generated_api_shadow/envoy/extensions/common/dynamic_forward_proxy/v4alpha/dns_cache.proto @@ -33,7 +33,7 @@ message DnsCacheCircuitBreakers { // Configuration for the dynamic forward proxy DNS cache. See the :ref:`architecture overview // ` for more information. -// [#next-free-field: 11] +// [#next-free-field: 12] message DnsCacheConfig { option (udpa.annotations.versioning).previous_message_type = "envoy.extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig"; @@ -117,4 +117,10 @@ message DnsCacheConfig { // performance improvement, in the form of cache hits, for hostnames that are going to be // resolved during steady state and are known at config load time. repeated config.core.v4alpha.SocketAddress preresolve_hostnames = 10; + + // The timeout used for DNS queries. This timeout is independent of any timeout and retry policy + // used by the underlying DNS implementation (e.g., c-areas and Apple DNS) which are opaque. + // Setting this timeout will ensure that queries succeed or fail within the specified time frame + // and are then retried using the standard refresh rates. Defaults to 5s if not set. + google.protobuf.Duration dns_query_timeout = 11 [(validate.rules).duration = {gt {}}]; } diff --git a/generated_api_shadow/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto b/generated_api_shadow/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto index 095f3a28d7124..465dbac4c2fc4 100644 --- a/generated_api_shadow/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto +++ b/generated_api_shadow/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto @@ -36,7 +36,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // HTTP connection manager :ref:`configuration overview `. // [#extension: envoy.filters.network.http_connection_manager] -// [#next-free-field: 48] +// [#next-free-field: 49] message HttpConnectionManager { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager"; @@ -376,6 +376,11 @@ message HttpConnectionManager { ServerHeaderTransformation server_header_transformation = 34 [(validate.rules).enum = {defined_only: true}]; + // Allows for explicit transformation of the :scheme header on the request path. + // If not set, Envoy's default :ref:`scheme ` + // handling applies. + config.core.v3.SchemeHeaderTransformation scheme_header_transformation = 48; + // The maximum request headers size for incoming connections. // If unconfigured, the default max request headers allowed is 60 KiB. // Requests that exceed this limit will receive a 431 response. diff --git a/generated_api_shadow/envoy/extensions/filters/network/http_connection_manager/v4alpha/http_connection_manager.proto b/generated_api_shadow/envoy/extensions/filters/network/http_connection_manager/v4alpha/http_connection_manager.proto index db628cc585ec1..43bf53e50f391 100644 --- a/generated_api_shadow/envoy/extensions/filters/network/http_connection_manager/v4alpha/http_connection_manager.proto +++ b/generated_api_shadow/envoy/extensions/filters/network/http_connection_manager/v4alpha/http_connection_manager.proto @@ -34,7 +34,7 @@ option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSIO // HTTP connection manager :ref:`configuration overview `. // [#extension: envoy.filters.network.http_connection_manager] -// [#next-free-field: 48] +// [#next-free-field: 49] message HttpConnectionManager { option (udpa.annotations.versioning).previous_message_type = "envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager"; @@ -374,6 +374,11 @@ message HttpConnectionManager { ServerHeaderTransformation server_header_transformation = 34 [(validate.rules).enum = {defined_only: true}]; + // Allows for explicit transformation of the :scheme header on the request path. + // If not set, Envoy's default :ref:`scheme ` + // handling applies. + config.core.v4alpha.SchemeHeaderTransformation scheme_header_transformation = 48; + // The maximum request headers size for incoming connections. // If unconfigured, the default max request headers allowed is 60 KiB. // Requests that exceed this limit will receive a 431 response. diff --git a/generated_api_shadow/envoy/extensions/request_id/uuid/v3/uuid.proto b/generated_api_shadow/envoy/extensions/request_id/uuid/v3/uuid.proto index 9b8b7a45f5607..5c3f00da28d71 100644 --- a/generated_api_shadow/envoy/extensions/request_id/uuid/v3/uuid.proto +++ b/generated_api_shadow/envoy/extensions/request_id/uuid/v3/uuid.proto @@ -40,4 +40,9 @@ message UuidRequestIdConfig { // stable sampling of traces, access logs, etc. will no longer work and only random sampling will // be possible. google.protobuf.BoolValue pack_trace_reason = 1; + + // Set whether to use :ref:`x-request-id` for sampling or not. + // This defaults to true. See the :ref:`context propagation ` + // overview for more information. + google.protobuf.BoolValue use_request_id_for_trace_sampling = 2; } diff --git a/source/common/common/matchers.cc b/source/common/common/matchers.cc index b1970aead08cb..ec93e4bbf11c1 100644 --- a/source/common/common/matchers.cc +++ b/source/common/common/matchers.cc @@ -112,6 +112,16 @@ bool StringMatcherImpl::match(const absl::string_view value) const { } } +bool StringMatcherImpl::getCaseSensitivePrefixMatch(std::string& prefix) const { + if (matcher_.match_pattern_case() == + envoy::type::matcher::v3::StringMatcher::MatchPatternCase::kPrefix && + !matcher_.ignore_case()) { + prefix = matcher_.prefix(); + return true; + } + return false; +} + ListMatcher::ListMatcher(const envoy::type::matcher::v3::ListMatcher& matcher) : matcher_(matcher) { ASSERT(matcher_.match_pattern_case() == envoy::type::matcher::v3::ListMatcher::MatchPatternCase::kOneOf); diff --git a/source/common/common/matchers.h b/source/common/common/matchers.h index bdc284f2d480f..3bcf1a88bf12b 100644 --- a/source/common/common/matchers.h +++ b/source/common/common/matchers.h @@ -85,11 +85,21 @@ class StringMatcherImpl : public ValueMatcher, public StringMatcher { public: explicit StringMatcherImpl(const envoy::type::matcher::v3::StringMatcher& matcher); + // StringMatcher bool match(const absl::string_view value) const override; bool match(const ProtobufWkt::Value& value) const override; const envoy::type::matcher::v3::StringMatcher& matcher() const { return matcher_; } + /** + * Helps applications optimize the case where a matcher is a case-sensitive + * prefix-match. + * + * @param prefix the returned prefix string + * @return true if the matcher is a case-sensitive prefix-match. + */ + bool getCaseSensitivePrefixMatch(std::string& prefix) const; + private: const envoy::type::matcher::v3::StringMatcher matcher_; Regex::CompiledMatcherPtr regex_; diff --git a/source/common/config/utility.cc b/source/common/config/utility.cc index d6d526b792be1..89b752be1940a 100644 --- a/source/common/config/utility.cc +++ b/source/common/config/utility.cc @@ -223,8 +223,9 @@ Utility::createTagProducer(const envoy::config::bootstrap::v3::Bootstrap& bootst } Stats::StatsMatcherPtr -Utility::createStatsMatcher(const envoy::config::bootstrap::v3::Bootstrap& bootstrap) { - return std::make_unique(bootstrap.stats_config()); +Utility::createStatsMatcher(const envoy::config::bootstrap::v3::Bootstrap& bootstrap, + Stats::SymbolTable& symbol_table) { + return std::make_unique(bootstrap.stats_config(), symbol_table); } Stats::HistogramSettingsConstPtr diff --git a/source/common/config/utility.h b/source/common/config/utility.h index bce7b7f688d3b..e4227ff06caec 100644 --- a/source/common/config/utility.h +++ b/source/common/config/utility.h @@ -431,7 +431,8 @@ class Utility { * Create StatsMatcher instance. */ static Stats::StatsMatcherPtr - createStatsMatcher(const envoy::config::bootstrap::v3::Bootstrap& bootstrap); + createStatsMatcher(const envoy::config::bootstrap::v3::Bootstrap& bootstrap, + Stats::SymbolTable& symbol_table); /** * Create HistogramSettings instance. diff --git a/source/common/config/xds_mux/BUILD b/source/common/config/xds_mux/BUILD new file mode 100644 index 0000000000000..f934e46e19dc4 --- /dev/null +++ b/source/common/config/xds_mux/BUILD @@ -0,0 +1,53 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_library( + name = "delta_subscription_state_lib", + srcs = ["delta_subscription_state.cc"], + hdrs = ["delta_subscription_state.h"], + deps = [ + ":subscription_state_lib", + "//source/common/config:api_version_lib", + "//source/common/config:utility_lib", + "//source/common/grpc:common_lib", + "//source/common/protobuf", + "@envoy_api//envoy/service/discovery/v3:pkg_cc_proto", + ], +) + +envoy_cc_library( + name = "sotw_subscription_state_lib", + srcs = ["sotw_subscription_state.cc"], + hdrs = ["sotw_subscription_state.h"], + deps = [ + ":subscription_state_lib", + "//source/common/config:api_version_lib", + "//source/common/config:decoded_resource_lib", + "//source/common/config:utility_lib", + "//source/common/grpc:common_lib", + "//source/common/protobuf", + "@envoy_api//envoy/service/discovery/v3:pkg_cc_proto", + ], +) + +envoy_cc_library( + name = "subscription_state_lib", + hdrs = ["subscription_state.h"], + deps = [ + "//envoy/config:subscription_interface", + "//envoy/event:dispatcher_interface", + "//envoy/local_info:local_info_interface", + "//source/common/common:minimal_logger_lib", + "//source/common/config:ttl_lib", + "//source/common/config:update_ack_lib", + "//source/common/config:utility_lib", + "@envoy_api//envoy/service/discovery/v3:pkg_cc_proto", + ], +) diff --git a/source/common/config/xds_mux/delta_subscription_state.cc b/source/common/config/xds_mux/delta_subscription_state.cc new file mode 100644 index 0000000000000..dd3b8e686cb73 --- /dev/null +++ b/source/common/config/xds_mux/delta_subscription_state.cc @@ -0,0 +1,196 @@ +#include "source/common/config/xds_mux/delta_subscription_state.h" + +#include "envoy/event/dispatcher.h" +#include "envoy/service/discovery/v3/discovery.pb.h" + +#include "source/common/common/hash.h" +#include "source/common/config/utility.h" +#include "source/common/runtime/runtime_features.h" + +namespace Envoy { +namespace Config { +namespace XdsMux { + +DeltaSubscriptionState::DeltaSubscriptionState(std::string type_url, + UntypedConfigUpdateCallbacks& watch_map, + Event::Dispatcher& dispatcher, const bool wildcard) + : BaseSubscriptionState(std::move(type_url), watch_map, dispatcher), + // TODO(snowp): Hard coding VHDS here is temporary until we can move it away from relying on + // empty resources as updates. + supports_heartbeats_(type_url_ != "envoy.config.route.v3.VirtualHost"), wildcard_(wildcard) {} + +DeltaSubscriptionState::~DeltaSubscriptionState() = default; + +void DeltaSubscriptionState::updateSubscriptionInterest( + const absl::flat_hash_set& cur_added, + const absl::flat_hash_set& cur_removed) { + for (const auto& a : cur_added) { + resource_state_[a] = ResourceState::waitingForServer(); + // If interest in a resource is removed-then-added (all before a discovery request + // can be sent), we must treat it as a "new" addition: our user may have forgotten its + // copy of the resource after instructing us to remove it, and need to be reminded of it. + names_removed_.erase(a); + names_added_.insert(a); + } + for (const auto& r : cur_removed) { + resource_state_.erase(r); + // Ideally, when interest in a resource is added-then-removed in between requests, + // we would avoid putting a superfluous "unsubscribe [resource that was never subscribed]" + // in the request. However, the removed-then-added case *does* need to go in the request, + // and due to how we accomplish that, it's difficult to distinguish remove-add-remove from + // add-remove (because "remove-add" has to be treated as equivalent to just "add"). + names_added_.erase(r); + names_removed_.insert(r); + } +} + +// Not having sent any requests yet counts as an "update pending" since you're supposed to resend +// the entirety of your interest at the start of a stream, even if nothing has changed. +bool DeltaSubscriptionState::subscriptionUpdatePending() const { + return !names_added_.empty() || !names_removed_.empty() || + !any_request_sent_yet_in_current_stream_ || dynamicContextChanged(); +} + +bool DeltaSubscriptionState::isHeartbeatResource( + const envoy::service::discovery::v3::Resource& resource) const { + if (!supports_heartbeats_ && + !Runtime::runtimeFeatureEnabled("envoy.reloadable_features.vhds_heartbeats")) { + return false; + } + const auto itr = resource_state_.find(resource.name()); + if (itr == resource_state_.end()) { + return false; + } + + return !resource.has_resource() && !itr->second.isWaitingForServer() && + resource.version() == itr->second.version(); +} + +void DeltaSubscriptionState::handleGoodResponse( + const envoy::service::discovery::v3::DeltaDiscoveryResponse& message) { + absl::flat_hash_set names_added_removed; + Protobuf::RepeatedPtrField non_heartbeat_resources; + for (const auto& resource : message.resources()) { + if (!names_added_removed.insert(resource.name()).second) { + throw EnvoyException( + fmt::format("duplicate name {} found among added/updated resources", resource.name())); + } + if (isHeartbeatResource(resource)) { + continue; + } + // TODO (dmitri-d) consider changing onConfigUpdate callback interface to avoid copying of + // resources + non_heartbeat_resources.Add()->CopyFrom(resource); + // DeltaDiscoveryResponses for unresolved aliases don't contain an actual resource + if (!resource.has_resource() && resource.aliases_size() > 0) { + continue; + } + if (message.type_url() != resource.resource().type_url()) { + throw EnvoyException(fmt::format("type URL {} embedded in an individual Any does not match " + "the message-wide type URL {} in DeltaDiscoveryResponse {}", + resource.resource().type_url(), message.type_url(), + message.DebugString())); + } + } + for (const auto& name : message.removed_resources()) { + if (!names_added_removed.insert(name).second) { + throw EnvoyException( + fmt::format("duplicate name {} found in the union of added+removed resources", name)); + } + } + + { + const auto scoped_update = ttl_.scopedTtlUpdate(); + for (const auto& resource : message.resources()) { + addResourceState(resource); + } + } + + callbacks().onConfigUpdate(non_heartbeat_resources, message.removed_resources(), + message.system_version_info()); + + // If a resource is gone, there is no longer a meaningful version for it that makes sense to + // provide to the server upon stream reconnect: either it will continue to not exist, in which + // case saying nothing is fine, or the server will bring back something new, which we should + // receive regardless (which is the logic that not specifying a version will get you). + // + // So, leave the version map entry present but blank. It will be left out of + // initial_resource_versions messages, but will remind us to explicitly tell the server "I'm + // cancelling my subscription" when we lose interest. + for (const auto& resource_name : message.removed_resources()) { + if (resource_state_.find(resource_name) != resource_state_.end()) { + resource_state_[resource_name] = ResourceState::waitingForServer(); + } + } + ENVOY_LOG(debug, "Delta config for {} accepted with {} resources added, {} removed", typeUrl(), + message.resources().size(), message.removed_resources().size()); +} + +std::unique_ptr +DeltaSubscriptionState::getNextRequestInternal() { + auto request = std::make_unique(); + request->set_type_url(typeUrl()); + if (!any_request_sent_yet_in_current_stream_) { + any_request_sent_yet_in_current_stream_ = true; + // initial_resource_versions "must be populated for first request in a stream". + // Also, since this might be a new server, we must explicitly state *all* of our subscription + // interest. + for (auto const& [resource_name, resource_state] : resource_state_) { + // Populate initial_resource_versions with the resource versions we currently have. + // Resources we are interested in, but are still waiting to get any version of from the + // server, do not belong in initial_resource_versions. (But do belong in new subscriptions!) + if (!resource_state.isWaitingForServer()) { + (*request->mutable_initial_resource_versions())[resource_name] = resource_state.version(); + } + // As mentioned above, fill resource_names_subscribe with everything, including names we + // have yet to receive any resource for unless this is a wildcard subscription, for which + // the first request on a stream must be without any resource names. + if (!wildcard_) { + names_added_.insert(resource_name); + } + } + // Wildcard subscription initial requests must have no resource_names_subscribe. + if (wildcard_) { + names_added_.clear(); + } + names_removed_.clear(); + } + + std::copy(names_added_.begin(), names_added_.end(), + Protobuf::RepeatedFieldBackInserter(request->mutable_resource_names_subscribe())); + std::copy(names_removed_.begin(), names_removed_.end(), + Protobuf::RepeatedFieldBackInserter(request->mutable_resource_names_unsubscribe())); + names_added_.clear(); + names_removed_.clear(); + + return request; +} + +void DeltaSubscriptionState::addResourceState( + const envoy::service::discovery::v3::Resource& resource) { + setResourceTtl(resource); + resource_state_[resource.name()] = ResourceState(resource.version()); +} + +void DeltaSubscriptionState::setResourceTtl( + const envoy::service::discovery::v3::Resource& resource) { + if (resource.has_ttl()) { + ttl_.add(std::chrono::milliseconds(DurationUtil::durationToMilliseconds(resource.ttl())), + resource.name()); + } else { + ttl_.clear(resource.name()); + } +} + +void DeltaSubscriptionState::ttlExpiryCallback(const std::vector& expired) { + Protobuf::RepeatedPtrField removed_resources; + for (const auto& resource : expired) { + resource_state_[resource] = ResourceState::waitingForServer(); + removed_resources.Add(std::string(resource)); + } + callbacks().onConfigUpdate({}, removed_resources, ""); +} + +} // namespace XdsMux +} // namespace Config +} // namespace Envoy diff --git a/source/common/config/xds_mux/delta_subscription_state.h b/source/common/config/xds_mux/delta_subscription_state.h new file mode 100644 index 0000000000000..801bd5edc0c1a --- /dev/null +++ b/source/common/config/xds_mux/delta_subscription_state.h @@ -0,0 +1,98 @@ +#pragma once + +#include "envoy/grpc/status.h" + +#include "source/common/common/assert.h" +#include "source/common/common/logger.h" +#include "source/common/config/api_version.h" +#include "source/common/config/xds_mux/subscription_state.h" + +#include "absl/container/node_hash_map.h" +#include "absl/types/optional.h" + +namespace Envoy { +namespace Config { +namespace XdsMux { + +// Tracks the state of a delta xDS-over-gRPC protocol session. +class DeltaSubscriptionState + : public BaseSubscriptionState { +public: + DeltaSubscriptionState(std::string type_url, UntypedConfigUpdateCallbacks& watch_map, + Event::Dispatcher& dispatcher, const bool wildcard); + + ~DeltaSubscriptionState() override; + + // Update which resources we're interested in subscribing to. + void updateSubscriptionInterest(const absl::flat_hash_set& cur_added, + const absl::flat_hash_set& cur_removed) override; + + // Whether there was a change in our subscription interest we have yet to inform the server of. + bool subscriptionUpdatePending() const override; + + void markStreamFresh() override { any_request_sent_yet_in_current_stream_ = false; } + + void ttlExpiryCallback(const std::vector& expired) override; + + DeltaSubscriptionState(const DeltaSubscriptionState&) = delete; + DeltaSubscriptionState& operator=(const DeltaSubscriptionState&) = delete; + +private: + std::unique_ptr + getNextRequestInternal() override; + + void setResourceTtl(const envoy::service::discovery::v3::Resource& resource); + bool isHeartbeatResource(const envoy::service::discovery::v3::Resource& resource) const; + void + handleGoodResponse(const envoy::service::discovery::v3::DeltaDiscoveryResponse& message) override; + void addResourceState(const envoy::service::discovery::v3::Resource& resource); + + class ResourceState { + public: + explicit ResourceState(absl::string_view version) : version_(version) {} + // Builds a ResourceVersion in the waitingForServer state. + ResourceState() = default; + // Self-documenting alias of default constructor. + static ResourceState waitingForServer() { return ResourceState(); } + + // If true, we currently have no version of this resource - we are waiting for the server to + // provide us with one. + bool isWaitingForServer() const { return version_ == absl::nullopt; } + + // Must not be called if waitingForServer() == true. + std::string version() const { + ASSERT(version_.has_value()); + return version_.value_or(""); + } + + private: + absl::optional version_; + }; + + // Not all xDS resources support heartbeats due to there being specific information encoded in + // an empty response, which is indistinguishable from a heartbeat in some cases. For now we just + // disable heartbeats for these resources (currently only VHDS). + const bool supports_heartbeats_; + + // Is the subscription is for a wildcard request. + const bool wildcard_; + + // A map from resource name to per-resource version. The keys of this map are exactly the resource + // names we are currently interested in. Those in the waitingForServer state currently don't have + // any version for that resource: we need to inform the server if we lose interest in them, but we + // also need to *not* include them in the initial_resource_versions map upon a reconnect. + absl::node_hash_map resource_state_; + + bool any_request_sent_yet_in_current_stream_{}; + + // Tracks changes in our subscription interest since the previous DeltaDiscoveryRequest we sent. + // TODO: Can't use absl::flat_hash_set due to ordering issues in gTest expectation matching. + // Feel free to change to an unordered container once we figure out how to make it work. + std::set names_added_; + std::set names_removed_; +}; + +} // namespace XdsMux +} // namespace Config +} // namespace Envoy diff --git a/source/common/config/xds_mux/sotw_subscription_state.cc b/source/common/config/xds_mux/sotw_subscription_state.cc new file mode 100644 index 0000000000000..bb7a9f4c3a9c6 --- /dev/null +++ b/source/common/config/xds_mux/sotw_subscription_state.cc @@ -0,0 +1,125 @@ +#include "source/common/config/xds_mux/sotw_subscription_state.h" + +#include "source/common/config/utility.h" + +namespace Envoy { +namespace Config { +namespace XdsMux { + +SotwSubscriptionState::SotwSubscriptionState(std::string type_url, + UntypedConfigUpdateCallbacks& callbacks, + Event::Dispatcher& dispatcher, + OpaqueResourceDecoder& resource_decoder) + : BaseSubscriptionState(std::move(type_url), callbacks, dispatcher), + resource_decoder_(resource_decoder) {} + +SotwSubscriptionState::~SotwSubscriptionState() = default; + +void SotwSubscriptionState::updateSubscriptionInterest( + const absl::flat_hash_set& cur_added, + const absl::flat_hash_set& cur_removed) { + for (const auto& a : cur_added) { + names_tracked_.insert(a); + } + for (const auto& r : cur_removed) { + names_tracked_.erase(r); + } + if (!cur_added.empty() || !cur_removed.empty()) { + update_pending_ = true; + } +} + +// Not having sent any requests yet counts as an "update pending" since you're supposed to resend +// the entirety of your interest at the start of a stream, even if nothing has changed. +bool SotwSubscriptionState::subscriptionUpdatePending() const { + return update_pending_ || dynamicContextChanged(); +} + +void SotwSubscriptionState::markStreamFresh() { + last_good_version_info_ = absl::nullopt; + last_good_nonce_ = absl::nullopt; + update_pending_ = true; + clearDynamicContextChanged(); +} + +void SotwSubscriptionState::handleGoodResponse( + const envoy::service::discovery::v3::DiscoveryResponse& message) { + Protobuf::RepeatedPtrField non_heartbeat_resources; + std::vector resources_with_ttl( + message.resources().size()); + + { + const auto scoped_update = ttl_.scopedTtlUpdate(); + for (const auto& any : message.resources()) { + if (!any.Is() && + any.type_url() != message.type_url()) { + throw EnvoyException(fmt::format("type URL {} embedded in an individual Any does not match " + "the message-wide type URL {} in DiscoveryResponse {}", + any.type_url(), message.type_url(), + message.DebugString())); + } + + auto decoded_resource = + DecodedResourceImpl::fromResource(resource_decoder_, any, message.version_info()); + setResourceTtl(*decoded_resource); + if (isHeartbeatResource(*decoded_resource, message.version_info())) { + continue; + } + non_heartbeat_resources.Add()->CopyFrom(any); + } + } + + // TODO (dmitri-d) to eliminate decoding of resources twice consider expanding the interface to + // support passing of decoded resources + callbacks().onConfigUpdate(non_heartbeat_resources, message.version_info()); + // Now that we're passed onConfigUpdate() without an exception thrown, we know we're good. + last_good_version_info_ = message.version_info(); + last_good_nonce_ = message.nonce(); + ENVOY_LOG(debug, "Config update for {} (version {}) accepted with {} resources", typeUrl(), + message.version_info(), message.resources().size()); +} + +std::unique_ptr +SotwSubscriptionState::getNextRequestInternal() { + auto request = std::make_unique(); + request->set_type_url(typeUrl()); + std::copy(names_tracked_.begin(), names_tracked_.end(), + Protobuf::RepeatedFieldBackInserter(request->mutable_resource_names())); + if (last_good_version_info_.has_value()) { + request->set_version_info(last_good_version_info_.value()); + } + // Default response_nonce to the last known good one. If we are being called by + // getNextRequestWithAck(), this value will be overwritten. + if (last_good_nonce_.has_value()) { + request->set_response_nonce(last_good_nonce_.value()); + } + + update_pending_ = false; + return request; +} + +void SotwSubscriptionState::ttlExpiryCallback(const std::vector& expired) { + Protobuf::RepeatedPtrField removed_resources; + for (const auto& resource : expired) { + removed_resources.Add(std::string(resource)); + } + callbacks().onConfigUpdate({}, removed_resources, ""); +} + +void SotwSubscriptionState::setResourceTtl(const DecodedResourceImpl& decoded_resource) { + if (decoded_resource.ttl()) { + ttl_.add(std::chrono::milliseconds(*decoded_resource.ttl()), decoded_resource.name()); + } else { + ttl_.clear(decoded_resource.name()); + } +} + +bool SotwSubscriptionState::isHeartbeatResource(const DecodedResource& resource, + const std::string& version) { + return !resource.hasResource() && last_good_version_info_.has_value() && + version == last_good_version_info_.value(); +} + +} // namespace XdsMux +} // namespace Config +} // namespace Envoy diff --git a/source/common/config/xds_mux/sotw_subscription_state.h b/source/common/config/xds_mux/sotw_subscription_state.h new file mode 100644 index 0000000000000..4d191fb93a3c4 --- /dev/null +++ b/source/common/config/xds_mux/sotw_subscription_state.h @@ -0,0 +1,67 @@ +#pragma once + +#include "envoy/grpc/status.h" +#include "envoy/service/discovery/v3/discovery.pb.h" + +#include "source/common/common/assert.h" +#include "source/common/common/hash.h" +#include "source/common/config/decoded_resource_impl.h" +#include "source/common/config/xds_mux/subscription_state.h" + +#include "absl/types/optional.h" + +namespace Envoy { +namespace Config { +namespace XdsMux { + +// Tracks the state of a "state-of-the-world" (i.e. not delta) xDS-over-gRPC protocol session. +class SotwSubscriptionState + : public BaseSubscriptionState { +public: + // Note that, outside of tests, we expect callbacks to always be a WatchMap. + SotwSubscriptionState(std::string type_url, UntypedConfigUpdateCallbacks& callbacks, + Event::Dispatcher& dispatcher, OpaqueResourceDecoder& resource_decoder); + ~SotwSubscriptionState() override; + + // Update which resources we're interested in subscribing to. + void updateSubscriptionInterest(const absl::flat_hash_set& cur_added, + const absl::flat_hash_set& cur_removed) override; + + // Whether there was a change in our subscription interest we have yet to inform the server of. + bool subscriptionUpdatePending() const override; + + void markStreamFresh() override; + + void ttlExpiryCallback(const std::vector& expired) override; + + SotwSubscriptionState(const SotwSubscriptionState&) = delete; + SotwSubscriptionState& operator=(const SotwSubscriptionState&) = delete; + +private: + std::unique_ptr + getNextRequestInternal() override; + + void handleGoodResponse(const envoy::service::discovery::v3::DiscoveryResponse& message) override; + void setResourceTtl(const DecodedResourceImpl& decoded_resource); + bool isHeartbeatResource(const DecodedResource& resource, const std::string& version); + + OpaqueResourceDecoder& resource_decoder_; + + // The version_info carried by the last accepted DiscoveryResponse. + // Remains empty until one is accepted. + absl::optional last_good_version_info_; + // The nonce carried by the last accepted DiscoveryResponse. + // Remains empty until one is accepted. + // Used when it's time to make a spontaneous (i.e. not primarily meant as an ACK) request. + absl::optional last_good_nonce_; + + // Starts true because we should send a request upon subscription start. + bool update_pending_{true}; + + absl::flat_hash_set names_tracked_; +}; + +} // namespace XdsMux +} // namespace Config +} // namespace Envoy diff --git a/source/common/config/xds_mux/subscription_state.h b/source/common/config/xds_mux/subscription_state.h new file mode 100644 index 0000000000000..6607989745763 --- /dev/null +++ b/source/common/config/xds_mux/subscription_state.h @@ -0,0 +1,118 @@ +#pragma once + +#include +#include + +#include "envoy/common/pure.h" +#include "envoy/config/subscription.h" +#include "envoy/event/dispatcher.h" +#include "envoy/service/discovery/v3/discovery.pb.h" + +#include "source/common/config/ttl.h" +#include "source/common/config/update_ack.h" +#include "source/common/config/utility.h" +#include "source/common/protobuf/protobuf.h" + +#include "absl/strings/string_view.h" + +namespace Envoy { +namespace Config { +namespace XdsMux { + +class SubscriptionState {}; + +// Tracks the protocol state of an individual ongoing xDS-over-gRPC session, for a single type_url. +// There can be multiple SubscriptionStates active, one per type_url. They will all be +// blissfully unaware of each other's existence, even when their messages are being multiplexed +// together by ADS. +// This is the abstract parent class for both the delta and state-of-the-world xDS variants. +template +class BaseSubscriptionState : public SubscriptionState, + public Logger::Loggable { +public: + // Note that, outside of tests, we expect callbacks to always be a WatchMap. + BaseSubscriptionState(std::string type_url, UntypedConfigUpdateCallbacks& callbacks, + Event::Dispatcher& dispatcher) + : ttl_([this](const std::vector& expired) { ttlExpiryCallback(expired); }, + dispatcher, dispatcher.timeSource()), + type_url_(std::move(type_url)), callbacks_(callbacks), dispatcher_(dispatcher) {} + + virtual ~BaseSubscriptionState() = default; + + // Update which resources we're interested in subscribing to. + virtual void updateSubscriptionInterest(const absl::flat_hash_set& cur_added, + const absl::flat_hash_set& cur_removed) PURE; + + void setDynamicContextChanged() { dynamic_context_changed_ = true; } + void clearDynamicContextChanged() { dynamic_context_changed_ = false; } + bool dynamicContextChanged() const { return dynamic_context_changed_; } + + // Whether there was a change in our subscription interest we have yet to inform the server of. + virtual bool subscriptionUpdatePending() const PURE; + + virtual void markStreamFresh() PURE; + + UpdateAck handleResponse(const RS& response) { + // We *always* copy the response's nonce into the next request, even if we're going to make that + // request a NACK by setting error_detail. + UpdateAck ack(response.nonce(), typeUrl()); + ENVOY_LOG(debug, "Handling response for {}", typeUrl()); + TRY_ASSERT_MAIN_THREAD { handleGoodResponse(response); } + END_TRY + catch (const EnvoyException& e) { + handleBadResponse(e, ack); + } + return ack; + } + + void handleEstablishmentFailure() { + ENVOY_LOG(debug, "SubscriptionState establishment failed for {}", typeUrl()); + callbacks().onConfigUpdateFailed(Envoy::Config::ConfigUpdateFailureReason::ConnectionFailure, + nullptr); + } + + // Returns the next gRPC request proto to be sent off to the server, based on this object's + // understanding of the current protocol state, and new resources that Envoy wants to request. + std::unique_ptr getNextRequestAckless() { return getNextRequestInternal(); } + + // The WithAck version first calls the ack-less version, then adds in the passed-in ack. + // Returns a new'd pointer, meant to be owned by the caller, who is expected to know what type the + // pointer actually is. + std::unique_ptr getNextRequestWithAck(const UpdateAck& ack) { + auto request = getNextRequestInternal(); + request->set_response_nonce(ack.nonce_); + ENVOY_LOG(debug, "ACK for {} will have nonce {}", typeUrl(), ack.nonce_); + if (ack.error_detail_.code() != Grpc::Status::WellKnownGrpcStatus::Ok) { + // Don't needlessly make the field present-but-empty if status is ok. + request->mutable_error_detail()->CopyFrom(ack.error_detail_); + } + return request; + } + + virtual void ttlExpiryCallback(const std::vector& type_url) PURE; + +protected: + virtual std::unique_ptr getNextRequestInternal() PURE; + virtual void handleGoodResponse(const RS& message) PURE; + void handleBadResponse(const EnvoyException& e, UpdateAck& ack) { + // Note that error_detail being set is what indicates that a (Delta)DiscoveryRequest is a NACK. + ack.error_detail_.set_code(Grpc::Status::WellKnownGrpcStatus::Internal); + ack.error_detail_.set_message(Config::Utility::truncateGrpcStatusMessage(e.what())); + ENVOY_LOG(warn, "Config for {} rejected: {}", typeUrl(), e.what()); + callbacks().onConfigUpdateFailed(Envoy::Config::ConfigUpdateFailureReason::UpdateRejected, &e); + } + + std::string typeUrl() const { return type_url_; } + UntypedConfigUpdateCallbacks& callbacks() const { return callbacks_; } + + TtlManager ttl_; + const std::string type_url_; + // callbacks_ is expected (outside of tests) to be a WatchMap. + UntypedConfigUpdateCallbacks& callbacks_; + Event::Dispatcher& dispatcher_; + bool dynamic_context_changed_{}; +}; + +} // namespace XdsMux +} // namespace Config +} // namespace Envoy diff --git a/source/common/formatter/substitution_formatter.cc b/source/common/formatter/substitution_formatter.cc index c8419263f367c..e1001871f038f 100644 --- a/source/common/formatter/substitution_formatter.cc +++ b/source/common/formatter/substitution_formatter.cc @@ -814,7 +814,7 @@ StreamInfoFormatter::StreamInfoFormatter(const std::string& field_name) { } else if (field_name == "CONNECTION_ID") { field_extractor_ = std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { - return stream_info.connectionID().value_or(0); + return stream_info.downstreamAddressProvider().connectionID().value_or(0); }); } else if (field_name == "REQUESTED_SERVER_NAME") { field_extractor_ = std::make_unique( diff --git a/source/common/http/conn_manager_config.h b/source/common/http/conn_manager_config.h index d69beb04e0805..b56821b665d12 100644 --- a/source/common/http/conn_manager_config.h +++ b/source/common/http/conn_manager_config.h @@ -348,6 +348,11 @@ class ConnectionManagerConfig { virtual HttpConnectionManagerProto::ServerHeaderTransformation serverHeaderTransformation() const PURE; + /** + * @return const absl::optional the scheme name to write into requests. + */ + virtual const absl::optional& schemeToSet() const PURE; + /** * @return ConnectionManagerStats& the stats to write to. */ diff --git a/source/common/http/conn_manager_impl.cc b/source/common/http/conn_manager_impl.cc index e792b2735d269..e70d5f0c91089 100644 --- a/source/common/http/conn_manager_impl.cc +++ b/source/common/http/conn_manager_impl.cc @@ -651,9 +651,6 @@ ConnectionManagerImpl::ActiveStream::ActiveStream(ConnectionManagerImpl& connect filter_manager_.streamInfo().setDownstreamSslConnection( connection_manager_.read_callbacks_->connection().ssl()); - filter_manager_.streamInfo().setConnectionID( - connection_manager_.read_callbacks_->connection().id()); - if (connection_manager_.config_.streamIdleTimeout().count()) { idle_timeout_ms_ = connection_manager_.config_.streamIdleTimeout(); stream_idle_timer_ = diff --git a/source/common/http/conn_manager_utility.cc b/source/common/http/conn_manager_utility.cc index 4c0bddcd6a61a..0b288a022bdb7 100644 --- a/source/common/http/conn_manager_utility.cc +++ b/source/common/http/conn_manager_utility.cc @@ -165,6 +165,10 @@ ConnectionManagerUtility::MutateRequestHeadersResult ConnectionManagerUtility::m request_headers.setReferenceForwardedProto(connection.ssl() ? Headers::get().SchemeValues.Https : Headers::get().SchemeValues.Http); } + if (config.schemeToSet().has_value()) { + request_headers.setScheme(config.schemeToSet().value()); + request_headers.setForwardedProto(config.schemeToSet().value()); + } // If :scheme is not set, sets :scheme based on X-Forwarded-Proto if a valid scheme, // else encryption level. // X-Forwarded-Proto and :scheme may still differ if different values are sent from downstream. @@ -283,6 +287,9 @@ Tracing::Reason ConnectionManagerUtility::mutateTracingRequestHeader( } auto rid_extension = config.requestIDExtension(); + if (!rid_extension->useRequestIdForTraceSampling()) { + return Tracing::Reason::Sampling; + } const auto rid_to_integer = rid_extension->toInteger(request_headers); // Skip if request-id is corrupted, or non-existent if (!rid_to_integer.has_value()) { diff --git a/source/common/http/filter_manager.cc b/source/common/http/filter_manager.cc index 8f855eba2495d..187647125234a 100644 --- a/source/common/http/filter_manager.cc +++ b/source/common/http/filter_manager.cc @@ -13,11 +13,13 @@ #include "source/common/http/header_utility.h" #include "source/common/http/utility.h" +#include "matching/data_impl.h" + namespace Envoy { namespace Http { namespace { -REGISTER_FACTORY(SkipActionFactory, Matcher::ActionFactory); +REGISTER_FACTORY(SkipActionFactory, Matcher::ActionFactory); template using FilterList = std::list>; diff --git a/source/common/http/filter_manager.h b/source/common/http/filter_manager.h index 008baa50bcd81..2445faed0a107 100644 --- a/source/common/http/filter_manager.h +++ b/source/common/http/filter_manager.h @@ -68,11 +68,12 @@ class FilterMatchState { using FilterMatchStateSharedPtr = std::shared_ptr; -class SkipActionFactory : public Matcher::ActionFactory { +class SkipActionFactory : public Matcher::ActionFactory { public: std::string name() const override { return "skip"; } - Matcher::ActionFactoryCb createActionFactoryCb(const Protobuf::Message&, const std::string&, - Server::Configuration::FactoryContext&) override { + Matcher::ActionFactoryCb createActionFactoryCb(const Protobuf::Message&, + Matching::HttpFilterActionContext&, + ProtobufMessage::ValidationVisitor&) override { return []() { return std::make_unique(); }; } ProtobufTypes::MessagePtr createEmptyConfigProto() override { @@ -621,6 +622,9 @@ class OverridableRemoteSocketAddressSetterStreamInfo : public StreamInfo::Stream absl::string_view requestedServerName() const override { return StreamInfoImpl::downstreamAddressProvider().requestedServerName(); } + absl::optional connectionID() const override { + return StreamInfoImpl::downstreamAddressProvider().connectionID(); + } void dumpState(std::ostream& os, int indent_level) const override { StreamInfoImpl::dumpState(os, indent_level); diff --git a/source/common/http/match_wrapper/BUILD b/source/common/http/match_wrapper/BUILD index 6515bcffe786e..43de46e486ba1 100644 --- a/source/common/http/match_wrapper/BUILD +++ b/source/common/http/match_wrapper/BUILD @@ -15,6 +15,7 @@ envoy_cc_library( deps = [ "//envoy/registry", "//envoy/server:filter_config_interface", + "//source/common/http/matching:data_impl_lib", "//source/common/matcher:matcher_lib", "//source/extensions/filters/http/common:factory_base_lib", "@envoy_api//envoy/extensions/common/matching/v3:pkg_cc_proto", diff --git a/source/common/http/match_wrapper/config.cc b/source/common/http/match_wrapper/config.cc index 5d9ecd2f1685a..3ed2eece72a21 100644 --- a/source/common/http/match_wrapper/config.cc +++ b/source/common/http/match_wrapper/config.cc @@ -5,6 +5,7 @@ #include "envoy/registry/registry.h" #include "source/common/config/utility.h" +#include "source/common/http/matching/data_impl.h" #include "source/common/matcher/matcher.h" #include "absl/status/status.h" @@ -103,9 +104,11 @@ Envoy::Http::FilterFactoryCb MatchWrapperConfig::createFilterFactoryFromProtoTyp MatchTreeValidationVisitor validation_visitor(*factory.matchingRequirements()); - auto match_tree = - Matcher::MatchTreeFactory(prefix, context, validation_visitor) - .create(proto_config.matcher()); + Envoy::Http::Matching::HttpFilterActionContext action_context{prefix, context}; + auto match_tree = Matcher::MatchTreeFactory( + action_context, context.getServerFactoryContext(), validation_visitor) + .create(proto_config.matcher()); if (!validation_visitor.errors().empty()) { // TODO(snowp): Output all violations. diff --git a/source/common/http/matching/data_impl.h b/source/common/http/matching/data_impl.h index 136ded3126100..3299b46f13cc5 100644 --- a/source/common/http/matching/data_impl.h +++ b/source/common/http/matching/data_impl.h @@ -1,6 +1,7 @@ #pragma once #include "envoy/http/filter.h" +#include "envoy/server/factory_context.h" namespace Envoy { namespace Http { @@ -55,6 +56,10 @@ class HttpMatchingDataImpl : public HttpMatchingData { using HttpMatchingDataImplSharedPtr = std::shared_ptr; +struct HttpFilterActionContext { + const std::string& stat_prefix_; + Server::Configuration::FactoryContext& factory_context_; +}; } // namespace Matching } // namespace Http } // namespace Envoy diff --git a/source/common/http/matching/inputs.h b/source/common/http/matching/inputs.h index 052308c5c547d..551cab256fb75 100644 --- a/source/common/http/matching/inputs.h +++ b/source/common/http/matching/inputs.h @@ -55,9 +55,9 @@ class HttpHeadersDataInputFactoryBase : public Matcher::DataInputFactory createDataInputFactoryCb(const Protobuf::Message& config, - Server::Configuration::FactoryContext& factory_context) override { - const auto& typed_config = MessageUtil::downcastAndValidate( - config, factory_context.messageValidationVisitor()); + ProtobufMessage::ValidationVisitor& validation_visitor) override { + const auto& typed_config = + MessageUtil::downcastAndValidate(config, validation_visitor); return [header_name = typed_config.header_name()] { return std::make_unique(header_name); diff --git a/source/common/http/utility.cc b/source/common/http/utility.cc index 50d2123b5a835..3320090c1c3a6 100644 --- a/source/common/http/utility.cc +++ b/source/common/http/utility.cc @@ -768,6 +768,23 @@ const std::string& Utility::getProtocolString(const Protocol protocol) { NOT_REACHED_GCOVR_EXCL_LINE; } +std::string Utility::buildOriginalUri(const Http::RequestHeaderMap& request_headers, + const absl::optional max_path_length) { + if (!request_headers.Path()) { + return ""; + } + absl::string_view path(request_headers.EnvoyOriginalPath() + ? request_headers.getEnvoyOriginalPathValue() + : request_headers.getPathValue()); + + if (max_path_length.has_value() && path.length() > max_path_length) { + path = path.substr(0, max_path_length.value()); + } + + return absl::StrCat(request_headers.getForwardedProtoValue(), "://", + request_headers.getHostValue(), path); +} + void Utility::extractHostPathFromUri(const absl::string_view& uri, absl::string_view& host, absl::string_view& path) { /** diff --git a/source/common/http/utility.h b/source/common/http/utility.h index a035fcafafd9f..c5dd2e270d0cb 100644 --- a/source/common/http/utility.h +++ b/source/common/http/utility.h @@ -395,6 +395,15 @@ bool sanitizeConnectionHeader(Http::RequestHeaderMap& headers); */ const std::string& getProtocolString(const Protocol p); +/** + * Constructs the original URI sent from the client from + * the request headers. + * @param request headers from the original request + * @param length to truncate the constructed URI's path + */ +std::string buildOriginalUri(const Http::RequestHeaderMap& request_headers, + absl::optional max_path_length); + /** * Extract host and path from a URI. The host may contain port. * This function doesn't validate if the URI is valid. It only parses the URI with following diff --git a/source/common/matcher/matcher.h b/source/common/matcher/matcher.h index 376e486060577..40f52b3ae699c 100644 --- a/source/common/matcher/matcher.h +++ b/source/common/matcher/matcher.h @@ -66,13 +66,15 @@ template using DataInputFactoryCb = std::function class MatchTreeFactory { +template class MatchTreeFactory { public: - MatchTreeFactory(const std::string& stats_prefix, - Server::Configuration::FactoryContext& factory_context, + MatchTreeFactory(ActionFactoryContext& context, + Server::Configuration::ServerFactoryContext& server_factory_context, MatchTreeValidationVisitor& validation_visitor) - : stats_prefix_(stats_prefix), factory_context_(factory_context), + : action_factory_context_(context), server_factory_context_(server_factory_context), validation_visitor_(validation_visitor) {} MatchTreeFactoryCb create(const envoy::config::common::matcher::v3::Matcher& config) { @@ -200,12 +202,14 @@ template class MatchTreeFactory { return OnMatch{{}, matcher_factory()}; }; } else if (on_match.has_action()) { - auto& factory = Config::Utility::getAndCheckFactory(on_match.action()); + auto& factory = Config::Utility::getAndCheckFactory>( + on_match.action()); ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig( - on_match.action().typed_config(), factory_context_.messageValidationVisitor(), factory); + on_match.action().typed_config(), server_factory_context_.messageValidationVisitor(), + factory); - auto action_factory = - factory.createActionFactoryCb(*message, stats_prefix_, factory_context_); + auto action_factory = factory.createActionFactoryCb( + *message, action_factory_context_, server_factory_context_.messageValidationVisitor()); return [action_factory] { return OnMatch{action_factory, {}}; }; } @@ -234,8 +238,9 @@ template class MatchTreeFactory { validation_visitor_.validateDataInput(*factory, config.typed_config().type_url()); ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig( - config.typed_config(), factory_context_.messageValidationVisitor(), *factory); - auto data_input = factory->createDataInputFactoryCb(*message, factory_context_); + config.typed_config(), server_factory_context_.messageValidationVisitor(), *factory); + auto data_input = factory->createDataInputFactoryCb( + *message, server_factory_context_.messageValidationVisitor()); return data_input; } @@ -244,9 +249,10 @@ template class MatchTreeFactory { auto& common_input_factory = Config::Utility::getAndCheckFactory(config); ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig( - config.typed_config(), factory_context_.messageValidationVisitor(), common_input_factory); - auto common_input = - common_input_factory.createCommonProtocolInputFactoryCb(*message, factory_context_); + config.typed_config(), server_factory_context_.messageValidationVisitor(), + common_input_factory); + auto common_input = common_input_factory.createCommonProtocolInputFactoryCb( + *message, server_factory_context_.messageValidationVisitor()); return [common_input]() { return std::make_unique(common_input()); }; } @@ -265,9 +271,9 @@ template class MatchTreeFactory { auto& factory = Config::Utility::getAndCheckFactory(predicate.custom_match()); ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig( - predicate.custom_match().typed_config(), factory_context_.messageValidationVisitor(), - factory); - return factory.createInputMatcherFactoryCb(*message, factory_context_); + predicate.custom_match().typed_config(), + server_factory_context_.messageValidationVisitor(), factory); + return factory.createInputMatcherFactoryCb(*message, server_factory_context_); } default: NOT_REACHED_GCOVR_EXCL_LINE; @@ -275,7 +281,8 @@ template class MatchTreeFactory { } const std::string stats_prefix_; - Server::Configuration::FactoryContext& factory_context_; + ActionFactoryContext& action_factory_context_; + Server::Configuration::ServerFactoryContext& server_factory_context_; MatchTreeValidationVisitor& validation_visitor_; }; } // namespace Matcher diff --git a/source/common/network/apple_dns_impl.cc b/source/common/network/apple_dns_impl.cc index 1325914a3dfb0..a991a99b0977c 100644 --- a/source/common/network/apple_dns_impl.cc +++ b/source/common/network/apple_dns_impl.cc @@ -265,7 +265,9 @@ AppleDnsResolverImpl::PendingResolution::~PendingResolution() { } } -void AppleDnsResolverImpl::PendingResolution::cancel() { +void AppleDnsResolverImpl::PendingResolution::cancel(Network::ActiveDnsQuery::CancelReason) { + // TODO(mattklein123): If cancel reason is timeout, do something more aggressive about destroying + // and recreating the DNS system to maximize the chance of success in following queries. ENVOY_LOG(debug, "Cancelling PendingResolution for {}", dns_name_); ASSERT(owned_); if (pending_cb_) { diff --git a/source/common/network/apple_dns_impl.h b/source/common/network/apple_dns_impl.h index d22bc2c7c87e2..b4b48a528d2a2 100644 --- a/source/common/network/apple_dns_impl.h +++ b/source/common/network/apple_dns_impl.h @@ -90,7 +90,7 @@ class AppleDnsResolverImpl : public DnsResolver, protected Logger::LoggablesetTransportSocketCallbacks(*this); + + // TODO(soulxu): generate the connection id inside the addressProvider directly, + // then we don't need a setter or any of the optional stuff. + socket_->addressProvider().setConnectionID(id()); } ConnectionImpl::~ConnectionImpl() { diff --git a/source/common/network/dns_impl.h b/source/common/network/dns_impl.h index f58609ad674a8..5f96db4c47e9e 100644 --- a/source/common/network/dns_impl.h +++ b/source/common/network/dns_impl.h @@ -44,9 +44,10 @@ class DnsResolverImpl : public DnsResolver, protected Logger::Loggable -NetworkListenSocket>::NetworkListenSocket( - const Address::InstanceConstSharedPtr& address, - const Network::Socket::OptionsSharedPtr& options, bool bind_to_port) - : ListenSocketImpl(Network::ioHandleForAddr(Socket::Type::Datagram, address), address) { - setPrebindSocketOptions(); - setupSocket(options, bind_to_port); -} - UdsListenSocket::UdsListenSocket(const Address::InstanceConstSharedPtr& address) : ListenSocketImpl(ioHandleForAddr(Socket::Type::Stream, address), address) { RELEASE_ASSERT(io_handle_->isOpen(), ""); diff --git a/source/common/network/listen_socket_impl.h b/source/common/network/listen_socket_impl.h index 65e09b2b461ae..a237e2d5b7e4d 100644 --- a/source/common/network/listen_socket_impl.h +++ b/source/common/network/listen_socket_impl.h @@ -80,10 +80,32 @@ template class NetworkListenSocket : public ListenSocketImpl { Socket::Type socketType() const override { return T::type; } + // These four overrides are introduced to perform check. A null io handle is possible only if the + // the owner socket is a listen socket that does not bind to port. + IoHandle& ioHandle() override { + ASSERT(io_handle_ != nullptr); + return *io_handle_; + } + const IoHandle& ioHandle() const override { + ASSERT(io_handle_ != nullptr); + return *io_handle_; + } + void close() override { + if (io_handle_ != nullptr) { + if (io_handle_->isOpen()) { + io_handle_->close(); + } + } + } + bool isOpen() const override { + ASSERT(io_handle_ != nullptr); + return io_handle_->isOpen(); + } + protected: void setPrebindSocketOptions() { // On Windows, SO_REUSEADDR does not restrict subsequent bind calls when there is a listener as - // on Linux and later BSD socket stacks + // on Linux and later BSD socket stacks. #ifndef WIN32 int on = 1; auto status = setSocketOption(SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); @@ -96,12 +118,6 @@ template <> inline void NetworkListenSocket>::setPrebindSocketOptions() {} -// UDP listen socket desires io handle regardless bind_to_port is true or false. -template <> -NetworkListenSocket>::NetworkListenSocket( - const Address::InstanceConstSharedPtr& address, - const Network::Socket::OptionsSharedPtr& options, bool bind_to_port); - template class NetworkListenSocket>; template class NetworkListenSocket>; diff --git a/source/common/network/socket_impl.h b/source/common/network/socket_impl.h index d277f536c3349..e04fae266b411 100644 --- a/source/common/network/socket_impl.h +++ b/source/common/network/socket_impl.h @@ -49,6 +49,8 @@ class SocketAddressSetterImpl : public SocketAddressSetter { void setRequestedServerName(const absl::string_view requested_server_name) override { server_name_ = std::string(requested_server_name); } + absl::optional connectionID() const override { return connection_id_; } + void setConnectionID(uint64_t id) override { connection_id_ = id; } private: Address::InstanceConstSharedPtr local_address_; @@ -56,6 +58,7 @@ class SocketAddressSetterImpl : public SocketAddressSetter { Address::InstanceConstSharedPtr remote_address_; Address::InstanceConstSharedPtr direct_remote_address_; std::string server_name_; + absl::optional connection_id_; }; class SocketImpl : public virtual Socket { diff --git a/source/common/protobuf/utility.cc b/source/common/protobuf/utility.cc index 543e8cf7a99f2..d611a3a69318b 100644 --- a/source/common/protobuf/utility.cc +++ b/source/common/protobuf/utility.cc @@ -774,11 +774,15 @@ bool redactOpaque(Protobuf::Message* message, bool ancestor_is_sensitive, const auto* reflection = message->GetReflection(); const auto* type_url_field_descriptor = opaque_descriptor->FindFieldByName("type_url"); const auto* value_field_descriptor = opaque_descriptor->FindFieldByName("value"); - ASSERT(type_url_field_descriptor != nullptr && value_field_descriptor != nullptr && - reflection->HasField(*message, type_url_field_descriptor)); - if (!reflection->HasField(*message, value_field_descriptor)) { + ASSERT(type_url_field_descriptor != nullptr && value_field_descriptor != nullptr); + if (!reflection->HasField(*message, type_url_field_descriptor) && + !reflection->HasField(*message, value_field_descriptor)) { return true; } + if (!reflection->HasField(*message, type_url_field_descriptor) || + !reflection->HasField(*message, value_field_descriptor)) { + return false; + } // Try to find a descriptor for `type_url` in the pool and instantiate a new message of the // correct concrete type. diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 546fc4e8eda9a..df75f156656a5 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -76,6 +76,7 @@ constexpr const char* runtime_features[] = { "envoy.reloadable_features.http_upstream_wait_connect_response", "envoy.reloadable_features.improved_stream_limit_handling", "envoy.reloadable_features.internal_redirects_with_body", + "envoy.reloadable_features.listener_wildcard_match_ip_family", "envoy.reloadable_features.new_tcp_connection_pool", "envoy.reloadable_features.no_chunked_encoding_header_for_304", "envoy.reloadable_features.prefer_quic_kernel_bpf_packet_routing", diff --git a/source/common/stats/BUILD b/source/common/stats/BUILD index 8391ecfd2c4d2..f71156c712426 100644 --- a/source/common/stats/BUILD +++ b/source/common/stats/BUILD @@ -223,6 +223,7 @@ envoy_cc_library( srcs = ["stats_matcher_impl.cc"], hdrs = ["stats_matcher_impl.h"], deps = [ + ":symbol_table_lib", "//envoy/stats:stats_interface", "//source/common/common:matchers_lib", "//source/common/protobuf", diff --git a/source/common/stats/stats_matcher_impl.cc b/source/common/stats/stats_matcher_impl.cc index 01b0049771f03..a4440f90494cf 100644 --- a/source/common/stats/stats_matcher_impl.cc +++ b/source/common/stats/stats_matcher_impl.cc @@ -7,12 +7,17 @@ #include "source/common/common/utility.h" +#include "absl/strings/match.h" + namespace Envoy { namespace Stats { // TODO(ambuc): Refactor this into common/matchers.cc, since StatsMatcher is really just a thin // wrapper around what might be called a StringMatcherList. -StatsMatcherImpl::StatsMatcherImpl(const envoy::config::metrics::v3::StatsConfig& config) { +StatsMatcherImpl::StatsMatcherImpl(const envoy::config::metrics::v3::StatsConfig& config, + SymbolTable& symbol_table) + : symbol_table_(symbol_table), stat_name_pool_(std::make_unique(symbol_table)) { + switch (config.stats_matcher().stats_matcher_case()) { case envoy::config::metrics::v3::StatsMatcher::StatsMatcherCase::kRejectAll: // In this scenario, there are no matchers to store. @@ -22,6 +27,7 @@ StatsMatcherImpl::StatsMatcherImpl(const envoy::config::metrics::v3::StatsConfig // If we have an inclusion list, we are being default-exclusive. for (const auto& stats_matcher : config.stats_matcher().inclusion_list().patterns()) { matchers_.push_back(Matchers::StringMatcherImpl(stats_matcher)); + optimizeLastMatcher(); } is_inclusive_ = false; break; @@ -29,6 +35,7 @@ StatsMatcherImpl::StatsMatcherImpl(const envoy::config::metrics::v3::StatsConfig // If we have an exclusion list, we are being default-inclusive. for (const auto& stats_matcher : config.stats_matcher().exclusion_list().patterns()) { matchers_.push_back(Matchers::StringMatcherImpl(stats_matcher)); + optimizeLastMatcher(); } FALLTHRU; default: @@ -38,19 +45,74 @@ StatsMatcherImpl::StatsMatcherImpl(const envoy::config::metrics::v3::StatsConfig } } -bool StatsMatcherImpl::rejects(const std::string& name) const { - // - // is_inclusive_ | match | return - // ---------------+-------+-------- - // T | T | T Default-inclusive and matching an (exclusion) matcher, deny. - // T | F | F Otherwise, allow. - // F | T | F Default-exclusive and matching an (inclusion) matcher, allow. - // F | F | T Otherwise, deny. - // - // This is an XNOR, which can be evaluated by checking for equality. - - return (is_inclusive_ == std::any_of(matchers_.begin(), matchers_.end(), - [&name](auto& matcher) { return matcher.match(name); })); +// If the last string-matcher added is a case-sensitive prefix match, and the +// prefix ends in ".", then this drops that match and adds it to a list of +// prefixes. This is beneficial because token prefixes can be handled more +// efficiently as a StatName without requiring conversion to a string. +// +// In the future, other matcher patterns could be optimized in a similar way, +// such as: +// * suffixes that begin with "." +// * exact-matches +// * substrings that begin and end with "." +// +// These are left unoptimized for the moment to keep the code-change simpler, +// and because we haven't observed an acute performance need to optimize those +// other patterns yet. +void StatsMatcherImpl::optimizeLastMatcher() { + std::string prefix; + if (matchers_.back().getCaseSensitivePrefixMatch(prefix) && absl::EndsWith(prefix, ".") && + prefix.size() > 1) { + prefixes_.push_back(stat_name_pool_->add(prefix.substr(0, prefix.size() - 1))); + matchers_.pop_back(); + } +} + +StatsMatcher::FastResult StatsMatcherImpl::fastRejects(StatName stat_name) const { + if (rejectsAll()) { + return FastResult::Rejects; + } + bool matches = fastRejectMatch(stat_name); + if ((is_inclusive_ || matchers_.empty()) && matches == is_inclusive_) { + // We can short-circuit the slow matchers only if they are empty, or if + // we are in inclusive-mode and we find a match. + return FastResult::Rejects; + } else if (matches) { + return FastResult::Matches; + } + return FastResult::NoMatch; +} + +bool StatsMatcherImpl::fastRejectMatch(StatName stat_name) const { + return std::any_of(prefixes_.begin(), prefixes_.end(), + [stat_name](StatName prefix) { return stat_name.startsWith(prefix); }); +} + +bool StatsMatcherImpl::slowRejects(FastResult fast_result, StatName stat_name) const { + bool match = slowRejectMatch(stat_name); + + if (is_inclusive_ || match || fast_result != FastResult::Matches) { + // is_inclusive_ | match | return + // ---------------+-------+-------- + // T | T | T Default-inclusive and matching an (exclusion) matcher, deny. + // T | F | F Otherwise, allow. + // F | T | F Default-exclusive and matching an (inclusion) matcher, allow. + // F | F | T Otherwise, deny. + // + // This is an XNOR, which can be evaluated by checking for equality. + return match == is_inclusive_; + } + + return false; +} + +bool StatsMatcherImpl::slowRejectMatch(StatName stat_name) const { + if (matchers_.empty()) { + return false; + } + std::string name = symbol_table_->toString(stat_name); + return std::any_of(matchers_.begin(), matchers_.end(), + [&name](auto& matcher) { return matcher.match(name); }); } } // namespace Stats diff --git a/source/common/stats/stats_matcher_impl.h b/source/common/stats/stats_matcher_impl.h index de8ece9a02da2..b9cad0fa35da2 100644 --- a/source/common/stats/stats_matcher_impl.h +++ b/source/common/stats/stats_matcher_impl.h @@ -2,11 +2,13 @@ #include +#include "envoy/common/optref.h" #include "envoy/config/metrics/v3/stats.pb.h" #include "envoy/stats/stats_matcher.h" #include "source/common/common/matchers.h" #include "source/common/protobuf/protobuf.h" +#include "source/common/stats/symbol_table_impl.h" #include "absl/strings/string_view.h" @@ -18,22 +20,40 @@ namespace Stats { */ class StatsMatcherImpl : public StatsMatcher { public: - explicit StatsMatcherImpl(const envoy::config::metrics::v3::StatsConfig& config); + StatsMatcherImpl(const envoy::config::metrics::v3::StatsConfig& config, + SymbolTable& symbol_table); // Default constructor simply allows everything. StatsMatcherImpl() = default; // StatsMatcher - bool rejects(const std::string& name) const override; - bool acceptsAll() const override { return is_inclusive_ && matchers_.empty(); } - bool rejectsAll() const override { return !is_inclusive_ && matchers_.empty(); } + bool rejects(StatName name) const override { + FastResult fast_result = fastRejects(name); + return fast_result == FastResult::Rejects || slowRejects(fast_result, name); + } + FastResult fastRejects(StatName name) const override; + bool slowRejects(FastResult, StatName name) const override; + bool acceptsAll() const override { + return is_inclusive_ && matchers_.empty() && prefixes_.empty(); + } + bool rejectsAll() const override { + return !is_inclusive_ && matchers_.empty() && prefixes_.empty(); + } private: + void optimizeLastMatcher(); + bool fastRejectMatch(StatName name) const; + bool slowRejectMatch(StatName name) const; + // Bool indicating whether or not the StatsMatcher is including or excluding stats by default. See // StatsMatcherImpl::rejects() for much more detail. bool is_inclusive_{true}; + OptRef symbol_table_; + std::unique_ptr stat_name_pool_; + std::vector matchers_; + std::vector prefixes_; }; } // namespace Stats diff --git a/source/common/stats/symbol_table_impl.cc b/source/common/stats/symbol_table_impl.cc index a60fa31d9b426..0b15b66ba4547 100644 --- a/source/common/stats/symbol_table_impl.cc +++ b/source/common/stats/symbol_table_impl.cc @@ -154,6 +154,43 @@ void SymbolTableImpl::Encoding::decodeTokens( } } +bool StatName::startsWith(StatName symbolic_prefix) const { + bool ret = true; + std::vector prefix_symbols; + + // Decode the prefix as a StatNameVec. + SymbolTableImpl::Encoding::decodeTokens( + symbolic_prefix.data(), symbolic_prefix.dataSize(), + [&prefix_symbols](Symbol symbol) { prefix_symbols.push_back(symbol); }, + [&ret](absl::string_view) { ret = false; }); + + // If there any dynamic components, our string_view lambda will be called, and + // then we'll return false for simplicity. We don't have a current need for + // prefixes to be expressed dynamically, and handling that case would add + // complexity. + if (!ret) { + return false; + } + + // Now decode the StatName, matching against the symbols. It's OK for there to + // be dynamic string_view elements after the prefix match. + uint32_t index = 0; + SymbolTableImpl::Encoding::decodeTokens( + data(), dataSize(), + [&prefix_symbols, &index, &ret](Symbol symbol) { + if (index < prefix_symbols.size() && prefix_symbols[index] != symbol) { + ret = false; + } + ++index; + }, + [&prefix_symbols, &index, &ret](absl::string_view) { + if (index < prefix_symbols.size()) { + ret = false; + } + }); + return ret && index >= prefix_symbols.size(); +} + std::vector SymbolTableImpl::decodeStrings(const SymbolTable::Storage array, size_t size) const { std::vector strings; diff --git a/source/common/stats/symbol_table_impl.h b/source/common/stats/symbol_table_impl.h index cee896e649fa3..52cfe3e924198 100644 --- a/source/common/stats/symbol_table_impl.h +++ b/source/common/stats/symbol_table_impl.h @@ -456,6 +456,16 @@ class StatName { */ bool empty() const { return size_and_data_ == nullptr || dataSize() == 0; } + /** + * Determines whether this starts with the prefix. Note: dynamic segments + * are not supported in the current implementation; this matching only works + * for symbolic segments. However it OK for this to include dynamic segments + * following the prefix. + * + * @param symbolic_prefix the prefix, which must not contain dynamic segments. + */ + bool startsWith(StatName symbolic_prefix) const; + private: /** * Casts the raw data as a string_view. Note that this string_view will not diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index de7b0c061a191..3b29f8b5d4df5 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -101,18 +101,13 @@ void ThreadLocalStoreImpl::removeRejectedStats(StatMapClass& map, StatListClass& } } -bool ThreadLocalStoreImpl::rejects(StatName stat_name) const { - ASSERT(!stats_matcher_->acceptsAll()); +StatsMatcher::FastResult ThreadLocalStoreImpl::fastRejects(StatName stat_name) const { + return stats_matcher_->fastRejects(stat_name); +} - // TODO(ambuc): If stats_matcher_ depends on regexes, this operation (on the - // hot path) could become prohibitively expensive. Revisit this usage in the - // future. - // - // Also note that the elaboration of the stat-name into a string is expensive, - // so I think it might be better to move the matcher test until after caching, - // unless its acceptsAll/rejectsAll. - return stats_matcher_->rejectsAll() || - stats_matcher_->rejects(constSymbolTable().toString(stat_name)); +bool ThreadLocalStoreImpl::slowRejects(StatsMatcher::FastResult fast_reject_result, + StatName stat_name) const { + return stats_matcher_->slowRejects(fast_reject_result, stat_name); } std::vector ThreadLocalStoreImpl::counters() const { @@ -437,6 +432,7 @@ class StatNameTagHelper { }; bool ThreadLocalStoreImpl::checkAndRememberRejection(StatName name, + StatsMatcher::FastResult fast_reject_result, StatNameStorageSet& central_rejected_stats, StatNameHashSet* tls_rejected_stats) { if (stats_matcher_->acceptsAll()) { @@ -448,7 +444,7 @@ bool ThreadLocalStoreImpl::checkAndRememberRejection(StatName name, if (iter != central_rejected_stats.end()) { rejected_name = &(*iter); } else { - if (rejects(name)) { + if (slowRejects(fast_reject_result, name)) { auto insertion = central_rejected_stats.insert(StatNameStorage(name, symbolTable())); const StatNameStorage& rejected_name_ref = *(insertion.first); rejected_name = &rejected_name_ref; @@ -468,8 +464,9 @@ StatType& ThreadLocalStoreImpl::ScopeImpl::safeMakeStat( StatName full_stat_name, StatName name_no_tags, const absl::optional& stat_name_tags, StatNameHashMap>& central_cache_map, - StatNameStorageSet& central_rejected_stats, MakeStatFn make_stat, - StatRefMap* tls_cache, StatNameHashSet* tls_rejected_stats, StatType& null_stat) { + StatsMatcher::FastResult fast_reject_result, StatNameStorageSet& central_rejected_stats, + MakeStatFn make_stat, StatRefMap* tls_cache, + StatNameHashSet* tls_rejected_stats, StatType& null_stat) { if (tls_rejected_stats != nullptr && tls_rejected_stats->find(full_stat_name) != tls_rejected_stats->end()) { @@ -491,8 +488,8 @@ StatType& ThreadLocalStoreImpl::ScopeImpl::safeMakeStat( RefcountPtr* central_ref = nullptr; if (iter != central_cache_map.end()) { central_ref = &(iter->second); - } else if (parent_.checkAndRememberRejection(full_stat_name, central_rejected_stats, - tls_rejected_stats)) { + } else if (parent_.checkAndRememberRejection(full_stat_name, fast_reject_result, + central_rejected_stats, tls_rejected_stats)) { return null_stat; } else { StatNameTagHelper tag_helper(parent_, name_no_tags, stat_name_tags); @@ -546,6 +543,11 @@ Counter& ThreadLocalStoreImpl::ScopeImpl::counterFromStatNameWithTags( TagUtility::TagStatNameJoiner joiner(prefix_.statName(), name, stat_name_tags, symbolTable()); Stats::StatName final_stat_name = joiner.nameWithTags(); + StatsMatcher::FastResult fast_reject_result = parent_.fastRejects(final_stat_name); + if (fast_reject_result == StatsMatcher::FastResult::Rejects) { + return parent_.null_counter_; + } + // We now find the TLS cache. This might remain null if we don't have TLS // initialized currently. StatRefMap* tls_cache = nullptr; @@ -558,7 +560,7 @@ Counter& ThreadLocalStoreImpl::ScopeImpl::counterFromStatNameWithTags( return safeMakeStat( final_stat_name, joiner.tagExtractedName(), stat_name_tags, central_cache_->counters_, - central_cache_->rejected_stats_, + fast_reject_result, central_cache_->rejected_stats_, [](Allocator& allocator, StatName name, StatName tag_extracted_name, const StatNameTagVector& tags) -> CounterSharedPtr { return allocator.makeCounter(name, tag_extracted_name, tags); @@ -600,6 +602,11 @@ Gauge& ThreadLocalStoreImpl::ScopeImpl::gaugeFromStatNameWithTags( TagUtility::TagStatNameJoiner joiner(prefix_.statName(), name, stat_name_tags, symbolTable()); StatName final_stat_name = joiner.nameWithTags(); + StatsMatcher::FastResult fast_reject_result = parent_.fastRejects(final_stat_name); + if (fast_reject_result == StatsMatcher::FastResult::Rejects) { + return parent_.null_gauge_; + } + StatRefMap* tls_cache = nullptr; StatNameHashSet* tls_rejected_stats = nullptr; if (!parent_.shutting_down_ && parent_.tls_cache_) { @@ -610,7 +617,7 @@ Gauge& ThreadLocalStoreImpl::ScopeImpl::gaugeFromStatNameWithTags( Gauge& gauge = safeMakeStat( final_stat_name, joiner.tagExtractedName(), stat_name_tags, central_cache_->gauges_, - central_cache_->rejected_stats_, + fast_reject_result, central_cache_->rejected_stats_, [import_mode](Allocator& allocator, StatName name, StatName tag_extracted_name, const StatNameTagVector& tags) -> GaugeSharedPtr { return allocator.makeGauge(name, tag_extracted_name, tags, import_mode); @@ -638,6 +645,11 @@ Histogram& ThreadLocalStoreImpl::ScopeImpl::histogramFromStatNameWithTags( TagUtility::TagStatNameJoiner joiner(prefix_.statName(), name, stat_name_tags, symbolTable()); StatName final_stat_name = joiner.nameWithTags(); + StatsMatcher::FastResult fast_reject_result = parent_.fastRejects(final_stat_name); + if (fast_reject_result == StatsMatcher::FastResult::Rejects) { + return parent_.null_histogram_; + } + StatNameHashMap* tls_cache = nullptr; StatNameHashSet* tls_rejected_stats = nullptr; if (!parent_.shutting_down_ && parent_.tls_cache_) { @@ -658,7 +670,8 @@ Histogram& ThreadLocalStoreImpl::ScopeImpl::histogramFromStatNameWithTags( ParentHistogramImplSharedPtr* central_ref = nullptr; if (iter != central_cache_->histograms_.end()) { central_ref = &iter->second; - } else if (parent_.checkAndRememberRejection(final_stat_name, central_cache_->rejected_stats_, + } else if (parent_.checkAndRememberRejection(final_stat_name, fast_reject_result, + central_cache_->rejected_stats_, tls_rejected_stats)) { return parent_.null_histogram_; } else { @@ -711,6 +724,11 @@ TextReadout& ThreadLocalStoreImpl::ScopeImpl::textReadoutFromStatNameWithTags( TagUtility::TagStatNameJoiner joiner(prefix_.statName(), name, stat_name_tags, symbolTable()); Stats::StatName final_stat_name = joiner.nameWithTags(); + StatsMatcher::FastResult fast_reject_result = parent_.fastRejects(final_stat_name); + if (fast_reject_result == StatsMatcher::FastResult::Rejects) { + return parent_.null_text_readout_; + } + // We now find the TLS cache. This might remain null if we don't have TLS // initialized currently. StatRefMap* tls_cache = nullptr; @@ -723,7 +741,7 @@ TextReadout& ThreadLocalStoreImpl::ScopeImpl::textReadoutFromStatNameWithTags( return safeMakeStat( final_stat_name, joiner.tagExtractedName(), stat_name_tags, central_cache_->text_readouts_, - central_cache_->rejected_stats_, + fast_reject_result, central_cache_->rejected_stats_, [](Allocator& allocator, StatName name, StatName tag_extracted_name, const StatNameTagVector& tags) -> TextReadoutSharedPtr { return allocator.makeTextReadout(name, tag_extracted_name, tags); diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index ecc359841bce0..eaf2946fd99f7 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -416,6 +416,7 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo StatType& safeMakeStat(StatName full_stat_name, StatName name_no_tags, const absl::optional& stat_name_tags, StatNameHashMap>& central_cache_map, + StatsMatcher::FastResult fast_reject_result, StatNameStorageSet& central_rejected_stats, MakeStatFn make_stat, StatRefMap* tls_cache, StatNameHashSet* tls_rejected_stats, StatType& null_stat); @@ -476,11 +477,14 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo void clearHistogramsFromCaches(); void releaseScopeCrossThread(ScopeImpl* scope); void mergeInternal(PostMergeCb merge_cb); - bool rejects(StatName name) const; + bool slowRejects(StatsMatcher::FastResult fast_reject_result, StatName name) const; + bool rejects(StatName name) const { return stats_matcher_->rejects(name); } + StatsMatcher::FastResult fastRejects(StatName name) const; bool rejectsAll() const { return stats_matcher_->rejectsAll(); } template void removeRejectedStats(StatMapClass& map, StatListClass& list); - bool checkAndRememberRejection(StatName name, StatNameStorageSet& central_rejected_stats, + bool checkAndRememberRejection(StatName name, StatsMatcher::FastResult fast_reject_result, + StatNameStorageSet& central_rejected_stats, StatNameHashSet* tls_rejected_stats); TlsCache& tlsCache() { return **tls_cache_; } diff --git a/source/common/stream_info/stream_info_impl.h b/source/common/stream_info/stream_info_impl.h index 1a41097e51420..53d4351749c10 100644 --- a/source/common/stream_info/stream_info_impl.h +++ b/source/common/stream_info/stream_info_impl.h @@ -274,10 +274,6 @@ struct StreamInfoImpl : public StreamInfo { return upstream_cluster_info_; } - void setConnectionID(uint64_t id) override { connection_id_ = id; } - - absl::optional connectionID() const override { return connection_id_; } - void setFilterChainName(absl::string_view filter_chain_name) override { filter_chain_name_ = std::string(filter_chain_name); } @@ -336,7 +332,6 @@ struct StreamInfoImpl : public StreamInfo { UpstreamTiming upstream_timing_; std::string upstream_transport_failure_reason_; absl::optional upstream_cluster_info_; - absl::optional connection_id_; std::string filter_chain_name_; Tracing::Reason trace_reason_; }; diff --git a/source/common/tracing/http_tracer_impl.cc b/source/common/tracing/http_tracer_impl.cc index 12aeace932b89..35263ac7ce052 100644 --- a/source/common/tracing/http_tracer_impl.cc +++ b/source/common/tracing/http_tracer_impl.cc @@ -36,23 +36,6 @@ static absl::string_view valueOrDefault(const Http::HeaderEntry* header, return header ? header->value().getStringView() : default_value; } -static std::string buildUrl(const Http::RequestHeaderMap& request_headers, - const uint32_t max_path_length) { - if (!request_headers.Path()) { - return ""; - } - absl::string_view path(request_headers.EnvoyOriginalPath() - ? request_headers.getEnvoyOriginalPathValue() - : request_headers.getPathValue()); - - if (path.length() > max_path_length) { - path = path.substr(0, max_path_length); - } - - return absl::StrCat(request_headers.getForwardedProtoValue(), "://", - request_headers.getHostValue(), path); -} - const std::string HttpTracerUtility::IngressOperation = "ingress"; const std::string HttpTracerUtility::EgressOperation = "egress"; @@ -159,8 +142,9 @@ void HttpTracerUtility::finalizeDownstreamSpan(Span& span, if (request_headers->RequestId()) { span.setTag(Tracing::Tags::get().GuidXRequestId, request_headers->getRequestIdValue()); } - span.setTag(Tracing::Tags::get().HttpUrl, - buildUrl(*request_headers, tracing_config.maxPathTagLength())); + span.setTag( + Tracing::Tags::get().HttpUrl, + Http::Utility::buildOriginalUri(*request_headers, tracing_config.maxPathTagLength())); span.setTag(Tracing::Tags::get().HttpMethod, request_headers->getMethodValue()); span.setTag(Tracing::Tags::get().DownstreamCluster, valueOrDefault(request_headers->EnvoyDownstreamServiceCluster(), "-")); @@ -317,12 +301,12 @@ RequestHeaderCustomTag::RequestHeaderCustomTag( default_value_(request_header.default_value()) {} absl::string_view RequestHeaderCustomTag::value(const CustomTagContext& ctx) const { - if (!ctx.request_headers) { + if (ctx.trace_context == nullptr) { return default_value_; } // TODO(https://github.com/envoyproxy/envoy/issues/13454): Potentially populate all header values. - const auto entry = ctx.request_headers->get(name_); - return !entry.empty() ? entry[0]->value().getStringView() : default_value_; + const auto entry = ctx.trace_context->getTraceContext(name_); + return entry.value_or(default_value_); } MetadataCustomTag::MetadataCustomTag(const std::string& tag, diff --git a/source/common/tracing/null_span_impl.h b/source/common/tracing/null_span_impl.h index 653147ffd88f0..1377000632349 100644 --- a/source/common/tracing/null_span_impl.h +++ b/source/common/tracing/null_span_impl.h @@ -22,7 +22,7 @@ class NullSpan : public Span { void setTag(absl::string_view, absl::string_view) override {} void log(SystemTime, const std::string&) override {} void finishSpan() override {} - void injectContext(Http::RequestHeaderMap&) override {} + void injectContext(Tracing::TraceContext&) override {} void setBaggage(absl::string_view, absl::string_view) override {} std::string getBaggage(absl::string_view) override { return EMPTY_STRING; } std::string getTraceIdAsHex() const override { return EMPTY_STRING; } diff --git a/source/common/upstream/logical_dns_cluster.cc b/source/common/upstream/logical_dns_cluster.cc index eaa949efdf095..ca9c9a809664b 100644 --- a/source/common/upstream/logical_dns_cluster.cc +++ b/source/common/upstream/logical_dns_cluster.cc @@ -98,7 +98,7 @@ void LogicalDnsCluster::startPreInit() { LogicalDnsCluster::~LogicalDnsCluster() { if (active_dns_query_) { - active_dns_query_->cancel(); + active_dns_query_->cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned); } } diff --git a/source/common/upstream/strict_dns_cluster.cc b/source/common/upstream/strict_dns_cluster.cc index c776e070a0085..d46f3f96edfa9 100644 --- a/source/common/upstream/strict_dns_cluster.cc +++ b/source/common/upstream/strict_dns_cluster.cc @@ -97,7 +97,7 @@ StrictDnsClusterImpl::ResolveTarget::ResolveTarget( StrictDnsClusterImpl::ResolveTarget::~ResolveTarget() { if (active_query_) { - active_query_->cancel(); + active_query_->cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned); } } diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index 83169b162d354..84d27d718f099 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -894,13 +894,7 @@ Network::TransportSocketFactoryPtr createTransportSocketFactory( // if necessary. auto transport_socket = config.transport_socket(); if (!config.has_transport_socket()) { - if (config.has_hidden_envoy_deprecated_tls_context()) { - transport_socket.set_name("envoy.transport_sockets.tls"); - transport_socket.mutable_typed_config()->PackFrom( - config.hidden_envoy_deprecated_tls_context()); - } else { - transport_socket.set_name("envoy.transport_sockets.raw_buffer"); - } + transport_socket.set_name("envoy.transport_sockets.raw_buffer"); } auto& config_factory = Config::Utility::getAndCheckFactory< diff --git a/source/docs/fancy_logger.md b/source/docs/fancy_logger.md index da4e5f2dc12ff..371b0fe89dfdc 100644 --- a/source/docs/fancy_logger.md +++ b/source/docs/fancy_logger.md @@ -25,7 +25,7 @@ To flush a logger, `FANCY_FLUSH_LOG()` can be used. ### Enable Fancy Logger using Command Line Option A command line option is provided to enable Fancy Logger: `--enable-fine-grain-logging`. It enables Fancy Logger for Envoy, i.e. replaces most Envoy's log macros (`ENVOY_LOG, ENVOY_FLUSH_LOG, ENVOY_CONN_LOG, ENVOY_STREAM_LOG`) with corresponding Fancy Logger's macros. -If Fancy Logger is enabled, the default log format is `"[%Y-%m-%d %T.%e][%t][%l] [%g:%#] %v"`, where the logger name is omitted compared to Envoy's default as it's the same as file name. The default log level is info, if not specidfied by user of any logging context. +If Fancy Logger is enabled, the default log format is `"[%Y-%m-%d %T.%e][%t][%l] [%g:%#] %v"`, where the logger name is omitted compared to Envoy's default as it's the same as file name. The default log level is info, if not specified by user of any logging context. Note that Envoy's logger can still be used in Fancy mode. These macros are not replaced: `GET_MISC_LOGGER, ENVOY_LOG_MISC, ENVOY_LOGGER, ENVOY_LOG_TO_LOGGER, ENVOY_CONN_LOG_TO_LOGGER, ENVOY_STREAM_LOG_TO_LOGGER`. For example, `ENVOY_LOG_LOGGER(ENVOY_LOGGER(), LEVEL, ...)` is equivalent to `ENVOY_LOG` in Envoy mode. diff --git a/source/extensions/clusters/dynamic_forward_proxy/cluster.cc b/source/extensions/clusters/dynamic_forward_proxy/cluster.cc index 6650a021ebaf8..ef8d2c89c641a 100644 --- a/source/extensions/clusters/dynamic_forward_proxy/cluster.cc +++ b/source/extensions/clusters/dynamic_forward_proxy/cluster.cc @@ -24,15 +24,7 @@ Cluster::Cluster( added_via_api, factory_context.dispatcher().timeSource()), dns_cache_manager_(cache_manager_factory.get()), dns_cache_(dns_cache_manager_->getCache(config.dns_cache_config())), - update_callbacks_handle_(dns_cache_->addUpdateCallbacks(*this)), local_info_(local_info) { - // Block certain TLS context parameters that don't make sense on a cluster-wide scale. We will - // support these parameters dynamically in the future. This is not an exhaustive list of - // parameters that don't make sense but should be the most obvious ones that a user might set - // in error. - if (!cluster.hidden_envoy_deprecated_tls_context().sni().empty()) { - throw EnvoyException("dynamic_forward_proxy cluster cannot configure 'sni'"); - } -} + update_callbacks_handle_(dns_cache_->addUpdateCallbacks(*this)), local_info_(local_info) {} void Cluster::startPreInit() { // If we are attaching to a pre-populated cache we need to initialize our hosts. diff --git a/source/extensions/clusters/redis/redis_cluster.cc b/source/extensions/clusters/redis/redis_cluster.cc index 18a0a79fda211..25ae1c8c7c66d 100644 --- a/source/extensions/clusters/redis/redis_cluster.cc +++ b/source/extensions/clusters/redis/redis_cluster.cc @@ -149,7 +149,7 @@ RedisCluster::DnsDiscoveryResolveTarget::DnsDiscoveryResolveTarget(RedisCluster& RedisCluster::DnsDiscoveryResolveTarget::~DnsDiscoveryResolveTarget() { if (active_query_) { - active_query_->cancel(); + active_query_->cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned); } // Disable timer for mock tests. if (resolve_timer_) { diff --git a/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.cc b/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.cc index 312c920c27bb6..e4d8204687893 100644 --- a/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.cc +++ b/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.cc @@ -26,6 +26,7 @@ DnsCacheImpl::DnsCacheImpl( stats_(generateDnsCacheStats(*scope_)), resource_manager_(*scope_, loader, config.name(), config.dns_cache_circuit_breaker()), refresh_interval_(PROTOBUF_GET_MS_OR_DEFAULT(config, dns_refresh_rate, 60000)), + timeout_interval_(PROTOBUF_GET_MS_OR_DEFAULT(config, dns_query_timeout, 5000)), failure_backoff_strategy_( Config::Utility::prepareDnsRefreshStrategy< envoy::extensions::common::dynamic_forward_proxy::v3::DnsCacheConfig>( @@ -57,7 +58,8 @@ DnsCacheImpl::DnsCacheImpl( DnsCacheImpl::~DnsCacheImpl() { for (const auto& primary_host : primary_hosts_) { if (primary_host.second->active_query_ != nullptr) { - primary_host.second->active_query_->cancel(); + primary_host.second->active_query_->cancel( + Network::ActiveDnsQuery::CancelReason::QueryAbandoned); } } @@ -200,13 +202,34 @@ void DnsCacheImpl::startCacheLoad(const std::string& host, uint16_t default_port *this, std::string(host_attributes.host_), host_attributes.port_.value_or(default_port), host_attributes.is_ip_address_, - [this, host]() { onReResolve(host); })) + [this, host]() { onReResolve(host); }, + [this, host]() { onResolveTimeout(host); })) .first->second.get(); } startResolve(host, *primary_host); } +DnsCacheImpl::PrimaryHostInfo& DnsCacheImpl::getPrimaryHost(const std::string& host) { + // Functions modify primary_hosts_ are only called in the main thread so we + // know it is safe to use the PrimaryHostInfo pointers outside of the lock. + ASSERT(main_thread_dispatcher_.isThreadSafe()); + absl::ReaderMutexLock reader_lock{&primary_hosts_lock_}; + const auto primary_host_it = primary_hosts_.find(host); + ASSERT(primary_host_it != primary_hosts_.end()); + return *(primary_host_it->second.get()); +} + +void DnsCacheImpl::onResolveTimeout(const std::string& host) { + ASSERT(main_thread_dispatcher_.isThreadSafe()); + + auto& primary_host = getPrimaryHost(host); + ENVOY_LOG(debug, "host='{}' resolution timeout", host); + stats_.dns_query_timeout_.inc(); + primary_host.active_query_->cancel(Network::ActiveDnsQuery::CancelReason::Timeout); + finishResolve(host, Network::DnsResolver::ResolutionStatus::Failure, {}); +} + void DnsCacheImpl::onReResolve(const std::string& host) { ASSERT(main_thread_dispatcher_.isThreadSafe()); // If we need to erase the host, hold onto the PrimaryHostInfo object that owns this callback. @@ -214,18 +237,10 @@ void DnsCacheImpl::onReResolve(const std::string& host) { // use-after-free issues PrimaryHostInfoPtr host_to_erase; - // Functions like this one that modify primary_hosts_ are only called in the main thread so we - // know it is safe to use the PrimaryHostInfo pointers outside of the lock. - auto* primary_host = [&]() { - absl::ReaderMutexLock reader_lock{&primary_hosts_lock_}; - const auto primary_host_it = primary_hosts_.find(host); - ASSERT(primary_host_it != primary_hosts_.end()); - return primary_host_it->second.get(); - }(); - + auto& primary_host = getPrimaryHost(host); const std::chrono::steady_clock::duration now_duration = main_thread_dispatcher_.timeSource().monotonicTime().time_since_epoch(); - auto last_used_time = primary_host->host_info_->lastUsedTime(); + auto last_used_time = primary_host.host_info_->lastUsedTime(); ENVOY_LOG(debug, "host='{}' TTL check: now={} last_used={}", host, now_duration.count(), last_used_time.count()); if ((now_duration - last_used_time) > host_ttl_) { @@ -233,7 +248,7 @@ void DnsCacheImpl::onReResolve(const std::string& host) { // If the host has no address then that means that the DnsCacheImpl has never // runAddUpdateCallbacks for this host, and thus the callback targets are not aware of it. // Therefore, runRemoveCallbacks should only be ran if the host's address != nullptr. - if (primary_host->host_info_->address()) { + if (primary_host.host_info_->address()) { runRemoveCallbacks(host); } { @@ -243,9 +258,9 @@ void DnsCacheImpl::onReResolve(const std::string& host) { host_to_erase = std::move(host_it->second); primary_hosts_.erase(host_it); } - notifyThreads(host, primary_host->host_info_); + notifyThreads(host, primary_host.host_info_); } else { - startResolve(host, *primary_host); + startResolve(host, primary_host); } } @@ -256,6 +271,7 @@ void DnsCacheImpl::startResolve(const std::string& host, PrimaryHostInfo& host_i stats_.dns_query_attempt_.inc(); + host_info.timeout_timer_->enableTimer(timeout_interval_, nullptr); host_info.active_query_ = resolver_->resolve(host_info.host_info_->resolvedHost(), dns_lookup_family_, [this, host](Network::DnsResolver::ResolutionStatus status, @@ -280,6 +296,7 @@ void DnsCacheImpl::finishResolve(const std::string& host, }(); const bool first_resolve = !primary_host_info->host_info_->firstResolveComplete(); + primary_host_info->timeout_timer_->disableTimer(); primary_host_info->active_query_ = nullptr; // If the DNS resolver successfully resolved with an empty response list, the dns cache does not @@ -379,9 +396,12 @@ void DnsCacheImpl::ThreadLocalHostInfo::onHostMapUpdate( DnsCacheImpl::PrimaryHostInfo::PrimaryHostInfo(DnsCacheImpl& parent, absl::string_view host_to_resolve, uint16_t port, - bool is_ip_address, const Event::TimerCb& timer_cb) + bool is_ip_address, + const Event::TimerCb& refresh_timer_cb, + const Event::TimerCb& timeout_timer_cb) : parent_(parent), port_(port), - refresh_timer_(parent.main_thread_dispatcher_.createTimer(timer_cb)), + refresh_timer_(parent.main_thread_dispatcher_.createTimer(refresh_timer_cb)), + timeout_timer_(parent.main_thread_dispatcher_.createTimer(timeout_timer_cb)), host_info_(std::make_shared(parent.main_thread_dispatcher_.timeSource(), host_to_resolve, is_ip_address)) { parent_.stats_.host_added_.inc(); diff --git a/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.h b/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.h index 1e668c9847f20..461b31ec1e424 100644 --- a/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.h +++ b/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.h @@ -24,6 +24,7 @@ namespace DynamicForwardProxy { COUNTER(dns_query_attempt) \ COUNTER(dns_query_failure) \ COUNTER(dns_query_success) \ + COUNTER(dns_query_timeout) \ COUNTER(host_added) \ COUNTER(host_address_changed) \ COUNTER(host_overflow) \ @@ -139,12 +140,14 @@ class DnsCacheImpl : public DnsCache, Logger::Loggable ConnectionWrapper::operator[](CelValue key) const { } else if (value == RequestedServerName) { return CelValue::CreateStringView(info_.downstreamAddressProvider().requestedServerName()); } else if (value == ID) { - auto id = info_.connectionID(); + auto id = info_.downstreamAddressProvider().connectionID(); if (id.has_value()) { return CelValue::CreateUint64(id.value()); } diff --git a/source/extensions/filters/http/composite/BUILD b/source/extensions/filters/http/composite/BUILD index e2f0946aa6aaa..512c9b0549139 100644 --- a/source/extensions/filters/http/composite/BUILD +++ b/source/extensions/filters/http/composite/BUILD @@ -16,6 +16,7 @@ envoy_cc_library( srcs = ["action.cc"], hdrs = ["action.h"], deps = [ + "//source/common/http/matching:data_impl_lib", "//source/common/matcher:matcher_lib", "@envoy_api//envoy/extensions/filters/http/composite/v3:pkg_cc_proto", ], diff --git a/source/extensions/filters/http/composite/action.cc b/source/extensions/filters/http/composite/action.cc index 14935fa819dbc..743430b5fad30 100644 --- a/source/extensions/filters/http/composite/action.cc +++ b/source/extensions/filters/http/composite/action.cc @@ -7,7 +7,8 @@ namespace Composite { void ExecuteFilterAction::createFilters(Http::FilterChainFactoryCallbacks& callbacks) const { cb_(callbacks); } -REGISTER_FACTORY(ExecuteFilterActionFactory, Matcher::ActionFactory); +REGISTER_FACTORY(ExecuteFilterActionFactory, + Matcher::ActionFactory); } // namespace Composite } // namespace HttpFilters } // namespace Extensions diff --git a/source/extensions/filters/http/composite/action.h b/source/extensions/filters/http/composite/action.h index 1d2b822e79949..f6c69321a1bad 100644 --- a/source/extensions/filters/http/composite/action.h +++ b/source/extensions/filters/http/composite/action.h @@ -2,6 +2,7 @@ #include "envoy/extensions/filters/http/composite/v3/composite.pb.validate.h" +#include "source/common/http/matching/data_impl.h" #include "source/common/matcher/matcher.h" namespace Envoy { @@ -21,23 +22,25 @@ class ExecuteFilterAction Http::FilterFactoryCb cb_; }; -class ExecuteFilterActionFactory : public Matcher::ActionFactory { +class ExecuteFilterActionFactory + : public Matcher::ActionFactory { public: std::string name() const override { return "composite-action"; } Matcher::ActionFactoryCb - createActionFactoryCb(const Protobuf::Message& config, const std::string& stat_prefix, - Server::Configuration::FactoryContext& factory_context) override { + createActionFactoryCb(const Protobuf::Message& config, + Http::Matching::HttpFilterActionContext& context, + ProtobufMessage::ValidationVisitor& validation_visitor) override { const auto& composite_action = MessageUtil::downcastAndValidate< const envoy::extensions::filters::http::composite::v3::ExecuteFilterAction&>( - config, factory_context.messageValidationVisitor()); + config, validation_visitor); auto& factory = Config::Utility::getAndCheckFactory( composite_action.typed_config()); ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig( - composite_action.typed_config().typed_config(), factory_context.messageValidationVisitor(), - factory); - auto callback = factory.createFilterFactoryFromProto(*message, stat_prefix, factory_context); + composite_action.typed_config().typed_config(), validation_visitor, factory); + auto callback = factory.createFilterFactoryFromProto(*message, context.stat_prefix_, + context.factory_context_); return [cb = std::move(callback)]() -> Matcher::ActionPtr { return std::make_unique(cb); }; diff --git a/source/extensions/filters/http/jwt_authn/filter.cc b/source/extensions/filters/http/jwt_authn/filter.cc index 14711ad2c80ff..c069e3a87b8d6 100644 --- a/source/extensions/filters/http/jwt_authn/filter.cc +++ b/source/extensions/filters/http/jwt_authn/filter.cc @@ -14,7 +14,8 @@ namespace HttpFilters { namespace JwtAuthn { namespace { - +constexpr absl::string_view InvalidTokenErrorString = ", error=\"invalid_token\""; +constexpr uint32_t MaximumUriLength = 256; Http::RegisterCustomInlineHeader access_control_request_method_handle(Http::CustomHeaders::get().AccessControlRequestMethod); Http::RegisterCustomInlineHeader @@ -85,6 +86,7 @@ Http::FilterHeadersStatus Filter::decodeHeaders(Http::RequestHeaderMap& headers, if (verifier == nullptr) { onComplete(Status::Ok); } else { + original_uri_ = Http::Utility::buildOriginalUri(headers, MaximumUriLength); // Verify the JWT token, onComplete() will be called when completed. context_ = Verifier::createContext(headers, decoder_callbacks_->activeSpan(), this); verifier->verify(context_); @@ -117,8 +119,15 @@ void Filter::onComplete(const Status& status) { status == Status::JwtAudienceNotAllowed ? Http::Code::Forbidden : Http::Code::Unauthorized; // return failure reason as message body decoder_callbacks_->sendLocalReply( - code, ::google::jwt_verify::getStatusString(status), nullptr, absl::nullopt, - generateRcDetails(::google::jwt_verify::getStatusString(status))); + code, ::google::jwt_verify::getStatusString(status), + [uri = this->original_uri_, status](Http::ResponseHeaderMap& headers) { + std::string value = absl::StrCat("Bearer realm=\"", uri, "\""); + if (status != Status::JwtMissed) { + absl::StrAppend(&value, InvalidTokenErrorString); + } + headers.setCopy(Http::Headers::get().WWWAuthenticate, value); + }, + absl::nullopt, generateRcDetails(::google::jwt_verify::getStatusString(status))); return; } stats_.allowed_.inc(); diff --git a/source/extensions/filters/http/jwt_authn/filter.h b/source/extensions/filters/http/jwt_authn/filter.h index f4d95f04d9004..e743324f8040a 100644 --- a/source/extensions/filters/http/jwt_authn/filter.h +++ b/source/extensions/filters/http/jwt_authn/filter.h @@ -49,6 +49,8 @@ class Filter : public Http::StreamDecoderFilter, FilterConfigSharedPtr config_; // Verify context for current request. ContextSharedPtr context_; + + std::string original_uri_; }; } // namespace JwtAuthn diff --git a/source/extensions/filters/listener/http_inspector/http_inspector.h b/source/extensions/filters/listener/http_inspector/http_inspector.h index 75d5eb5e76af8..b43422f9e66ed 100644 --- a/source/extensions/filters/listener/http_inspector/http_inspector.h +++ b/source/extensions/filters/listener/http_inspector/http_inspector.h @@ -66,11 +66,6 @@ using ConfigSharedPtr = std::shared_ptr; class Filter : public Network::ListenerFilter, Logger::Loggable { public: Filter(const ConfigSharedPtr config); - ~Filter() override { - if (cb_) { - cb_->socket().ioHandle().resetFileEvents(); - } - } // Network::ListenerFilter Network::FilterStatus onAccept(Network::ListenerFilterCallbacks& cb) override; diff --git a/source/extensions/filters/listener/proxy_protocol/proxy_protocol.h b/source/extensions/filters/listener/proxy_protocol/proxy_protocol.h index 3eeb067466f31..469c838f9b83f 100644 --- a/source/extensions/filters/listener/proxy_protocol/proxy_protocol.h +++ b/source/extensions/filters/listener/proxy_protocol/proxy_protocol.h @@ -83,11 +83,7 @@ enum class ReadOrParseState { Done, TryAgainLater, Error }; class Filter : public Network::ListenerFilter, Logger::Loggable { public: Filter(const ConfigSharedPtr& config) : config_(config) {} - ~Filter() override { - if (cb_) { - cb_->socket().ioHandle().resetFileEvents(); - } - } + // Network::ListenerFilter Network::FilterStatus onAccept(Network::ListenerFilterCallbacks& cb) override; diff --git a/source/extensions/filters/listener/tls_inspector/tls_inspector.h b/source/extensions/filters/listener/tls_inspector/tls_inspector.h index bd64cd2bd3f54..f7adb93076e51 100644 --- a/source/extensions/filters/listener/tls_inspector/tls_inspector.h +++ b/source/extensions/filters/listener/tls_inspector/tls_inspector.h @@ -73,11 +73,6 @@ using ConfigSharedPtr = std::shared_ptr; class Filter : public Network::ListenerFilter, Logger::Loggable { public: Filter(const ConfigSharedPtr config); - ~Filter() override { - if (cb_) { - cb_->socket().ioHandle().resetFileEvents(); - } - } // Network::ListenerFilter Network::FilterStatus onAccept(Network::ListenerFilterCallbacks& cb) override; diff --git a/source/extensions/filters/network/http_connection_manager/config.cc b/source/extensions/filters/network/http_connection_manager/config.cc index c30836230797f..b5701d718ef4f 100644 --- a/source/extensions/filters/network/http_connection_manager/config.cc +++ b/source/extensions/filters/network/http_connection_manager/config.cc @@ -518,6 +518,10 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( server_transformation_ = config.server_header_transformation(); + if (!config.scheme_header_transformation().scheme_to_overwrite().empty()) { + scheme_to_set_ = config.scheme_header_transformation().scheme_to_overwrite(); + } + if (!config.server_name().empty()) { server_name_ = config.server_name(); } else { diff --git a/source/extensions/filters/network/http_connection_manager/config.h b/source/extensions/filters/network/http_connection_manager/config.h index b2b8ae2a9e275..52236df5ec680 100644 --- a/source/extensions/filters/network/http_connection_manager/config.h +++ b/source/extensions/filters/network/http_connection_manager/config.h @@ -147,6 +147,7 @@ class HttpConnectionManagerConfig : Logger::Loggable, serverHeaderTransformation() const override { return server_transformation_; } + const absl::optional& schemeToSet() const override { return scheme_to_set_; } Http::ConnectionManagerStats& stats() override { return stats_; } Http::ConnectionManagerTracingStats& tracingStats() override { return tracing_stats_; } bool useRemoteAddress() const override { return use_remote_address_; } @@ -245,6 +246,7 @@ class HttpConnectionManagerConfig : Logger::Loggable, HttpConnectionManagerProto::ServerHeaderTransformation server_transformation_{ HttpConnectionManagerProto::OVERWRITE}; std::string server_name_; + absl::optional scheme_to_set_; Tracing::HttpTracerSharedPtr http_tracer_{std::make_shared()}; Http::TracingConnectionManagerConfigPtr tracing_config_; absl::optional user_agent_; diff --git a/source/extensions/matching/common_inputs/environment_variable/config.cc b/source/extensions/matching/common_inputs/environment_variable/config.cc index bebbe41a7fd91..be8235c2c5a3f 100644 --- a/source/extensions/matching/common_inputs/environment_variable/config.cc +++ b/source/extensions/matching/common_inputs/environment_variable/config.cc @@ -12,10 +12,10 @@ namespace EnvironmentVariable { Envoy::Matcher::CommonProtocolInputFactoryCb Config::createCommonProtocolInputFactoryCb(const Protobuf::Message& config, - Server::Configuration::FactoryContext& factory_context) { + ProtobufMessage::ValidationVisitor& validation_visitor) { const auto& environment_config = MessageUtil::downcastAndValidate< const envoy::extensions::matching::common_inputs::environment_variable::v3::Config&>( - config, factory_context.messageValidationVisitor()); + config, validation_visitor); // We read the env variable at construction time to avoid repeat lookups. // This assumes that the environment remains stable during the process lifetime. diff --git a/source/extensions/matching/common_inputs/environment_variable/config.h b/source/extensions/matching/common_inputs/environment_variable/config.h index 842e7c4bb23f4..9db12bdcaf215 100644 --- a/source/extensions/matching/common_inputs/environment_variable/config.h +++ b/source/extensions/matching/common_inputs/environment_variable/config.h @@ -18,7 +18,7 @@ class Config : public Envoy::Matcher::CommonProtocolInputFactory { public: Envoy::Matcher::CommonProtocolInputFactoryCb createCommonProtocolInputFactoryCb( const Protobuf::Message& config, - Server::Configuration::FactoryContext& factory_context) override; + ProtobufMessage::ValidationVisitor& validation_visitor) override; std::string name() const override { return "envoy.matching.common_inputs.environment_variable"; } diff --git a/source/extensions/matching/input_matchers/consistent_hashing/config.cc b/source/extensions/matching/input_matchers/consistent_hashing/config.cc index cfcac842d3667..1f7f4eb61ebea 100644 --- a/source/extensions/matching/input_matchers/consistent_hashing/config.cc +++ b/source/extensions/matching/input_matchers/consistent_hashing/config.cc @@ -7,7 +7,7 @@ namespace InputMatchers { namespace ConsistentHashing { Envoy::Matcher::InputMatcherFactoryCb ConsistentHashingConfig::createInputMatcherFactoryCb( - const Protobuf::Message& config, Server::Configuration::FactoryContext& factory_context) { + const Protobuf::Message& config, Server::Configuration::ServerFactoryContext& factory_context) { const auto& consistent_hashing_config = MessageUtil::downcastAndValidate( diff --git a/source/extensions/matching/input_matchers/consistent_hashing/config.h b/source/extensions/matching/input_matchers/consistent_hashing/config.h index 1ce2dd6289863..eda2f6fdf4faa 100644 --- a/source/extensions/matching/input_matchers/consistent_hashing/config.h +++ b/source/extensions/matching/input_matchers/consistent_hashing/config.h @@ -16,9 +16,9 @@ namespace ConsistentHashing { class ConsistentHashingConfig : public Envoy::Matcher::InputMatcherFactory { public: - Envoy::Matcher::InputMatcherFactoryCb - createInputMatcherFactoryCb(const Protobuf::Message& config, - Server::Configuration::FactoryContext& factory_context) override; + Envoy::Matcher::InputMatcherFactoryCb createInputMatcherFactoryCb( + const Protobuf::Message& config, + Server::Configuration::ServerFactoryContext& factory_context) override; std::string name() const override { return "envoy.matching.matchers.consistent_hashing"; } diff --git a/source/extensions/matching/input_matchers/ip/config.cc b/source/extensions/matching/input_matchers/ip/config.cc index 8798dd4a007fe..70a29e97ffeab 100644 --- a/source/extensions/matching/input_matchers/ip/config.cc +++ b/source/extensions/matching/input_matchers/ip/config.cc @@ -8,10 +8,10 @@ namespace IP { Envoy::Matcher::InputMatcherFactoryCb Config::createInputMatcherFactoryCb(const Protobuf::Message& config, - Server::Configuration::FactoryContext& context) { + Server::Configuration::ServerFactoryContext& factory_context) { const auto& ip_config = MessageUtil::downcastAndValidate< const envoy::extensions::matching::input_matchers::ip::v3::Ip&>( - config, context.messageValidationVisitor()); + config, factory_context.messageValidationVisitor()); const auto& cidr_ranges = ip_config.cidr_ranges(); std::vector ranges; @@ -30,7 +30,7 @@ Config::createInputMatcherFactoryCb(const Protobuf::Message& config, } const std::string& stat_prefix = ip_config.stat_prefix(); - Stats::Scope& scope = context.scope(); + Stats::Scope& scope = factory_context.scope(); return [ranges, stat_prefix, &scope]() { return std::make_unique(ranges, stat_prefix, scope); }; diff --git a/source/extensions/matching/input_matchers/ip/config.h b/source/extensions/matching/input_matchers/ip/config.h index ade3ecbfae1f1..9ba2849584689 100644 --- a/source/extensions/matching/input_matchers/ip/config.h +++ b/source/extensions/matching/input_matchers/ip/config.h @@ -16,9 +16,9 @@ namespace IP { class Config : public Envoy::Matcher::InputMatcherFactory { public: - Envoy::Matcher::InputMatcherFactoryCb - createInputMatcherFactoryCb(const Protobuf::Message& config, - Server::Configuration::FactoryContext& factory_context) override; + Envoy::Matcher::InputMatcherFactoryCb createInputMatcherFactoryCb( + const Protobuf::Message& config, + Server::Configuration::ServerFactoryContext& factory_context) override; std::string name() const override { return "envoy.matching.matchers.ip"; } diff --git a/source/extensions/request_id/uuid/config.h b/source/extensions/request_id/uuid/config.h index bd21f9eaa6bc2..61cb67a5c5ea7 100644 --- a/source/extensions/request_id/uuid/config.h +++ b/source/extensions/request_id/uuid/config.h @@ -16,7 +16,9 @@ class UUIDRequestIDExtension : public Http::RequestIDExtension { UUIDRequestIDExtension(const envoy::extensions::request_id::uuid::v3::UuidRequestIdConfig& config, Random::RandomGenerator& random) : random_(random), - pack_trace_reason_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, pack_trace_reason, true)) {} + pack_trace_reason_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, pack_trace_reason, true)), + use_request_id_for_trace_sampling_( + PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, use_request_id_for_trace_sampling, true)) {} static Http::RequestIDExtensionSharedPtr defaultInstance(Random::RandomGenerator& random) { return std::make_shared( @@ -32,10 +34,12 @@ class UUIDRequestIDExtension : public Http::RequestIDExtension { absl::optional toInteger(const Http::RequestHeaderMap& request_headers) const override; Tracing::Reason getTraceReason(const Http::RequestHeaderMap& request_headers) override; void setTraceReason(Http::RequestHeaderMap& request_headers, Tracing::Reason status) override; + bool useRequestIdForTraceSampling() const override { return use_request_id_for_trace_sampling_; } private: Envoy::Random::RandomGenerator& random_; const bool pack_trace_reason_; + const bool use_request_id_for_trace_sampling_; // Byte on this position has predefined value of 4 for UUID4. static const int TRACE_BYTE_POSITION = 14; diff --git a/source/extensions/tracers/common/ot/opentracing_driver_impl.cc b/source/extensions/tracers/common/ot/opentracing_driver_impl.cc index e8064a6bd2a92..aa1856a218003 100644 --- a/source/extensions/tracers/common/ot/opentracing_driver_impl.cc +++ b/source/extensions/tracers/common/ot/opentracing_driver_impl.cc @@ -21,28 +21,34 @@ Http::RegisterCustomInlineHeader Set(opentracing::string_view key, opentracing::string_view value) const override { Http::LowerCaseString lowercase_key{{key.data(), key.size()}}; - request_headers_.remove(lowercase_key); - request_headers_.addCopy(std::move(lowercase_key), {value.data(), value.size()}); + trace_context_.setTraceContext(lowercase_key, {value.data(), value.size()}); return {}; } private: - Http::HeaderMap& request_headers_; + Tracing::TraceContext& trace_context_; }; -class OpenTracingHTTPHeadersReader : public opentracing::HTTPHeadersReader { +/** + * TODO(wbpcode): Use opentracing::TextMapReader to replace opentracing::HTTPHeadersReader. + */ +class OpenTracingHeadersReader : public opentracing::HTTPHeadersReader { public: - explicit OpenTracingHTTPHeadersReader(const Http::RequestHeaderMap& request_headers) - : request_headers_(request_headers) {} + explicit OpenTracingHeadersReader(const Tracing::TraceContext& trace_context) + : trace_context_(trace_context) {} using OpenTracingCb = std::function(opentracing::string_view, opentracing::string_view)>; @@ -50,23 +56,29 @@ class OpenTracingHTTPHeadersReader : public opentracing::HTTPHeadersReader { // opentracing::HTTPHeadersReader opentracing::expected LookupKey(opentracing::string_view key) const override { - const auto entry = request_headers_.get(Http::LowerCaseString{{key.data(), key.size()}}); - if (!entry.empty()) { - // This is an implicitly untrusted header, so only the first value is used. - return opentracing::string_view{entry[0]->value().getStringView().data(), - entry[0]->value().getStringView().length()}; + Http::LowerCaseString lowercase_key{{key.data(), key.size()}}; + const auto entry = trace_context_.getTraceContext(lowercase_key); + if (entry.has_value()) { + return opentracing::string_view{entry.value().data(), entry.value().length()}; } else { return opentracing::make_unexpected(opentracing::key_not_found_error); } } opentracing::expected ForeachKey(OpenTracingCb f) const override { - request_headers_.iterate(headerMapCallback(f)); + // TODO(wbpcode): TraceContext currently does not provide an API to traverse all entries. So + // dynamic_cast has to be used here. This is a temporary compromise to ensure that the existing + // functions are correct. After TraceContext provides the iterative API, this part of the code + // needs to be rewritten. + const auto headers = dynamic_cast(&trace_context_); + if (headers != nullptr) { + headers->iterate(headerMapCallback(f)); + } return {}; } private: - const Http::RequestHeaderMap& request_headers_; + const Tracing::TraceContext& trace_context_; static Http::HeaderMap::ConstIterateCb headerMapCallback(OpenTracingCb callback) { return [callback = @@ -113,7 +125,7 @@ std::string OpenTracingSpan::getBaggage(absl::string_view key) { return span_->BaggageItem({key.data(), key.length()}); } -void OpenTracingSpan::injectContext(Http::RequestHeaderMap& request_headers) { +void OpenTracingSpan::injectContext(Tracing::TraceContext& trace_context) { if (driver_.propagationMode() == OpenTracingDriver::PropagationMode::SingleHeader) { // Inject the span context using Envoy's single-header format. std::ostringstream oss; @@ -125,12 +137,12 @@ void OpenTracingSpan::injectContext(Http::RequestHeaderMap& request_headers) { return; } const std::string current_span_context = oss.str(); - request_headers.setInline( - ot_span_context_handle.handle(), + trace_context.setTraceContextReferenceKey( + Http::CustomHeaders::get().OtSpanContext, Base64::encode(current_span_context.c_str(), current_span_context.length())); } else { - // Inject the context using the tracer's standard HTTP header format. - const OpenTracingHTTPHeadersWriter writer{request_headers}; + // Inject the context using the tracer's standard header format. + const OpenTracingHeadersWriter writer{trace_context}; const opentracing::expected was_successful = span_->tracer().Inject(span_->context(), writer); if (!was_successful) { @@ -157,7 +169,7 @@ OpenTracingDriver::OpenTracingDriver(Stats::Scope& scope) : tracer_stats_{OPENTRACING_TRACER_STATS(POOL_COUNTER_PREFIX(scope, "tracing.opentracing."))} {} Tracing::SpanPtr OpenTracingDriver::startSpan(const Tracing::Config& config, - Http::RequestHeaderMap& request_headers, + Tracing::TraceContext& trace_context, const std::string& operation_name, SystemTime start_time, const Tracing::Decision tracing_decision) { @@ -165,11 +177,11 @@ Tracing::SpanPtr OpenTracingDriver::startSpan(const Tracing::Config& config, const opentracing::Tracer& tracer = this->tracer(); std::unique_ptr active_span; std::unique_ptr parent_span_ctx; - if (propagation_mode == PropagationMode::SingleHeader && - request_headers.getInline(ot_span_context_handle.handle())) { + + const auto entry = trace_context.getTraceContext(Http::CustomHeaders::get().OtSpanContext); + if (propagation_mode == PropagationMode::SingleHeader && entry.has_value()) { opentracing::expected> parent_span_ctx_maybe; - std::string parent_context = Base64::decode( - std::string(request_headers.getInlineValue(ot_span_context_handle.handle()))); + std::string parent_context = Base64::decode(std::string(entry.value())); if (!parent_context.empty()) { InputConstMemoryStream istream{parent_context.data(), parent_context.size()}; @@ -187,7 +199,7 @@ Tracing::SpanPtr OpenTracingDriver::startSpan(const Tracing::Config& config, tracerStats().span_context_extraction_error_.inc(); } } else if (propagation_mode == PropagationMode::TracerNative) { - const OpenTracingHTTPHeadersReader reader{request_headers}; + const OpenTracingHeadersReader reader{trace_context}; opentracing::expected> parent_span_ctx_maybe = tracer.Extract(reader); if (parent_span_ctx_maybe) { diff --git a/source/extensions/tracers/common/ot/opentracing_driver_impl.h b/source/extensions/tracers/common/ot/opentracing_driver_impl.h index 582e31ed31d54..6494b8b140f89 100644 --- a/source/extensions/tracers/common/ot/opentracing_driver_impl.h +++ b/source/extensions/tracers/common/ot/opentracing_driver_impl.h @@ -37,7 +37,7 @@ class OpenTracingSpan : public Tracing::Span, Logger::Loggable; class Span : public Tracing::Span { public: Span(const Tracing::Config& config, const envoy::config::trace::v3::OpenCensusConfig& oc_config, - Http::RequestHeaderMap& request_headers, const std::string& operation_name, + Tracing::TraceContext& trace_context, const std::string& operation_name, SystemTime start_time, const Tracing::Decision tracing_decision); // Used by spawnChild(). @@ -69,7 +69,7 @@ class Span : public Tracing::Span { void setTag(absl::string_view name, absl::string_view value) override; void log(SystemTime timestamp, const std::string& event) override; void finishSpan() override; - void injectContext(Http::RequestHeaderMap& request_headers) override; + void injectContext(Tracing::TraceContext& trace_context) override; Tracing::SpanPtr spawnChild(const Tracing::Config& config, const std::string& name, SystemTime start_time) override; void setSampled(bool sampled) override; @@ -86,7 +86,7 @@ class Span : public Tracing::Span { }; ::opencensus::trace::Span -startSpanHelper(const std::string& name, bool traced, const Http::RequestHeaderMap& request_headers, +startSpanHelper(const std::string& name, bool traced, const Tracing::TraceContext& trace_context, const envoy::config::trace::v3::OpenCensusConfig& oc_config) { // Determine if there is a parent context. using OpenCensusConfig = envoy::config::trace::v3::OpenCensusConfig; @@ -95,34 +95,32 @@ startSpanHelper(const std::string& name, bool traced, const Http::RequestHeaderM bool found = false; switch (incoming) { case OpenCensusConfig::TRACE_CONTEXT: { - const auto header = request_headers.get(Constants::get().TRACEPARENT); - if (!header.empty()) { + const auto entry = trace_context.getTraceContext(Constants::get().TRACEPARENT); + if (entry.has_value()) { found = true; // This is an implicitly untrusted header, so only the first value is used. - parent_ctx = ::opencensus::trace::propagation::FromTraceParentHeader( - header[0]->value().getStringView()); + parent_ctx = ::opencensus::trace::propagation::FromTraceParentHeader(entry.value()); } break; } case OpenCensusConfig::GRPC_TRACE_BIN: { - const auto header = request_headers.get(Constants::get().GRPC_TRACE_BIN); - if (!header.empty()) { + const auto entry = trace_context.getTraceContext(Constants::get().GRPC_TRACE_BIN); + if (entry.has_value()) { found = true; // This is an implicitly untrusted header, so only the first value is used. parent_ctx = ::opencensus::trace::propagation::FromGrpcTraceBinHeader( - Base64::decodeWithoutPadding(header[0]->value().getStringView())); + Base64::decodeWithoutPadding(entry.value())); } break; } case OpenCensusConfig::CLOUD_TRACE_CONTEXT: { - const auto header = request_headers.get(Constants::get().X_CLOUD_TRACE_CONTEXT); - if (!header.empty()) { + const auto entry = trace_context.getTraceContext(Constants::get().X_CLOUD_TRACE_CONTEXT); + if (entry.has_value()) { found = true; // This is an implicitly untrusted header, so only the first value is used. - parent_ctx = ::opencensus::trace::propagation::FromCloudTraceContextHeader( - header[0]->value().getStringView()); + parent_ctx = ::opencensus::trace::propagation::FromCloudTraceContextHeader(entry.value()); } break; } @@ -132,27 +130,23 @@ startSpanHelper(const std::string& name, bool traced, const Http::RequestHeaderM absl::string_view b3_span_id; absl::string_view b3_sampled; absl::string_view b3_flags; - const auto h_b3_trace_id = request_headers.get(Constants::get().X_B3_TRACEID); - if (!h_b3_trace_id.empty()) { - // This is an implicitly untrusted header, so only the first value is used. - b3_trace_id = h_b3_trace_id[0]->value().getStringView(); + const auto h_b3_trace_id = trace_context.getTraceContext(Constants::get().X_B3_TRACEID); + if (h_b3_trace_id.has_value()) { + b3_trace_id = h_b3_trace_id.value(); } - const auto h_b3_span_id = request_headers.get(Constants::get().X_B3_SPANID); - if (!h_b3_span_id.empty()) { - // This is an implicitly untrusted header, so only the first value is used. - b3_span_id = h_b3_span_id[0]->value().getStringView(); + const auto h_b3_span_id = trace_context.getTraceContext(Constants::get().X_B3_SPANID); + if (h_b3_span_id.has_value()) { + b3_span_id = h_b3_span_id.value(); } - const auto h_b3_sampled = request_headers.get(Constants::get().X_B3_SAMPLED); - if (!h_b3_sampled.empty()) { - // This is an implicitly untrusted header, so only the first value is used. - b3_sampled = h_b3_sampled[0]->value().getStringView(); + const auto h_b3_sampled = trace_context.getTraceContext(Constants::get().X_B3_SAMPLED); + if (h_b3_sampled.has_value()) { + b3_sampled = h_b3_sampled.value(); } - const auto h_b3_flags = request_headers.get(Constants::get().X_B3_FLAGS); - if (!h_b3_flags.empty()) { - // This is an implicitly untrusted header, so only the first value is used. - b3_flags = h_b3_flags[0]->value().getStringView(); + const auto h_b3_flags = trace_context.getTraceContext(Constants::get().X_B3_FLAGS); + if (h_b3_flags.has_value()) { + b3_flags = h_b3_flags.value(); } - if (!h_b3_trace_id.empty() && !h_b3_span_id.empty()) { + if (h_b3_trace_id.has_value() && h_b3_span_id.has_value()) { found = true; parent_ctx = ::opencensus::trace::propagation::FromB3Headers(b3_trace_id, b3_span_id, b3_sampled, b3_flags); @@ -183,9 +177,9 @@ startSpanHelper(const std::string& name, bool traced, const Http::RequestHeaderM Span::Span(const Tracing::Config& config, const envoy::config::trace::v3::OpenCensusConfig& oc_config, - Http::RequestHeaderMap& request_headers, const std::string& operation_name, + Tracing::TraceContext& trace_context, const std::string& operation_name, SystemTime /*start_time*/, const Tracing::Decision tracing_decision) - : span_(startSpanHelper(operation_name, tracing_decision.traced, request_headers, oc_config)), + : span_(startSpanHelper(operation_name, tracing_decision.traced, trace_context, oc_config)), oc_config_(oc_config) { span_.AddAttribute("OperationName", config.operationName() == Tracing::OperationName::Ingress ? "Ingress" @@ -209,36 +203,36 @@ void Span::log(SystemTime /*timestamp*/, const std::string& event) { void Span::finishSpan() { span_.End(); } -void Span::injectContext(Http::RequestHeaderMap& request_headers) { +void Span::injectContext(Tracing::TraceContext& trace_context) { using OpenCensusConfig = envoy::config::trace::v3::OpenCensusConfig; const auto& ctx = span_.context(); for (const auto& outgoing : oc_config_.outgoing_trace_context()) { switch (outgoing) { case OpenCensusConfig::TRACE_CONTEXT: - request_headers.setReferenceKey(Constants::get().TRACEPARENT, - ::opencensus::trace::propagation::ToTraceParentHeader(ctx)); + trace_context.setTraceContextReferenceKey( + Constants::get().TRACEPARENT, ::opencensus::trace::propagation::ToTraceParentHeader(ctx)); break; case OpenCensusConfig::GRPC_TRACE_BIN: { std::string val = ::opencensus::trace::propagation::ToGrpcTraceBinHeader(ctx); val = Base64::encode(val.data(), val.size(), /*add_padding=*/false); - request_headers.setReferenceKey(Constants::get().GRPC_TRACE_BIN, val); + trace_context.setTraceContextReferenceKey(Constants::get().GRPC_TRACE_BIN, val); break; } case OpenCensusConfig::CLOUD_TRACE_CONTEXT: - request_headers.setReferenceKey( + trace_context.setTraceContextReferenceKey( Constants::get().X_CLOUD_TRACE_CONTEXT, ::opencensus::trace::propagation::ToCloudTraceContextHeader(ctx)); break; case OpenCensusConfig::B3: - request_headers.setReferenceKey(Constants::get().X_B3_TRACEID, - ::opencensus::trace::propagation::ToB3TraceIdHeader(ctx)); - request_headers.setReferenceKey(Constants::get().X_B3_SPANID, - ::opencensus::trace::propagation::ToB3SpanIdHeader(ctx)); - request_headers.setReferenceKey(Constants::get().X_B3_SAMPLED, - ::opencensus::trace::propagation::ToB3SampledHeader(ctx)); + trace_context.setTraceContextReferenceKey( + Constants::get().X_B3_TRACEID, ::opencensus::trace::propagation::ToB3TraceIdHeader(ctx)); + trace_context.setTraceContextReferenceKey( + Constants::get().X_B3_SPANID, ::opencensus::trace::propagation::ToB3SpanIdHeader(ctx)); + trace_context.setTraceContextReferenceKey( + Constants::get().X_B3_SAMPLED, ::opencensus::trace::propagation::ToB3SampledHeader(ctx)); // OpenCensus's trace context propagation doesn't produce the // "X-B3-Flags:" header. break; @@ -384,10 +378,10 @@ void Driver::applyTraceConfig(const opencensus::proto::trace::v1::TraceConfig& c } Tracing::SpanPtr Driver::startSpan(const Tracing::Config& config, - Http::RequestHeaderMap& request_headers, + Tracing::TraceContext& trace_context, const std::string& operation_name, SystemTime start_time, const Tracing::Decision tracing_decision) { - return std::make_unique(config, oc_config_, request_headers, operation_name, start_time, + return std::make_unique(config, oc_config_, trace_context, operation_name, start_time, tracing_decision); } diff --git a/source/extensions/tracers/opencensus/opencensus_tracer_impl.h b/source/extensions/tracers/opencensus/opencensus_tracer_impl.h index ab0c14d1c5efc..0fdb17c4c6838 100644 --- a/source/extensions/tracers/opencensus/opencensus_tracer_impl.h +++ b/source/extensions/tracers/opencensus/opencensus_tracer_impl.h @@ -23,7 +23,7 @@ class Driver : public Tracing::Driver, Logger::Loggable { /** * Implements the abstract Driver's startSpan operation. */ - Tracing::SpanPtr startSpan(const Tracing::Config& config, Http::RequestHeaderMap& request_headers, + Tracing::SpanPtr startSpan(const Tracing::Config& config, Tracing::TraceContext& trace_context, const std::string& operation_name, SystemTime start_time, const Tracing::Decision tracing_decision) override; diff --git a/source/extensions/tracers/skywalking/skywalking_tracer_impl.cc b/source/extensions/tracers/skywalking/skywalking_tracer_impl.cc index 716db5434ee10..93a34abd254ff 100644 --- a/source/extensions/tracers/skywalking/skywalking_tracer_impl.cc +++ b/source/extensions/tracers/skywalking/skywalking_tracer_impl.cc @@ -45,14 +45,14 @@ Driver::Driver(const envoy::config::trace::v3::SkyWalkingConfig& proto_config, } Tracing::SpanPtr Driver::startSpan(const Tracing::Config& config, - Http::RequestHeaderMap& request_headers, + Tracing::TraceContext& trace_context, const std::string& operation_name, Envoy::SystemTime start_time, const Tracing::Decision decision) { auto& tracer = tls_slot_ptr_->getTyped().tracer(); TracingContextPtr tracing_context; // TODO(shikugawa): support extension span header. - auto propagation_header = request_headers.get(skywalkingPropagationHeaderKey()); - if (propagation_header.empty()) { + auto propagation_header = trace_context.getTraceContext(skywalkingPropagationHeaderKey()); + if (!propagation_header.has_value()) { tracing_context = tracing_context_factory_->create(); // Sampling status is always true on SkyWalking. But with disabling skip_analysis, // this span can't be analyzed. @@ -60,7 +60,7 @@ Tracing::SpanPtr Driver::startSpan(const Tracing::Config& config, tracing_context->setSkipAnalysis(); } } else { - auto header_value_string = propagation_header[0]->value().getStringView(); + auto header_value_string = propagation_header.value(); try { SpanContextPtr span_context = createSpanContext(toStdStringView(header_value_string)); // NOLINT(std::string_view) diff --git a/source/extensions/tracers/skywalking/skywalking_tracer_impl.h b/source/extensions/tracers/skywalking/skywalking_tracer_impl.h index aeb4f91434fb5..433c92706ea2a 100644 --- a/source/extensions/tracers/skywalking/skywalking_tracer_impl.h +++ b/source/extensions/tracers/skywalking/skywalking_tracer_impl.h @@ -24,7 +24,7 @@ class Driver : public Tracing::Driver, public Logger::LoggablecreateSW8HeaderValue(std::string(request_headers.getHostValue())); +void Span::injectContext(Tracing::TraceContext& trace_context) { + const auto host = trace_context.getTraceContext(Http::Headers::get().HostLegacy).value_or(""); + + // TODO(wbpcode): Due to https://github.com/SkyAPM/cpp2sky/issues/83 in cpp2sky, it is necessary + // to ensure that there is '\0' at the end of the string_view parameter to ensure that the + // corresponding trace header is generated correctly. For this reason, we cannot directly use host + // as argument. We need create a copy of std::string based on host and std::string will + // automatically add '\0' to the end of the string content. + auto sw8_header = tracing_context_->createSW8HeaderValue(std::string(host)); if (sw8_header.has_value()) { - request_headers.setReferenceKey(skywalkingPropagationHeaderKey(), sw8_header.value()); + trace_context.setTraceContextReferenceKey(skywalkingPropagationHeaderKey(), sw8_header.value()); } } diff --git a/source/extensions/tracers/skywalking/tracer.h b/source/extensions/tracers/skywalking/tracer.h index d0e1887277c65..dd5d641d0b7be 100644 --- a/source/extensions/tracers/skywalking/tracer.h +++ b/source/extensions/tracers/skywalking/tracer.h @@ -66,7 +66,7 @@ class Span : public Tracing::Span { void setTag(absl::string_view name, absl::string_view value) override; void log(SystemTime timestamp, const std::string& event) override; void finishSpan() override; - void injectContext(Http::RequestHeaderMap& request_headers) override; + void injectContext(Tracing::TraceContext& trace_context) override; Tracing::SpanPtr spawnChild(const Tracing::Config& config, const std::string& name, SystemTime start_time) override; void setSampled(bool do_sample) override; diff --git a/source/extensions/tracers/xray/BUILD b/source/extensions/tracers/xray/BUILD index a0824d8ea9bce..4944d38060b91 100644 --- a/source/extensions/tracers/xray/BUILD +++ b/source/extensions/tracers/xray/BUILD @@ -46,6 +46,7 @@ envoy_cc_library( "//source/common/common:macros", "//source/common/common:random_generator_lib", "//source/common/http:header_map_lib", + "//source/common/http:headers_lib", "//source/common/json:json_loader_lib", "//source/common/protobuf:utility_lib", "//source/common/runtime:runtime_lib", diff --git a/source/extensions/tracers/xray/sampling_strategy.h b/source/extensions/tracers/xray/sampling_strategy.h index 4d744aaef02e4..26bffa90a7250 100644 --- a/source/extensions/tracers/xray/sampling_strategy.h +++ b/source/extensions/tracers/xray/sampling_strategy.h @@ -15,9 +15,9 @@ namespace Tracers { namespace XRay { struct SamplingRequest { - std::string host_; - std::string http_method_; - std::string http_url_; + absl::string_view host_; + absl::string_view http_method_; + absl::string_view http_url_; }; /** diff --git a/source/extensions/tracers/xray/tracer.cc b/source/extensions/tracers/xray/tracer.cc index b7e1f603fcd62..bec05c4fa1018 100644 --- a/source/extensions/tracers/xray/tracer.cc +++ b/source/extensions/tracers/xray/tracer.cc @@ -97,10 +97,10 @@ void Span::finishSpan() { broker_.send(json); } // namespace XRay -void Span::injectContext(Http::RequestHeaderMap& request_headers) { +void Span::injectContext(Tracing::TraceContext& trace_context) { const std::string xray_header_value = fmt::format("Root={};Parent={};Sampled={}", traceId(), id(), sampled() ? "1" : "0"); - request_headers.setCopy(Http::LowerCaseString(XRayTraceHeader), xray_header_value); + trace_context.setTraceContextReferenceKey(XRayTraceHeader, xray_header_value); } Tracing::SpanPtr Span::spawnChild(const Tracing::Config&, const std::string& operation_name, diff --git a/source/extensions/tracers/xray/tracer.h b/source/extensions/tracers/xray/tracer.h index 7e252f7cfc295..bd3c772be3160 100644 --- a/source/extensions/tracers/xray/tracer.h +++ b/source/extensions/tracers/xray/tracer.h @@ -123,7 +123,7 @@ class Span : public Tracing::Span, Logger::Loggable { /** * Adds X-Ray trace header to the set of outgoing headers. */ - void injectContext(Http::RequestHeaderMap& request_headers) override; + void injectContext(Tracing::TraceContext& trace_context) override; /** * Gets the start time of this Span. diff --git a/source/extensions/tracers/xray/xray_tracer_impl.cc b/source/extensions/tracers/xray/xray_tracer_impl.cc index 22bba12bf348a..964b982cba8ee 100644 --- a/source/extensions/tracers/xray/xray_tracer_impl.cc +++ b/source/extensions/tracers/xray/xray_tracer_impl.cc @@ -2,6 +2,7 @@ #include "source/common/common/macros.h" #include "source/common/common/utility.h" +#include "source/common/http/headers.h" #include "source/extensions/tracers/xray/localized_sampling.h" #include "source/extensions/tracers/xray/tracer.h" #include "source/extensions/tracers/xray/xray_configuration.h" @@ -61,7 +62,7 @@ Driver::Driver(const XRayConfiguration& config, } Tracing::SpanPtr Driver::startSpan(const Tracing::Config& config, - Http::RequestHeaderMap& request_headers, + Tracing::TraceContext& trace_context, const std::string& operation_name, Envoy::SystemTime start_time, const Tracing::Decision tracing_decision) { // First thing is to determine whether this request will be sampled or not. @@ -76,12 +77,12 @@ Tracing::SpanPtr Driver::startSpan(const Tracing::Config& config, UNREFERENCED_PARAMETER(config); // TODO(marcomagdy) - how do we factor this into the logic above UNREFERENCED_PARAMETER(tracing_decision); - const auto header = request_headers.get(Http::LowerCaseString(XRayTraceHeader)); + const auto header = trace_context.getTraceContext(XRayTraceHeader); absl::optional should_trace; XRayHeader xray_header; - if (!header.empty()) { + if (header.has_value()) { // This is an implicitly untrusted header, so only the first value is used. - Http::LowerCaseString lowered_header_value{std::string(header[0]->value().getStringView())}; + Http::LowerCaseString lowered_header_value{header.value()}; xray_header = parseXRayHeader(lowered_header_value); // if the sample_decision in the x-ray header is unknown then we try to make a decision based // on the sampling strategy @@ -97,9 +98,10 @@ Tracing::SpanPtr Driver::startSpan(const Tracing::Config& config, } if (!should_trace.has_value()) { - const SamplingRequest request{std::string{request_headers.getHostValue()}, - std::string{request_headers.getMethodValue()}, - std::string{request_headers.getPathValue()}}; + const SamplingRequest request{ + trace_context.getTraceContext(Http::Headers::get().HostLegacy).value_or(""), + trace_context.getTraceContext(Http::Headers::get().Method).value_or(""), + trace_context.getTraceContext(Http::Headers::get().Path).value_or("")}; should_trace = sampling_strategy_->shouldTrace(request); } @@ -107,8 +109,8 @@ Tracing::SpanPtr Driver::startSpan(const Tracing::Config& config, auto* tracer = tls_slot_ptr_->getTyped().tracer_.get(); if (should_trace.value()) { return tracer->startSpan(operation_name, start_time, - !header.empty() ? absl::optional(xray_header) - : absl::nullopt); + header.has_value() ? absl::optional(xray_header) + : absl::nullopt); } // instead of returning nullptr, we return a Span that is marked as not-sampled. diff --git a/source/extensions/tracers/xray/xray_tracer_impl.h b/source/extensions/tracers/xray/xray_tracer_impl.h index 37357dfff8e03..775f11a836ac8 100644 --- a/source/extensions/tracers/xray/xray_tracer_impl.h +++ b/source/extensions/tracers/xray/xray_tracer_impl.h @@ -17,7 +17,7 @@ class Driver : public Tracing::Driver, public Logger::Loggablevalue().getStringView(); + absl::string_view b3 = b3_header_entry.value(); int sampled_pos = 0; switch (b3.length()) { case 1: @@ -61,20 +61,20 @@ bool SpanContextExtractor::extractSampled(const Tracing::Decision tracing_decisi return getSamplingFlags(b3[sampled_pos], tracing_decision); } - auto x_b3_sampled_entry = request_headers_.get(ZipkinCoreConstants::get().X_B3_SAMPLED); - if (x_b3_sampled_entry.empty()) { + auto x_b3_sampled_entry = trace_context_.getTraceContext(ZipkinCoreConstants::get().X_B3_SAMPLED); + if (!x_b3_sampled_entry.has_value()) { return tracing_decision.traced; } // Checking if sampled flag has been specified. Also checking for 'true' value, as some old // zipkin tracers may still use that value, although should be 0 or 1. // This is an implicitly untrusted header, so only the first value is used. - absl::string_view xb3_sampled = x_b3_sampled_entry[0]->value().getStringView(); + absl::string_view xb3_sampled = x_b3_sampled_entry.value(); sampled = xb3_sampled == SAMPLED || xb3_sampled == "true"; return sampled; } std::pair SpanContextExtractor::extractSpanContext(bool is_sampled) { - if (!request_headers_.get(ZipkinCoreConstants::get().B3).empty()) { + if (trace_context_.getTraceContext(ZipkinCoreConstants::get().B3).has_value()) { return extractSpanContextFromB3SingleFormat(is_sampled); } uint64_t trace_id(0); @@ -82,14 +82,14 @@ std::pair SpanContextExtractor::extractSpanContext(bool is_sa uint64_t span_id(0); uint64_t parent_id(0); - auto b3_trace_id_entry = request_headers_.get(ZipkinCoreConstants::get().X_B3_TRACE_ID); - auto b3_span_id_entry = request_headers_.get(ZipkinCoreConstants::get().X_B3_SPAN_ID); - if (!b3_span_id_entry.empty() && !b3_trace_id_entry.empty()) { + auto b3_trace_id_entry = trace_context_.getTraceContext(ZipkinCoreConstants::get().X_B3_TRACE_ID); + auto b3_span_id_entry = trace_context_.getTraceContext(ZipkinCoreConstants::get().X_B3_SPAN_ID); + if (b3_span_id_entry.has_value() && b3_trace_id_entry.has_value()) { // Extract trace id - which can either be 128 or 64 bit. For 128 bit, // it needs to be divided into two 64 bit numbers (high and low). // This is an implicitly untrusted header, so only the first value is used. - const std::string tid(b3_trace_id_entry[0]->value().getStringView()); - if (b3_trace_id_entry[0]->value().size() == 32) { + const std::string tid(b3_trace_id_entry.value()); + if (b3_trace_id_entry.value().size() == 32) { const std::string high_tid = tid.substr(0, 16); const std::string low_tid = tid.substr(16, 16); if (!StringUtil::atoull(high_tid.c_str(), trace_id_high, 16) || @@ -102,15 +102,16 @@ std::pair SpanContextExtractor::extractSpanContext(bool is_sa } // This is an implicitly untrusted header, so only the first value is used. - const std::string spid(b3_span_id_entry[0]->value().getStringView()); + const std::string spid(b3_span_id_entry.value()); if (!StringUtil::atoull(spid.c_str(), span_id, 16)) { throw ExtractorException(absl::StrCat("Invalid span id ", spid.c_str())); } - auto b3_parent_id_entry = request_headers_.get(ZipkinCoreConstants::get().X_B3_PARENT_SPAN_ID); - if (!b3_parent_id_entry.empty() && !b3_parent_id_entry[0]->value().empty()) { + auto b3_parent_id_entry = + trace_context_.getTraceContext(ZipkinCoreConstants::get().X_B3_PARENT_SPAN_ID); + if (b3_parent_id_entry.has_value() && !b3_parent_id_entry.value().empty()) { // This is an implicitly untrusted header, so only the first value is used. - const std::string pspid(b3_parent_id_entry[0]->value().getStringView()); + const std::string pspid(b3_parent_id_entry.value()); if (!StringUtil::atoull(pspid.c_str(), parent_id, 16)) { throw ExtractorException(absl::StrCat("Invalid parent span id ", pspid.c_str())); } @@ -124,10 +125,10 @@ std::pair SpanContextExtractor::extractSpanContext(bool is_sa std::pair SpanContextExtractor::extractSpanContextFromB3SingleFormat(bool is_sampled) { - auto b3_head_entry = request_headers_.get(ZipkinCoreConstants::get().B3); - ASSERT(!b3_head_entry.empty()); + auto b3_head_entry = trace_context_.getTraceContext(ZipkinCoreConstants::get().B3); + ASSERT(b3_head_entry.has_value()); // This is an implicitly untrusted header, so only the first value is used. - const std::string b3(b3_head_entry[0]->value().getStringView()); + const std::string b3(b3_head_entry.value()); if (!b3.length()) { throw ExtractorException("Invalid input: empty"); } diff --git a/source/extensions/tracers/zipkin/span_context_extractor.h b/source/extensions/tracers/zipkin/span_context_extractor.h index b364d6e528c38..c326740176072 100644 --- a/source/extensions/tracers/zipkin/span_context_extractor.h +++ b/source/extensions/tracers/zipkin/span_context_extractor.h @@ -21,7 +21,7 @@ struct ExtractorException : public EnvoyException { */ class SpanContextExtractor { public: - SpanContextExtractor(Http::RequestHeaderMap& request_headers); + SpanContextExtractor(Tracing::TraceContext& trace_context); ~SpanContextExtractor(); bool extractSampled(const Tracing::Decision tracing_decision); std::pair extractSpanContext(bool is_sampled); @@ -34,7 +34,7 @@ class SpanContextExtractor { */ std::pair extractSpanContextFromB3SingleFormat(bool is_sampled); bool tryExtractSampledFromB3SingleFormat(); - const Http::RequestHeaderMap& request_headers_; + const Tracing::TraceContext& trace_context_; }; } // namespace Zipkin diff --git a/source/extensions/tracers/zipkin/zipkin_tracer_impl.cc b/source/extensions/tracers/zipkin/zipkin_tracer_impl.cc index 77c7944550adf..9e87e1343e27e 100644 --- a/source/extensions/tracers/zipkin/zipkin_tracer_impl.cc +++ b/source/extensions/tracers/zipkin/zipkin_tracer_impl.cc @@ -39,21 +39,22 @@ void ZipkinSpan::log(SystemTime timestamp, const std::string& event) { void ZipkinSpan::setBaggage(absl::string_view, absl::string_view) {} std::string ZipkinSpan::getBaggage(absl::string_view) { return EMPTY_STRING; } -void ZipkinSpan::injectContext(Http::RequestHeaderMap& request_headers) { +void ZipkinSpan::injectContext(Tracing::TraceContext& trace_context) { // Set the trace-id and span-id headers properly, based on the newly-created span structure. - request_headers.setReferenceKey(ZipkinCoreConstants::get().X_B3_TRACE_ID, - span_.traceIdAsHexString()); - request_headers.setReferenceKey(ZipkinCoreConstants::get().X_B3_SPAN_ID, span_.idAsHexString()); + trace_context.setTraceContextReferenceKey(ZipkinCoreConstants::get().X_B3_TRACE_ID, + span_.traceIdAsHexString()); + trace_context.setTraceContextReferenceKey(ZipkinCoreConstants::get().X_B3_SPAN_ID, + span_.idAsHexString()); // Set the parent-span header properly, based on the newly-created span structure. if (span_.isSetParentId()) { - request_headers.setReferenceKey(ZipkinCoreConstants::get().X_B3_PARENT_SPAN_ID, - span_.parentIdAsHexString()); + trace_context.setTraceContextReferenceKey(ZipkinCoreConstants::get().X_B3_PARENT_SPAN_ID, + span_.parentIdAsHexString()); } // Set the sampled header. - request_headers.setReferenceKey(ZipkinCoreConstants::get().X_B3_SAMPLED, - span_.sampled() ? SAMPLED : NOT_SAMPLED); + trace_context.setTraceContextReferenceKey(ZipkinCoreConstants::get().X_B3_SAMPLED, + span_.sampled() ? SAMPLED : NOT_SAMPLED); } void ZipkinSpan::setSampled(bool sampled) { span_.setSampled(sampled); } @@ -107,23 +108,27 @@ Driver::Driver(const envoy::config::trace::v3::ZipkinConfig& zipkin_config, } Tracing::SpanPtr Driver::startSpan(const Tracing::Config& config, - Http::RequestHeaderMap& request_headers, const std::string&, + Tracing::TraceContext& trace_context, const std::string&, SystemTime start_time, const Tracing::Decision tracing_decision) { Tracer& tracer = *tls_->getTyped().tracer_; SpanPtr new_zipkin_span; - SpanContextExtractor extractor(request_headers); + SpanContextExtractor extractor(trace_context); bool sampled{extractor.extractSampled(tracing_decision)}; try { auto ret_span_context = extractor.extractSpanContext(sampled); if (!ret_span_context.second) { // Create a root Zipkin span. No context was found in the headers. - new_zipkin_span = - tracer.startSpan(config, std::string(request_headers.getHostValue()), start_time); + new_zipkin_span = tracer.startSpan( + config, + std::string(trace_context.getTraceContext(Http::Headers::get().HostLegacy).value_or("")), + start_time); new_zipkin_span->setSampled(sampled); } else { - new_zipkin_span = tracer.startSpan(config, std::string(request_headers.getHostValue()), - start_time, ret_span_context.first); + new_zipkin_span = tracer.startSpan( + config, + std::string(trace_context.getTraceContext(Http::Headers::get().HostLegacy).value_or("")), + start_time, ret_span_context.first); } } catch (const ExtractorException& e) { diff --git a/source/extensions/tracers/zipkin/zipkin_tracer_impl.h b/source/extensions/tracers/zipkin/zipkin_tracer_impl.h index 7def3ced8e491..9e000cafd6601 100644 --- a/source/extensions/tracers/zipkin/zipkin_tracer_impl.h +++ b/source/extensions/tracers/zipkin/zipkin_tracer_impl.h @@ -72,7 +72,7 @@ class ZipkinSpan : public Tracing::Span { void log(SystemTime timestamp, const std::string& event) override; - void injectContext(Http::RequestHeaderMap& request_headers) override; + void injectContext(Tracing::TraceContext& trace_context) override; Tracing::SpanPtr spawnChild(const Tracing::Config&, const std::string& name, SystemTime start_time) override; @@ -122,7 +122,7 @@ class Driver : public Tracing::Driver { * Thus, this implementation of the virtual function startSpan() ignores the operation name * ("ingress" or "egress") passed by the caller. */ - Tracing::SpanPtr startSpan(const Tracing::Config&, Http::RequestHeaderMap& request_headers, + Tracing::SpanPtr startSpan(const Tracing::Config&, Tracing::TraceContext& trace_context, const std::string&, SystemTime start_time, const Tracing::Decision tracing_decision) override; diff --git a/source/server/BUILD b/source/server/BUILD index 78b978d16a4f0..9c893e5444288 100644 --- a/source/server/BUILD +++ b/source/server/BUILD @@ -86,6 +86,7 @@ envoy_cc_library( "//envoy/network:filter_interface", "//envoy/network:listen_socket_interface", "//envoy/network:listener_interface", + "//envoy/runtime:runtime_interface", "//envoy/server:listener_manager_interface", "//envoy/stats:timespan_interface", "//source/common/common:linked_object", diff --git a/source/server/active_tcp_listener.cc b/source/server/active_tcp_listener.cc index a8a2120ba2a08..d48ae15f26244 100644 --- a/source/server/active_tcp_listener.cc +++ b/source/server/active_tcp_listener.cc @@ -199,8 +199,9 @@ void ActiveTcpSocket::newConnection() { if (socket_->detectedTransportProtocol().empty()) { socket_->setDetectedTransportProtocol("raw_buffer"); } - // Clear the listener filter to ensure the file event registered by - // listener filter to be removed. reference https://github.com/envoyproxy/envoy/issues/8925. + // Reset the file events which are registered by listener filter. + // reference https://github.com/envoyproxy/envoy/issues/8925. + socket_->ioHandle().resetFileEvents(); accept_filters_.clear(); // Create a new connection on this listener. listener_.newConnection(std::move(socket_), std::move(stream_info_)); @@ -409,7 +410,6 @@ ActiveTcpConnection::ActiveTcpConnection(ActiveConnections& active_connections, listener.stats_.downstream_cx_active_.inc(); listener.per_worker_stats_.downstream_cx_total_.inc(); listener.per_worker_stats_.downstream_cx_active_.inc(); - stream_info_->setConnectionID(connection_->id()); // Active connections on the handler (not listener). The per listener connections have already // been incremented at this point either via the connection balancer or in the socket accept diff --git a/source/server/admin/admin.h b/source/server/admin/admin.h index 5fcaa5ddcf603..95db2bba4f8c7 100644 --- a/source/server/admin/admin.h +++ b/source/server/admin/admin.h @@ -146,6 +146,7 @@ class AdminImpl : public Admin, return &scoped_route_config_provider_; } const std::string& serverName() const override { return Http::DefaultServerString::get(); } + const absl::optional& schemeToSet() const override { return scheme_; } HttpConnectionManagerProto::ServerHeaderTransformation serverHeaderTransformation() const override { return HttpConnectionManagerProto::OVERWRITE; @@ -451,6 +452,7 @@ class AdminImpl : public Admin, const AdminInternalAddressConfig internal_address_config_; const LocalReply::LocalReplyPtr local_reply_; const std::vector detection_extensions_{}; + const absl::optional scheme_{}; }; } // namespace Server diff --git a/source/server/admin/config_dump_handler.cc b/source/server/admin/config_dump_handler.cc index ec48f7c97090f..ad23b2434fce5 100644 --- a/source/server/admin/config_dump_handler.cc +++ b/source/server/admin/config_dump_handler.cc @@ -271,17 +271,22 @@ absl::optional> ConfigDumpHandler::addAllConf Protobuf::FieldMask field_mask; ProtobufUtil::FieldMaskUtil::FromString(mask.value(), &field_mask); // We don't use trimMessage() above here since masks don't support - // indexing through repeated fields. + // indexing through repeated fields. We don't return error on failure + // because different callback return types will have different valid + // field masks. if (!checkFieldMaskAndTrimMessage(field_mask, *message)) { - return absl::optional>{std::make_pair( - Http::Code::BadRequest, absl::StrCat("FieldMask ", field_mask.DebugString(), - " could not be successfully used."))}; + continue; } } auto* config = dump.add_configs(); config->PackFrom(*message); } + if (dump.configs().empty() && mask.has_value()) { + return absl::optional>{std::make_pair( + Http::Code::BadRequest, + absl::StrCat("FieldMask ", *mask, " could not be successfully applied to any configs."))}; + } return absl::nullopt; } diff --git a/source/server/connection_handler_impl.cc b/source/server/connection_handler_impl.cc index e13ca96dbffff..14961a5c18747 100644 --- a/source/server/connection_handler_impl.cc +++ b/source/server/connection_handler_impl.cc @@ -7,6 +7,7 @@ #include "source/common/event/deferred_task.h" #include "source/common/network/utility.h" +#include "source/common/runtime/runtime_features.h" #include "source/server/active_tcp_listener.h" namespace Envoy { @@ -191,17 +192,33 @@ ConnectionHandlerImpl::getBalancedHandlerByAddress(const Network::Address::Insta // Otherwise, we need to look for the wild card match, i.e., 0.0.0.0:[address_port]. // We do not return stopped listeners. // TODO(wattli): consolidate with previous search for more efficiency. - listener_it = - std::find_if(listeners_.begin(), listeners_.end(), - [&address](const std::pair& p) { - return absl::holds_alternative>( - p.second.typed_listener_) && - p.second.listener_->listener() != nullptr && - p.first->type() == Network::Address::Type::Ip && - p.first->ip()->port() == address.ip()->port() && - p.first->ip()->isAnyAddress(); - }); + if (Runtime::runtimeFeatureEnabled( + "envoy.reloadable_features.listener_wildcard_match_ip_family")) { + listener_it = + std::find_if(listeners_.begin(), listeners_.end(), + [&address](const std::pair& p) { + return absl::holds_alternative>( + p.second.typed_listener_) && + p.second.listener_->listener() != nullptr && + p.first->type() == Network::Address::Type::Ip && + p.first->ip()->port() == address.ip()->port() && + p.first->ip()->isAnyAddress() && + p.first->ip()->version() == address.ip()->version(); + }); + } else { + listener_it = + std::find_if(listeners_.begin(), listeners_.end(), + [&address](const std::pair& p) { + return absl::holds_alternative>( + p.second.typed_listener_) && + p.second.listener_->listener() != nullptr && + p.first->type() == Network::Address::Type::Ip && + p.first->ip()->port() == address.ip()->port() && + p.first->ip()->isAnyAddress(); + }); + } return (listener_it != listeners_.end()) ? Network::BalancedConnectionHandlerOptRef( ActiveTcpListenerOptRef(absl::get>( diff --git a/source/server/listener_impl.h b/source/server/listener_impl.h index 1fa60494715f3..42382a785964b 100644 --- a/source/server/listener_impl.h +++ b/source/server/listener_impl.h @@ -54,14 +54,18 @@ class ListenSocketFactoryImpl : public Network::ListenSocketFactory, Network::SocketSharedPtr getListenSocket() override; /** - * @return the socket shared by worker threads; otherwise return null. + * @return the socket shared by worker threads; otherwise return nullopt. */ Network::SocketOptRef sharedSocket() const override { + // If listen socket doesn't bind to port, consider it not shared. + if (!bind_to_port_) { + return absl::nullopt; + } if (!reuse_port_) { ASSERT(socket_ != nullptr); return *socket_; } - // If reuse_port is true, always return null, even socket_ is created for reserving + // If reuse_port is true, always return nullopt, even socket_ is created for reserving // port number. return absl::nullopt; } diff --git a/source/server/listener_manager_impl.cc b/source/server/listener_manager_impl.cc index b5de079b49fcc..1ce0a0aba68df 100644 --- a/source/server/listener_manager_impl.cc +++ b/source/server/listener_manager_impl.cc @@ -954,13 +954,7 @@ Network::DrainableFilterChainSharedPtr ListenerFilterChainFactoryBuilder::buildF // We copy by value first then override if necessary. auto transport_socket = filter_chain.transport_socket(); if (!filter_chain.has_transport_socket()) { - if (filter_chain.has_hidden_envoy_deprecated_tls_context()) { - transport_socket.set_name("envoy.transport_sockets.tls"); - transport_socket.mutable_typed_config()->PackFrom( - filter_chain.hidden_envoy_deprecated_tls_context()); - } else { - transport_socket.set_name("envoy.transport_sockets.raw_buffer"); - } + transport_socket.set_name("envoy.transport_sockets.raw_buffer"); } auto& config_factory = Config::Utility::getAndCheckFactory< diff --git a/source/server/server.cc b/source/server/server.cc index 9dc4000dac77b..14bded52bcf77 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -364,7 +364,8 @@ void InstanceImpl::initialize(const Options& options, // Needs to happen as early as possible in the instantiation to preempt the objects that require // stats. stats_store_.setTagProducer(Config::Utility::createTagProducer(bootstrap_)); - stats_store_.setStatsMatcher(Config::Utility::createStatsMatcher(bootstrap_)); + stats_store_.setStatsMatcher( + Config::Utility::createStatsMatcher(bootstrap_, stats_store_.symbolTable())); stats_store_.setHistogramSettings(Config::Utility::createHistogramSettings(bootstrap_)); const std::string server_stats_prefix = "server."; diff --git a/test/common/config/BUILD b/test/common/config/BUILD index a7c5aaf0e4470..f8cd436f49465 100644 --- a/test/common/config/BUILD +++ b/test/common/config/BUILD @@ -77,6 +77,7 @@ envoy_cc_test( "//source/common/config:delta_subscription_state_lib", "//source/common/config:grpc_subscription_lib", "//source/common/config:new_grpc_mux_lib", + "//source/common/config/xds_mux:delta_subscription_state_lib", "//source/common/stats:isolated_store_lib", "//test/mocks:common_lib", "//test/mocks/config:config_mocks", @@ -91,6 +92,25 @@ envoy_cc_test( ], ) +envoy_cc_test( + name = "sotw_subscription_state_test", + srcs = ["sotw_subscription_state_test.cc"], + deps = [ + "//source/common/config:resource_name_lib", + "//source/common/config/xds_mux:sotw_subscription_state_lib", + "//source/common/stats:isolated_store_lib", + "//test/mocks:common_lib", + "//test/mocks/config:config_mocks", + "//test/mocks/event:event_mocks", + "//test/mocks/grpc:grpc_mocks", + "//test/mocks/local_info:local_info_mocks", + "//test/mocks/runtime:runtime_mocks", + "//test/test_common:logging_lib", + "//test/test_common:utility_lib", + "@envoy_api//envoy/config/endpoint/v3:pkg_cc_proto", + ], +) + envoy_cc_test( name = "filesystem_subscription_impl_test", srcs = ["filesystem_subscription_impl_test.cc"], diff --git a/test/common/config/delta_subscription_state_test.cc b/test/common/config/delta_subscription_state_test.cc index fea85b717740c..0aedc138039b1 100644 --- a/test/common/config/delta_subscription_state_test.cc +++ b/test/common/config/delta_subscription_state_test.cc @@ -5,6 +5,7 @@ #include "source/common/config/delta_subscription_state.h" #include "source/common/config/utility.h" +#include "source/common/config/xds_mux/delta_subscription_state.h" #include "source/common/stats/isolated_store_impl.h" #include "test/mocks/config/mocks.h" @@ -16,6 +17,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +using testing::IsSubstring; using testing::NiceMock; using testing::Throw; using testing::UnorderedElementsAre; @@ -26,22 +28,55 @@ namespace Config { namespace { const char TypeUrl[] = "type.googleapis.com/envoy.api.v2.Cluster"; +enum class LegacyOrUnified { Legacy, Unified }; -class DeltaSubscriptionStateTestBase : public testing::Test { +class DeltaSubscriptionStateTestBase : public testing::TestWithParam { protected: DeltaSubscriptionStateTestBase( - const std::string& type_url, const bool wildcard, + const std::string& type_url, const bool wildcard, LegacyOrUnified legacy_or_unified, const absl::flat_hash_set initial_resources = {"name1", "name2", "name3"}) - : timer_(new Event::MockTimer(&dispatcher_)), - state_(type_url, callbacks_, local_info_, dispatcher_, wildcard) { - state_.updateSubscriptionInterest(initial_resources, {}); - envoy::service::discovery::v3::DeltaDiscoveryRequest cur_request = - state_.getNextRequestAckless(); - EXPECT_THAT(cur_request.resource_names_subscribe(), + : should_use_unified_(legacy_or_unified == LegacyOrUnified::Unified) { + ttl_timer_ = new Event::MockTimer(&dispatcher_); + + if (should_use_unified_) { + state_ = std::make_unique( + type_url, callbacks_, dispatcher_, wildcard); + } else { + state_ = std::make_unique( + type_url, callbacks_, local_info_, dispatcher_, wildcard); + } + updateSubscriptionInterest(initial_resources, {}); + auto cur_request = getNextRequestAckless(); + EXPECT_THAT(cur_request->resource_names_subscribe(), // UnorderedElementsAre("name1", "name2", "name3")); UnorderedElementsAreArray(initial_resources.cbegin(), initial_resources.cend())); } + void updateSubscriptionInterest(const absl::flat_hash_set& cur_added, + const absl::flat_hash_set& cur_removed) { + if (should_use_unified_) { + absl::get<1>(state_)->updateSubscriptionInterest(cur_added, cur_removed); + } else { + absl::get<0>(state_)->updateSubscriptionInterest(cur_added, cur_removed); + } + } + + std::unique_ptr getNextRequestAckless() { + if (should_use_unified_) { + return absl::get<1>(state_)->getNextRequestAckless(); + } + return std::make_unique( + absl::get<0>(state_)->getNextRequestAckless()); + } + + UpdateAck + handleResponse(const envoy::service::discovery::v3::DeltaDiscoveryResponse& response_proto) { + if (should_use_unified_) { + return absl::get<1>(state_)->handleResponse(response_proto); + } + return absl::get<0>(state_)->handleResponse(response_proto); + } + UpdateAck deliverDiscoveryResponse( const Protobuf::RepeatedPtrField& added_resources, const Protobuf::RepeatedPtrField& removed_resources, @@ -61,7 +96,7 @@ class DeltaSubscriptionStateTestBase : public testing::Test { EXPECT_EQ(added.size(), *updated_resources); } })); - return state_.handleResponse(message); + return handleResponse(message); } UpdateAck deliverBadDiscoveryResponse( @@ -74,15 +109,33 @@ class DeltaSubscriptionStateTestBase : public testing::Test { message.set_system_version_info(version_info); message.set_nonce(nonce); EXPECT_CALL(callbacks_, onConfigUpdate(_, _, _)).WillOnce(Throw(EnvoyException(error_message))); - return state_.handleResponse(message); + return handleResponse(message); + } + + void markStreamFresh() { + if (should_use_unified_) { + absl::get<1>(state_)->markStreamFresh(); + } else { + absl::get<0>(state_)->markStreamFresh(); + } + } + + bool subscriptionUpdatePending() { + if (should_use_unified_) { + return absl::get<1>(state_)->subscriptionUpdatePending(); + } + return absl::get<0>(state_)->subscriptionUpdatePending(); } NiceMock callbacks_; NiceMock local_info_; NiceMock dispatcher_; - Event::MockTimer* timer_; + Event::MockTimer* ttl_timer_; // We start out interested in three resources: name1, name2, and name3. - DeltaSubscriptionState state_; + absl::variant, + std::unique_ptr> + state_; + bool should_use_unified_; }; Protobuf::RepeatedPtrField @@ -98,30 +151,35 @@ populateRepeatedResource(std::vector> items) class DeltaSubscriptionStateTest : public DeltaSubscriptionStateTestBase { public: - DeltaSubscriptionStateTest() : DeltaSubscriptionStateTestBase(TypeUrl, false) {} + DeltaSubscriptionStateTest() : DeltaSubscriptionStateTestBase(TypeUrl, false, GetParam()) {} }; +INSTANTIATE_TEST_SUITE_P(DeltaSubscriptionStateTest, DeltaSubscriptionStateTest, + testing::ValuesIn({LegacyOrUnified::Legacy, LegacyOrUnified::Unified})); + // Delta subscription state of a wildcard subscription request. class WildcardDeltaSubscriptionStateTest : public DeltaSubscriptionStateTestBase { public: - WildcardDeltaSubscriptionStateTest() : DeltaSubscriptionStateTestBase(TypeUrl, true, {}) {} + WildcardDeltaSubscriptionStateTest() + : DeltaSubscriptionStateTestBase(TypeUrl, true, GetParam(), {}) {} }; +INSTANTIATE_TEST_SUITE_P(WildcardDeltaSubscriptionStateTest, WildcardDeltaSubscriptionStateTest, + testing::ValuesIn({LegacyOrUnified::Legacy, LegacyOrUnified::Unified})); + // Basic gaining/losing interest in resources should lead to subscription updates. -TEST_F(DeltaSubscriptionStateTest, SubscribeAndUnsubscribe) { +TEST_P(DeltaSubscriptionStateTest, SubscribeAndUnsubscribe) { { - state_.updateSubscriptionInterest({"name4"}, {"name1"}); - envoy::service::discovery::v3::DeltaDiscoveryRequest cur_request = - state_.getNextRequestAckless(); - EXPECT_THAT(cur_request.resource_names_subscribe(), UnorderedElementsAre("name4")); - EXPECT_THAT(cur_request.resource_names_unsubscribe(), UnorderedElementsAre("name1")); + updateSubscriptionInterest({"name4"}, {"name1"}); + auto cur_request = getNextRequestAckless(); + EXPECT_THAT(cur_request->resource_names_subscribe(), UnorderedElementsAre("name4")); + EXPECT_THAT(cur_request->resource_names_unsubscribe(), UnorderedElementsAre("name1")); } { - state_.updateSubscriptionInterest({"name1"}, {"name3", "name4"}); - envoy::service::discovery::v3::DeltaDiscoveryRequest cur_request = - state_.getNextRequestAckless(); - EXPECT_THAT(cur_request.resource_names_subscribe(), UnorderedElementsAre("name1")); - EXPECT_THAT(cur_request.resource_names_unsubscribe(), UnorderedElementsAre("name3", "name4")); + updateSubscriptionInterest({"name1"}, {"name3", "name4"}); + auto cur_request = getNextRequestAckless(); + EXPECT_THAT(cur_request->resource_names_subscribe(), UnorderedElementsAre("name1")); + EXPECT_THAT(cur_request->resource_names_unsubscribe(), UnorderedElementsAre("name3", "name4")); } } @@ -134,12 +192,12 @@ TEST_F(DeltaSubscriptionStateTest, SubscribeAndUnsubscribe) { // again. So, we *should* send a request "re-"subscribing. This means that the server needs to // interpret the resource_names_subscribe field as "send these resources even if you think Envoy // already has them". -TEST_F(DeltaSubscriptionStateTest, RemoveThenAdd) { - state_.updateSubscriptionInterest({}, {"name3"}); - state_.updateSubscriptionInterest({"name3"}, {}); - envoy::service::discovery::v3::DeltaDiscoveryRequest cur_request = state_.getNextRequestAckless(); - EXPECT_THAT(cur_request.resource_names_subscribe(), UnorderedElementsAre("name3")); - EXPECT_TRUE(cur_request.resource_names_unsubscribe().empty()); +TEST_P(DeltaSubscriptionStateTest, RemoveThenAdd) { + updateSubscriptionInterest({}, {"name3"}); + updateSubscriptionInterest({"name3"}, {}); + auto cur_request = getNextRequestAckless(); + EXPECT_THAT(cur_request->resource_names_subscribe(), UnorderedElementsAre("name3")); + EXPECT_TRUE(cur_request->resource_names_unsubscribe().empty()); } // Due to how our implementation provides the required behavior tested in RemoveThenAdd, the @@ -150,62 +208,62 @@ TEST_F(DeltaSubscriptionStateTest, RemoveThenAdd) { // This test is just here to illustrate that this behavior exists, not to enforce that it // should be like this. What *is* important: the server must happily and cleanly ignore // "unsubscribe from [resource name I have never before referred to]" requests. -TEST_F(DeltaSubscriptionStateTest, AddThenRemove) { - state_.updateSubscriptionInterest({"name4"}, {}); - state_.updateSubscriptionInterest({}, {"name4"}); - envoy::service::discovery::v3::DeltaDiscoveryRequest cur_request = state_.getNextRequestAckless(); - EXPECT_TRUE(cur_request.resource_names_subscribe().empty()); - EXPECT_THAT(cur_request.resource_names_unsubscribe(), UnorderedElementsAre("name4")); +TEST_P(DeltaSubscriptionStateTest, AddThenRemove) { + updateSubscriptionInterest({"name4"}, {}); + updateSubscriptionInterest({}, {"name4"}); + auto cur_request = getNextRequestAckless(); + EXPECT_TRUE(cur_request->resource_names_subscribe().empty()); + EXPECT_THAT(cur_request->resource_names_unsubscribe(), UnorderedElementsAre("name4")); } // add/remove/add == add. -TEST_F(DeltaSubscriptionStateTest, AddRemoveAdd) { - state_.updateSubscriptionInterest({"name4"}, {}); - state_.updateSubscriptionInterest({}, {"name4"}); - state_.updateSubscriptionInterest({"name4"}, {}); - envoy::service::discovery::v3::DeltaDiscoveryRequest cur_request = state_.getNextRequestAckless(); - EXPECT_THAT(cur_request.resource_names_subscribe(), UnorderedElementsAre("name4")); - EXPECT_TRUE(cur_request.resource_names_unsubscribe().empty()); +TEST_P(DeltaSubscriptionStateTest, AddRemoveAdd) { + updateSubscriptionInterest({"name4"}, {}); + updateSubscriptionInterest({}, {"name4"}); + updateSubscriptionInterest({"name4"}, {}); + auto cur_request = getNextRequestAckless(); + EXPECT_THAT(cur_request->resource_names_subscribe(), UnorderedElementsAre("name4")); + EXPECT_TRUE(cur_request->resource_names_unsubscribe().empty()); } // remove/add/remove == remove. -TEST_F(DeltaSubscriptionStateTest, RemoveAddRemove) { - state_.updateSubscriptionInterest({}, {"name3"}); - state_.updateSubscriptionInterest({"name3"}, {}); - state_.updateSubscriptionInterest({}, {"name3"}); - envoy::service::discovery::v3::DeltaDiscoveryRequest cur_request = state_.getNextRequestAckless(); - EXPECT_TRUE(cur_request.resource_names_subscribe().empty()); - EXPECT_THAT(cur_request.resource_names_unsubscribe(), UnorderedElementsAre("name3")); +TEST_P(DeltaSubscriptionStateTest, RemoveAddRemove) { + updateSubscriptionInterest({}, {"name3"}); + updateSubscriptionInterest({"name3"}, {}); + updateSubscriptionInterest({}, {"name3"}); + auto cur_request = getNextRequestAckless(); + EXPECT_TRUE(cur_request->resource_names_subscribe().empty()); + EXPECT_THAT(cur_request->resource_names_unsubscribe(), UnorderedElementsAre("name3")); } // Starts with 1,2,3. 4 is added/removed/added. In those same updates, 1,2,3 are // removed/added/removed. End result should be 4 added and 1,2,3 removed. -TEST_F(DeltaSubscriptionStateTest, BothAddAndRemove) { - state_.updateSubscriptionInterest({"name4"}, {"name1", "name2", "name3"}); - state_.updateSubscriptionInterest({"name1", "name2", "name3"}, {"name4"}); - state_.updateSubscriptionInterest({"name4"}, {"name1", "name2", "name3"}); - envoy::service::discovery::v3::DeltaDiscoveryRequest cur_request = state_.getNextRequestAckless(); - EXPECT_THAT(cur_request.resource_names_subscribe(), UnorderedElementsAre("name4")); - EXPECT_THAT(cur_request.resource_names_unsubscribe(), +TEST_P(DeltaSubscriptionStateTest, BothAddAndRemove) { + updateSubscriptionInterest({"name4"}, {"name1", "name2", "name3"}); + updateSubscriptionInterest({"name1", "name2", "name3"}, {"name4"}); + updateSubscriptionInterest({"name4"}, {"name1", "name2", "name3"}); + auto cur_request = getNextRequestAckless(); + EXPECT_THAT(cur_request->resource_names_subscribe(), UnorderedElementsAre("name4")); + EXPECT_THAT(cur_request->resource_names_unsubscribe(), UnorderedElementsAre("name1", "name2", "name3")); } -TEST_F(DeltaSubscriptionStateTest, CumulativeUpdates) { - state_.updateSubscriptionInterest({"name4"}, {}); - state_.updateSubscriptionInterest({"name5"}, {}); - envoy::service::discovery::v3::DeltaDiscoveryRequest cur_request = state_.getNextRequestAckless(); - EXPECT_THAT(cur_request.resource_names_subscribe(), UnorderedElementsAre("name4", "name5")); - EXPECT_TRUE(cur_request.resource_names_unsubscribe().empty()); +TEST_P(DeltaSubscriptionStateTest, CumulativeUpdates) { + updateSubscriptionInterest({"name4"}, {}); + updateSubscriptionInterest({"name5"}, {}); + auto cur_request = getNextRequestAckless(); + EXPECT_THAT(cur_request->resource_names_subscribe(), UnorderedElementsAre("name4", "name5")); + EXPECT_TRUE(cur_request->resource_names_unsubscribe().empty()); } // Verifies that a sequence of good and bad responses from the server all get the appropriate // ACKs/NACKs from Envoy. -TEST_F(DeltaSubscriptionStateTest, AckGenerated) { +TEST_P(DeltaSubscriptionStateTest, AckGenerated) { // The xDS server's first response includes items for name1 and 2, but not 3. { Protobuf::RepeatedPtrField added_resources = populateRepeatedResource({{"name1", "version1A"}, {"name2", "version2A"}}); - EXPECT_CALL(*timer_, disableTimer()); + EXPECT_CALL(*ttl_timer_, disableTimer()); UpdateAck ack = deliverDiscoveryResponse(added_resources, {}, "debug1", "nonce1"); EXPECT_EQ("nonce1", ack.nonce_); EXPECT_EQ(Grpc::Status::WellKnownGrpcStatus::Ok, ack.error_detail_.code()); @@ -215,7 +273,7 @@ TEST_F(DeltaSubscriptionStateTest, AckGenerated) { Protobuf::RepeatedPtrField added_resources = populateRepeatedResource( {{"name1", "version1B"}, {"name2", "version2B"}, {"name3", "version3A"}}); - EXPECT_CALL(*timer_, disableTimer()); + EXPECT_CALL(*ttl_timer_, disableTimer()); UpdateAck ack = deliverDiscoveryResponse(added_resources, {}, "debug2", "nonce2"); EXPECT_EQ("nonce2", ack.nonce_); EXPECT_EQ(Grpc::Status::WellKnownGrpcStatus::Ok, ack.error_detail_.code()); @@ -225,7 +283,7 @@ TEST_F(DeltaSubscriptionStateTest, AckGenerated) { Protobuf::RepeatedPtrField added_resources = populateRepeatedResource( {{"name1", "version1C"}, {"name2", "version2C"}, {"name3", "version3B"}}); - EXPECT_CALL(*timer_, disableTimer()); + EXPECT_CALL(*ttl_timer_, disableTimer()); UpdateAck ack = deliverBadDiscoveryResponse(added_resources, {}, "debug3", "nonce3", "oh no"); EXPECT_EQ("nonce3", ack.nonce_); EXPECT_NE(Grpc::Status::WellKnownGrpcStatus::Ok, ack.error_detail_.code()); @@ -235,7 +293,7 @@ TEST_F(DeltaSubscriptionStateTest, AckGenerated) { Protobuf::RepeatedPtrField added_resources = populateRepeatedResource( {{"name1", "version1D"}, {"name2", "version2D"}, {"name3", "version3C"}}); - EXPECT_CALL(*timer_, disableTimer()); + EXPECT_CALL(*ttl_timer_, disableTimer()); UpdateAck ack = deliverDiscoveryResponse(added_resources, {}, "debug4", "nonce4"); EXPECT_EQ("nonce4", ack.nonce_); EXPECT_EQ(Grpc::Status::WellKnownGrpcStatus::Ok, ack.error_detail_.code()); @@ -246,7 +304,7 @@ TEST_F(DeltaSubscriptionStateTest, AckGenerated) { Protobuf::RepeatedPtrField added_resources = populateRepeatedResource( {{"name1", "version1D"}, {"name2", "version2D"}, {"name3", "version3D"}}); - EXPECT_CALL(*timer_, disableTimer()); + EXPECT_CALL(*ttl_timer_, disableTimer()); UpdateAck ack = deliverBadDiscoveryResponse(added_resources, {}, "debug5", "nonce5", very_large_error_message); EXPECT_EQ("nonce5", ack.nonce_); @@ -261,20 +319,19 @@ TEST_F(DeltaSubscriptionStateTest, AckGenerated) { // 1) resources we have a version of are present in the map, // 2) resources we are interested in but don't have are not present, and // 3) resources we have lost interest in are not present. -TEST_F(DeltaSubscriptionStateTest, ResourceGoneLeadsToBlankInitialVersion) { +TEST_P(DeltaSubscriptionStateTest, ResourceGoneLeadsToBlankInitialVersion) { { // The xDS server's first update includes items for name1 and 2, but not 3. Protobuf::RepeatedPtrField add1_2 = populateRepeatedResource({{"name1", "version1A"}, {"name2", "version2A"}}); - EXPECT_CALL(*timer_, disableTimer()); + EXPECT_CALL(*ttl_timer_, disableTimer()); deliverDiscoveryResponse(add1_2, {}, "debugversion1"); - state_.markStreamFresh(); // simulate a stream reconnection - envoy::service::discovery::v3::DeltaDiscoveryRequest cur_request = - state_.getNextRequestAckless(); - EXPECT_EQ("version1A", cur_request.initial_resource_versions().at("name1")); - EXPECT_EQ("version2A", cur_request.initial_resource_versions().at("name2")); - EXPECT_EQ(cur_request.initial_resource_versions().end(), - cur_request.initial_resource_versions().find("name3")); + markStreamFresh(); // simulate a stream reconnection + auto cur_request = getNextRequestAckless(); + EXPECT_EQ("version1A", cur_request->initial_resource_versions().at("name1")); + EXPECT_EQ("version2A", cur_request->initial_resource_versions().at("name2")); + EXPECT_EQ(cur_request->initial_resource_versions().end(), + cur_request->initial_resource_versions().find("name3")); } { @@ -283,15 +340,14 @@ TEST_F(DeltaSubscriptionStateTest, ResourceGoneLeadsToBlankInitialVersion) { populateRepeatedResource({{"name1", "version1B"}, {"name3", "version3A"}}); Protobuf::RepeatedPtrField remove2; *remove2.Add() = "name2"; - EXPECT_CALL(*timer_, disableTimer()).Times(2); + EXPECT_CALL(*ttl_timer_, disableTimer()).Times(2); deliverDiscoveryResponse(add1_3, remove2, "debugversion2"); - state_.markStreamFresh(); // simulate a stream reconnection - envoy::service::discovery::v3::DeltaDiscoveryRequest cur_request = - state_.getNextRequestAckless(); - EXPECT_EQ("version1B", cur_request.initial_resource_versions().at("name1")); - EXPECT_EQ(cur_request.initial_resource_versions().end(), - cur_request.initial_resource_versions().find("name2")); - EXPECT_EQ("version3A", cur_request.initial_resource_versions().at("name3")); + markStreamFresh(); // simulate a stream reconnection + auto cur_request = getNextRequestAckless(); + EXPECT_EQ("version1B", cur_request->initial_resource_versions().at("name1")); + EXPECT_EQ(cur_request->initial_resource_versions().end(), + cur_request->initial_resource_versions().find("name2")); + EXPECT_EQ("version3A", cur_request->initial_resource_versions().at("name3")); } { @@ -300,20 +356,18 @@ TEST_F(DeltaSubscriptionStateTest, ResourceGoneLeadsToBlankInitialVersion) { *remove1_3.Add() = "name1"; *remove1_3.Add() = "name3"; deliverDiscoveryResponse({}, remove1_3, "debugversion3"); - state_.markStreamFresh(); // simulate a stream reconnection - envoy::service::discovery::v3::DeltaDiscoveryRequest cur_request = - state_.getNextRequestAckless(); - EXPECT_TRUE(cur_request.initial_resource_versions().empty()); + markStreamFresh(); // simulate a stream reconnection + auto cur_request = getNextRequestAckless(); + EXPECT_TRUE(cur_request->initial_resource_versions().empty()); } { // ...but our own map should remember our interest. In particular, losing interest in a // resource should cause its name to appear in the next request's resource_names_unsubscribe. - state_.updateSubscriptionInterest({"name4"}, {"name1", "name2"}); - envoy::service::discovery::v3::DeltaDiscoveryRequest cur_request = - state_.getNextRequestAckless(); - EXPECT_THAT(cur_request.resource_names_subscribe(), UnorderedElementsAre("name4")); - EXPECT_THAT(cur_request.resource_names_unsubscribe(), UnorderedElementsAre("name1", "name2")); + updateSubscriptionInterest({"name4"}, {"name1", "name2"}); + auto cur_request = getNextRequestAckless(); + EXPECT_THAT(cur_request->resource_names_subscribe(), UnorderedElementsAre("name4")); + EXPECT_THAT(cur_request->resource_names_unsubscribe(), UnorderedElementsAre("name1", "name2")); } } @@ -326,24 +380,24 @@ TEST_F(DeltaSubscriptionStateTest, ResourceGoneLeadsToBlankInitialVersion) { // in between the last request of the last stream and the first request of the new stream, Envoy // lost interest in a resource. The unsubscription implicitly takes effect by simply saying // nothing about the resource in the newly reconnected stream. -TEST_F(DeltaSubscriptionStateTest, SubscribeAndUnsubscribeAfterReconnect) { +TEST_P(DeltaSubscriptionStateTest, SubscribeAndUnsubscribeAfterReconnect) { Protobuf::RepeatedPtrField add1_2 = populateRepeatedResource({{"name1", "version1A"}, {"name2", "version2A"}}); - EXPECT_CALL(*timer_, disableTimer()); + EXPECT_CALL(*ttl_timer_, disableTimer()); deliverDiscoveryResponse(add1_2, {}, "debugversion1"); - state_.updateSubscriptionInterest({"name4"}, {"name1"}); - state_.markStreamFresh(); // simulate a stream reconnection - envoy::service::discovery::v3::DeltaDiscoveryRequest cur_request = state_.getNextRequestAckless(); + updateSubscriptionInterest({"name4"}, {"name1"}); + markStreamFresh(); // simulate a stream reconnection + auto cur_request = getNextRequestAckless(); // Regarding the resource_names_subscribe field: // name1: do not include: we lost interest. // name2: yes do include: we are interested, its non-wildcard, and we have a version of it. // name3: yes do include: even though we don't have a version of it, we are interested. // name4: yes do include: we are newly interested. (If this wasn't a stream reconnect, only // name4 would belong in this subscribe field). - EXPECT_THAT(cur_request.resource_names_subscribe(), + EXPECT_THAT(cur_request->resource_names_subscribe(), UnorderedElementsAre("name2", "name3", "name4")); - EXPECT_TRUE(cur_request.resource_names_unsubscribe().empty()); + EXPECT_TRUE(cur_request->resource_names_unsubscribe().empty()); } // For wildcard subscription, upon a reconnection, the server is supposed to assume a @@ -353,77 +407,75 @@ TEST_F(DeltaSubscriptionStateTest, SubscribeAndUnsubscribeAfterReconnect) { // last stream and the first request of the new stream, Envoy gained or lost interest in a // resource. The subscription & unsubscription implicitly takes effect by simply requesting a // wildcard subscription in the newly reconnected stream. -TEST_F(WildcardDeltaSubscriptionStateTest, SubscribeAndUnsubscribeAfterReconnect) { +TEST_P(WildcardDeltaSubscriptionStateTest, SubscribeAndUnsubscribeAfterReconnect) { Protobuf::RepeatedPtrField add1_2 = populateRepeatedResource({{"name1", "version1A"}, {"name2", "version2A"}}); - EXPECT_CALL(*timer_, disableTimer()); + EXPECT_CALL(*ttl_timer_, disableTimer()); deliverDiscoveryResponse(add1_2, {}, "debugversion1"); - state_.updateSubscriptionInterest({"name3"}, {"name1"}); - state_.markStreamFresh(); // simulate a stream reconnection - envoy::service::discovery::v3::DeltaDiscoveryRequest cur_request = state_.getNextRequestAckless(); + updateSubscriptionInterest({"name3"}, {"name1"}); + markStreamFresh(); // simulate a stream reconnection + auto cur_request = getNextRequestAckless(); // Regarding the resource_names_subscribe field: // name1: do not include: we lost interest. // name2: do not include: we are interested, but for wildcard it shouldn't be provided. // name4: do not include: although we are newly interested, an initial wildcard request // must be with no resources. - EXPECT_TRUE(cur_request.resource_names_subscribe().empty()); - EXPECT_TRUE(cur_request.resource_names_unsubscribe().empty()); + EXPECT_TRUE(cur_request->resource_names_subscribe().empty()); + EXPECT_TRUE(cur_request->resource_names_unsubscribe().empty()); } // initial_resource_versions should not be present on messages after the first in a stream. -TEST_F(DeltaSubscriptionStateTest, InitialVersionMapFirstMessageOnly) { +TEST_P(DeltaSubscriptionStateTest, InitialVersionMapFirstMessageOnly) { // First, verify that the first message of a new stream sends initial versions. { // The xDS server's first update gives us all three resources. Protobuf::RepeatedPtrField add_all = populateRepeatedResource( {{"name1", "version1A"}, {"name2", "version2A"}, {"name3", "version3A"}}); - EXPECT_CALL(*timer_, disableTimer()); + EXPECT_CALL(*ttl_timer_, disableTimer()); deliverDiscoveryResponse(add_all, {}, "debugversion1"); - state_.markStreamFresh(); // simulate a stream reconnection - envoy::service::discovery::v3::DeltaDiscoveryRequest cur_request = - state_.getNextRequestAckless(); - EXPECT_EQ("version1A", cur_request.initial_resource_versions().at("name1")); - EXPECT_EQ("version2A", cur_request.initial_resource_versions().at("name2")); - EXPECT_EQ("version3A", cur_request.initial_resource_versions().at("name3")); + markStreamFresh(); // simulate a stream reconnection + auto cur_request = getNextRequestAckless(); + EXPECT_EQ("version1A", cur_request->initial_resource_versions().at("name1")); + EXPECT_EQ("version2A", cur_request->initial_resource_versions().at("name2")); + EXPECT_EQ("version3A", cur_request->initial_resource_versions().at("name3")); } // Then, after updating the resources but not reconnecting the stream, verify that initial // versions are not sent. { - state_.updateSubscriptionInterest({"name4"}, {}); + updateSubscriptionInterest({"name4"}, {}); // The xDS server updates our resources, and gives us our newly requested one too. Protobuf::RepeatedPtrField add_all = populateRepeatedResource({{"name1", "version1B"}, {"name2", "version2B"}, {"name3", "version3B"}, {"name4", "version4A"}}); - EXPECT_CALL(*timer_, disableTimer()); + EXPECT_CALL(*ttl_timer_, disableTimer()); deliverDiscoveryResponse(add_all, {}, "debugversion2"); - envoy::service::discovery::v3::DeltaDiscoveryRequest cur_request = - state_.getNextRequestAckless(); - EXPECT_TRUE(cur_request.initial_resource_versions().empty()); + auto cur_request = getNextRequestAckless(); + EXPECT_TRUE(cur_request->initial_resource_versions().empty()); } } -TEST_F(DeltaSubscriptionStateTest, CheckUpdatePending) { +TEST_P(DeltaSubscriptionStateTest, CheckUpdatePending) { // Note that the test fixture ctor causes the first request to be "sent", so we start in the // middle of a stream, with our initially interested resources having been requested already. - EXPECT_FALSE(state_.subscriptionUpdatePending()); - state_.updateSubscriptionInterest({}, {}); // no change - EXPECT_FALSE(state_.subscriptionUpdatePending()); - state_.markStreamFresh(); - EXPECT_TRUE(state_.subscriptionUpdatePending()); // no change, BUT fresh stream - state_.updateSubscriptionInterest({}, {"name3"}); // one removed - EXPECT_TRUE(state_.subscriptionUpdatePending()); - state_.updateSubscriptionInterest({"name3"}, {}); // one added - EXPECT_TRUE(state_.subscriptionUpdatePending()); + EXPECT_FALSE(subscriptionUpdatePending()); + updateSubscriptionInterest({}, {}); // no change + EXPECT_FALSE(subscriptionUpdatePending()); + markStreamFresh(); + EXPECT_TRUE(subscriptionUpdatePending()); // no change, BUT fresh stream + updateSubscriptionInterest({}, {"name3"}); // one removed + EXPECT_TRUE(subscriptionUpdatePending()); + updateSubscriptionInterest({"name3"}, {}); // one added + EXPECT_TRUE(subscriptionUpdatePending()); } // The next three tests test that duplicate resource names (whether additions or removals) cause // DeltaSubscriptionState to reject the update without even trying to hand it to the consuming // API's onConfigUpdate(). -TEST_F(DeltaSubscriptionStateTest, DuplicatedAdd) { +TEST_P(DeltaSubscriptionStateTest, DuplicatedAdd) { Protobuf::RepeatedPtrField additions = populateRepeatedResource({{"name1", "version1A"}, {"name1", "sdfsdfsdfds"}}); UpdateAck ack = deliverDiscoveryResponse(additions, {}, "debugversion1", absl::nullopt, false); @@ -431,7 +483,7 @@ TEST_F(DeltaSubscriptionStateTest, DuplicatedAdd) { ack.error_detail_.message()); } -TEST_F(DeltaSubscriptionStateTest, DuplicatedRemove) { +TEST_P(DeltaSubscriptionStateTest, DuplicatedRemove) { Protobuf::RepeatedPtrField removals; *removals.Add() = "name1"; *removals.Add() = "name1"; @@ -440,7 +492,7 @@ TEST_F(DeltaSubscriptionStateTest, DuplicatedRemove) { ack.error_detail_.message()); } -TEST_F(DeltaSubscriptionStateTest, AddedAndRemoved) { +TEST_P(DeltaSubscriptionStateTest, AddedAndRemoved) { Protobuf::RepeatedPtrField additions = populateRepeatedResource({{"name1", "version1A"}}); Protobuf::RepeatedPtrField removals; @@ -451,7 +503,7 @@ TEST_F(DeltaSubscriptionStateTest, AddedAndRemoved) { ack.error_detail_.message()); } -TEST_F(DeltaSubscriptionStateTest, ResourceTTL) { +TEST_P(DeltaSubscriptionStateTest, ResourceTTL) { Event::SimulatedTimeSystem time_system; time_system.setSystemTime(std::chrono::milliseconds(0)); @@ -476,53 +528,82 @@ TEST_F(DeltaSubscriptionStateTest, ResourceTTL) { }; { - EXPECT_CALL(*timer_, enabled()); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1000), _)); + EXPECT_CALL(*ttl_timer_, enabled()); + EXPECT_CALL(*ttl_timer_, enableTimer(std::chrono::milliseconds(1000), _)); deliverDiscoveryResponse(create_resource_with_ttl(std::chrono::seconds(1), true), {}, "debug1", "nonce1"); } { // Increase the TTL. - EXPECT_CALL(*timer_, enabled()); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(2000), _)); + EXPECT_CALL(*ttl_timer_, enabled()); + EXPECT_CALL(*ttl_timer_, enableTimer(std::chrono::milliseconds(2000), _)); deliverDiscoveryResponse(create_resource_with_ttl(std::chrono::seconds(2), true), {}, "debug1", "nonce1", true, 1); } { // Refresh the TTL with a heartbeat. The resource should not be passed to the update callbacks. - EXPECT_CALL(*timer_, enabled()); + EXPECT_CALL(*ttl_timer_, enabled()); deliverDiscoveryResponse(create_resource_with_ttl(std::chrono::seconds(2), false), {}, "debug1", "nonce1", true, 0); } // Remove the TTL. - EXPECT_CALL(*timer_, disableTimer()); + EXPECT_CALL(*ttl_timer_, disableTimer()); deliverDiscoveryResponse(create_resource_with_ttl(absl::nullopt, true), {}, "debug1", "nonce1", true, 1); // Add back the TTL. - EXPECT_CALL(*timer_, enabled()); - EXPECT_CALL(*timer_, enableTimer(_, _)); + EXPECT_CALL(*ttl_timer_, enabled()); + EXPECT_CALL(*ttl_timer_, enableTimer(_, _)); deliverDiscoveryResponse(create_resource_with_ttl(std::chrono::seconds(2), true), {}, "debug1", "nonce1"); EXPECT_CALL(callbacks_, onConfigUpdate(_, _, _)); - EXPECT_CALL(*timer_, disableTimer()); + EXPECT_CALL(*ttl_timer_, disableTimer()); time_system.setSystemTime(std::chrono::seconds(2)); // Invoke the TTL. - timer_->invokeCallback(); + ttl_timer_->invokeCallback(); +} + +TEST_P(DeltaSubscriptionStateTest, TypeUrlMismatch) { + envoy::service::discovery::v3::DeltaDiscoveryResponse message; + + Protobuf::RepeatedPtrField additions; + auto* resource = additions.Add(); + resource->set_name("name1"); + resource->set_version("version1"); + resource->mutable_resource()->set_type_url("foo"); + + *message.mutable_resources() = additions; + *message.mutable_removed_resources() = {}; + message.set_system_version_info("version1"); + message.set_nonce("nonce1"); + message.set_type_url("bar"); + + EXPECT_CALL(callbacks_, + onConfigUpdateFailed(Envoy::Config::ConfigUpdateFailureReason::UpdateRejected, _)) + .WillOnce(Invoke([](Envoy::Config::ConfigUpdateFailureReason, const EnvoyException* e) { + EXPECT_TRUE(IsSubstring("", "", + "type URL foo embedded in an individual Any does not match the " + "message-wide type URL bar", + e->what())); + })); + handleResponse(message); } class VhdsDeltaSubscriptionStateTest : public DeltaSubscriptionStateTestBase { public: VhdsDeltaSubscriptionStateTest() - : DeltaSubscriptionStateTestBase("envoy.config.route.v3.VirtualHost", false) {} + : DeltaSubscriptionStateTestBase("envoy.config.route.v3.VirtualHost", false, GetParam()) {} }; -TEST_F(VhdsDeltaSubscriptionStateTest, ResourceTTL) { +INSTANTIATE_TEST_SUITE_P(VhdsDeltaSubscriptionStateTest, VhdsDeltaSubscriptionStateTest, + testing::ValuesIn({LegacyOrUnified::Legacy, LegacyOrUnified::Unified})); + +TEST_P(VhdsDeltaSubscriptionStateTest, ResourceTTL) { Event::SimulatedTimeSystem time_system; time_system.setSystemTime(std::chrono::milliseconds(0)); @@ -545,12 +626,12 @@ TEST_F(VhdsDeltaSubscriptionStateTest, ResourceTTL) { return added_resources; }; - EXPECT_CALL(*timer_, enabled()); - EXPECT_CALL(*timer_, enableTimer(std::chrono::milliseconds(1000), _)); + EXPECT_CALL(*ttl_timer_, enabled()); + EXPECT_CALL(*ttl_timer_, enableTimer(std::chrono::milliseconds(1000), _)); deliverDiscoveryResponse(create_resource_with_ttl(true), {}, "debug1", "nonce1", true, 1); // Heartbeat update should not be propagated to the subscription callback. - EXPECT_CALL(*timer_, enabled()); + EXPECT_CALL(*ttl_timer_, enabled()); deliverDiscoveryResponse(create_resource_with_ttl(false), {}, "debug1", "nonce1", true, 0); // When runtime flag is disabled, maintain old behavior where we do propagate @@ -558,7 +639,7 @@ TEST_F(VhdsDeltaSubscriptionStateTest, ResourceTTL) { Runtime::LoaderSingleton::getExisting()->mergeValues( {{"envoy.reloadable_features.vhds_heartbeats", "false"}}); - EXPECT_CALL(*timer_, enabled()); + EXPECT_CALL(*ttl_timer_, enabled()); deliverDiscoveryResponse(create_resource_with_ttl(false), {}, "debug1", "nonce1", true, 1); } diff --git a/test/common/config/sotw_subscription_state_test.cc b/test/common/config/sotw_subscription_state_test.cc new file mode 100644 index 0000000000000..b7d5b5bae581d --- /dev/null +++ b/test/common/config/sotw_subscription_state_test.cc @@ -0,0 +1,307 @@ +#include "envoy/config/endpoint/v3/endpoint.pb.h" +#include "envoy/config/endpoint/v3/endpoint.pb.validate.h" + +#include "source/common/config/resource_name.h" +#include "source/common/config/utility.h" +#include "source/common/config/xds_mux/sotw_subscription_state.h" +#include "source/common/stats/isolated_store_impl.h" + +#include "test/mocks/config/mocks.h" +#include "test/mocks/event/mocks.h" +#include "test/mocks/local_info/mocks.h" +#include "test/test_common/simulated_time_system.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using testing::IsSubstring; +using testing::NiceMock; +using testing::Throw; +using testing::UnorderedElementsAre; + +namespace Envoy { +namespace Config { +namespace { + +class SotwSubscriptionStateTest : public testing::Test { +protected: + SotwSubscriptionStateTest() : resource_decoder_("cluster_name") { + ttl_timer_ = new Event::MockTimer(&dispatcher_); + state_ = std::make_unique( + Config::getTypeUrl( + envoy::config::core::v3::ApiVersion::V3), + callbacks_, dispatcher_, resource_decoder_); + state_->updateSubscriptionInterest({"name1", "name2", "name3"}, {}); + auto cur_request = getNextDiscoveryRequestAckless(); + EXPECT_THAT(cur_request->resource_names(), UnorderedElementsAre("name1", "name2", "name3")); + } + + std::unique_ptr + getNextDiscoveryRequestAckless() { + return state_->getNextRequestAckless(); + } + + envoy::service::discovery::v3::Resource heartbeatResource(std::chrono::milliseconds ttl, + const std::string& name) { + envoy::service::discovery::v3::Resource resource; + resource.mutable_ttl()->CopyFrom(Protobuf::util::TimeUtil::MillisecondsToDuration(ttl.count())); + resource.set_name(name); + return resource; + } + + envoy::service::discovery::v3::Resource + resourceWithTtl(std::chrono::milliseconds ttl, + const envoy::config::endpoint::v3::ClusterLoadAssignment& cla) { + envoy::service::discovery::v3::Resource resource; + resource.mutable_resource()->PackFrom(cla); + resource.mutable_ttl()->CopyFrom(Protobuf::util::TimeUtil::MillisecondsToDuration(ttl.count())); + resource.set_name(cla.cluster_name()); + return resource; + } + + const envoy::config::endpoint::v3::ClusterLoadAssignment + resource(const std::string& cluster_name) { + envoy::config::endpoint::v3::ClusterLoadAssignment resource; + resource.set_cluster_name(cluster_name); + return resource; + } + + UpdateAck deliverDiscoveryResponse(const std::vector& resource_names, + const std::string& version_info, const std::string& nonce) { + envoy::service::discovery::v3::DiscoveryResponse response; + response.set_version_info(version_info); + response.set_nonce(nonce); + response.set_type_url(Config::getTypeUrl( + envoy::config::core::v3::ApiVersion::V3)); + for (const auto& resource_name : resource_names) { + response.add_resources()->PackFrom(resource(resource_name)); + } + EXPECT_CALL(callbacks_, onConfigUpdate(_, version_info)); + return state_->handleResponse(response); + } + + UpdateAck + deliverDiscoveryResponseWithTtlResource(const envoy::service::discovery::v3::Resource& resource, + const std::string& version_info, + const std::string& nonce) { + envoy::service::discovery::v3::DiscoveryResponse response; + response.set_version_info(version_info); + response.set_nonce(nonce); + response.set_type_url(Config::getTypeUrl( + envoy::config::core::v3::ApiVersion::V3)); + response.add_resources()->PackFrom(resource); + EXPECT_CALL(callbacks_, onConfigUpdate(_, version_info)); + return state_->handleResponse(response); + } + + UpdateAck deliverBadDiscoveryResponse(const std::string& version_info, const std::string& nonce) { + envoy::service::discovery::v3::DiscoveryResponse message; + message.set_version_info(version_info); + message.set_nonce(nonce); + EXPECT_CALL(callbacks_, onConfigUpdate(_, _)).WillOnce(Throw(EnvoyException("oh no"))); + return state_->handleResponse(message); + } + + NiceMock callbacks_; + TestUtility::TestOpaqueResourceDecoderImpl + resource_decoder_; + NiceMock dispatcher_; + Event::MockTimer* ttl_timer_; + // We start out interested in three resources: name1, name2, and name3. + std::unique_ptr state_; +}; + +// Basic gaining/losing interest in resources should lead to changes in subscriptions. +TEST_F(SotwSubscriptionStateTest, SubscribeAndUnsubscribe) { + { + state_->updateSubscriptionInterest({"name4"}, {"name1"}); + auto cur_request = getNextDiscoveryRequestAckless(); + EXPECT_THAT(cur_request->resource_names(), UnorderedElementsAre("name2", "name3", "name4")); + } + { + state_->updateSubscriptionInterest({"name1"}, {"name3", "name4"}); + auto cur_request = getNextDiscoveryRequestAckless(); + EXPECT_THAT(cur_request->resource_names(), UnorderedElementsAre("name1", "name2")); + } +} + +// Unlike delta, if SotW gets multiple interest updates before being able to send a request, they +// all collapse to a single update. However, even if the updates all cancel each other out, there +// still will be a request generated. All of the following tests explore different such cases. +TEST_F(SotwSubscriptionStateTest, RemoveThenAdd) { + state_->updateSubscriptionInterest({}, {"name3"}); + state_->updateSubscriptionInterest({"name3"}, {}); + auto cur_request = getNextDiscoveryRequestAckless(); + EXPECT_THAT(cur_request->resource_names(), UnorderedElementsAre("name1", "name2", "name3")); +} + +TEST_F(SotwSubscriptionStateTest, AddThenRemove) { + state_->updateSubscriptionInterest({"name4"}, {}); + state_->updateSubscriptionInterest({}, {"name4"}); + auto cur_request = getNextDiscoveryRequestAckless(); + EXPECT_THAT(cur_request->resource_names(), UnorderedElementsAre("name1", "name2", "name3")); +} + +TEST_F(SotwSubscriptionStateTest, AddRemoveAdd) { + state_->updateSubscriptionInterest({"name4"}, {}); + state_->updateSubscriptionInterest({}, {"name4"}); + state_->updateSubscriptionInterest({"name4"}, {}); + auto cur_request = getNextDiscoveryRequestAckless(); + EXPECT_THAT(cur_request->resource_names(), + UnorderedElementsAre("name1", "name2", "name3", "name4")); +} + +TEST_F(SotwSubscriptionStateTest, RemoveAddRemove) { + state_->updateSubscriptionInterest({}, {"name3"}); + state_->updateSubscriptionInterest({"name3"}, {}); + state_->updateSubscriptionInterest({}, {"name3"}); + auto cur_request = getNextDiscoveryRequestAckless(); + EXPECT_THAT(cur_request->resource_names(), UnorderedElementsAre("name1", "name2")); +} + +TEST_F(SotwSubscriptionStateTest, BothAddAndRemove) { + state_->updateSubscriptionInterest({"name4"}, {"name1", "name2", "name3"}); + state_->updateSubscriptionInterest({"name1", "name2", "name3"}, {"name4"}); + state_->updateSubscriptionInterest({"name4"}, {"name1", "name2", "name3"}); + auto cur_request = getNextDiscoveryRequestAckless(); + EXPECT_THAT(cur_request->resource_names(), UnorderedElementsAre("name4")); +} + +TEST_F(SotwSubscriptionStateTest, CumulativeUpdates) { + state_->updateSubscriptionInterest({"name4"}, {}); + state_->updateSubscriptionInterest({"name5"}, {}); + auto cur_request = getNextDiscoveryRequestAckless(); + EXPECT_THAT(cur_request->resource_names(), + UnorderedElementsAre("name1", "name2", "name3", "name4", "name5")); +} + +TEST_F(SotwSubscriptionStateTest, LastUpdateNonceAndVersionUsed) { + EXPECT_CALL(*ttl_timer_, disableTimer()); + deliverDiscoveryResponse({"name1", "name2"}, "version1", "nonce1"); + state_->updateSubscriptionInterest({"name3"}, {}); + auto cur_request = getNextDiscoveryRequestAckless(); + EXPECT_EQ("nonce1", cur_request->response_nonce()); + EXPECT_EQ("version1", cur_request->version_info()); +} + +// Verifies that a sequence of good and bad responses from the server all get the appropriate +// ACKs/NACKs from Envoy. +TEST_F(SotwSubscriptionStateTest, AckGenerated) { + // The xDS server's first response includes items for name1 and 2, but not 3. + { + EXPECT_CALL(*ttl_timer_, disableTimer()); + UpdateAck ack = deliverDiscoveryResponse({"name1", "name2"}, "version1", "nonce1"); + EXPECT_EQ("nonce1", ack.nonce_); + EXPECT_EQ(Grpc::Status::WellKnownGrpcStatus::Ok, ack.error_detail_.code()); + } + // The next response updates 1 and 2, and adds 3. + { + EXPECT_CALL(*ttl_timer_, disableTimer()); + UpdateAck ack = deliverDiscoveryResponse({"name1", "name2", "name3"}, "version2", "nonce2"); + EXPECT_EQ("nonce2", ack.nonce_); + EXPECT_EQ(Grpc::Status::WellKnownGrpcStatus::Ok, ack.error_detail_.code()); + } + // The next response tries but fails to update all 3, and so should produce a NACK. + { + EXPECT_CALL(*ttl_timer_, disableTimer()); + UpdateAck ack = deliverBadDiscoveryResponse("version3", "nonce3"); + EXPECT_EQ("nonce3", ack.nonce_); + EXPECT_NE(Grpc::Status::WellKnownGrpcStatus::Ok, ack.error_detail_.code()); + } + // The last response successfully updates all 3. + { + EXPECT_CALL(*ttl_timer_, disableTimer()); + UpdateAck ack = deliverDiscoveryResponse({"name1", "name2", "name3"}, "version4", "nonce4"); + EXPECT_EQ("nonce4", ack.nonce_); + EXPECT_EQ(Grpc::Status::WellKnownGrpcStatus::Ok, ack.error_detail_.code()); + } +} + +TEST_F(SotwSubscriptionStateTest, CheckUpdatePending) { + // Note that the test fixture ctor causes the first request to be "sent", so we start in the + // middle of a stream, with our initially interested resources having been requested already. + EXPECT_FALSE(state_->subscriptionUpdatePending()); + state_->updateSubscriptionInterest({}, {}); // no change + EXPECT_FALSE(state_->subscriptionUpdatePending()); + state_->markStreamFresh(); + EXPECT_TRUE(state_->subscriptionUpdatePending()); // no change, BUT fresh stream + state_->updateSubscriptionInterest({}, {"name3"}); // one removed + EXPECT_TRUE(state_->subscriptionUpdatePending()); + state_->updateSubscriptionInterest({"name3"}, {}); // one added + EXPECT_TRUE(state_->subscriptionUpdatePending()); +} + +TEST_F(SotwSubscriptionStateTest, HandleEstablishmentFailure) { + // Although establishment failure is not supposed to cause an onConfigUpdateFailed() on the + // ultimate actual subscription callbacks, the callbacks reference held is actually to + // the WatchMap, which then calls GrpcSubscriptionImpl(s). It is the GrpcSubscriptionImpl + // that will decline to pass on an onConfigUpdateFailed(ConnectionFailure). + EXPECT_CALL(callbacks_, onConfigUpdateFailed(_, _)); + state_->handleEstablishmentFailure(); +} + +TEST_F(SotwSubscriptionStateTest, ResourceTTL) { + Event::SimulatedTimeSystem time_system; + time_system.setSystemTime(std::chrono::milliseconds(0)); + { + EXPECT_CALL(*ttl_timer_, enabled()); + EXPECT_CALL(*ttl_timer_, enableTimer(std::chrono::milliseconds(1000), _)); + deliverDiscoveryResponseWithTtlResource( + resourceWithTtl(std::chrono::seconds(1), resource("name1")), "debug1", "nonce1"); + } + + { + // Increase the TTL. + EXPECT_CALL(*ttl_timer_, enabled()); + EXPECT_CALL(*ttl_timer_, enableTimer(std::chrono::milliseconds(2000), _)); + deliverDiscoveryResponseWithTtlResource( + resourceWithTtl(std::chrono::seconds(2), resource("name1")), "debug1", "nonce1"); + } + + { + // Refresh the TTL with a heartbeat. The resource should not be passed to the update callbacks. + EXPECT_CALL(*ttl_timer_, enabled()); + deliverDiscoveryResponseWithTtlResource(heartbeatResource(std::chrono::seconds(2), "name1"), + "debug1", "nonce1"); + } + + // Remove the TTL. + EXPECT_CALL(*ttl_timer_, disableTimer()); + deliverDiscoveryResponse({"name1"}, "version1", "nonce1"); + + // Add back the TTL. + EXPECT_CALL(*ttl_timer_, enabled()); + EXPECT_CALL(*ttl_timer_, enableTimer(_, _)); + deliverDiscoveryResponseWithTtlResource( + resourceWithTtl(std::chrono::seconds(2), resource("name1")), "debug1", "nonce1"); + + EXPECT_CALL(callbacks_, onConfigUpdate(_, _, _)); + EXPECT_CALL(*ttl_timer_, disableTimer()); + time_system.setSystemTime(std::chrono::seconds(2)); + + // Invoke the TTL. + ttl_timer_->invokeCallback(); +} + +TEST_F(SotwSubscriptionStateTest, TypeUrlMismatch) { + envoy::service::discovery::v3::DiscoveryResponse response; + response.set_version_info("version1"); + response.set_nonce("nonce1"); + response.set_type_url("badtypeurl"); + response.add_resources()->PackFrom(resource("resource")); + EXPECT_CALL(callbacks_, + onConfigUpdateFailed(Envoy::Config::ConfigUpdateFailureReason::UpdateRejected, _)) + .WillOnce(Invoke([](Envoy::Config::ConfigUpdateFailureReason, const EnvoyException* e) { + EXPECT_TRUE(IsSubstring( + "", "", + "type URL type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment embedded " + "in an individual Any does not match the message-wide type URL badtypeurl", + e->what())); + })); + EXPECT_CALL(*ttl_timer_, disableTimer()); + state_->handleResponse(response); +} + +} // namespace +} // namespace Config +} // namespace Envoy diff --git a/test/common/formatter/substitution_formatter_test.cc b/test/common/formatter/substitution_formatter_test.cc index 72fdcafdfef38..00d0ce6a3a4d5 100644 --- a/test/common/formatter/substitution_formatter_test.cc +++ b/test/common/formatter/substitution_formatter_test.cc @@ -663,7 +663,7 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { { StreamInfoFormatter upstream_format("CONNECTION_ID"); uint64_t id = 123; - EXPECT_CALL(stream_info, connectionID()).WillRepeatedly(Return(id)); + stream_info.downstream_address_provider_->setConnectionID(id); EXPECT_EQ("123", upstream_format.format(request_headers, response_headers, response_trailers, stream_info, body)); EXPECT_THAT(upstream_format.formatValue(request_headers, response_headers, response_trailers, diff --git a/test/common/http/conn_manager_impl_fuzz_test.cc b/test/common/http/conn_manager_impl_fuzz_test.cc index 6af101e44ee98..928289062dcef 100644 --- a/test/common/http/conn_manager_impl_fuzz_test.cc +++ b/test/common/http/conn_manager_impl_fuzz_test.cc @@ -173,6 +173,7 @@ class FuzzConfig : public ConnectionManagerConfig { serverHeaderTransformation() const override { return server_transformation_; } + const absl::optional& schemeToSet() const override { return scheme_; } ConnectionManagerStats& stats() override { return stats_; } ConnectionManagerTracingStats& tracingStats() override { return tracing_stats_; } bool useRemoteAddress() const override { return use_remote_address_; } @@ -233,6 +234,7 @@ class FuzzConfig : public ConnectionManagerConfig { std::string server_name_; HttpConnectionManagerProto::ServerHeaderTransformation server_transformation_{ HttpConnectionManagerProto::OVERWRITE}; + absl::optional scheme_; Stats::IsolatedStoreImpl fake_stats_; ConnectionManagerStats stats_; ConnectionManagerTracingStats tracing_stats_; diff --git a/test/common/http/conn_manager_impl_test.cc b/test/common/http/conn_manager_impl_test.cc index 164f7d46f947d..69894daf187aa 100644 --- a/test/common/http/conn_manager_impl_test.cc +++ b/test/common/http/conn_manager_impl_test.cc @@ -293,7 +293,6 @@ TEST_F(HttpConnectionManagerImplTest, 100ContinueResponseWithDecoderPause) { // When create new stream, the stream info will be populated from the connection. TEST_F(HttpConnectionManagerImplTest, PopulateStreamInfo) { setup(true, "", false); - EXPECT_CALL(filter_callbacks_.connection_, id()).WillRepeatedly(Return(1234)); // Set up the codec. Buffer::OwnedImpl fake_input("input"); @@ -303,7 +302,8 @@ TEST_F(HttpConnectionManagerImplTest, PopulateStreamInfo) { EXPECT_EQ(requestIDExtension().get(), decoder_->streamInfo().getRequestIDProvider()); EXPECT_EQ(ssl_connection_, decoder_->streamInfo().downstreamSslConnection()); - EXPECT_EQ(1234U, decoder_->streamInfo().connectionID()); + EXPECT_EQ(filter_callbacks_.connection_.id_, + decoder_->streamInfo().downstreamAddressProvider().connectionID()); EXPECT_EQ(server_name_, decoder_->streamInfo().downstreamAddressProvider().requestedServerName()); // Clean up. @@ -1285,8 +1285,8 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlow) { auto* span = new NiceMock(); EXPECT_CALL(*tracer_, startSpan_(_, _, _, _)) .WillOnce( - Invoke([&](const Tracing::Config& config, const HeaderMap&, const StreamInfo::StreamInfo&, - const Tracing::Decision) -> Tracing::Span* { + Invoke([&](const Tracing::Config& config, const RequestHeaderMap&, + const StreamInfo::StreamInfo&, const Tracing::Decision) -> Tracing::Span* { EXPECT_EQ(Tracing::OperationName::Ingress, config.operationName()); return span; @@ -1443,8 +1443,8 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowIngressDecorat auto* span = new NiceMock(); EXPECT_CALL(*tracer_, startSpan_(_, _, _, _)) .WillOnce( - Invoke([&](const Tracing::Config& config, const HeaderMap&, const StreamInfo::StreamInfo&, - const Tracing::Decision) -> Tracing::Span* { + Invoke([&](const Tracing::Config& config, const RequestHeaderMap&, + const StreamInfo::StreamInfo&, const Tracing::Decision) -> Tracing::Span* { EXPECT_EQ(Tracing::OperationName::Ingress, config.operationName()); return span; @@ -1509,8 +1509,8 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowIngressDecorat auto* span = new NiceMock(); EXPECT_CALL(*tracer_, startSpan_(_, _, _, _)) .WillOnce( - Invoke([&](const Tracing::Config& config, const HeaderMap&, const StreamInfo::StreamInfo&, - const Tracing::Decision) -> Tracing::Span* { + Invoke([&](const Tracing::Config& config, const RequestHeaderMap&, + const StreamInfo::StreamInfo&, const Tracing::Decision) -> Tracing::Span* { EXPECT_EQ(Tracing::OperationName::Ingress, config.operationName()); return span; @@ -1576,8 +1576,8 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowIngressDecorat auto* span = new NiceMock(); EXPECT_CALL(*tracer_, startSpan_(_, _, _, _)) .WillOnce( - Invoke([&](const Tracing::Config& config, const HeaderMap&, const StreamInfo::StreamInfo&, - const Tracing::Decision) -> Tracing::Span* { + Invoke([&](const Tracing::Config& config, const RequestHeaderMap&, + const StreamInfo::StreamInfo&, const Tracing::Decision) -> Tracing::Span* { EXPECT_EQ(Tracing::OperationName::Ingress, config.operationName()); return span; @@ -1657,8 +1657,8 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowEgressDecorato auto* span = new NiceMock(); EXPECT_CALL(*tracer_, startSpan_(_, _, _, _)) .WillOnce( - Invoke([&](const Tracing::Config& config, const HeaderMap&, const StreamInfo::StreamInfo&, - const Tracing::Decision) -> Tracing::Span* { + Invoke([&](const Tracing::Config& config, const RequestHeaderMap&, + const StreamInfo::StreamInfo&, const Tracing::Decision) -> Tracing::Span* { EXPECT_EQ(Tracing::OperationName::Egress, config.operationName()); return span; @@ -1739,8 +1739,8 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowEgressDecorato auto* span = new NiceMock(); EXPECT_CALL(*tracer_, startSpan_(_, _, _, _)) .WillOnce( - Invoke([&](const Tracing::Config& config, const HeaderMap&, const StreamInfo::StreamInfo&, - const Tracing::Decision) -> Tracing::Span* { + Invoke([&](const Tracing::Config& config, const RequestHeaderMap&, + const StreamInfo::StreamInfo&, const Tracing::Decision) -> Tracing::Span* { EXPECT_EQ(Tracing::OperationName::Egress, config.operationName()); return span; @@ -1823,8 +1823,8 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowEgressDecorato auto* span = new NiceMock(); EXPECT_CALL(*tracer_, startSpan_(_, _, _, _)) .WillOnce( - Invoke([&](const Tracing::Config& config, const HeaderMap&, const StreamInfo::StreamInfo&, - const Tracing::Decision) -> Tracing::Span* { + Invoke([&](const Tracing::Config& config, const RequestHeaderMap&, + const StreamInfo::StreamInfo&, const Tracing::Decision) -> Tracing::Span* { EXPECT_EQ(Tracing::OperationName::Egress, config.operationName()); return span; diff --git a/test/common/http/conn_manager_impl_test_base.h b/test/common/http/conn_manager_impl_test_base.h index 98a4b95b6e6f8..53a19ce2ecd57 100644 --- a/test/common/http/conn_manager_impl_test_base.h +++ b/test/common/http/conn_manager_impl_test_base.h @@ -110,6 +110,7 @@ class HttpConnectionManagerImplTest : public testing::Test, public ConnectionMan serverHeaderTransformation() const override { return server_transformation_; } + const absl::optional& schemeToSet() const override { return scheme_; } ConnectionManagerStats& stats() override { return stats_; } ConnectionManagerTracingStats& tracingStats() override { return tracing_stats_; } bool useRemoteAddress() const override { return use_remote_address_; } @@ -173,6 +174,7 @@ class HttpConnectionManagerImplTest : public testing::Test, public ConnectionMan std::string server_name_; HttpConnectionManagerProto::ServerHeaderTransformation server_transformation_{ HttpConnectionManagerProto::OVERWRITE}; + absl::optional scheme_; Network::Address::Ipv4Instance local_address_{"127.0.0.1"}; bool use_remote_address_{true}; Http::DefaultInternalAddressConfig internal_address_config_; diff --git a/test/common/http/conn_manager_utility_test.cc b/test/common/http/conn_manager_utility_test.cc index 7948e3d19def4..ec66b39ad50d4 100644 --- a/test/common/http/conn_manager_utility_test.cc +++ b/test/common/http/conn_manager_utility_test.cc @@ -63,6 +63,7 @@ class MockRequestIDExtension : public RequestIDExtension { [this](Http::RequestHeaderMap& request_headers, Tracing::Reason trace_status) { real_->setTraceReason(request_headers, trace_status); }); + ON_CALL(*this, useRequestIdForTraceSampling()).WillByDefault(Return(true)); } MOCK_METHOD(void, set, (Http::RequestHeaderMap&, bool)); @@ -70,6 +71,7 @@ class MockRequestIDExtension : public RequestIDExtension { MOCK_METHOD(absl::optional, toInteger, (const Http::RequestHeaderMap&), (const)); MOCK_METHOD(Tracing::Reason, getTraceReason, (const Http::RequestHeaderMap&)); MOCK_METHOD(void, setTraceReason, (Http::RequestHeaderMap&, Tracing::Reason)); + MOCK_METHOD(bool, useRequestIdForTraceSampling, (), (const)); private: RequestIDExtensionSharedPtr real_; @@ -332,6 +334,23 @@ TEST_F(ConnectionManagerUtilityTest, SchemeIsRespected) { EXPECT_EQ("https", headers.getSchemeValue()); } +TEST_F(ConnectionManagerUtilityTest, SchemeOverwrite) { + ON_CALL(config_, useRemoteAddress()).WillByDefault(Return(true)); + ON_CALL(config_, xffNumTrustedHops()).WillByDefault(Return(0)); + connection_.stream_info_.downstream_address_provider_->setRemoteAddress( + std::make_shared("127.0.0.1")); + TestRequestHeaderMapImpl headers{}; + Network::Address::Ipv4Instance local_address("10.3.2.1"); + ON_CALL(config_, localAddress()).WillByDefault(ReturnRef(local_address)); + + // Scheme was present. Do not overwrite anything + // Scheme and X-Forwarded-Proto will be overwritten. + config_.scheme_ = "https"; + callMutateRequestHeaders(headers, Protocol::Http2); + EXPECT_EQ("https", headers.getSchemeValue()); + EXPECT_EQ("https", headers.getForwardedProtoValue()); +} + // Verify internal request and XFF is set when we are using remote address and the address is // internal according to user configuration. TEST_F(ConnectionManagerUtilityTest, UseRemoteAddressWhenUserConfiguredRemoteAddress) { @@ -1229,6 +1248,14 @@ TEST_F(ConnectionManagerUtilityTest, SamplingWithoutRouteOverride) { EXPECT_EQ(Tracing::Reason::Sampling, request_id_extension_->getTraceReason(request_headers)); } +TEST_F(ConnectionManagerUtilityTest, CheckSamplingDecisionWithBypassSamplingWithRequestId) { + EXPECT_CALL(*request_id_extension_, useRequestIdForTraceSampling()).WillOnce(Return(false)); + Http::TestRequestHeaderMapImpl request_headers{ + {"x-request-id", "125a4afb-6f55-44ba-ad80-413f09f48a28"}}; + const auto ret = callMutateRequestHeaders(request_headers, Protocol::Http2); + EXPECT_EQ(Tracing::Reason::Sampling, ret.trace_reason_); +} + TEST_F(ConnectionManagerUtilityTest, SamplingWithRouteOverride) { EXPECT_CALL( runtime_.snapshot_, diff --git a/test/common/http/utility_test.cc b/test/common/http/utility_test.cc index 18fe7de3c6ca5..f86935dece080 100644 --- a/test/common/http/utility_test.cc +++ b/test/common/http/utility_test.cc @@ -1315,6 +1315,31 @@ TEST(HttpUtility, TestRejectTeHeaderTooLong) { EXPECT_EQ(sanitized_headers, request_headers); } +TEST(HttpUtility, TestRejectUriWithNoPath) { + Http::TestRequestHeaderMapImpl request_headers_no_path = { + {":method", "GET"}, {":authority", "example.com"}, {"x-forwarded-proto", "http"}}; + EXPECT_EQ(Utility::buildOriginalUri(request_headers_no_path, {}), ""); +} + +TEST(HttpUtility, TestTruncateUri) { + Http::TestRequestHeaderMapImpl request_headers_truncated_path = {{":method", "GET"}, + {":path", "/hello_world"}, + {":authority", "example.com"}, + {"x-forwarded-proto", "http"}}; + EXPECT_EQ(Utility::buildOriginalUri(request_headers_truncated_path, 2), "http://example.com/h"); +} + +TEST(HttpUtility, TestUriUsesOriginalPath) { + Http::TestRequestHeaderMapImpl request_headers_truncated_path = { + {":method", "GET"}, + {":path", "/hello_world"}, + {":authority", "example.com"}, + {"x-forwarded-proto", "http"}, + {"x-envoy-original-path", "/goodbye_world"}}; + EXPECT_EQ(Utility::buildOriginalUri(request_headers_truncated_path, {}), + "http://example.com/goodbye_world"); +} + TEST(Url, ParsingFails) { Utility::Url url; EXPECT_FALSE(url.initialize("", false)); diff --git a/test/common/matcher/matcher_test.cc b/test/common/matcher/matcher_test.cc index 99558fc1af9ed..8d1bdc895485b 100644 --- a/test/common/matcher/matcher_test.cc +++ b/test/common/matcher/matcher_test.cc @@ -22,12 +22,17 @@ namespace Envoy { namespace Matcher { class MatcherTest : public ::testing::Test { public: - MatcherTest() : inject_action_(action_factory_) {} + MatcherTest() + : inject_action_(action_factory_), factory_(context_, factory_context_, validation_visitor_) { + } StringActionFactory action_factory_; - Registry::InjectFactory inject_action_; - NiceMock factory_context_; + Registry::InjectFactory> inject_action_; MockMatchTreeValidationVisitor validation_visitor_; + + absl::string_view context_ = ""; + NiceMock factory_context_; + MatchTreeFactory factory_; }; TEST_F(MatcherTest, TestMatcher) { @@ -64,15 +69,13 @@ TEST_F(MatcherTest, TestMatcher) { TestUtility::validate(matcher); - MatchTreeFactory factory("", factory_context_, validation_visitor_); - auto outer_factory = TestDataInputFactory("outer_input", "value"); auto inner_factory = TestDataInputFactory("inner_input", "foo"); EXPECT_CALL(validation_visitor_, performDataInputValidation(_, "type.googleapis.com/google.protobuf.StringValue")) .Times(2); - auto match_tree = factory.create(matcher); + auto match_tree = factory_.create(matcher); const auto result = match_tree()->match(TestData()); EXPECT_EQ(result.match_state_, MatchState::MatchComplete); @@ -104,10 +107,9 @@ TEST_F(MatcherTest, CustomGenericInput) { MessageUtil::loadFromYaml(yaml, matcher, ProtobufMessage::getStrictValidationVisitor()); TestUtility::validate(matcher); - MatchTreeFactory factory("", factory_context_, validation_visitor_); auto common_input_factory = TestCommonProtocolInputFactory("generic", "foo"); - auto match_tree = factory.create(matcher); + auto match_tree = factory_.create(matcher); const auto result = match_tree()->match(TestData()); EXPECT_EQ(result.match_state_, MatchState::MatchComplete); @@ -141,14 +143,12 @@ TEST_F(MatcherTest, CustomMatcher) { TestUtility::validate(matcher); - MatchTreeFactory factory("", factory_context_, validation_visitor_); - auto inner_factory = TestDataInputFactory("inner_input", "foo"); NeverMatchFactory match_factory; EXPECT_CALL(validation_visitor_, performDataInputValidation(_, "type.googleapis.com/google.protobuf.StringValue")); - auto match_tree = factory.create(matcher); + auto match_tree = factory_.create(matcher); const auto result = match_tree()->match(TestData()); EXPECT_EQ(result.match_state_, MatchState::MatchComplete); @@ -198,15 +198,13 @@ TEST_F(MatcherTest, TestAndMatcher) { TestUtility::validate(matcher); - MatchTreeFactory factory("", factory_context_, validation_visitor_); - auto outer_factory = TestDataInputFactory("outer_input", "value"); auto inner_factory = TestDataInputFactory("inner_input", "foo"); EXPECT_CALL(validation_visitor_, performDataInputValidation(_, "type.googleapis.com/google.protobuf.StringValue")) .Times(3); - auto match_tree = factory.create(matcher); + auto match_tree = factory_.create(matcher); const auto result = match_tree()->match(TestData()); EXPECT_EQ(result.match_state_, MatchState::MatchComplete); @@ -257,15 +255,13 @@ TEST_F(MatcherTest, TestOrMatcher) { TestUtility::validate(matcher); - MatchTreeFactory factory("", factory_context_, validation_visitor_); - auto outer_factory = TestDataInputFactory("outer_input", "value"); auto inner_factory = TestDataInputFactory("inner_input", "foo"); EXPECT_CALL(validation_visitor_, performDataInputValidation(_, "type.googleapis.com/google.protobuf.StringValue")) .Times(3); - auto match_tree = factory.create(matcher); + auto match_tree = factory_.create(matcher); const auto result = match_tree()->match(TestData()); EXPECT_EQ(result.match_state_, MatchState::MatchComplete); @@ -299,14 +295,12 @@ TEST_F(MatcherTest, TestNotMatcher) { TestUtility::validate(matcher); - MatchTreeFactory factory("", factory_context_, validation_visitor_); - auto inner_factory = TestDataInputFactory("inner_input", "foo"); NeverMatchFactory match_factory; EXPECT_CALL(validation_visitor_, performDataInputValidation(_, "type.googleapis.com/google.protobuf.StringValue")); - auto match_tree = factory.create(matcher); + auto match_tree = factory_.create(matcher); const auto result = match_tree()->match(TestData()); EXPECT_EQ(result.match_state_, MatchState::MatchComplete); @@ -350,15 +344,13 @@ TEST_F(MatcherTest, TestRecursiveMatcher) { TestUtility::validate(matcher); - MatchTreeFactory factory("", factory_context_, validation_visitor_); - auto outer_factory = TestDataInputFactory("outer_input", "value"); auto inner_factory = TestDataInputFactory("inner_input", "foo"); EXPECT_CALL(validation_visitor_, performDataInputValidation(_, "type.googleapis.com/google.protobuf.StringValue")) .Times(2); - auto match_tree = factory.create(matcher); + auto match_tree = factory_.create(matcher); const auto result = match_tree()->match(TestData()); EXPECT_EQ(result.match_state_, MatchState::MatchComplete); diff --git a/test/common/matcher/test_utility.h b/test/common/matcher/test_utility.h index 08d1debbf0e6d..b5c1a6410602c 100644 --- a/test/common/matcher/test_utility.h +++ b/test/common/matcher/test_utility.h @@ -30,7 +30,7 @@ class TestCommonProtocolInputFactory : public CommonProtocolInputFactory { CommonProtocolInputFactoryCb createCommonProtocolInputFactoryCb(const Protobuf::Message&, - Server::Configuration::FactoryContext&) override { + ProtobufMessage::ValidationVisitor&) override { return [&]() { return std::make_unique(value_); }; } @@ -60,8 +60,7 @@ class TestDataInputFactory : public DataInputFactory { : factory_name_(std::string(factory_name)), value_(std::string(data)), injection_(*this) {} DataInputFactoryCb - createDataInputFactoryCb(const Protobuf::Message&, - Server::Configuration::FactoryContext&) override { + createDataInputFactoryCb(const Protobuf::Message&, ProtobufMessage::ValidationVisitor&) override { return [&]() { return std::make_unique( DataInputGetResult{DataInputGetResult::DataAvailability::AllDataAvailable, value_}); @@ -108,10 +107,10 @@ struct StringAction : public ActionBase { }; // Factory for StringAction. -class StringActionFactory : public ActionFactory { +class StringActionFactory : public ActionFactory { public: - ActionFactoryCb createActionFactoryCb(const Protobuf::Message& config, const std::string&, - Server::Configuration::FactoryContext&) override { + ActionFactoryCb createActionFactoryCb(const Protobuf::Message& config, absl::string_view&, + ProtobufMessage::ValidationVisitor&) override { const auto& string = dynamic_cast(config); return [string]() { return std::make_unique(string.value()); }; } @@ -137,7 +136,7 @@ class NeverMatchFactory : public InputMatcherFactory { InputMatcherFactoryCb createInputMatcherFactoryCb(const Protobuf::Message&, - Server::Configuration::FactoryContext&) override { + Server::Configuration::ServerFactoryContext&) override { return []() { return std::make_unique(); }; } diff --git a/test/common/network/apple_dns_impl_test.cc b/test/common/network/apple_dns_impl_test.cc index d2f00c15cdb29..5763e4b52e8dc 100644 --- a/test/common/network/apple_dns_impl_test.cc +++ b/test/common/network/apple_dns_impl_test.cc @@ -137,7 +137,7 @@ TEST_F(AppleDnsImplTest, InvalidConfigOptions) { TEST_F(AppleDnsImplTest, DestructPending) { ActiveDnsQuery* query = resolveWithUnreferencedParameters("", DnsLookupFamily::V4Only, 0); ASSERT_NE(nullptr, query); - query->cancel(); + query->cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned); } TEST_F(AppleDnsImplTest, LocalLookup) { @@ -194,7 +194,7 @@ TEST_F(AppleDnsImplTest, Cancel) { DnsResolver::ResolutionStatus::Success, true)); ASSERT_NE(nullptr, query); - query->cancel(); + query->cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned); dispatcher_->run(Event::Dispatcher::RunType::Block); } diff --git a/test/common/network/dns_impl_test.cc b/test/common/network/dns_impl_test.cc index e3c1401c673b2..11a6cfb2001fb 100644 --- a/test/common/network/dns_impl_test.cc +++ b/test/common/network/dns_impl_test.cc @@ -572,7 +572,7 @@ INSTANTIATE_TEST_SUITE_P(IpVersions, DnsImplTest, TEST_P(DnsImplTest, DestructPending) { ActiveDnsQuery* query = resolveWithUnreferencedParameters("", DnsLookupFamily::V4Only, false); ASSERT_NE(nullptr, query); - query->cancel(); + query->cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned); // Also validate that pending events are around to exercise the resource // reclamation path. EXPECT_GT(peer_->events().size(), 0U); @@ -806,7 +806,7 @@ TEST_P(DnsImplTest, Cancel) { {"201.134.56.7"}, {}, absl::nullopt)); ASSERT_NE(nullptr, query); - query->cancel(); + query->cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned); dispatcher_->run(Event::Dispatcher::RunType::Block); } diff --git a/test/common/network/listen_socket_impl_test.cc b/test/common/network/listen_socket_impl_test.cc index 7e787507020fe..5c78af7a2e2c5 100644 --- a/test/common/network/listen_socket_impl_test.cc +++ b/test/common/network/listen_socket_impl_test.cc @@ -8,7 +8,6 @@ #include "source/common/api/os_sys_calls_impl.h" #include "source/common/network/io_socket_handle_impl.h" #include "source/common/network/listen_socket_impl.h" -#include "source/common/network/socket_interface_impl.h" #include "source/common/network/utility.h" #include "test/mocks/network/mocks.h" @@ -25,18 +24,6 @@ namespace Envoy { namespace Network { namespace { -class MockSingleFamilySocketInterface : public SocketInterfaceImpl { -public: - explicit MockSingleFamilySocketInterface(Address::IpVersion version) : version_(version) {} - MOCK_METHOD(IoHandlePtr, socket, (Socket::Type, Address::Type, Address::IpVersion, bool), - (const)); - MOCK_METHOD(IoHandlePtr, socket, (Socket::Type, const Address::InstanceConstSharedPtr), (const)); - bool ipFamilySupported(int domain) override { - return (version_ == Address::IpVersion::v4) ? domain == AF_INET : domain == AF_INET6; - } - const Address::IpVersion version_; -}; - TEST(ConnectionSocketImplTest, LowerCaseRequestedServerName) { absl::string_view serverName("www.EXAMPLE.com"); absl::string_view expectedServerName("www.example.com"); @@ -198,7 +185,8 @@ TEST_P(ListenSocketImplTestTcp, CheckIpVersionWithNullLocalAddress) { } TEST_P(ListenSocketImplTestTcp, SupportedIpFamilyVirtualSocketIsCreatedWithNoBsdSocketCreated) { - auto mock_interface = std::make_unique(version_); + auto mock_interface = + std::make_unique(std::vector{version_}); auto* mock_interface_ptr = mock_interface.get(); auto any_address = version_ == Address::IpVersion::v4 ? Utility::getIpv4AnyAddress() : Utility::getIpv6AnyAddress(); @@ -214,7 +202,8 @@ TEST_P(ListenSocketImplTestTcp, SupportedIpFamilyVirtualSocketIsCreatedWithNoBsd } TEST_P(ListenSocketImplTestTcp, DeathAtUnSupportedIpFamilyListenSocket) { - auto mock_interface = std::make_unique(version_); + auto mock_interface = + std::make_unique(std::vector{version_}); auto* mock_interface_ptr = mock_interface.get(); auto the_other_address = version_ == Address::IpVersion::v4 ? Utility::getIpv6AnyAddress() : Utility::getIpv4AnyAddress(); diff --git a/test/common/protobuf/utility_test.cc b/test/common/protobuf/utility_test.cc index 92480788872d5..070b6cecb46b0 100644 --- a/test/common/protobuf/utility_test.cc +++ b/test/common/protobuf/utility_test.cc @@ -986,6 +986,19 @@ type_url: type.googleapis.com/envoy.test.Sensitive EXPECT_TRUE(TestUtility::protoEqual(expected, actual)); } +TEST_F(ProtobufUtilityTest, RedactTypedStructWithNoTypeUrl) { + udpa::type::v1::TypedStruct actual; + TestUtility::loadFromYaml(R"EOF( +value: + sensitive_string: This field is sensitive, but we have no way of knowing. +)EOF", + actual); + + udpa::type::v1::TypedStruct expected = actual; + MessageUtil::redact(actual); + EXPECT_TRUE(TestUtility::protoEqual(expected, actual)); +} + // Messages packed into `TypedStruct` with unknown type URLs are skipped. TEST_F(ProtobufUtilityTest, RedactTypedStructWithUnknownTypeUrl) { udpa::type::v1::TypedStruct actual; @@ -1001,6 +1014,20 @@ type_url: type.googleapis.com/envoy.unknown.Message EXPECT_TRUE(TestUtility::protoEqual(expected, actual)); } +TEST_F(ProtobufUtilityTest, RedactEmptyTypeUrlTypedStruct) { + udpa::type::v1::TypedStruct actual; + udpa::type::v1::TypedStruct expected = actual; + MessageUtil::redact(actual); + EXPECT_TRUE(TestUtility::protoEqual(expected, actual)); +} + +TEST_F(ProtobufUtilityTest, RedactEmptyTypeUrlAny) { + ProtobufWkt::Any actual; + MessageUtil::redact(actual); + ProtobufWkt::Any expected = actual; + EXPECT_TRUE(TestUtility::protoEqual(expected, actual)); +} + // Deeply-nested opaque protos (`Any` and `TypedStruct`), which are reified using the // `DynamicMessageFactory`, should be redacted correctly. TEST_F(ProtobufUtilityTest, RedactDeeplyNestedOpaqueProtos) { diff --git a/test/common/quic/active_quic_listener_test.cc b/test/common/quic/active_quic_listener_test.cc index 1dbd90bb6009b..edc1bb8fee0c7 100644 --- a/test/common/quic/active_quic_listener_test.cc +++ b/test/common/quic/active_quic_listener_test.cc @@ -75,9 +75,6 @@ class ActiveQuicListenerFactoryPeer { class ActiveQuicListenerTest : public QuicMultiVersionTest { protected: - using Socket = - Network::NetworkListenSocket>; - ActiveQuicListenerTest() : version_(GetParam().first), api_(Api::createApiForTest(simulated_time_system_)), dispatcher_(api_->allocateDispatcher("test_thread")), clock_(*dispatcher_), @@ -208,7 +205,8 @@ class ActiveQuicListenerTest : public QuicMultiVersionTest { } void sendCHLO(quic::QuicConnectionId connection_id) { - client_sockets_.push_back(std::make_unique(local_address_, nullptr, /*bind*/ false)); + client_sockets_.push_back(std::make_unique(Network::Socket::Type::Datagram, + local_address_, nullptr)); Buffer::OwnedImpl payload = generateChloPacketToSend( quic_version_, quic_config_, ActiveQuicListenerPeer::cryptoConfig(*quic_listener_), connection_id, clock_, envoyIpAddressToQuicSocketAddress(local_address_->ip()), @@ -317,7 +315,7 @@ class ActiveQuicListenerTest : public QuicMultiVersionTest { Init::MockManager init_manager_; NiceMock validation_visitor_; - std::list> client_sockets_; + std::list> client_sockets_; std::list> read_filters_; Network::MockFilterChainManager filter_chain_manager_; // The following two containers must guarantee pointer stability as addresses diff --git a/test/common/stats/BUILD b/test/common/stats/BUILD index e7b87df994711..cc3e4b21f360d 100644 --- a/test/common/stats/BUILD +++ b/test/common/stats/BUILD @@ -282,6 +282,7 @@ envoy_cc_benchmark_binary( ":stat_test_utility_lib", "//source/common/common:thread_lib", "//source/common/event:dispatcher_lib", + "//source/common/stats:stats_matcher_lib", "//source/common/stats:thread_local_store_lib", "//source/common/thread_local:thread_local_lib", "//test/test_common:simulated_time_system_lib", diff --git a/test/common/stats/stat_test_utility.cc b/test/common/stats/stat_test_utility.cc index 9f9438a7bf9a5..3eb489dbfa6cf 100644 --- a/test/common/stats/stat_test_utility.cc +++ b/test/common/stats/stat_test_utility.cc @@ -7,7 +7,8 @@ namespace Envoy { namespace Stats { namespace TestUtil { -void forEachSampleStat(int num_clusters, std::function fn) { +void forEachSampleStat(int num_clusters, bool include_other_stats, + std::function fn) { // These are stats that are repeated for each cluster as of Oct 2018, with a // very basic configuration with no traffic. static const char* cluster_stats[] = {"bind_errors", @@ -95,8 +96,10 @@ void forEachSampleStat(int num_clusters, std::function fn(absl::StrCat("cluster.service_", cluster, ".", cluster_stat)); } } - for (const auto& other_stat : other_stats) { - fn(other_stat); + if (include_other_stats) { + for (const auto& other_stat : other_stats) { + fn(other_stat); + } } } diff --git a/test/common/stats/stat_test_utility.h b/test/common/stats/stat_test_utility.h index 213775d4bb218..ea9e5b15c3776 100644 --- a/test/common/stats/stat_test_utility.h +++ b/test/common/stats/stat_test_utility.h @@ -49,7 +49,8 @@ class TestSymbolTable { * @param num_clusters the number of clusters for which to generate stats. * @param fn the function to call with every stat name. */ -void forEachSampleStat(int num_clusters, std::function fn); +void forEachSampleStat(int num_clusters, bool include_other_stats, + std::function fn); // Tracks memory consumption over a span of time. Test classes instantiate a // MemoryTest object to start measuring heap memory, and call consumedBytes() to @@ -185,7 +186,9 @@ class TestStore : public SymbolTableProvider, public IsolatedStoreImpl { do { \ if (Stats::TestUtil::MemoryTest::mode() != Stats::TestUtil::MemoryTest::Mode::Disabled) { \ EXPECT_LE(consumed_bytes, upper_bound); \ - EXPECT_GT(consumed_bytes, 0); \ + if (upper_bound != 0) { \ + EXPECT_GT(consumed_bytes, 0); \ + } \ } else { \ ENVOY_LOG_MISC( \ info, "Skipping upper-bound memory test against {} bytes as platform lacks tcmalloc", \ diff --git a/test/common/stats/stats_matcher_impl_test.cc b/test/common/stats/stats_matcher_impl_test.cc index 87b806cd9f133..821a47c69b1df 100644 --- a/test/common/stats/stats_matcher_impl_test.cc +++ b/test/common/stats/stats_matcher_impl_test.cc @@ -12,6 +12,8 @@ namespace Stats { class StatsMatcherTest : public testing::Test { protected: + StatsMatcherTest() : pool_(symbol_table_) {} + envoy::type::matcher::v3::StringMatcher* inclusionList() { return stats_config_.mutable_stats_matcher()->mutable_inclusion_list()->add_patterns(); } @@ -21,18 +23,22 @@ class StatsMatcherTest : public testing::Test { void rejectAll(const bool should_reject) { stats_config_.mutable_stats_matcher()->set_reject_all(should_reject); } - void initMatcher() { stats_matcher_impl_ = std::make_unique(stats_config_); } - void expectAccepted(std::vector expected_to_pass) { + void initMatcher() { + stats_matcher_impl_ = std::make_unique(stats_config_, symbol_table_); + } + void expectAccepted(const std::vector& expected_to_pass) { for (const auto& stat_name : expected_to_pass) { - EXPECT_FALSE(stats_matcher_impl_->rejects(stat_name)) << "Accepted: " << stat_name; + EXPECT_FALSE(stats_matcher_impl_->rejects(pool_.add(stat_name))) << "Accepted: " << stat_name; } } - void expectDenied(std::vector expected_to_fail) { + void expectDenied(const std::vector& expected_to_fail) { for (const auto& stat_name : expected_to_fail) { - EXPECT_TRUE(stats_matcher_impl_->rejects(stat_name)) << "Rejected: " << stat_name; + EXPECT_TRUE(stats_matcher_impl_->rejects(pool_.add(stat_name))) << "Rejected: " << stat_name; } } + SymbolTableImpl symbol_table_; + StatNamePool pool_; std::unique_ptr stats_matcher_impl_; private: @@ -56,6 +62,7 @@ TEST_F(StatsMatcherTest, CheckRejectAll) { expectDenied({"foo", "bar", "foo.bar", "foo.bar.baz", "foobarbaz"}); EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); EXPECT_TRUE(stats_matcher_impl_->rejectsAll()); + EXPECT_EQ(StatsMatcher::FastResult::Rejects, stats_matcher_impl_->fastRejects(StatName())); } TEST_F(StatsMatcherTest, CheckNotRejectAll) { @@ -65,6 +72,7 @@ TEST_F(StatsMatcherTest, CheckNotRejectAll) { expectAccepted({"foo", "bar", "foo.bar", "foo.bar.baz", "foobarbaz"}); EXPECT_TRUE(stats_matcher_impl_->acceptsAll()); EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); + EXPECT_EQ(StatsMatcher::FastResult::NoMatch, stats_matcher_impl_->fastRejects(StatName())); } TEST_F(StatsMatcherTest, CheckIncludeAll) { @@ -90,8 +98,7 @@ TEST_F(StatsMatcherTest, CheckIncludeExact) { inclusionList()->set_exact("abc"); initMatcher(); expectAccepted({"abc"}); - expectDenied({"abcd", "abc.d", "d.abc", "dabc", "ab", "ac", "abcc", "Abc", "aBc", "abC", "abc.", - ".abc", "ABC"}); + expectDenied({"abcd", "abc.d", "d.abc", "dabc", "ab", "ac", "abcc", "Abc", "aBc", "abC", "ABC"}); EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } @@ -99,8 +106,8 @@ TEST_F(StatsMatcherTest, CheckIncludeExact) { TEST_F(StatsMatcherTest, CheckExcludeExact) { exclusionList()->set_exact("abc"); initMatcher(); - expectAccepted({"abcd", "abc.d", "d.abc", "dabc", "ab", "ac", "abcc", "Abc", "aBc", "abC", "abc.", - ".abc", "ABC"}); + expectAccepted( + {"abcd", "abc.d", "d.abc", "dabc", "ab", "ac", "abcc", "Abc", "aBc", "abC", "ABC"}); expectDenied({"abc"}); EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); @@ -117,6 +124,18 @@ TEST_F(StatsMatcherTest, CheckIncludePrefix) { EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } +TEST_F(StatsMatcherTest, CheckIncludePrefixDot) { + inclusionList()->set_prefix("abc."); + initMatcher(); + expectAccepted({"abc", "abc.foo"}); + expectDenied( + {"abcfoo", "ABC", "ABC.foo", "ABCfoo", "foo", "abb", "a.b.c", "_abc", "foo.abc", "fooabc"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); + EXPECT_EQ(StatsMatcher::FastResult::Matches, + stats_matcher_impl_->fastRejects(pool_.add("abc.foo"))); +} + TEST_F(StatsMatcherTest, CheckExcludePrefix) { exclusionList()->set_prefix("abc"); initMatcher(); @@ -283,5 +302,33 @@ TEST_F(StatsMatcherTest, CheckMultipleAssortedExclusionMatchers) { EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); } +TEST_F(StatsMatcherTest, CheckMultipleAssortedInclusionMatchersWithPrefix) { + inclusionList()->MergeFrom(TestUtility::createRegexMatcher(".*envoy.*")); + inclusionList()->set_suffix("requests"); + inclusionList()->set_exact("regex"); + inclusionList()->set_prefix("prefix."); + initMatcher(); + expectAccepted({"envoy.matchers.requests", "requests.for.envoy", "envoyrequests", "regex", + "prefix", "prefix.foo"}); + expectAccepted({"prefix.envoy.matchers.requests", "prefix.requests.for.envoy", + "prefix.envoyrequests", "prefix.regex"}); + expectDenied({"requestsEnvoy", "EnvoyProxy", "foo", "regex_etc"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); +} + +TEST_F(StatsMatcherTest, CheckMultipleAssortedExclusionMatchersWithPrefix) { + exclusionList()->MergeFrom(TestUtility::createRegexMatcher(".*envoy.*")); + exclusionList()->set_suffix("requests"); + exclusionList()->set_exact("regex"); + exclusionList()->set_prefix("prefix."); + initMatcher(); + expectAccepted({"requestsEnvoy", "EnvoyProxy", "foo", "regex_etc", "prefixfoo"}); + expectDenied({"envoy.matchers.requests", "requests.for.envoy", "envoyrequests", "regex", "prefix", + "prefix.foo", "prefix.requests.for.envoy"}); + EXPECT_FALSE(stats_matcher_impl_->acceptsAll()); + EXPECT_FALSE(stats_matcher_impl_->rejectsAll()); +} + } // namespace Stats } // namespace Envoy diff --git a/test/common/stats/symbol_table_impl_test.cc b/test/common/stats/symbol_table_impl_test.cc index 0f8dd73fedae5..a54394975983a 100644 --- a/test/common/stats/symbol_table_impl_test.cc +++ b/test/common/stats/symbol_table_impl_test.cc @@ -694,6 +694,20 @@ TEST_F(StatNameTest, StatNameEmptyEquivalent) { EXPECT_NE(empty2.hash(), non_empty.hash()); } +TEST_F(StatNameTest, StartsWith) { + StatName prefix = makeStat("prefix"); + EXPECT_TRUE(prefix.startsWith(prefix)); + EXPECT_TRUE(makeStat("prefix").startsWith(prefix)); + EXPECT_TRUE(makeStat("prefix.foo").startsWith(prefix)); + EXPECT_TRUE(makeStat("prefix.foo.bar").startsWith(prefix)); + EXPECT_FALSE(makeStat("").startsWith(prefix)); + EXPECT_FALSE(makeStat("foo").startsWith(prefix)); + StatNameDynamicPool dynamic(table_); + StatName dynamic_prefix = dynamic.add("prefix"); + EXPECT_FALSE(dynamic_prefix.startsWith(prefix)); + EXPECT_FALSE(dynamic_prefix.startsWith(dynamic_prefix)); +} + TEST_F(StatNameTest, SupportsAbslHash) { EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({ StatName(), @@ -709,7 +723,7 @@ TEST(SymbolTableTest, Memory) { // Tests a stat-name allocation strategy. auto test_memory_usage = [](std::function fn) -> size_t { TestUtil::MemoryTest memory_test; - TestUtil::forEachSampleStat(1000, fn); + TestUtil::forEachSampleStat(1000, true, fn); return memory_test.consumedBytes(); }; diff --git a/test/common/stats/thread_local_store_speed_test.cc b/test/common/stats/thread_local_store_speed_test.cc index 8ce08a84d052e..620af46c698ab 100644 --- a/test/common/stats/thread_local_store_speed_test.cc +++ b/test/common/stats/thread_local_store_speed_test.cc @@ -7,6 +7,7 @@ #include "source/common/common/thread.h" #include "source/common/event/dispatcher_impl.h" #include "source/common/stats/allocator_impl.h" +#include "source/common/stats/stats_matcher_impl.h" #include "source/common/stats/symbol_table_impl.h" #include "source/common/stats/tag_producer_impl.h" #include "source/common/stats/thread_local_store.h" @@ -28,7 +29,7 @@ class ThreadLocalStorePerf { api_(Api::createApiForTest(store_, time_system_)) { store_.setTagProducer(std::make_unique(stats_config_)); - Stats::TestUtil::forEachSampleStat(1000, [this](absl::string_view name) { + Stats::TestUtil::forEachSampleStat(1000, true, [this](absl::string_view name) { stat_names_.push_back(std::make_unique(name, symbol_table_)); }); } @@ -62,6 +63,12 @@ class ThreadLocalStorePerf { store_.initializeThreading(*dispatcher_, *tls_); } + void initPrefixRejections(const std::string& prefix) { + stats_config_.mutable_stats_matcher()->mutable_exclusion_list()->add_patterns()->set_prefix( + prefix); + store_.setStatsMatcher(std::make_unique(stats_config_, symbol_table_)); + } + private: Stats::SymbolTableImpl symbol_table_; Event::SimulatedTimeSystem time_system_; @@ -82,7 +89,7 @@ class ThreadLocalStorePerf { static void BM_StatsNoTls(benchmark::State& state) { Envoy::ThreadLocalStorePerf context; - for (auto _ : state) { + for (auto _ : state) { // NOLINT context.accessCounters(); } } @@ -96,11 +103,35 @@ static void BM_StatsWithTls(benchmark::State& state) { Envoy::ThreadLocalStorePerf context; context.initThreading(); - for (auto _ : state) { + for (auto _ : state) { // NOLINT context.accessCounters(); } } BENCHMARK(BM_StatsWithTls); +// NOLINTNEXTLINE(readability-identifier-naming) +static void BM_StatsWithTlsAndRejectionsWithDot(benchmark::State& state) { + Envoy::ThreadLocalStorePerf context; + context.initThreading(); + context.initPrefixRejections("cluster."); + + for (auto _ : state) { // NOLINT + context.accessCounters(); + } +} +BENCHMARK(BM_StatsWithTlsAndRejectionsWithDot); + +// NOLINTNEXTLINE(readability-identifier-naming) +static void BM_StatsWithTlsAndRejectionsWithoutDot(benchmark::State& state) { + Envoy::ThreadLocalStorePerf context; + context.initThreading(); + context.initPrefixRejections("cluster"); + + for (auto _ : state) { // NOLINT + context.accessCounters(); + } +} +BENCHMARK(BM_StatsWithTlsAndRejectionsWithoutDot); + // TODO(jmarantz): add multi-threaded variant of this test, that aggressively // looks up stats in multiple threads to try to trigger contention issues. diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index b6ee2c6a1a4ff..b723fe3083fb9 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -743,6 +743,28 @@ TEST_F(LookupWithStatNameTest, NotFound) { class StatsMatcherTLSTest : public StatsThreadLocalStoreTest { public: envoy::config::metrics::v3::StatsConfig stats_config_; + + ~StatsMatcherTLSTest() override { + tls_.shutdownGlobalThreading(); + store_->shutdownThreading(); + } + + // Adds counters for 1000 clusters and returns the amount of memory consumed. + uint64_t memoryConsumedAddingClusterStats() { + StatNamePool pool(symbol_table_); + std::vector stat_names; + Stats::TestUtil::forEachSampleStat(1000, false, [&pool, &stat_names](absl::string_view name) { + stat_names.push_back(pool.add(name)); + }); + + { + TestUtil::MemoryTest memory_test; + for (StatName stat_name : stat_names) { + store_->counterFromStatName(stat_name); + } + return memory_test.consumedBytes(); + } + } }; TEST_F(StatsMatcherTLSTest, TestNoOpStatImpls) { @@ -750,7 +772,7 @@ TEST_F(StatsMatcherTLSTest, TestNoOpStatImpls) { stats_config_.mutable_stats_matcher()->mutable_exclusion_list()->add_patterns()->set_prefix( "noop"); - store_->setStatsMatcher(std::make_unique(stats_config_)); + store_->setStatsMatcher(std::make_unique(stats_config_, symbol_table_)); // Testing No-op counters, gauges, histograms which match the prefix "noop". @@ -815,9 +837,6 @@ TEST_F(StatsMatcherTLSTest, TestNoOpStatImpls) { Histogram& noop_histogram_2 = store_->histogramFromString("noop_histogram_2", Stats::Histogram::Unit::Unspecified); EXPECT_EQ(&noop_histogram, &noop_histogram_2); - - tls_.shutdownGlobalThreading(); - store_->shutdownThreading(); } // We only test the exclusion list -- the inclusion list is the inverse, and both are tested in @@ -830,7 +849,7 @@ TEST_F(StatsMatcherTLSTest, TestExclusionRegex) { // Will block all stats containing any capital alphanumeric letter. stats_config_.mutable_stats_matcher()->mutable_exclusion_list()->add_patterns()->MergeFrom( TestUtility::createRegexMatcher(".*[A-Z].*")); - store_->setStatsMatcher(std::make_unique(stats_config_)); + store_->setStatsMatcher(std::make_unique(stats_config_, symbol_table_)); // The creation of counters/gauges/histograms which have no uppercase letters should succeed. Counter& lowercase_counter = store_->counterFromString("lowercase_counter"); @@ -875,7 +894,7 @@ TEST_F(StatsMatcherTLSTest, TestExclusionRegex) { // the string "invalid". stats_config_.mutable_stats_matcher()->mutable_exclusion_list()->add_patterns()->set_prefix( "invalid"); - store_->setStatsMatcher(std::make_unique(stats_config_)); + store_->setStatsMatcher(std::make_unique(stats_config_, symbol_table_)); Counter& valid_counter = store_->counterFromString("valid_counter"); valid_counter.inc(); @@ -927,10 +946,40 @@ TEST_F(StatsMatcherTLSTest, TestExclusionRegex) { TextReadout& invalid_string_2 = store_->textReadoutFromString("also_INVLD_string"); invalid_string_2.set("still no"); EXPECT_EQ("", invalid_string_2.value()); +} - // Expected to free lowercase_counter, lowercase_gauge, valid_counter, valid_gauge - tls_.shutdownGlobalThreading(); - store_->shutdownThreading(); +// Rejecting stats of the form "cluster." enables an optimization in the matcher +// infrastructure that performs the rejection without converting from StatName +// to string, obviating the need to memoize the rejection in a set. This saves +// a ton of memory, allowing us to reject thousands of stats without consuming +// memory. Note that the trailing "." is critical so we can compare symbolically. +TEST_F(StatsMatcherTLSTest, RejectPrefixDot) { + store_->initializeThreading(main_thread_dispatcher_, tls_); + stats_config_.mutable_stats_matcher()->mutable_exclusion_list()->add_patterns()->set_prefix( + "cluster."); // Prefix match can be executed symbolically. + store_->setStatsMatcher(std::make_unique(stats_config_, symbol_table_)); + uint64_t mem_consumed = memoryConsumedAddingClusterStats(); + + // No memory is consumed at all while rejecting stats from "prefix." + EXPECT_MEMORY_EQ(mem_consumed, 0); + EXPECT_MEMORY_LE(mem_consumed, 0); +} + +// Repeating the same test but retaining the dot means that the StatsMatcher +// infrastructure requires us remember the rejected StatNames in an ever-growing +// map. That map is needed to avoid taking locks while re-rejecting stats we've +// rejected in the past. +TEST_F(StatsMatcherTLSTest, RejectPrefixNoDot) { + store_->initializeThreading(main_thread_dispatcher_, tls_); + stats_config_.mutable_stats_matcher()->mutable_exclusion_list()->add_patterns()->set_prefix( + "cluster"); // No dot at the end means we have to compare as strings. + store_->setStatsMatcher(std::make_unique(stats_config_, symbol_table_)); + uint64_t mem_consumed = memoryConsumedAddingClusterStats(); + + // Memory is consumed at all while rejecting stats from "prefix" in proportion + // to the number of stat instantiations attempted. + EXPECT_MEMORY_EQ(mem_consumed, 2936480); + EXPECT_MEMORY_LE(mem_consumed, 3500000); } // Tests the logic for caching the stats-matcher results, and in particular the @@ -966,11 +1015,21 @@ class RememberStatsMatcherTest : public testing::TestWithParam { StatsMatcherPtr matcher_ptr(matcher); store_.setStatsMatcher(std::move(matcher_ptr)); - EXPECT_CALL(*matcher, rejects("scope.reject")).WillOnce(Return(true)); - EXPECT_CALL(*matcher, rejects("scope.ok")).WillOnce(Return(false)); - + StatNamePool pool(symbol_table_); + StatsMatcher::FastResult no_fast_rejection = StatsMatcher::FastResult::NoMatch; for (int j = 0; j < 5; ++j) { + EXPECT_CALL(*matcher, fastRejects(pool.add("scope.reject"))) + .WillOnce(Return(no_fast_rejection)); + if (j == 0) { + EXPECT_CALL(*matcher, slowRejects(no_fast_rejection, pool.add("scope.reject"))) + .WillOnce(Return(true)); + } EXPECT_EQ("", lookup_stat("reject")); + EXPECT_CALL(*matcher, fastRejects(pool.add("scope.ok"))).WillOnce(Return(no_fast_rejection)); + if (j == 0) { + EXPECT_CALL(*matcher, slowRejects(no_fast_rejection, pool.add("scope.ok"))) + .WillOnce(Return(false)); + } EXPECT_EQ("scope.ok", lookup_stat("ok")); } } @@ -985,8 +1044,10 @@ class RememberStatsMatcherTest : public testing::TestWithParam { ScopePtr scope = store_.createScope("scope."); + StatNamePool pool(symbol_table_); for (int j = 0; j < 5; ++j) { - // Note: zero calls to reject() are made, as reject-all should short-circuit. + // Note: zero calls to fastReject() or slowReject() are made, as + // reject-all should short-circuit. EXPECT_EQ("", lookup_stat("reject")); } } @@ -998,9 +1059,12 @@ class RememberStatsMatcherTest : public testing::TestWithParam { matcher->accepts_all_ = true; StatsMatcherPtr matcher_ptr(matcher); store_.setStatsMatcher(std::move(matcher_ptr)); + StatNamePool pool(symbol_table_); + StatsMatcher::FastResult no_fast_rejection = StatsMatcher::FastResult::NoMatch; for (int j = 0; j < 5; ++j) { - // Note: zero calls to reject() are made, as accept-all should short-circuit. + EXPECT_CALL(*matcher, fastRejects(pool.add("scope.ok"))).WillOnce(Return(no_fast_rejection)); + // Note: zero calls to slowReject() are made, as accept-all should short-circuit. EXPECT_EQ("scope.ok", lookup_stat("ok")); } } @@ -1109,7 +1173,7 @@ TEST_F(StatsThreadLocalStoreTest, RemoveRejectedStats) { envoy::config::metrics::v3::StatsConfig stats_config; stats_config.mutable_stats_matcher()->mutable_inclusion_list()->add_patterns()->set_exact( "no-such-stat"); - store_->setStatsMatcher(std::make_unique(stats_config)); + store_->setStatsMatcher(std::make_unique(stats_config, symbol_table_)); // They can no longer be found. EXPECT_EQ(0, store_->counters().size()); @@ -1183,7 +1247,7 @@ class StatsThreadLocalStoreTestNoFixture : public testing::Test { TEST_F(StatsThreadLocalStoreTestNoFixture, MemoryWithoutTlsRealSymbolTable) { TestUtil::MemoryTest memory_test; TestUtil::forEachSampleStat( - 100, [this](absl::string_view name) { store_.counterFromString(std::string(name)); }); + 100, true, [this](absl::string_view name) { store_.counterFromString(std::string(name)); }); EXPECT_MEMORY_EQ(memory_test.consumedBytes(), 688080); // July 2, 2020 EXPECT_MEMORY_LE(memory_test.consumedBytes(), 0.75 * million_); } @@ -1192,7 +1256,7 @@ TEST_F(StatsThreadLocalStoreTestNoFixture, MemoryWithTlsRealSymbolTable) { initThreading(); TestUtil::MemoryTest memory_test; TestUtil::forEachSampleStat( - 100, [this](absl::string_view name) { store_.counterFromString(std::string(name)); }); + 100, true, [this](absl::string_view name) { store_.counterFromString(std::string(name)); }); EXPECT_MEMORY_EQ(memory_test.consumedBytes(), 827616); // Sep 25, 2020 EXPECT_MEMORY_LE(memory_test.consumedBytes(), 0.9 * million_); } diff --git a/test/common/stream_info/stream_info_impl_test.cc b/test/common/stream_info/stream_info_impl_test.cc index 482490ca7874d..6ff19887f4fc7 100644 --- a/test/common/stream_info/stream_info_impl_test.cc +++ b/test/common/stream_info/stream_info_impl_test.cc @@ -252,14 +252,6 @@ TEST_F(StreamInfoImplTest, DefaultRequestIDExtensionTest) { EXPECT_EQ(nullptr, stream_info.getRequestIDProvider()); } -TEST_F(StreamInfoImplTest, ConnectionID) { - StreamInfoImpl stream_info(test_time_.timeSystem(), nullptr); - EXPECT_FALSE(stream_info.connectionID().has_value()); - uint64_t id = 123; - stream_info.setConnectionID(id); - EXPECT_EQ(id, stream_info.connectionID()); -} - TEST_F(StreamInfoImplTest, Details) { StreamInfoImpl stream_info(test_time_.timeSystem(), nullptr); EXPECT_FALSE(stream_info.responseCodeDetails().has_value()); diff --git a/test/common/stream_info/test_util.h b/test/common/stream_info/test_util.h index 39b95e2b76549..a4ab278e8db3e 100644 --- a/test/common/stream_info/test_util.h +++ b/test/common/stream_info/test_util.h @@ -213,10 +213,6 @@ class TestStreamInfo : public StreamInfo::StreamInfo { return upstream_cluster_info_; } - void setConnectionID(uint64_t id) override { connection_id_ = id; } - - absl::optional connectionID() const override { return connection_id_; } - void setFilterChainName(absl::string_view filter_chain_name) override { filter_chain_name_ = std::string(filter_chain_name); } diff --git a/test/common/thread_local/thread_local_impl_test.cc b/test/common/thread_local/thread_local_impl_test.cc index f6937240803d4..6e2586db580ac 100644 --- a/test/common/thread_local/thread_local_impl_test.cc +++ b/test/common/thread_local/thread_local_impl_test.cc @@ -18,11 +18,13 @@ namespace ThreadLocal { TEST(MainThreadVerificationTest, All) { // Before threading is on, assertion on main thread should be true. EXPECT_TRUE(Thread::MainThread::isMainThread()); + EXPECT_TRUE(Thread::MainThread::isWorkerThread()); { InstanceImpl tls; // Tls instance has been initialized. // Call to main thread verification should succeed in main thread. EXPECT_TRUE(Thread::MainThread::isMainThread()); + EXPECT_FALSE(Thread::MainThread::isWorkerThread()); tls.shutdownGlobalThreading(); tls.shutdownThread(); } diff --git a/test/common/tracing/http_tracer_impl_test.cc b/test/common/tracing/http_tracer_impl_test.cc index 256270dffe70a..e3289d042f3f7 100644 --- a/test/common/tracing/http_tracer_impl_test.cc +++ b/test/common/tracing/http_tracer_impl_test.cc @@ -727,6 +727,7 @@ TEST(HttpNullTracerTest, BasicFunctionality) { ASSERT_EQ("", span_ptr->getBaggage("baggage_key")); ASSERT_EQ(span_ptr->getTraceIdAsHex(), ""); span_ptr->injectContext(request_headers); + span_ptr->log(SystemTime(), "fake_event"); EXPECT_NE(nullptr, span_ptr->spawnChild(config, "foo", SystemTime())); } @@ -829,6 +830,17 @@ TEST_F(HttpTracerImplTest, ChildUpstreamSpanTest) { stream_info_, config_); } +TEST_F(HttpTracerImplTest, MetadataCustomTagReturnsDefaultValue) { + envoy::type::tracing::v3::CustomTag::Metadata testing_metadata; + testing_metadata.mutable_metadata_key()->set_key("key"); + *testing_metadata.mutable_default_value() = "default_value"; + MetadataCustomTag tag("testing", testing_metadata); + StreamInfo::MockStreamInfo testing_info_; + Http::TestRequestHeaderMapImpl header_map_; + CustomTagContext context{&header_map_, testing_info_}; + EXPECT_EQ(tag.value(context), "default_value"); +} + } // namespace } // namespace Tracing } // namespace Envoy diff --git a/test/common/tracing/http_tracer_manager_impl_test.cc b/test/common/tracing/http_tracer_manager_impl_test.cc index a7c564d4eb2f2..00277ce871552 100644 --- a/test/common/tracing/http_tracer_manager_impl_test.cc +++ b/test/common/tracing/http_tracer_manager_impl_test.cc @@ -22,7 +22,7 @@ namespace { class SampleDriver : public Driver { public: - SpanPtr startSpan(const Config&, Http::RequestHeaderMap&, const std::string&, SystemTime, + SpanPtr startSpan(const Config&, Tracing::TraceContext&, const std::string&, SystemTime, const Tracing::Decision) override { return nullptr; } diff --git a/test/common/upstream/load_balancer_impl_test.cc b/test/common/upstream/load_balancer_impl_test.cc index 77b391e6e090b..dd3f8de8d86f5 100644 --- a/test/common/upstream/load_balancer_impl_test.cc +++ b/test/common/upstream/load_balancer_impl_test.cc @@ -574,7 +574,7 @@ class RoundRobinLoadBalancerTest : public LoadBalancerTestBase { std::shared_ptr lb_; HostsPerLocalityConstSharedPtr empty_locality_; HostVector empty_host_vector_; -}; // namespace +}; // For the tests which mutate primary and failover host sets explicitly, only // run once. diff --git a/test/common/upstream/logical_dns_cluster_test.cc b/test/common/upstream/logical_dns_cluster_test.cc index d3c0e88df67a4..bd7f0805a7aca 100644 --- a/test/common/upstream/logical_dns_cluster_test.cc +++ b/test/common/upstream/logical_dns_cluster_test.cc @@ -188,7 +188,7 @@ class LogicalDnsClusterTest : public Event::TestUsingSimulatedTime, public testi logical_host->createConnection(dispatcher_, nullptr, nullptr); // Make sure we cancel. - EXPECT_CALL(active_dns_query_, cancel()); + EXPECT_CALL(active_dns_query_, cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned)); expectResolve(Network::DnsLookupFamily::V4Only, expected_address); resolve_timer_->invokeCallback(); diff --git a/test/common/upstream/upstream_impl_test.cc b/test/common/upstream/upstream_impl_test.cc index 7bb0850425601..00577b8c4df78 100644 --- a/test/common/upstream/upstream_impl_test.cc +++ b/test/common/upstream/upstream_impl_test.cc @@ -506,8 +506,10 @@ TEST_F(StrictDnsClusterImplTest, Basic) { resolver2.expectResolve(*dns_resolver_); resolver2.timer_->invokeCallback(); - EXPECT_CALL(resolver1.active_dns_query_, cancel()); - EXPECT_CALL(resolver2.active_dns_query_, cancel()); + EXPECT_CALL(resolver1.active_dns_query_, + cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned)); + EXPECT_CALL(resolver2.active_dns_query_, + cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned)); } // Verifies that host removal works correctly when hosts are being health checked @@ -903,9 +905,12 @@ TEST_F(StrictDnsClusterImplTest, LoadAssignmentBasic) { resolver3.expectResolve(*dns_resolver_); resolver3.timer_->invokeCallback(); - EXPECT_CALL(resolver1.active_dns_query_, cancel()); - EXPECT_CALL(resolver2.active_dns_query_, cancel()); - EXPECT_CALL(resolver3.active_dns_query_, cancel()); + EXPECT_CALL(resolver1.active_dns_query_, + cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned)); + EXPECT_CALL(resolver2.active_dns_query_, + cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned)); + EXPECT_CALL(resolver3.active_dns_query_, + cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned)); } TEST_F(StrictDnsClusterImplTest, LoadAssignmentBasicMultiplePriorities) { @@ -1042,9 +1047,12 @@ TEST_F(StrictDnsClusterImplTest, LoadAssignmentBasicMultiplePriorities) { resolver3.expectResolve(*dns_resolver_); resolver3.timer_->invokeCallback(); - EXPECT_CALL(resolver1.active_dns_query_, cancel()); - EXPECT_CALL(resolver2.active_dns_query_, cancel()); - EXPECT_CALL(resolver3.active_dns_query_, cancel()); + EXPECT_CALL(resolver1.active_dns_query_, + cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned)); + EXPECT_CALL(resolver2.active_dns_query_, + cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned)); + EXPECT_CALL(resolver3.active_dns_query_, + cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned)); } // Verifies that specifying a custom resolver when using STRICT_DNS fails diff --git a/test/config/utility.cc b/test/config/utility.cc index 10ad584efcbc3..820f2c60d82a8 100644 --- a/test/config/utility.cc +++ b/test/config/utility.cc @@ -807,14 +807,8 @@ void ConfigHelper::finalize(const std::vector& ports) { for (int j = 0; j < listener->filter_chains_size(); ++j) { if (tap_path) { auto* filter_chain = listener->mutable_filter_chains(j); - const bool has_tls = filter_chain->has_hidden_envoy_deprecated_tls_context(); - const Protobuf::Message* tls_config = nullptr; - if (has_tls) { - tls_config = &filter_chain->hidden_envoy_deprecated_tls_context(); - filter_chain->clear_hidden_envoy_deprecated_tls_context(); - } setTapTransportSocket(tap_path.value(), fmt::format("listener_{}_{}", i, j), - *filter_chain->mutable_transport_socket(), tls_config); + *filter_chain->mutable_transport_socket()); } } } @@ -849,14 +843,8 @@ void ConfigHelper::finalize(const std::vector& ports) { } if (tap_path) { - const bool has_tls = cluster->has_hidden_envoy_deprecated_tls_context(); - const Protobuf::Message* tls_config = nullptr; - if (has_tls) { - tls_config = &cluster->hidden_envoy_deprecated_tls_context(); - cluster->clear_hidden_envoy_deprecated_tls_context(); - } setTapTransportSocket(tap_path.value(), absl::StrCat("cluster_", i), - *cluster->mutable_transport_socket(), tls_config); + *cluster->mutable_transport_socket()); } } ASSERT(skip_port_usage_validation_ || port_idx == ports.size() || eds_hosts || @@ -876,17 +864,13 @@ void ConfigHelper::finalize(const std::vector& ports) { finalized_ = true; } -void ConfigHelper::setTapTransportSocket(const std::string& tap_path, const std::string& type, - envoy::config::core::v3::TransportSocket& transport_socket, - const Protobuf::Message* tls_config) { +void ConfigHelper::setTapTransportSocket( + const std::string& tap_path, const std::string& type, + envoy::config::core::v3::TransportSocket& transport_socket) { // Determine inner transport socket. envoy::config::core::v3::TransportSocket inner_transport_socket; if (!transport_socket.name().empty()) { - RELEASE_ASSERT(!tls_config, ""); inner_transport_socket.MergeFrom(transport_socket); - } else if (tls_config) { - inner_transport_socket.set_name("envoy.transport_sockets.tls"); - inner_transport_socket.mutable_typed_config()->PackFrom(*tls_config); } else { inner_transport_socket.set_name("envoy.transport_sockets.raw_buffer"); } diff --git a/test/config/utility.h b/test/config/utility.h index 17d634eb1cafb..1c764f11ee21e 100644 --- a/test/config/utility.h +++ b/test/config/utility.h @@ -374,8 +374,7 @@ class ConfigHelper { // Configure a tap transport socket for a cluster/filter chain. void setTapTransportSocket(const std::string& tap_path, const std::string& type, - envoy::config::core::v3::TransportSocket& transport_socket, - const Protobuf::Message* tls_config); + envoy::config::core::v3::TransportSocket& transport_socket); // The bootstrap proto Envoy will start up with. envoy::config::bootstrap::v3::Bootstrap bootstrap_; diff --git a/test/extensions/clusters/dynamic_forward_proxy/cluster_test.cc b/test/extensions/clusters/dynamic_forward_proxy/cluster_test.cc index 2f0550321a85a..595be9f596d86 100644 --- a/test/extensions/clusters/dynamic_forward_proxy/cluster_test.cc +++ b/test/extensions/clusters/dynamic_forward_proxy/cluster_test.cc @@ -234,30 +234,6 @@ class ClusterFactoryTest : public testing::Test { Server::MockOptions options_; }; -// Verify that using 'sni' causes a failure. -TEST_F(ClusterFactoryTest, DEPRECATED_FEATURE_TEST(InvalidSNI)) { - TestDeprecatedV2Api _deprecated_v2_api; - const std::string yaml_config = TestEnvironment::substitute(R"EOF( -name: name -connect_timeout: 0.25s -cluster_type: - name: dynamic_forward_proxy - typed_config: - "@type": type.googleapis.com/envoy.config.cluster.dynamic_forward_proxy.v2alpha.ClusterConfig - dns_cache_config: - name: foo -tls_context: - sni: api.lyft.com - common_tls_context: - validation_context: - trusted_ca: - filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" -)EOF"); - - EXPECT_THROW_WITH_MESSAGE(createCluster(yaml_config, false), EnvoyException, - "dynamic_forward_proxy cluster cannot configure 'sni'"); -} - TEST_F(ClusterFactoryTest, InvalidUpstreamHttpProtocolOptions) { const std::string yaml_config = TestEnvironment::substitute(R"EOF( name: name diff --git a/test/extensions/clusters/redis/redis_cluster_test.cc b/test/extensions/clusters/redis/redis_cluster_test.cc index 4bc85dd7150fe..526c10a3039f2 100644 --- a/test/extensions/clusters/redis/redis_cluster_test.cc +++ b/test/extensions/clusters/redis/redis_cluster_test.cc @@ -544,10 +544,9 @@ class RedisClusterTest : public testing::Test, Network::DnsResolver::ResolveCb) -> Network::ActiveDnsQuery* { return &active_dns_query_; })); - ; resolver_target.startResolveDns(); - EXPECT_CALL(active_dns_query_, cancel()); + EXPECT_CALL(active_dns_query_, cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned)); } Stats::TestUtil::TestStore stats_store_; diff --git a/test/extensions/common/dynamic_forward_proxy/dns_cache_impl_test.cc b/test/extensions/common/dynamic_forward_proxy/dns_cache_impl_test.cc index 215e391d5fb63..397b87bc1adb2 100644 --- a/test/extensions/common/dynamic_forward_proxy/dns_cache_impl_test.cc +++ b/test/extensions/common/dynamic_forward_proxy/dns_cache_impl_test.cc @@ -142,6 +142,8 @@ TEST_F(DnsCacheImplTest, ResolveSuccess) { MockLoadDnsCacheEntryCallbacks callbacks; Network::DnsResolver::ResolveCb resolve_cb; Event::MockTimer* resolve_timer = new Event::MockTimer(&dispatcher_); + Event::MockTimer* timeout_timer = new Event::MockTimer(&dispatcher_); + EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(5000), nullptr)); EXPECT_CALL(*resolver_, resolve("foo.com", _, _)) .WillOnce(DoAll(SaveArg<2>(&resolve_cb), Return(&resolver_->active_query_))); auto result = dns_cache_->loadDnsCacheEntry("foo.com", 80, callbacks); @@ -152,6 +154,7 @@ TEST_F(DnsCacheImplTest, ResolveSuccess) { checkStats(1 /* attempt */, 0 /* success */, 0 /* failure */, 0 /* address changed */, 1 /* added */, 0 /* removed */, 1 /* num hosts */); + EXPECT_CALL(*timeout_timer, disableTimer()); EXPECT_CALL(update_callbacks_, onDnsHostAddOrUpdate("foo.com", DnsHostInfoEquals("10.0.0.1:80", "foo.com", false))); EXPECT_CALL(callbacks, @@ -164,6 +167,7 @@ TEST_F(DnsCacheImplTest, ResolveSuccess) { 1 /* added */, 0 /* removed */, 1 /* num hosts */); // Re-resolve timer. + EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(5000), nullptr)); EXPECT_CALL(*resolver_, resolve("foo.com", _, _)) .WillOnce(DoAll(SaveArg<2>(&resolve_cb), Return(&resolver_->active_query_))); resolve_timer->invokeCallback(); @@ -172,6 +176,7 @@ TEST_F(DnsCacheImplTest, ResolveSuccess) { 1 /* added */, 0 /* removed */, 1 /* num hosts */); // Address does not change. + EXPECT_CALL(*timeout_timer, disableTimer()); EXPECT_CALL(*resolve_timer, enableTimer(std::chrono::milliseconds(60000), _)); resolve_cb(Network::DnsResolver::ResolutionStatus::Success, TestUtility::makeDnsResponse({"10.0.0.1"})); @@ -180,6 +185,7 @@ TEST_F(DnsCacheImplTest, ResolveSuccess) { 1 /* added */, 0 /* removed */, 1 /* num hosts */); // Re-resolve timer. + EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(5000), nullptr)); EXPECT_CALL(*resolver_, resolve("foo.com", _, _)) .WillOnce(DoAll(SaveArg<2>(&resolve_cb), Return(&resolver_->active_query_))); resolve_timer->invokeCallback(); @@ -188,6 +194,7 @@ TEST_F(DnsCacheImplTest, ResolveSuccess) { 1 /* added */, 0 /* removed */, 1 /* num hosts */); // Address does change. + EXPECT_CALL(*timeout_timer, disableTimer()); EXPECT_CALL(update_callbacks_, onDnsHostAddOrUpdate("foo.com", DnsHostInfoEquals("10.0.0.2:80", "foo.com", false))); EXPECT_CALL(*resolve_timer, enableTimer(std::chrono::milliseconds(60000), _)); @@ -206,6 +213,8 @@ TEST_F(DnsCacheImplTest, Ipv4Address) { MockLoadDnsCacheEntryCallbacks callbacks; Network::DnsResolver::ResolveCb resolve_cb; Event::MockTimer* resolve_timer = new Event::MockTimer(&dispatcher_); + Event::MockTimer* timeout_timer = new Event::MockTimer(&dispatcher_); + EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(5000), nullptr)); EXPECT_CALL(*resolver_, resolve("127.0.0.1", _, _)) .WillOnce(DoAll(SaveArg<2>(&resolve_cb), Return(&resolver_->active_query_))); auto result = dns_cache_->loadDnsCacheEntry("127.0.0.1", 80, callbacks); @@ -213,6 +222,7 @@ TEST_F(DnsCacheImplTest, Ipv4Address) { EXPECT_NE(result.handle_, nullptr); EXPECT_EQ(absl::nullopt, result.host_info_); + EXPECT_CALL(*timeout_timer, disableTimer()); EXPECT_CALL( update_callbacks_, onDnsHostAddOrUpdate("127.0.0.1", DnsHostInfoEquals("127.0.0.1:80", "127.0.0.1", true))); @@ -231,6 +241,8 @@ TEST_F(DnsCacheImplTest, Ipv4AddressWithPort) { MockLoadDnsCacheEntryCallbacks callbacks; Network::DnsResolver::ResolveCb resolve_cb; Event::MockTimer* resolve_timer = new Event::MockTimer(&dispatcher_); + Event::MockTimer* timeout_timer = new Event::MockTimer(&dispatcher_); + EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(5000), nullptr)); EXPECT_CALL(*resolver_, resolve("127.0.0.1", _, _)) .WillOnce(DoAll(SaveArg<2>(&resolve_cb), Return(&resolver_->active_query_))); auto result = dns_cache_->loadDnsCacheEntry("127.0.0.1:10000", 80, callbacks); @@ -238,6 +250,7 @@ TEST_F(DnsCacheImplTest, Ipv4AddressWithPort) { EXPECT_NE(result.handle_, nullptr); EXPECT_EQ(absl::nullopt, result.host_info_); + EXPECT_CALL(*timeout_timer, disableTimer()); EXPECT_CALL(update_callbacks_, onDnsHostAddOrUpdate("127.0.0.1:10000", DnsHostInfoEquals("127.0.0.1:10000", "127.0.0.1", true))); @@ -256,6 +269,8 @@ TEST_F(DnsCacheImplTest, Ipv6Address) { MockLoadDnsCacheEntryCallbacks callbacks; Network::DnsResolver::ResolveCb resolve_cb; Event::MockTimer* resolve_timer = new Event::MockTimer(&dispatcher_); + Event::MockTimer* timeout_timer = new Event::MockTimer(&dispatcher_); + EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(5000), nullptr)); EXPECT_CALL(*resolver_, resolve("::1", _, _)) .WillOnce(DoAll(SaveArg<2>(&resolve_cb), Return(&resolver_->active_query_))); auto result = dns_cache_->loadDnsCacheEntry("[::1]", 80, callbacks); @@ -263,6 +278,7 @@ TEST_F(DnsCacheImplTest, Ipv6Address) { EXPECT_NE(result.handle_, nullptr); EXPECT_EQ(absl::nullopt, result.host_info_); + EXPECT_CALL(*timeout_timer, disableTimer()); EXPECT_CALL(update_callbacks_, onDnsHostAddOrUpdate("[::1]", DnsHostInfoEquals("[::1]:80", "::1", true))); EXPECT_CALL(callbacks, onLoadDnsCacheComplete(DnsHostInfoEquals("[::1]:80", "::1", true))); @@ -279,6 +295,8 @@ TEST_F(DnsCacheImplTest, Ipv6AddressWithPort) { MockLoadDnsCacheEntryCallbacks callbacks; Network::DnsResolver::ResolveCb resolve_cb; Event::MockTimer* resolve_timer = new Event::MockTimer(&dispatcher_); + Event::MockTimer* timeout_timer = new Event::MockTimer(&dispatcher_); + EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(5000), nullptr)); EXPECT_CALL(*resolver_, resolve("::1", _, _)) .WillOnce(DoAll(SaveArg<2>(&resolve_cb), Return(&resolver_->active_query_))); auto result = dns_cache_->loadDnsCacheEntry("[::1]:10000", 80, callbacks); @@ -286,6 +304,7 @@ TEST_F(DnsCacheImplTest, Ipv6AddressWithPort) { EXPECT_NE(result.handle_, nullptr); EXPECT_EQ(absl::nullopt, result.host_info_); + EXPECT_CALL(*timeout_timer, disableTimer()); EXPECT_CALL(update_callbacks_, onDnsHostAddOrUpdate("[::1]:10000", DnsHostInfoEquals("[::1]:10000", "::1", true))); EXPECT_CALL(callbacks, onLoadDnsCacheComplete(DnsHostInfoEquals("[::1]:10000", "::1", true))); @@ -302,6 +321,8 @@ TEST_F(DnsCacheImplTest, TTL) { MockLoadDnsCacheEntryCallbacks callbacks; Network::DnsResolver::ResolveCb resolve_cb; Event::MockTimer* resolve_timer = new Event::MockTimer(&dispatcher_); + Event::MockTimer* timeout_timer = new Event::MockTimer(&dispatcher_); + EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(5000), nullptr)); EXPECT_CALL(*resolver_, resolve("foo.com", _, _)) .WillOnce(DoAll(SaveArg<2>(&resolve_cb), Return(&resolver_->active_query_))); auto result = dns_cache_->loadDnsCacheEntry("foo.com", 80, callbacks); @@ -312,6 +333,7 @@ TEST_F(DnsCacheImplTest, TTL) { checkStats(1 /* attempt */, 0 /* success */, 0 /* failure */, 0 /* address changed */, 1 /* added */, 0 /* removed */, 1 /* num hosts */); + EXPECT_CALL(*timeout_timer, disableTimer()); EXPECT_CALL(update_callbacks_, onDnsHostAddOrUpdate("foo.com", DnsHostInfoEquals("10.0.0.1:80", "foo.com", false))); EXPECT_CALL(callbacks, @@ -325,12 +347,14 @@ TEST_F(DnsCacheImplTest, TTL) { // Re-resolve with ~60s passed. TTL should still be OK at default of 5 minutes. simTime().advanceTimeWait(std::chrono::milliseconds(60001)); + EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(5000), nullptr)); EXPECT_CALL(*resolver_, resolve("foo.com", _, _)) .WillOnce(DoAll(SaveArg<2>(&resolve_cb), Return(&resolver_->active_query_))); resolve_timer->invokeCallback(); checkStats(2 /* attempt */, 1 /* success */, 0 /* failure */, 1 /* address changed */, 1 /* added */, 0 /* removed */, 1 /* num hosts */); + EXPECT_CALL(*timeout_timer, disableTimer()); EXPECT_CALL(*resolve_timer, enableTimer(std::chrono::milliseconds(60000), _)); resolve_cb(Network::DnsResolver::ResolutionStatus::Success, TestUtility::makeDnsResponse({"10.0.0.1"})); @@ -347,6 +371,8 @@ TEST_F(DnsCacheImplTest, TTL) { // Make sure we don't get a cache hit the next time the host is requested. resolve_timer = new Event::MockTimer(&dispatcher_); + timeout_timer = new Event::MockTimer(&dispatcher_); + EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(5000), nullptr)); EXPECT_CALL(*resolver_, resolve("foo.com", _, _)) .WillOnce(DoAll(SaveArg<2>(&resolve_cb), Return(&resolver_->active_query_))); result = dns_cache_->loadDnsCacheEntry("foo.com", 80, callbacks); @@ -361,12 +387,15 @@ TEST_F(DnsCacheImplTest, TTL) { TEST_F(DnsCacheImplTest, TTLWithCustomParameters) { *config_.mutable_dns_refresh_rate() = Protobuf::util::TimeUtil::SecondsToDuration(30); *config_.mutable_host_ttl() = Protobuf::util::TimeUtil::SecondsToDuration(60); + *config_.mutable_dns_query_timeout() = Protobuf::util::TimeUtil::SecondsToDuration(1); initialize(); InSequence s; MockLoadDnsCacheEntryCallbacks callbacks; Network::DnsResolver::ResolveCb resolve_cb; Event::MockTimer* resolve_timer = new Event::MockTimer(&dispatcher_); + Event::MockTimer* timeout_timer = new Event::MockTimer(&dispatcher_); + EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(1000), nullptr)); EXPECT_CALL(*resolver_, resolve("foo.com", _, _)) .WillOnce(DoAll(SaveArg<2>(&resolve_cb), Return(&resolver_->active_query_))); auto result = dns_cache_->loadDnsCacheEntry("foo.com", 80, callbacks); @@ -374,6 +403,7 @@ TEST_F(DnsCacheImplTest, TTLWithCustomParameters) { EXPECT_NE(result.handle_, nullptr); EXPECT_EQ(absl::nullopt, result.host_info_); + EXPECT_CALL(*timeout_timer, disableTimer()); EXPECT_CALL(update_callbacks_, onDnsHostAddOrUpdate("foo.com", DnsHostInfoEquals("10.0.0.1:80", "foo.com", false))); EXPECT_CALL(callbacks, @@ -384,9 +414,11 @@ TEST_F(DnsCacheImplTest, TTLWithCustomParameters) { // Re-resolve with ~30s passed. TTL should still be OK at 60s. simTime().advanceTimeWait(std::chrono::milliseconds(30001)); + EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(1000), nullptr)); EXPECT_CALL(*resolver_, resolve("foo.com", _, _)) .WillOnce(DoAll(SaveArg<2>(&resolve_cb), Return(&resolver_->active_query_))); resolve_timer->invokeCallback(); + EXPECT_CALL(*timeout_timer, disableTimer()); EXPECT_CALL(*resolve_timer, enableTimer(std::chrono::milliseconds(30000), _)); resolve_cb(Network::DnsResolver::ResolutionStatus::Success, TestUtility::makeDnsResponse({"10.0.0.1"})); @@ -411,6 +443,8 @@ TEST_F(DnsCacheImplTest, InlineResolve) { EXPECT_EQ(absl::nullopt, result.host_info_); Event::MockTimer* resolve_timer = new Event::MockTimer(&dispatcher_); + Event::MockTimer* timeout_timer = new Event::MockTimer(&dispatcher_); + EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(5000), nullptr)); EXPECT_CALL(*resolver_, resolve("localhost", _, _)) .WillOnce(Invoke([](const std::string&, Network::DnsLookupFamily, Network::DnsResolver::ResolveCb callback) { @@ -418,6 +452,7 @@ TEST_F(DnsCacheImplTest, InlineResolve) { TestUtility::makeDnsResponse({"127.0.0.1"})); return nullptr; })); + EXPECT_CALL(*timeout_timer, disableTimer()); EXPECT_CALL( update_callbacks_, onDnsHostAddOrUpdate("localhost", DnsHostInfoEquals("127.0.0.1:80", "localhost", false))); @@ -427,6 +462,36 @@ TEST_F(DnsCacheImplTest, InlineResolve) { post_cb(); } +// Resolve timeout. +TEST_F(DnsCacheImplTest, ResolveTimeout) { + initialize(); + InSequence s; + + MockLoadDnsCacheEntryCallbacks callbacks; + Network::DnsResolver::ResolveCb resolve_cb; + Event::MockTimer* resolve_timer = new Event::MockTimer(&dispatcher_); + Event::MockTimer* timeout_timer = new Event::MockTimer(&dispatcher_); + EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(5000), nullptr)); + EXPECT_CALL(*resolver_, resolve("foo.com", _, _)) + .WillOnce(DoAll(SaveArg<2>(&resolve_cb), Return(&resolver_->active_query_))); + auto result = dns_cache_->loadDnsCacheEntry("foo.com", 80, callbacks); + EXPECT_EQ(DnsCache::LoadDnsCacheEntryStatus::Loading, result.status_); + EXPECT_NE(result.handle_, nullptr); + EXPECT_EQ(absl::nullopt, result.host_info_); + checkStats(1 /* attempt */, 0 /* success */, 0 /* failure */, 0 /* address changed */, + 1 /* added */, 0 /* removed */, 1 /* num hosts */); + + EXPECT_CALL(resolver_->active_query_, cancel(Network::ActiveDnsQuery::CancelReason::Timeout)); + EXPECT_CALL(*timeout_timer, disableTimer()); + EXPECT_CALL(update_callbacks_, onDnsHostAddOrUpdate(_, _)).Times(0); + EXPECT_CALL(callbacks, onLoadDnsCacheComplete(DnsHostInfoAddressIsNull())); + EXPECT_CALL(*resolve_timer, enableTimer(std::chrono::milliseconds(60000), _)); + timeout_timer->invokeCallback(); + checkStats(1 /* attempt */, 0 /* success */, 1 /* failure */, 0 /* address changed */, + 1 /* added */, 0 /* removed */, 1 /* num hosts */); + EXPECT_EQ(1, TestUtility::findCounter(store_, "dns_cache.foo.dns_query_timeout")->value()); +} + // Resolve failure that returns no addresses. TEST_F(DnsCacheImplTest, ResolveFailure) { initialize(); @@ -435,6 +500,8 @@ TEST_F(DnsCacheImplTest, ResolveFailure) { MockLoadDnsCacheEntryCallbacks callbacks; Network::DnsResolver::ResolveCb resolve_cb; Event::MockTimer* resolve_timer = new Event::MockTimer(&dispatcher_); + Event::MockTimer* timeout_timer = new Event::MockTimer(&dispatcher_); + EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(5000), nullptr)); EXPECT_CALL(*resolver_, resolve("foo.com", _, _)) .WillOnce(DoAll(SaveArg<2>(&resolve_cb), Return(&resolver_->active_query_))); auto result = dns_cache_->loadDnsCacheEntry("foo.com", 80, callbacks); @@ -444,6 +511,7 @@ TEST_F(DnsCacheImplTest, ResolveFailure) { checkStats(1 /* attempt */, 0 /* success */, 0 /* failure */, 0 /* address changed */, 1 /* added */, 0 /* removed */, 1 /* num hosts */); + EXPECT_CALL(*timeout_timer, disableTimer()); EXPECT_CALL(update_callbacks_, onDnsHostAddOrUpdate(_, _)).Times(0); EXPECT_CALL(callbacks, onLoadDnsCacheComplete(DnsHostInfoAddressIsNull())); EXPECT_CALL(*resolve_timer, enableTimer(std::chrono::milliseconds(60000), _)); @@ -481,6 +549,8 @@ TEST_F(DnsCacheImplTest, ResolveFailureWithFailureRefreshRate) { MockLoadDnsCacheEntryCallbacks callbacks; Network::DnsResolver::ResolveCb resolve_cb; Event::MockTimer* resolve_timer = new Event::MockTimer(&dispatcher_); + Event::MockTimer* timeout_timer = new Event::MockTimer(&dispatcher_); + EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(5000), nullptr)); EXPECT_CALL(*resolver_, resolve("foo.com", _, _)) .WillOnce(DoAll(SaveArg<2>(&resolve_cb), Return(&resolver_->active_query_))); auto result = dns_cache_->loadDnsCacheEntry("foo.com", 80, callbacks); @@ -490,6 +560,7 @@ TEST_F(DnsCacheImplTest, ResolveFailureWithFailureRefreshRate) { checkStats(1 /* attempt */, 0 /* success */, 0 /* failure */, 0 /* address changed */, 1 /* added */, 0 /* removed */, 1 /* num hosts */); + EXPECT_CALL(*timeout_timer, disableTimer()); EXPECT_CALL(update_callbacks_, onDnsHostAddOrUpdate(_, _)).Times(0); EXPECT_CALL(callbacks, onLoadDnsCacheComplete(DnsHostInfoAddressIsNull())); ON_CALL(random_, random()).WillByDefault(Return(8000)); @@ -524,6 +595,8 @@ TEST_F(DnsCacheImplTest, ResolveSuccessWithEmptyResult) { MockLoadDnsCacheEntryCallbacks callbacks; Network::DnsResolver::ResolveCb resolve_cb; Event::MockTimer* resolve_timer = new Event::MockTimer(&dispatcher_); + Event::MockTimer* timeout_timer = new Event::MockTimer(&dispatcher_); + EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(5000), nullptr)); EXPECT_CALL(*resolver_, resolve("foo.com", _, _)) .WillOnce(DoAll(SaveArg<2>(&resolve_cb), Return(&resolver_->active_query_))); auto result = dns_cache_->loadDnsCacheEntry("foo.com", 80, callbacks); @@ -534,6 +607,7 @@ TEST_F(DnsCacheImplTest, ResolveSuccessWithEmptyResult) { 1 /* added */, 0 /* removed */, 1 /* num hosts */); // A successful empty resolution DOES NOT update the host information. + EXPECT_CALL(*timeout_timer, disableTimer()); EXPECT_CALL(update_callbacks_, onDnsHostAddOrUpdate(_, _)).Times(0); EXPECT_CALL(callbacks, onLoadDnsCacheComplete(DnsHostInfoAddressIsNull())); EXPECT_CALL(*resolve_timer, enableTimer(std::chrono::milliseconds(60000), _)); @@ -709,7 +783,8 @@ TEST_F(DnsCacheImplTest, CancelActiveQueriesOnDestroy) { EXPECT_NE(result.handle_, nullptr); EXPECT_EQ(absl::nullopt, result.host_info_); - EXPECT_CALL(resolver_->active_query_, cancel()); + EXPECT_CALL(resolver_->active_query_, + cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned)); dns_cache_.reset(); } diff --git a/test/extensions/filters/common/expr/context_test.cc b/test/extensions/filters/common/expr/context_test.cc index 79475ef353c70..f7b1931195e46 100644 --- a/test/extensions/filters/common/expr/context_test.cc +++ b/test/extensions/filters/common/expr/context_test.cc @@ -449,6 +449,7 @@ TEST(Context, ConnectionAttributes) { EXPECT_CALL(info, upstreamTransportFailureReason()) .WillRepeatedly(ReturnRef(upstream_transport_failure_reason)); EXPECT_CALL(info, connectionID()).WillRepeatedly(Return(123)); + info.downstream_address_provider_->setConnectionID(123); const absl::optional connection_termination_details = "unauthorized"; EXPECT_CALL(info, connectionTerminationDetails()) .WillRepeatedly(ReturnRef(connection_termination_details)); diff --git a/test/extensions/filters/http/jwt_authn/filter_integration_test.cc b/test/extensions/filters/http/jwt_authn/filter_integration_test.cc index 59b937dd8095f..9ea2b6a372409 100644 --- a/test/extensions/filters/http/jwt_authn/filter_integration_test.cc +++ b/test/extensions/filters/http/jwt_authn/filter_integration_test.cc @@ -21,7 +21,6 @@ namespace JwtAuthn { namespace { const char HeaderToFilterStateFilterName[] = "envoy.filters.http.header_to_filter_state_for_test"; - // This filter extracts a string header from "header" and // save it into FilterState as name "state" as read-only Router::StringAccessor. class HeaderToFilterStateFilter : public Http::PassThroughDecoderFilter { @@ -152,6 +151,10 @@ TEST_P(LocalJwksIntegrationTest, ExpiredToken) { ASSERT_TRUE(response->waitForEndStream()); ASSERT_TRUE(response->complete()); EXPECT_EQ("401", response->headers().getStatusValue()); + EXPECT_EQ(1, response->headers().get(Http::Headers::get().WWWAuthenticate).size()); + EXPECT_EQ( + "Bearer realm=\"http://host/\", error=\"invalid_token\"", + response->headers().get(Http::Headers::get().WWWAuthenticate)[0]->value().getStringView()); } TEST_P(LocalJwksIntegrationTest, MissingToken) { @@ -170,6 +173,9 @@ TEST_P(LocalJwksIntegrationTest, MissingToken) { ASSERT_TRUE(response->waitForEndStream()); ASSERT_TRUE(response->complete()); EXPECT_EQ("401", response->headers().getStatusValue()); + EXPECT_EQ( + "Bearer realm=\"http://host/\"", + response->headers().get(Http::Headers::get().WWWAuthenticate)[0]->value().getStringView()); } TEST_P(LocalJwksIntegrationTest, ExpiredTokenHeadReply) { @@ -189,6 +195,10 @@ TEST_P(LocalJwksIntegrationTest, ExpiredTokenHeadReply) { ASSERT_TRUE(response->waitForEndStream()); ASSERT_TRUE(response->complete()); EXPECT_EQ("401", response->headers().getStatusValue()); + EXPECT_EQ( + "Bearer realm=\"http://host/\", error=\"invalid_token\"", + response->headers().get(Http::Headers::get().WWWAuthenticate)[0]->value().getStringView()); + EXPECT_NE("0", response->headers().getContentLengthValue()); EXPECT_THAT(response->body(), ::testing::IsEmpty()); } @@ -448,6 +458,9 @@ TEST_P(RemoteJwksIntegrationTest, FetchFailedJwks) { ASSERT_TRUE(response->waitForEndStream()); ASSERT_TRUE(response->complete()); EXPECT_EQ("401", response->headers().getStatusValue()); + EXPECT_EQ( + "Bearer realm=\"http://host/\", error=\"invalid_token\"", + response->headers().get(Http::Headers::get().WWWAuthenticate)[0]->value().getStringView()); cleanup(); } @@ -468,7 +481,9 @@ TEST_P(RemoteJwksIntegrationTest, FetchFailedMissingCluster) { ASSERT_TRUE(response->waitForEndStream()); ASSERT_TRUE(response->complete()); EXPECT_EQ("401", response->headers().getStatusValue()); - + EXPECT_EQ( + "Bearer realm=\"http://host/\", error=\"invalid_token\"", + response->headers().get(Http::Headers::get().WWWAuthenticate)[0]->value().getStringView()); cleanup(); } diff --git a/test/extensions/filters/listener/http_inspector/http_inspector_test.cc b/test/extensions/filters/listener/http_inspector/http_inspector_test.cc index 1104855f41060..8499c8e3428d4 100644 --- a/test/extensions/filters/listener/http_inspector/http_inspector_test.cc +++ b/test/extensions/filters/listener/http_inspector/http_inspector_test.cc @@ -31,20 +31,7 @@ class HttpInspectorTest : public testing::Test { HttpInspectorTest() : cfg_(std::make_shared(store_)), io_handle_(std::make_unique(42)) {} - ~HttpInspectorTest() override { - filter_.reset(); - EXPECT_CALL(dispatcher_, - createFileEvent_(_, _, Event::PlatformDefaultTriggerType, - Event::FileReadyType::Read | Event::FileReadyType::Closed)) - .WillOnce(ReturnNew>()); - // This is used to test the FileEvent was reset by the listener filters. - // Otherwise the assertion inside `initializeFileEvent` will be trigger. - io_handle_->initializeFileEvent( - dispatcher_, [](uint32_t) -> void {}, Event::PlatformDefaultTriggerType, - Event::FileReadyType::Read | Event::FileReadyType::Closed); - io_handle_->resetFileEvents(); - io_handle_->close(); - } + ~HttpInspectorTest() override { io_handle_->close(); } void init(bool include_inline_recv = true) { filter_ = std::make_unique(cfg_); diff --git a/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc b/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc index b02c80e6b07c0..ff3082ed5d042 100644 --- a/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc +++ b/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc @@ -53,7 +53,7 @@ class ProxyProtocolTest : public testing::TestWithParam { public: ProxyProtocolTest() - : api_(Api::createApiForTest(stats_store_, time_system_)), + : api_(Api::createApiForTest(stats_store_)), dispatcher_(api_->allocateDispatcher("test_thread")), socket_(std::make_shared( Network::Test::getCanonicalLoopbackAddress(GetParam()), nullptr, true)), @@ -78,10 +78,8 @@ class ProxyProtocolTest : public testing::TestWithParamaddressProvider().remoteAddress()->ip()->addressAsString(), - "127.0.0.1"); - } else { - EXPECT_EQ(server_connection_->addressProvider().remoteAddress()->ip()->addressAsString(), - "::1"); - } - EXPECT_EQ(stats_store_.counter("downstream_cx_total").value(), 1); - disconnect(); -} - TEST_P(ProxyProtocolTest, V1Basic) { connect(); write("PROXY TCP4 1.2.3.4 253.253.253.253 65535 1234\r\nmore data"); diff --git a/test/extensions/filters/listener/tls_inspector/tls_inspector_test.cc b/test/extensions/filters/listener/tls_inspector/tls_inspector_test.cc index bb152489a569b..6e119c87dcd92 100644 --- a/test/extensions/filters/listener/tls_inspector/tls_inspector_test.cc +++ b/test/extensions/filters/listener/tls_inspector/tls_inspector_test.cc @@ -32,20 +32,7 @@ class TlsInspectorTest : public testing::TestWithParam(store_)), io_handle_(std::make_unique(42)) {} - ~TlsInspectorTest() override { - filter_.reset(); - EXPECT_CALL(dispatcher_, - createFileEvent_(_, _, Event::PlatformDefaultTriggerType, - Event::FileReadyType::Read | Event::FileReadyType::Closed)) - .WillOnce(ReturnNew>()); - // This is used to test the FileEvent was reset by the listener filters. - // Otherwise the assertion inside `initializeFileEvent` will be trigger. - io_handle_->initializeFileEvent( - dispatcher_, [](uint32_t) -> void {}, Event::PlatformDefaultTriggerType, - Event::FileReadyType::Read | Event::FileReadyType::Closed); - io_handle_->resetFileEvents(); - io_handle_->close(); - } + ~TlsInspectorTest() override { io_handle_->close(); } void init() { filter_ = std::make_unique(cfg_); diff --git a/test/extensions/filters/network/http_connection_manager/config_test.cc b/test/extensions/filters/network/http_connection_manager/config_test.cc index 4bdd11bd6695b..ce69404c0fb55 100644 --- a/test/extensions/filters/network/http_connection_manager/config_test.cc +++ b/test/extensions/filters/network/http_connection_manager/config_test.cc @@ -892,6 +892,27 @@ TEST_F(HttpConnectionManagerConfigTest, ServerPassThrough) { config.serverHeaderTransformation()); } +TEST_F(HttpConnectionManagerConfigTest, SchemeOverwrite) { + const std::string yaml_string = R"EOF( + stat_prefix: ingress_http + scheme_header_transformation: + scheme_to_overwrite: http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.router + )EOF"; + + EXPECT_CALL(context_.runtime_loader_.snapshot_, featureEnabled(_, An())) + .WillRepeatedly(Invoke(&context_.runtime_loader_.snapshot_, + &Runtime::MockSnapshot::featureEnabledDefault)); + HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, + date_provider_, route_config_provider_manager_, + scoped_routes_config_provider_manager_, http_tracer_manager_, + filter_config_provider_manager_); + EXPECT_EQ(config.schemeToSet(), "http"); +} + // Validated that by default we don't normalize paths // unless set build flag path_normalization_by_default=true TEST_F(HttpConnectionManagerConfigTest, NormalizePathDefault) { @@ -1712,7 +1733,7 @@ class TestRequestIDExtension : public Http::RequestIDExtension { return Tracing::Reason::Sampling; } void setTraceReason(Http::RequestHeaderMap&, Tracing::Reason) override {} - + bool useRequestIdForTraceSampling() const override { return true; } std::string testField() { return config_.test_field(); } private: @@ -1827,6 +1848,7 @@ TEST_F(HttpConnectionManagerConfigTest, DefaultRequestIDExtension) { config.requestIDExtension().get()); ASSERT_NE(nullptr, request_id_extension); EXPECT_TRUE(request_id_extension->packTraceReason()); + EXPECT_EQ(request_id_extension->useRequestIdForTraceSampling(), true); } TEST_F(HttpConnectionManagerConfigTest, DefaultRequestIDExtensionWithParams) { @@ -1838,6 +1860,7 @@ TEST_F(HttpConnectionManagerConfigTest, DefaultRequestIDExtensionWithParams) { typed_config: "@type": type.googleapis.com/envoy.extensions.request_id.uuid.v3.UuidRequestIdConfig pack_trace_reason: false + use_request_id_for_trace_sampling: false http_filters: - name: envoy.filters.http.router )EOF"; @@ -1850,6 +1873,7 @@ TEST_F(HttpConnectionManagerConfigTest, DefaultRequestIDExtensionWithParams) { config.requestIDExtension().get()); ASSERT_NE(nullptr, request_id_extension); EXPECT_FALSE(request_id_extension->packTraceReason()); + EXPECT_EQ(request_id_extension->useRequestIdForTraceSampling(), false); } TEST_F(HttpConnectionManagerConfigTest, UnknownOriginalIPDetectionExtension) { diff --git a/test/extensions/filters/network/wasm/BUILD b/test/extensions/filters/network/wasm/BUILD index 2d4b634840909..f0e84376871f1 100644 --- a/test/extensions/filters/network/wasm/BUILD +++ b/test/extensions/filters/network/wasm/BUILD @@ -18,6 +18,7 @@ envoy_package() envoy_extension_cc_test( name = "config_test", + size = "enormous", srcs = ["config_test.cc"], data = envoy_select_wasm_cpp_tests([ "//test/extensions/filters/network/wasm/test_data:test_cpp.wasm", @@ -40,7 +41,7 @@ envoy_extension_cc_test( envoy_extension_cc_test( name = "wasm_filter_test", - size = "large", + size = "enormous", srcs = ["wasm_filter_test.cc"], data = envoy_select_wasm_cpp_tests([ "//test/extensions/filters/network/wasm/test_data:test_cpp.wasm", diff --git a/test/extensions/matching/common_inputs/environment_variable/config_test.cc b/test/extensions/matching/common_inputs/environment_variable/config_test.cc index 86c66037780d2..94dd3d0bea242 100644 --- a/test/extensions/matching/common_inputs/environment_variable/config_test.cc +++ b/test/extensions/matching/common_inputs/environment_variable/config_test.cc @@ -13,8 +13,6 @@ namespace CommonInputs { namespace EnvironmentVariable { TEST(ConfigTest, TestConfig) { - NiceMock context; - const std::string yaml_string = R"EOF( name: hashing typed_config: @@ -30,14 +28,16 @@ TEST(ConfigTest, TestConfig) { config.typed_config(), ProtobufMessage::getStrictValidationVisitor(), factory); { - auto input_factory = factory.createCommonProtocolInputFactoryCb(*message, context); + auto input_factory = factory.createCommonProtocolInputFactoryCb( + *message, ProtobufMessage::getStrictValidationVisitor()); EXPECT_NE(nullptr, input_factory); EXPECT_EQ(input_factory()->get(), absl::nullopt); } TestEnvironment::setEnvVar("foo", "bar", 1); { - auto input_factory = factory.createCommonProtocolInputFactoryCb(*message, context); + auto input_factory = factory.createCommonProtocolInputFactoryCb( + *message, ProtobufMessage::getStrictValidationVisitor()); EXPECT_NE(nullptr, input_factory); EXPECT_EQ(input_factory()->get(), absl::make_optional("bar")); } diff --git a/test/extensions/matching/input_matchers/consistent_hashing/config_test.cc b/test/extensions/matching/input_matchers/consistent_hashing/config_test.cc index f70eb2825b9d0..821b9b8998c0e 100644 --- a/test/extensions/matching/input_matchers/consistent_hashing/config_test.cc +++ b/test/extensions/matching/input_matchers/consistent_hashing/config_test.cc @@ -11,8 +11,7 @@ namespace InputMatchers { namespace ConsistentHashing { TEST(ConfigTest, TestConfig) { - NiceMock context; - + NiceMock factory_context; const std::string yaml_string = R"EOF( name: hashing typed_config: @@ -27,13 +26,13 @@ TEST(ConfigTest, TestConfig) { ConsistentHashingConfig factory; auto message = Config::Utility::translateAnyToFactoryConfig( config.typed_config(), ProtobufMessage::getStrictValidationVisitor(), factory); - auto matcher = factory.createInputMatcherFactoryCb(*message, context); + auto matcher = factory.createInputMatcherFactoryCb(*message, factory_context); ASSERT_NE(nullptr, matcher); matcher(); } TEST(ConfigTest, InvalidConfig) { - NiceMock context; + NiceMock factory_context; const std::string yaml_string = R"EOF( name: hashing @@ -49,8 +48,8 @@ TEST(ConfigTest, InvalidConfig) { ConsistentHashingConfig factory; auto message = Config::Utility::translateAnyToFactoryConfig( config.typed_config(), ProtobufMessage::getStrictValidationVisitor(), factory); - EXPECT_THROW_WITH_MESSAGE(factory.createInputMatcherFactoryCb(*message, context), EnvoyException, - "threshold cannot be greater than modulo: 200 > 100"); + EXPECT_THROW_WITH_MESSAGE(factory.createInputMatcherFactoryCb(*message, factory_context), + EnvoyException, "threshold cannot be greater than modulo: 200 > 100"); } } // namespace ConsistentHashing } // namespace InputMatchers diff --git a/test/extensions/matching/input_matchers/ip/config_test.cc b/test/extensions/matching/input_matchers/ip/config_test.cc index 82f8428622af8..ef5b99d0e5136 100644 --- a/test/extensions/matching/input_matchers/ip/config_test.cc +++ b/test/extensions/matching/input_matchers/ip/config_test.cc @@ -11,7 +11,7 @@ namespace InputMatchers { namespace IP { TEST(ConfigTest, TestConfig) { - NiceMock context; + NiceMock context; const std::string yaml_string = R"EOF( name: ip @@ -34,7 +34,7 @@ TEST(ConfigTest, TestConfig) { } TEST(ConfigTest, InvalidConfigIP) { - NiceMock context; + NiceMock context; const std::string yaml_string = R"EOF( name: ip @@ -57,7 +57,7 @@ TEST(ConfigTest, InvalidConfigIP) { } TEST(ConfigTest, InvalidConfigStats) { - NiceMock context; + NiceMock context; const std::string yaml_string = R"EOF( name: ip diff --git a/test/extensions/tracers/opencensus/config_test.cc b/test/extensions/tracers/opencensus/config_test.cc index e6fe23920dbb9..b03f3445822bb 100644 --- a/test/extensions/tracers/opencensus/config_test.cc +++ b/test/extensions/tracers/opencensus/config_test.cc @@ -376,6 +376,27 @@ TEST(OpenCensusTracerConfigTest, OpenCensusHttpTracerStackdriverGrpc) { #endif } +TEST(OpenCensusTracerConfigTest, OpenCensusHttpTracerStackdriverAddress) { + NiceMock context; + const std::string yaml_string = R"EOF( + http: + name: opencensus + typed_config: + "@type": type.googleapis.com/envoy.config.trace.v3.OpenCensusConfig + stackdriver_exporter_enabled: true + stackdriver_address: 127.0.0.1:55678 + )EOF"; + + envoy::config::trace::v3::Tracing configuration; + TestUtility::loadFromYaml(yaml_string, configuration); + + OpenCensusTracerFactory factory; + auto message = Config::Utility::translateToFactoryConfig( + configuration.http(), ProtobufMessage::getStrictValidationVisitor(), factory); + auto tracer = factory.createTracerDriver(*message, context); + EXPECT_NE(nullptr, tracer); +} + } // namespace OpenCensus } // namespace Tracers } // namespace Extensions diff --git a/test/extensions/transport_sockets/tls/ssl_socket_test.cc b/test/extensions/transport_sockets/tls/ssl_socket_test.cc index 61812d034b551..1c4cb541fae78 100644 --- a/test/extensions/transport_sockets/tls/ssl_socket_test.cc +++ b/test/extensions/transport_sockets/tls/ssl_socket_test.cc @@ -49,6 +49,7 @@ #include "test/test_common/utility.h" #include "absl/strings/str_replace.h" +#include "absl/types/optional.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "openssl/ssl.h" @@ -617,8 +618,14 @@ void testUtilV2(const TestUtilOptionsV2& options) { server_factory_context; ON_CALL(server_factory_context, api()).WillByDefault(ReturnRef(*server_api)); - auto server_cfg = std::make_unique( - filter_chain.hidden_envoy_deprecated_tls_context(), server_factory_context); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; + const envoy::config::core::v3::TransportSocket& transport_socket = + filter_chain.transport_socket(); + ASSERT(transport_socket.has_typed_config()); + transport_socket.typed_config().UnpackTo(&tls_context); + + auto server_cfg = std::make_unique(tls_context, server_factory_context); + ServerSslSocketFactory server_ssl_socket_factory(std::move(server_cfg), manager, server_stats_store, server_names); @@ -779,26 +786,48 @@ void testUtilV2(const TestUtilOptionsV2& options) { } } -// Configure the listener with unittest{cert,key}.pem and ca_cert.pem. -// Configure the client with expired_san_uri_{cert,key}.pem +void updateFilterChain( + const envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext& tls_context, + envoy::config::listener::v3::FilterChain& filter_chain) { + filter_chain.mutable_transport_socket()->mutable_typed_config()->PackFrom(tls_context); +} + +struct OptionalServerConfig { + absl::optional cert_hash{}; + absl::optional trusted_ca{}; + absl::optional allow_expired_cert{}; +}; + void configureServerAndExpiredClientCertificate( envoy::config::listener::v3::Listener& listener, - envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext& client) { + envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext& client, + const OptionalServerConfig& server_config) { envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/unittest_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/unittest_key.pem")); + envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); - server_validation_ctx->mutable_trusted_ca()->set_filename(TestEnvironment::substitute( - "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem")); + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); + if (server_config.trusted_ca.has_value()) { + server_validation_ctx->mutable_trusted_ca()->set_filename( + TestEnvironment::substitute(server_config.trusted_ca.value())); + } else { + server_validation_ctx->mutable_trusted_ca()->set_filename(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem")); + } + if (server_config.allow_expired_cert.has_value()) { + server_validation_ctx->set_allow_expired_certificate(server_config.allow_expired_cert.value()); + } + if (server_config.cert_hash.has_value()) { + server_validation_ctx->add_verify_certificate_hash(server_config.cert_hash.value()); + } + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::TlsCertificate* client_cert = client.mutable_common_tls_context()->add_tls_certificates(); @@ -809,6 +838,16 @@ void configureServerAndExpiredClientCertificate( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/expired_san_uri_key.pem")); } +TestUtilOptionsV2 createProtocolTestOptions( + const envoy::config::listener::v3::Listener& listener, + const envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext& client_ctx, + Network::Address::IpVersion version, std::string protocol) { + std::string stats = "ssl.versions." + protocol; + TestUtilOptionsV2 options(listener, client_ctx, true, version); + options.setExpectedServerStats(stats).setExpectedClientStats(stats); + return options.setExpectedProtocolVersion(protocol); +} + } // namespace class SslSocketTest : public SslCertsTest, @@ -885,10 +924,9 @@ TEST_P(SslSocketTest, GetCertDigestInvalidFiles) { TEST_P(SslSocketTest, GetCertDigestInline) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); // From test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem. server_cert->mutable_certificate_chain()->set_inline_bytes( @@ -900,15 +938,17 @@ TEST_P(SslSocketTest, GetCertDigestInline) { TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem"))); + envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); // From test/extensions/transport_sockets/tls/test_data/ca_certificates.pem. - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context() - ->mutable_trusted_ca() - ->set_inline_bytes(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( + server_validation_ctx->mutable_trusted_ca()->set_inline_bytes( + TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( "{{ test_rundir " "}}/test/extensions/transport_sockets/tls/test_data/ca_certificates.pem"))); + updateFilterChain(tls_context, *filter_chain); + envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client_ctx; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* client_cert = client_ctx.mutable_common_tls_context()->add_tls_certificates(); @@ -1515,7 +1555,7 @@ TEST_P(SslSocketTest, FailedClientCertificateDefaultExpirationVerification) { envoy::config::listener::v3::Listener listener; envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; - configureServerAndExpiredClientCertificate(listener, client); + configureServerAndExpiredClientCertificate(listener, client, /*server_config=*/{}); TestUtilOptionsV2 test_options(listener, client, false, GetParam()); testUtilV2(test_options.setExpectedClientCertUri("spiffe://lyft.com/test-team") @@ -1528,13 +1568,9 @@ TEST_P(SslSocketTest, FailedClientCertificateExpirationVerification) { envoy::config::listener::v3::Listener listener; envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; - configureServerAndExpiredClientCertificate(listener, client); - - listener.mutable_filter_chains(0) - ->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context() - ->set_allow_expired_certificate(false); + OptionalServerConfig server_config; + server_config.allow_expired_cert = false; + configureServerAndExpiredClientCertificate(listener, client, server_config); TestUtilOptionsV2 test_options(listener, client, false, GetParam()); testUtilV2(test_options.setExpectedClientCertUri("spiffe://lyft.com/test-team") @@ -1546,13 +1582,9 @@ TEST_P(SslSocketTest, ClientCertificateExpirationAllowedVerification) { envoy::config::listener::v3::Listener listener; envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; - configureServerAndExpiredClientCertificate(listener, client); - - listener.mutable_filter_chains(0) - ->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context() - ->set_allow_expired_certificate(true); + OptionalServerConfig server_config; + server_config.allow_expired_cert = true; + configureServerAndExpiredClientCertificate(listener, client, server_config); TestUtilOptionsV2 test_options(listener, client, true, GetParam()); testUtilV2(test_options.setExpectedClientCertUri("spiffe://lyft.com/test-team") @@ -1564,17 +1596,10 @@ TEST_P(SslSocketTest, FailedClientCertAllowExpiredBadHashVerification) { envoy::config::listener::v3::Listener listener; envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; - configureServerAndExpiredClientCertificate(listener, client); - - envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = listener.mutable_filter_chains(0) - ->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); - - server_validation_ctx->set_allow_expired_certificate(true); - server_validation_ctx->add_verify_certificate_hash( - "0000000000000000000000000000000000000000000000000000000000000000"); + OptionalServerConfig server_config; + server_config.allow_expired_cert = true; + server_config.cert_hash = "0000000000000000000000000000000000000000000000000000000000000000"; + configureServerAndExpiredClientCertificate(listener, client, server_config); TestUtilOptionsV2 test_options(listener, client, false, GetParam()); testUtilV2(test_options.setExpectedServerStats("ssl.fail_verify_cert_hash") @@ -1587,19 +1612,12 @@ TEST_P(SslSocketTest, FailedClientCertAllowServerExpiredWrongCAVerification) { envoy::config::listener::v3::Listener listener; envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; - configureServerAndExpiredClientCertificate(listener, client); - - envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = listener.mutable_filter_chains(0) - ->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); - - server_validation_ctx->set_allow_expired_certificate(true); - - // This fake CA was not used to sign the client's certificate. - server_validation_ctx->mutable_trusted_ca()->set_filename(TestEnvironment::substitute( - "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/fake_ca_cert.pem")); + OptionalServerConfig server_config; + server_config.allow_expired_cert = true; + // Fake CA is not used to sign the client's certificate. + server_config.trusted_ca = "{{ test_rundir " + "}}/test/extensions/transport_sockets/tls/test_data/fake_ca_cert.pem"; + configureServerAndExpiredClientCertificate(listener, client, server_config); TestUtilOptionsV2 test_options(listener, client, false, GetParam()); testUtilV2(test_options.setExpectedClientCertUri("spiffe://lyft.com/test-team") @@ -1663,23 +1681,22 @@ TEST_P(SslSocketTest, ClientCertificateHashVerificationNoCA) { TEST_P(SslSocketTest, ClientCertificateHashListVerification) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); server_validation_ctx->mutable_trusted_ca()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem")); server_validation_ctx->add_verify_certificate_hash( "0000000000000000000000000000000000000000000000000000000000000000"); server_validation_ctx->add_verify_certificate_hash(TEST_SAN_URI_CERT_256_HASH); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* client_cert = @@ -1701,21 +1718,20 @@ TEST_P(SslSocketTest, ClientCertificateHashListVerification) { TEST_P(SslSocketTest, ClientCertificateHashListVerificationNoCA) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); server_validation_ctx->add_verify_certificate_hash( "0000000000000000000000000000000000000000000000000000000000000000"); server_validation_ctx->add_verify_certificate_hash(TEST_SAN_URI_CERT_256_HASH); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* client_cert = @@ -1858,10 +1874,10 @@ TEST_P(SslSocketTest, FailedClientCertificateHashVerificationWrongCA) { TEST_P(SslSocketTest, CertificatesWithPassword) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); + server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir " "}}/test/extensions/transport_sockets/tls/test_data/password_protected_cert.pem")); @@ -1872,14 +1888,14 @@ TEST_P(SslSocketTest, CertificatesWithPassword) { "{{ test_rundir " "}}/test/extensions/transport_sockets/tls/test_data/password_protected_password.txt")); envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); server_validation_ctx->mutable_trusted_ca()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem")); server_validation_ctx->add_verify_certificate_hash( "0000000000000000000000000000000000000000000000000000000000000000"); server_validation_ctx->add_verify_certificate_hash(TEST_PASSWORD_PROTECTED_CERT_256_HASH); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* client_cert = @@ -1907,22 +1923,23 @@ TEST_P(SslSocketTest, CertificatesWithPassword) { TEST_P(SslSocketTest, ClientCertificateSpkiVerification) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); + server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); + server_validation_ctx->mutable_trusted_ca()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem")); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_DNS_CERT_SPKI); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_URI_CERT_SPKI); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* client_cert = @@ -1944,20 +1961,19 @@ TEST_P(SslSocketTest, ClientCertificateSpkiVerification) { TEST_P(SslSocketTest, ClientCertificateSpkiVerificationNoCA) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_DNS_CERT_SPKI); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_URI_CERT_SPKI); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* client_cert = @@ -1979,22 +1995,21 @@ TEST_P(SslSocketTest, ClientCertificateSpkiVerificationNoCA) { TEST_P(SslSocketTest, FailedClientCertificateSpkiVerificationNoClientCertificate) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); server_validation_ctx->mutable_trusted_ca()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem")); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_DNS_CERT_SPKI); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_URI_CERT_SPKI); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; TestUtilOptionsV2 test_options(listener, client, false, GetParam()); @@ -2009,20 +2024,19 @@ TEST_P(SslSocketTest, FailedClientCertificateSpkiVerificationNoClientCertificate TEST_P(SslSocketTest, FailedClientCertificateSpkiVerificationNoCANoClientCertificate) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_DNS_CERT_SPKI); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_URI_CERT_SPKI); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; @@ -2038,22 +2052,21 @@ TEST_P(SslSocketTest, FailedClientCertificateSpkiVerificationNoCANoClientCertifi TEST_P(SslSocketTest, FailedClientCertificateSpkiVerificationWrongClientCertificate) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); server_validation_ctx->mutable_trusted_ca()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem")); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_DNS_CERT_SPKI); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_URI_CERT_SPKI); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* client_cert = @@ -2075,20 +2088,19 @@ TEST_P(SslSocketTest, FailedClientCertificateSpkiVerificationWrongClientCertific TEST_P(SslSocketTest, FailedClientCertificateSpkiVerificationNoCAWrongClientCertificate) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_DNS_CERT_SPKI); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_URI_CERT_SPKI); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* client_cert = @@ -2110,22 +2122,21 @@ TEST_P(SslSocketTest, FailedClientCertificateSpkiVerificationNoCAWrongClientCert TEST_P(SslSocketTest, FailedClientCertificateSpkiVerificationWrongCA) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); server_validation_ctx->mutable_trusted_ca()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/fake_ca_cert.pem")); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_DNS_CERT_SPKI); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_URI_CERT_SPKI); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* client_cert = @@ -2146,24 +2157,23 @@ TEST_P(SslSocketTest, FailedClientCertificateSpkiVerificationWrongCA) { TEST_P(SslSocketTest, ClientCertificateHashAndSpkiVerification) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); server_validation_ctx->mutable_trusted_ca()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem")); server_validation_ctx->add_verify_certificate_hash( "0000000000000000000000000000000000000000000000000000000000000000"); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_DNS_CERT_SPKI); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_URI_CERT_SPKI); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* client_cert = @@ -2185,22 +2195,21 @@ TEST_P(SslSocketTest, ClientCertificateHashAndSpkiVerification) { TEST_P(SslSocketTest, ClientCertificateHashAndSpkiVerificationNoCA) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); server_validation_ctx->add_verify_certificate_hash( "0000000000000000000000000000000000000000000000000000000000000000"); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_DNS_CERT_SPKI); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_URI_CERT_SPKI); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* client_cert = @@ -2222,23 +2231,22 @@ TEST_P(SslSocketTest, ClientCertificateHashAndSpkiVerificationNoCA) { TEST_P(SslSocketTest, FailedClientCertificateHashAndSpkiVerificationNoClientCertificate) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); server_validation_ctx->mutable_trusted_ca()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem")); server_validation_ctx->add_verify_certificate_hash( "0000000000000000000000000000000000000000000000000000000000000000"); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_URI_CERT_SPKI); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; @@ -2254,21 +2262,20 @@ TEST_P(SslSocketTest, FailedClientCertificateHashAndSpkiVerificationNoClientCert TEST_P(SslSocketTest, FailedClientCertificateHashAndSpkiVerificationNoCANoClientCertificate) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); server_validation_ctx->add_verify_certificate_hash( "0000000000000000000000000000000000000000000000000000000000000000"); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_URI_CERT_SPKI); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; @@ -2284,23 +2291,22 @@ TEST_P(SslSocketTest, FailedClientCertificateHashAndSpkiVerificationNoCANoClient TEST_P(SslSocketTest, FailedClientCertificateHashAndSpkiVerificationWrongClientCertificate) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); server_validation_ctx->mutable_trusted_ca()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem")); server_validation_ctx->add_verify_certificate_hash( "0000000000000000000000000000000000000000000000000000000000000000"); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_URI_CERT_SPKI); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* client_cert = @@ -2322,21 +2328,20 @@ TEST_P(SslSocketTest, FailedClientCertificateHashAndSpkiVerificationWrongClientC TEST_P(SslSocketTest, FailedClientCertificateHashAndSpkiVerificationNoCAWrongClientCertificate) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); server_validation_ctx->add_verify_certificate_hash( "0000000000000000000000000000000000000000000000000000000000000000"); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_URI_CERT_SPKI); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* client_cert = @@ -2358,23 +2363,22 @@ TEST_P(SslSocketTest, FailedClientCertificateHashAndSpkiVerificationNoCAWrongCli TEST_P(SslSocketTest, FailedClientCertificateHashAndSpkiVerificationWrongCA) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); server_validation_ctx->mutable_trusted_ca()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/fake_ca_cert.pem")); server_validation_ctx->add_verify_certificate_hash( "0000000000000000000000000000000000000000000000000000000000000000"); server_validation_ctx->add_verify_certificate_spki(TEST_SAN_URI_CERT_SPKI); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* client_cert = @@ -3836,31 +3840,18 @@ TEST_P(SslSocketTest, SslError) { EXPECT_EQ(1UL, server_stats_store.counter("ssl.connection_error").value()); } -static TestUtilOptionsV2 createProtocolTestOptions( - const envoy::config::listener::v3::Listener& listener, - const envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext& client_ctx, - Network::Address::IpVersion version, std::string protocol) { - std::string stats = "ssl.versions." + protocol; - TestUtilOptionsV2 options(listener, client_ctx, true, version); - options.setExpectedServerStats(stats).setExpectedClientStats(stats); - return options.setExpectedProtocolVersion(protocol); -} - TEST_P(SslSocketTest, ProtocolVersions) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); envoy::extensions::transport_sockets::tls::v3::TlsParameters* server_params = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_tls_params(); + tls_context.mutable_common_tls_context()->mutable_tls_params(); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; envoy::extensions::transport_sockets::tls::v3::TlsParameters* client_params = @@ -3870,6 +3861,7 @@ TEST_P(SslSocketTest, ProtocolVersions) { // so enable them to avoid false positives. client_params->add_cipher_suites("ECDHE-RSA-AES128-SHA"); server_params->add_cipher_suites("ECDHE-RSA-AES128-SHA"); + updateFilterChain(tls_context, *filter_chain); // Connection using defaults (client & server) succeeds, negotiating TLSv1.2. TestUtilOptionsV2 tls_v1_2_test_options = @@ -3945,6 +3937,7 @@ TEST_P(SslSocketTest, ProtocolVersions) { envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_0); server_params->set_tls_maximum_protocol_version( envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_3); + updateFilterChain(tls_context, *filter_chain); testUtilV2(tls_v1_test_options); client_params->clear_tls_minimum_protocol_version(); client_params->clear_tls_maximum_protocol_version(); @@ -3960,6 +3953,7 @@ TEST_P(SslSocketTest, ProtocolVersions) { envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_0); server_params->set_tls_maximum_protocol_version( envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_3); + updateFilterChain(tls_context, *filter_chain); testUtilV2(tls_v1_3_test_options); client_params->clear_tls_minimum_protocol_version(); client_params->clear_tls_maximum_protocol_version(); @@ -3975,6 +3969,7 @@ TEST_P(SslSocketTest, ProtocolVersions) { envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_0); server_params->set_tls_maximum_protocol_version( envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_0); + updateFilterChain(tls_context, *filter_chain); testUtilV2(unsupported_protocol_test_options); server_params->clear_tls_minimum_protocol_version(); server_params->clear_tls_maximum_protocol_version(); @@ -3984,6 +3979,7 @@ TEST_P(SslSocketTest, ProtocolVersions) { envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_1); server_params->set_tls_maximum_protocol_version( envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_1); + updateFilterChain(tls_context, *filter_chain); testUtilV2(unsupported_protocol_test_options); server_params->clear_tls_minimum_protocol_version(); server_params->clear_tls_maximum_protocol_version(); @@ -3993,6 +3989,7 @@ TEST_P(SslSocketTest, ProtocolVersions) { envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_2); server_params->set_tls_maximum_protocol_version( envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_2); + updateFilterChain(tls_context, *filter_chain); testUtilV2(tls_v1_2_test_options); server_params->clear_tls_minimum_protocol_version(); server_params->clear_tls_maximum_protocol_version(); @@ -4002,6 +3999,7 @@ TEST_P(SslSocketTest, ProtocolVersions) { envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_3); server_params->set_tls_maximum_protocol_version( envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_3); + updateFilterChain(tls_context, *filter_chain); testUtilV2(error_test_options); server_params->clear_tls_minimum_protocol_version(); server_params->clear_tls_maximum_protocol_version(); @@ -4011,6 +4009,7 @@ TEST_P(SslSocketTest, ProtocolVersions) { envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_0); server_params->set_tls_maximum_protocol_version( envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_3); + updateFilterChain(tls_context, *filter_chain); testUtilV2(tls_v1_2_test_options); server_params->clear_tls_minimum_protocol_version(); server_params->clear_tls_maximum_protocol_version(); @@ -4024,6 +4023,7 @@ TEST_P(SslSocketTest, ProtocolVersions) { envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_0); server_params->set_tls_maximum_protocol_version( envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_0); + updateFilterChain(tls_context, *filter_chain); testUtilV2(tls_v1_test_options); client_params->clear_tls_minimum_protocol_version(); client_params->clear_tls_maximum_protocol_version(); @@ -4039,6 +4039,7 @@ TEST_P(SslSocketTest, ProtocolVersions) { envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_3); server_params->set_tls_maximum_protocol_version( envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_3); + updateFilterChain(tls_context, *filter_chain); testUtilV2(tls_v1_3_test_options); client_params->clear_tls_minimum_protocol_version(); client_params->clear_tls_maximum_protocol_version(); @@ -4049,16 +4050,16 @@ TEST_P(SslSocketTest, ProtocolVersions) { TEST_P(SslSocketTest, ALPN) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); envoy::extensions::transport_sockets::tls::v3::CommonTlsContext* server_ctx = - filter_chain->mutable_hidden_envoy_deprecated_tls_context()->mutable_common_tls_context(); + tls_context.mutable_common_tls_context(); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; envoy::extensions::transport_sockets::tls::v3::CommonTlsContext* client_ctx = @@ -4076,6 +4077,7 @@ TEST_P(SslSocketTest, ALPN) { // Client connects without ALPN to a server with "test" ALPN, no ALPN is negotiated. server_ctx->add_alpn_protocols("test"); + updateFilterChain(tls_context, *filter_chain); testUtilV2(test_options); server_ctx->clear_alpn_protocols(); @@ -4088,6 +4090,7 @@ TEST_P(SslSocketTest, ALPN) { // Client connects with "test" ALPN to a server with "test" ALPN, "test" ALPN is negotiated. client_ctx->add_alpn_protocols("test"); server_ctx->add_alpn_protocols("test"); + updateFilterChain(tls_context, *filter_chain); test_options.setExpectedALPNProtocol("test"); testUtilV2(test_options); test_options.setExpectedALPNProtocol(""); @@ -4099,6 +4102,7 @@ TEST_P(SslSocketTest, ALPN) { client.set_allow_renegotiation(true); client_ctx->add_alpn_protocols("test"); server_ctx->add_alpn_protocols("test"); + updateFilterChain(tls_context, *filter_chain); test_options.setExpectedALPNProtocol("test"); testUtilV2(test_options); test_options.setExpectedALPNProtocol(""); @@ -4109,6 +4113,7 @@ TEST_P(SslSocketTest, ALPN) { // Client connects with "test" ALPN to a server with "test2" ALPN, no ALPN is negotiated. client_ctx->add_alpn_protocols("test"); server_ctx->add_alpn_protocols("test2"); + updateFilterChain(tls_context, *filter_chain); testUtilV2(test_options); client_ctx->clear_alpn_protocols(); server_ctx->clear_alpn_protocols(); @@ -4122,18 +4127,15 @@ TEST_P(SslSocketTest, ALPN) { TEST_P(SslSocketTest, CipherSuites) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; + envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); - envoy::extensions::transport_sockets::tls::v3::TlsParameters* server_params = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_tls_params(); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; envoy::extensions::transport_sockets::tls::v3::TlsParameters* client_params = @@ -4151,8 +4153,11 @@ TEST_P(SslSocketTest, CipherSuites) { // Client connects with one of the supported cipher suites, connection succeeds. std::string common_cipher_suite = "ECDHE-RSA-CHACHA20-POLY1305"; client_params->add_cipher_suites(common_cipher_suite); + envoy::extensions::transport_sockets::tls::v3::TlsParameters* server_params = + tls_context.mutable_common_tls_context()->mutable_tls_params(); server_params->add_cipher_suites(common_cipher_suite); server_params->add_cipher_suites("ECDHE-RSA-AES128-GCM-SHA256"); + updateFilterChain(tls_context, *filter_chain); TestUtilOptionsV2 cipher_test_options(listener, client, true, GetParam()); cipher_test_options.setExpectedCiphersuite(common_cipher_suite); std::string stats = "ssl.ciphers." + common_cipher_suite; @@ -4164,6 +4169,7 @@ TEST_P(SslSocketTest, CipherSuites) { // Client connects with unsupported cipher suite, connection fails. client_params->add_cipher_suites("ECDHE-RSA-AES128-GCM-SHA256"); server_params->add_cipher_suites("ECDHE-RSA-CHACHA20-POLY1305"); + updateFilterChain(tls_context, *filter_chain); TestUtilOptionsV2 error_test_options(listener, client, false, GetParam()); error_test_options.setExpectedServerStats("ssl.connection_error"); testUtilV2(error_test_options); @@ -4172,10 +4178,11 @@ TEST_P(SslSocketTest, CipherSuites) { // Client connects to a server offering only deprecated cipher suites, connection fails. server_params->add_cipher_suites("ECDHE-RSA-AES128-SHA"); + updateFilterChain(tls_context, *filter_chain); error_test_options.setExpectedServerStats("ssl.connection_error"); testUtilV2(error_test_options); server_params->clear_cipher_suites(); - + updateFilterChain(tls_context, *filter_chain); // Verify that ECDHE-RSA-CHACHA20-POLY1305 is not offered by default in FIPS builds. client_params->add_cipher_suites(common_cipher_suite); #ifdef BORINGSSL_FIPS @@ -4189,18 +4196,16 @@ TEST_P(SslSocketTest, CipherSuites) { TEST_P(SslSocketTest, EcdhCurves) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); envoy::extensions::transport_sockets::tls::v3::TlsParameters* server_params = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_tls_params(); + tls_context.mutable_common_tls_context()->mutable_tls_params(); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; envoy::extensions::transport_sockets::tls::v3::TlsParameters* client_params = @@ -4220,6 +4225,7 @@ TEST_P(SslSocketTest, EcdhCurves) { server_params->add_ecdh_curves("X25519"); server_params->add_ecdh_curves("P-256"); server_params->add_cipher_suites("ECDHE-RSA-AES128-GCM-SHA256"); + updateFilterChain(tls_context, *filter_chain); TestUtilOptionsV2 ecdh_curves_test_options(listener, client, true, GetParam()); std::string stats = "ssl.curves.X25519"; ecdh_curves_test_options.setExpectedServerStats(stats).setExpectedClientStats(stats); @@ -4232,7 +4238,7 @@ TEST_P(SslSocketTest, EcdhCurves) { client_params->add_ecdh_curves("X25519"); server_params->add_ecdh_curves("P-256"); server_params->add_cipher_suites("ECDHE-RSA-AES128-GCM-SHA256"); - + updateFilterChain(tls_context, *filter_chain); TestUtilOptionsV2 error_test_options(listener, client, false, GetParam()); error_test_options.setExpectedServerStats("ssl.connection_error"); testUtilV2(error_test_options); @@ -4244,6 +4250,7 @@ TEST_P(SslSocketTest, EcdhCurves) { // Verify that X25519 is not offered by default in FIPS builds. client_params->add_ecdh_curves("X25519"); server_params->add_cipher_suites("ECDHE-RSA-AES128-GCM-SHA256"); + updateFilterChain(tls_context, *filter_chain); #ifdef BORINGSSL_FIPS testUtilV2(error_test_options); #else @@ -4256,23 +4263,23 @@ TEST_P(SslSocketTest, EcdhCurves) { TEST_P(SslSocketTest, SignatureAlgorithms) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext* - server_validation_ctx = filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->mutable_validation_context(); + server_validation_ctx = + tls_context.mutable_common_tls_context()->mutable_validation_context(); + server_validation_ctx->mutable_trusted_ca()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem")); // Server ECDSA certificate. envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir " "}}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir " "}}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_key.pem")); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; // Client RSA certificate. @@ -4558,14 +4565,15 @@ TEST_P(SslSocketTest, RevokedIntermediateCertificateCRLInTrustedCA) { TEST_P(SslSocketTest, GetRequestedServerName) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); + server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; client.set_sni("lyft.com"); @@ -4577,14 +4585,14 @@ TEST_P(SslSocketTest, GetRequestedServerName) { TEST_P(SslSocketTest, OverrideRequestedServerName) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; client.set_sni("lyft.com"); @@ -4600,14 +4608,14 @@ TEST_P(SslSocketTest, OverrideRequestedServerName) { TEST_P(SslSocketTest, OverrideRequestedServerNameWithoutSniInUpstreamTlsContext) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); + updateFilterChain(tls_context, *filter_chain); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; @@ -4621,26 +4629,28 @@ TEST_P(SslSocketTest, OverrideRequestedServerNameWithoutSniInUpstreamTlsContext) TEST_P(SslSocketTest, OverrideApplicationProtocols) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; envoy::extensions::transport_sockets::tls::v3::TlsCertificate* server_cert = - filter_chain->mutable_hidden_envoy_deprecated_tls_context() - ->mutable_common_tls_context() - ->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificates(); + server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); - envoy::extensions::transport_sockets::tls::v3::CommonTlsContext* server_ctx = - filter_chain->mutable_hidden_envoy_deprecated_tls_context()->mutable_common_tls_context(); envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext client; TestUtilOptionsV2 test_options(listener, client, true, GetParam()); // Client connects without ALPN to a server with "test" ALPN, no ALPN is negotiated. + envoy::extensions::transport_sockets::tls::v3::CommonTlsContext* server_ctx = + tls_context.mutable_common_tls_context(); server_ctx->add_alpn_protocols("test"); + updateFilterChain(tls_context, *filter_chain); testUtilV2(test_options); server_ctx->clear_alpn_protocols(); // Override client side ALPN, "test" ALPN is used. server_ctx->add_alpn_protocols("test"); + updateFilterChain(tls_context, *filter_chain); auto transport_socket_options = std::make_shared( "", std::vector{}, std::vector{"foo", "test", "bar"}); @@ -4674,6 +4684,7 @@ TEST_P(SslSocketTest, OverrideApplicationProtocols) { // Note that the server prefers "test" over "bar", but since the client only configures "bar", // the resulting ALPN will be "bar" even though "test" is included in the fallback. server_ctx->add_alpn_protocols("bar"); + updateFilterChain(tls_context, *filter_chain); client.mutable_common_tls_context()->add_alpn_protocols("bar"); testUtilV2(test_options.setExpectedALPNProtocol("bar").setTransportSocketOptions( transport_socket_options)); diff --git a/test/mocks/http/mocks.h b/test/mocks/http/mocks.h index ea45b0b6044ff..55e35884ce9ef 100644 --- a/test/mocks/http/mocks.h +++ b/test/mocks/http/mocks.h @@ -529,6 +529,7 @@ class MockConnectionManagerConfig : public ConnectionManagerConfig { ON_CALL(*this, isRoutable()).WillByDefault(testing::Return(true)); ON_CALL(*this, preserveExternalRequestId()).WillByDefault(testing::Return(false)); ON_CALL(*this, alwaysSetRequestIdInResponse()).WillByDefault(testing::Return(false)); + ON_CALL(*this, schemeToSet()).WillByDefault(testing::ReturnRef(scheme_)); } // Http::ConnectionManagerConfig @@ -562,6 +563,7 @@ class MockConnectionManagerConfig : public ConnectionManagerConfig { MOCK_METHOD(const std::string&, serverName, (), (const)); MOCK_METHOD(HttpConnectionManagerProto::ServerHeaderTransformation, serverHeaderTransformation, (), (const)); + MOCK_METHOD(const absl::optional&, schemeToSet, (), (const)); MOCK_METHOD(ConnectionManagerStats&, stats, ()); MOCK_METHOD(ConnectionManagerTracingStats&, tracingStats, ()); MOCK_METHOD(bool, useRemoteAddress, (), (const)); @@ -599,6 +601,7 @@ class MockConnectionManagerConfig : public ConnectionManagerConfig { std::unique_ptr internal_address_config_ = std::make_unique(); + absl::optional scheme_; }; class MockReceivedSettings : public ReceivedSettings { diff --git a/test/mocks/network/BUILD b/test/mocks/network/BUILD index 8bc9532c114d4..38cd2003b09d7 100644 --- a/test/mocks/network/BUILD +++ b/test/mocks/network/BUILD @@ -56,6 +56,7 @@ envoy_cc_mock( "//envoy/network:transport_socket_interface", "//envoy/server:listener_manager_interface", "//source/common/network:address_lib", + "//source/common/network:socket_interface_lib", "//source/common/network:utility_lib", "//source/common/stats:isolated_store_lib", "//test/mocks/event:event_mocks", diff --git a/test/mocks/network/connection.cc b/test/mocks/network/connection.cc index 54b318559ee46..adcb9c971dd18 100644 --- a/test/mocks/network/connection.cc +++ b/test/mocks/network/connection.cc @@ -81,6 +81,7 @@ template static void initializeMockConnection(T& connection) { connection.raiseEvent(Network::ConnectionEvent::LocalClose); })); ON_CALL(connection, id()).WillByDefault(Return(connection.next_id_)); + connection.stream_info_.downstream_address_provider_->setConnectionID(connection.id_); ON_CALL(connection, state()).WillByDefault(ReturnPointee(&connection.state_)); // The real implementation will move the buffer data into the socket. diff --git a/test/mocks/network/mocks.h b/test/mocks/network/mocks.h index b112ccf480d0f..f9943d9f44158 100644 --- a/test/mocks/network/mocks.h +++ b/test/mocks/network/mocks.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -19,6 +20,7 @@ #include "source/common/network/filter_manager_impl.h" #include "source/common/network/socket_interface.h" +#include "source/common/network/socket_interface_impl.h" #include "source/common/stats/isolated_store_impl.h" #include "test/mocks/event/mocks.h" @@ -37,7 +39,7 @@ class MockActiveDnsQuery : public ActiveDnsQuery { ~MockActiveDnsQuery() override; // Network::ActiveDnsQuery - MOCK_METHOD(void, cancel, ()); + MOCK_METHOD(void, cancel, (CancelReason reason)); }; class MockDnsResolver : public DnsResolver { @@ -600,5 +602,20 @@ class MockUdpPacketProcessor : public UdpPacketProcessor { MOCK_METHOD(size_t, numPacketsExpectedPerEventLoop, (), (const)); }; +class MockSocketInterface : public SocketInterfaceImpl { +public: + explicit MockSocketInterface(const std::vector& versions) + : versions_(versions.begin(), versions.end()) {} + MOCK_METHOD(IoHandlePtr, socket, (Socket::Type, Address::Type, Address::IpVersion, bool), + (const)); + MOCK_METHOD(IoHandlePtr, socket, (Socket::Type, const Address::InstanceConstSharedPtr), (const)); + bool ipFamilySupported(int domain) override { + const auto to_version = domain == AF_INET ? Address::IpVersion::v4 : Address::IpVersion::v6; + return std::any_of(versions_.begin(), versions_.end(), + [to_version](auto version) { return to_version == version; }); + } + const std::vector versions_; +}; + } // namespace Network } // namespace Envoy diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index b83dd3310c4cc..928f3ab9c7671 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -348,7 +348,9 @@ class MockStatsMatcher : public StatsMatcher { public: MockStatsMatcher(); ~MockStatsMatcher() override; - MOCK_METHOD(bool, rejects, (const std::string& name), (const)); + MOCK_METHOD(bool, rejects, (StatName name), (const)); + MOCK_METHOD(StatsMatcher::FastResult, fastRejects, (StatName name), (const)); + MOCK_METHOD(bool, slowRejects, (FastResult, StatName name), (const)); bool acceptsAll() const override { return accepts_all_; } bool rejectsAll() const override { return rejects_all_; } diff --git a/test/mocks/stream_info/mocks.cc b/test/mocks/stream_info/mocks.cc index a2b7c768162d3..28194ab7ab700 100644 --- a/test/mocks/stream_info/mocks.cc +++ b/test/mocks/stream_info/mocks.cc @@ -118,7 +118,6 @@ MockStreamInfo::MockStreamInfo() ON_CALL(*this, getRouteName()).WillByDefault(ReturnRef(route_name_)); ON_CALL(*this, upstreamTransportFailureReason()) .WillByDefault(ReturnRef(upstream_transport_failure_reason_)); - ON_CALL(*this, connectionID()).WillByDefault(Return(connection_id_)); ON_CALL(*this, setConnectionID(_)).WillByDefault(Invoke([this](uint64_t id) { connection_id_ = id; })); diff --git a/test/mocks/tracing/mocks.h b/test/mocks/tracing/mocks.h index 2d26d45798022..0b954e10aaaf1 100644 --- a/test/mocks/tracing/mocks.h +++ b/test/mocks/tracing/mocks.h @@ -5,6 +5,7 @@ #include "envoy/tracing/http_tracer.h" #include "envoy/tracing/http_tracer_manager.h" +#include "envoy/tracing/trace_driver.h" #include "gmock/gmock.h" @@ -35,7 +36,7 @@ class MockSpan : public Span { MOCK_METHOD(void, setTag, (absl::string_view name, absl::string_view value)); MOCK_METHOD(void, log, (SystemTime timestamp, const std::string& event)); MOCK_METHOD(void, finishSpan, ()); - MOCK_METHOD(void, injectContext, (Http::RequestHeaderMap & request_headers)); + MOCK_METHOD(void, injectContext, (Tracing::TraceContext & request_headers)); MOCK_METHOD(void, setSampled, (const bool sampled)); MOCK_METHOD(void, setBaggage, (absl::string_view key, absl::string_view value)); MOCK_METHOD(std::string, getBaggage, (absl::string_view key)); @@ -62,7 +63,7 @@ class MockHttpTracer : public HttpTracer { } MOCK_METHOD(Span*, startSpan_, - (const Config& config, Http::HeaderMap& request_headers, + (const Config& config, Http::RequestHeaderMap& request_headers, const StreamInfo::StreamInfo& stream_info, const Tracing::Decision tracing_decision)); }; @@ -72,17 +73,15 @@ class MockDriver : public Driver { MockDriver(); ~MockDriver() override; - SpanPtr startSpan(const Config& config, Http::RequestHeaderMap& request_headers, + SpanPtr startSpan(const Config& config, TraceContext& trace_context, const std::string& operation_name, SystemTime start_time, const Tracing::Decision tracing_decision) override { - return SpanPtr{ - startSpan_(config, request_headers, operation_name, start_time, tracing_decision)}; + return SpanPtr{startSpan_(config, trace_context, operation_name, start_time, tracing_decision)}; } MOCK_METHOD(Span*, startSpan_, - (const Config& config, Http::HeaderMap& request_headers, - const std::string& operation_name, SystemTime start_time, - const Tracing::Decision tracing_decision)); + (const Config& config, TraceContext& trace_context, const std::string& operation_name, + SystemTime start_time, const Tracing::Decision tracing_decision)); }; class MockHttpTracerManager : public HttpTracerManager { diff --git a/test/server/BUILD b/test/server/BUILD index 70fa142dc5dfa..15fdece73619b 100644 --- a/test/server/BUILD +++ b/test/server/BUILD @@ -86,6 +86,7 @@ envoy_cc_test( "//test/mocks/api:api_mocks", "//test/mocks/network:network_mocks", "//test/test_common:network_utility_lib", + "//test/test_common:test_runtime_lib", "//test/test_common:threadsafe_singleton_injector_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", "@envoy_api//envoy/config/listener/v3:pkg_cc_proto", diff --git a/test/server/admin/admin_test.cc b/test/server/admin/admin_test.cc index b0f8e4532248a..91a09d5f3533f 100644 --- a/test/server/admin/admin_test.cc +++ b/test/server/admin/admin_test.cc @@ -51,6 +51,7 @@ TEST_P(AdminInstanceTest, Getters) { EXPECT_EQ(false, admin_.preserveExternalRequestId()); EXPECT_EQ(nullptr, admin_.tracer()); EXPECT_EQ(false, admin_.streamErrorOnInvalidHttpMessaging()); + EXPECT_EQ(false, admin_.schemeToSet().has_value()); } TEST_P(AdminInstanceTest, WriteAddressToFile) { diff --git a/test/server/admin/config_dump_handler_test.cc b/test/server/admin/config_dump_handler_test.cc index 7bd46281445bf..92e8d56fa586a 100644 --- a/test/server/admin/config_dump_handler_test.cc +++ b/test/server/admin/config_dump_handler_test.cc @@ -1,5 +1,6 @@ #include "test/server/admin/admin_instance.h" +using testing::HasSubstr; using testing::Return; using testing::ReturnPointee; using testing::ReturnRef; @@ -670,7 +671,7 @@ TEST_P(AdminInstanceTest, ConfigDumpNonExistentMask) { EXPECT_EQ(Http::Code::BadRequest, getCallback("/config_dump?resource=static_clusters&mask=bad", header_map, response)); std::string output = response.toString(); - EXPECT_THAT(output, testing::HasSubstr("could not be successfully used")); + EXPECT_THAT(output, HasSubstr("could not be successfully used")); } // Test that a 404 Not found is returned if a non-existent resource is passed in as the @@ -741,13 +742,49 @@ TEST_P(AdminInstanceTest, InvalidFieldMaskWithoutResourceDoesNotCrash) { // `extensions` is a repeated field, and cannot be indexed through in a FieldMask. EXPECT_EQ(Http::Code::BadRequest, getCallback("/config_dump?mask=bootstrap.node.extensions.name", header_map, response)); - EXPECT_EQ("FieldMask paths: \"bootstrap.node.extensions.name\"\n could not be " - "successfully used.", + EXPECT_THAT(response.toString(), HasSubstr("could not be successfully applied to any configs")); +} + +TEST_P(AdminInstanceTest, FieldMasksWorkWhenFetchingAllResources) { + Buffer::OwnedImpl response; + Http::TestResponseHeaderMapImpl header_map; + auto bootstrap = admin_.getConfigTracker().add("bootstrap", [](const Matchers::StringMatcher&) { + auto msg = std::make_unique(); + auto* bootstrap = msg->mutable_bootstrap(); + bootstrap->mutable_node()->add_extensions()->set_name("ext1"); + bootstrap->mutable_node()->add_extensions()->set_name("ext2"); + return msg; + }); + auto clusters = admin_.getConfigTracker().add("clusters", [](const Matchers::StringMatcher&) { + auto msg = std::make_unique(); + auto* static_cluster = msg->add_static_clusters(); + envoy::config::cluster::v3::Cluster inner_cluster; + inner_cluster.set_name("cluster1"); + static_cluster->mutable_cluster()->PackFrom(inner_cluster); + return msg; + }); + EXPECT_EQ(Http::Code::OK, getCallback("/config_dump?mask=bootstrap", header_map, response)); + EXPECT_EQ(R"EOF({ + "configs": [ + { + "@type": "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump", + "bootstrap": { + "node": { + "extensions": [ + { + "name": "ext1" + }, + { + "name": "ext2" + } + ] + } + } + } + ] +} +)EOF", response.toString()); - EXPECT_EQ(header_map.ContentType()->value().getStringView(), - Http::Headers::get().ContentTypeValues.Text); - EXPECT_EQ(header_map.get(Http::Headers::get().XContentTypeOptions)[0]->value(), - Http::Headers::get().XContentTypeOptionValues.Nosniff); } } // namespace Server diff --git a/test/server/connection_handler_test.cc b/test/server/connection_handler_test.cc index b97a3d140c614..a7df85f9333b8 100644 --- a/test/server/connection_handler_test.cc +++ b/test/server/connection_handler_test.cc @@ -21,6 +21,7 @@ #include "test/mocks/common.h" #include "test/mocks/network/mocks.h" #include "test/test_common/network_utility.h" +#include "test/test_common/test_runtime.h" #include "test/test_common/threadsafe_singleton_injector.h" #include "gmock/gmock.h" @@ -746,6 +747,162 @@ TEST_F(ConnectionHandlerTest, FallbackToWildcardListener) { EXPECT_CALL(*access_log_, log(_, _, _, _)); } +TEST_F(ConnectionHandlerTest, OldBehaviorMatchFirstWildcardListener) { + auto scoped_runtime = std::make_unique(); + + Runtime::LoaderSingleton::getExisting()->mergeValues( + {{"envoy.reloadable_features.listener_wildcard_match_ip_family", "false"}}); + + Network::TcpListenerCallbacks* listener_callbacks1; + auto listener1 = new NiceMock(); + TestListener* test_listener1 = + addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1); + Network::Address::InstanceConstSharedPtr normal_address( + new Network::Address::Ipv4Instance("127.0.0.1", 10001)); + EXPECT_CALL(*socket_factory_, localAddress()).WillRepeatedly(ReturnRef(normal_address)); + handler_->addListener(absl::nullopt, *test_listener1); + + auto ipv4_overridden_filter_chain_manager = + std::make_shared>(); + Network::TcpListenerCallbacks* ipv4_any_listener_callbacks; + auto listener2 = new NiceMock(); + TestListener* ipv4_any_listener = + addListener(1, false, false, "ipv4_any_test_listener", listener2, + &ipv4_any_listener_callbacks, nullptr, nullptr, Network::Socket::Type::Stream, + std::chrono::milliseconds(15000), false, ipv4_overridden_filter_chain_manager); + Network::Address::InstanceConstSharedPtr any_address( + new Network::Address::Ipv4Instance("0.0.0.0", 80)); + EXPECT_CALL(*socket_factory_, localAddress()).WillRepeatedly(ReturnRef(any_address)); + handler_->addListener(absl::nullopt, *ipv4_any_listener); + + auto ipv6_overridden_filter_chain_manager = + std::make_shared>(); + Network::TcpListenerCallbacks* ipv6_any_listener_callbacks; + auto listener3 = new NiceMock(); + TestListener* ipv6_any_listener = + addListener(1, false, false, "ipv6_any_test_listener", listener3, + &ipv6_any_listener_callbacks, nullptr, nullptr, Network::Socket::Type::Stream, + std::chrono::milliseconds(15000), false, ipv6_overridden_filter_chain_manager); + Network::Address::InstanceConstSharedPtr any_address_ipv6( + new Network::Address::Ipv6Instance("::", 80)); + EXPECT_CALL(*socket_factory_, localAddress()).WillRepeatedly(ReturnRef(any_address_ipv6)); + handler_->addListener(absl::nullopt, *ipv6_any_listener); + + Network::MockListenerFilter* test_filter = new Network::MockListenerFilter(); + EXPECT_CALL(*test_filter, destroy_()); + Network::MockConnectionSocket* accepted_socket = new NiceMock(); + bool redirected = false; + EXPECT_CALL(factory_, createListenerFilterChain(_)) + .WillRepeatedly(Invoke([&](Network::ListenerFilterManager& manager) -> bool { + // Insert the Mock filter. + if (!redirected) { + manager.addAcceptFilter(listener_filter_matcher_, + Network::ListenerFilterPtr{test_filter}); + redirected = true; + } + return true; + })); + + Network::Address::InstanceConstSharedPtr alt_address( + new Network::Address::Ipv6Instance("::2", 80, nullptr)); + EXPECT_CALL(*test_filter, onAccept(_)) + .WillOnce(Invoke([&](Network::ListenerFilterCallbacks& cb) -> Network::FilterStatus { + cb.socket().addressProvider().restoreLocalAddress(alt_address); + return Network::FilterStatus::Continue; + })); + EXPECT_CALL(manager_, findFilterChain(_)).Times(0); + EXPECT_CALL(*ipv4_overridden_filter_chain_manager, findFilterChain(_)) + .WillOnce(Return(filter_chain_.get())); + EXPECT_CALL(*ipv6_overridden_filter_chain_manager, findFilterChain(_)).Times(0); + auto* connection = new NiceMock(); + EXPECT_CALL(dispatcher_, createServerConnection_()).WillOnce(Return(connection)); + EXPECT_CALL(factory_, createNetworkFilterChain(_, _)).WillOnce(Return(true)); + listener_callbacks1->onAccept(Network::ConnectionSocketPtr{accepted_socket}); + EXPECT_EQ(1UL, handler_->numConnections()); + + EXPECT_CALL(*listener3, onDestroy()); + EXPECT_CALL(*listener2, onDestroy()); + EXPECT_CALL(*listener1, onDestroy()); + EXPECT_CALL(*access_log_, log(_, _, _, _)); +} + +TEST_F(ConnectionHandlerTest, MatchIPv6WildcardListener) { + auto scoped_runtime = std::make_unique(); + + Network::TcpListenerCallbacks* listener_callbacks1; + auto listener1 = new NiceMock(); + TestListener* test_listener1 = + addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1); + Network::Address::InstanceConstSharedPtr normal_address( + new Network::Address::Ipv4Instance("127.0.0.1", 10001)); + EXPECT_CALL(*socket_factory_, localAddress()).WillRepeatedly(ReturnRef(normal_address)); + handler_->addListener(absl::nullopt, *test_listener1); + + auto ipv4_overridden_filter_chain_manager = + std::make_shared>(); + Network::TcpListenerCallbacks* ipv4_any_listener_callbacks; + auto listener2 = new NiceMock(); + TestListener* ipv4_any_listener = + addListener(1, false, false, "ipv4_any_test_listener", listener2, + &ipv4_any_listener_callbacks, nullptr, nullptr, Network::Socket::Type::Stream, + std::chrono::milliseconds(15000), false, ipv4_overridden_filter_chain_manager); + + Network::Address::InstanceConstSharedPtr any_address( + new Network::Address::Ipv4Instance("0.0.0.0", 80)); + EXPECT_CALL(*socket_factory_, localAddress()).WillRepeatedly(ReturnRef(any_address)); + handler_->addListener(absl::nullopt, *ipv4_any_listener); + + auto ipv6_overridden_filter_chain_manager = + std::make_shared>(); + Network::TcpListenerCallbacks* ipv6_any_listener_callbacks; + auto listener3 = new NiceMock(); + TestListener* ipv6_any_listener = + addListener(1, false, false, "ipv6_any_test_listener", listener3, + &ipv6_any_listener_callbacks, nullptr, nullptr, Network::Socket::Type::Stream, + std::chrono::milliseconds(15000), false, ipv6_overridden_filter_chain_manager); + Network::Address::InstanceConstSharedPtr any_address_ipv6( + new Network::Address::Ipv6Instance("::", 80)); + EXPECT_CALL(*socket_factory_, localAddress()).WillRepeatedly(ReturnRef(any_address_ipv6)); + handler_->addListener(absl::nullopt, *ipv6_any_listener); + + Network::MockListenerFilter* test_filter = new Network::MockListenerFilter(); + EXPECT_CALL(*test_filter, destroy_()); + Network::MockConnectionSocket* accepted_socket = new NiceMock(); + bool redirected = false; + EXPECT_CALL(factory_, createListenerFilterChain(_)) + .WillRepeatedly(Invoke([&](Network::ListenerFilterManager& manager) -> bool { + // Insert the Mock filter. + if (!redirected) { + manager.addAcceptFilter(listener_filter_matcher_, + Network::ListenerFilterPtr{test_filter}); + redirected = true; + } + return true; + })); + + Network::Address::InstanceConstSharedPtr alt_address( + new Network::Address::Ipv6Instance("::2", 80, nullptr)); + EXPECT_CALL(*test_filter, onAccept(_)) + .WillOnce(Invoke([&](Network::ListenerFilterCallbacks& cb) -> Network::FilterStatus { + cb.socket().addressProvider().restoreLocalAddress(alt_address); + return Network::FilterStatus::Continue; + })); + EXPECT_CALL(manager_, findFilterChain(_)).Times(0); + EXPECT_CALL(*ipv4_overridden_filter_chain_manager, findFilterChain(_)).Times(0); + EXPECT_CALL(*ipv6_overridden_filter_chain_manager, findFilterChain(_)) + .WillOnce(Return(filter_chain_.get())); + auto* connection = new NiceMock(); + EXPECT_CALL(dispatcher_, createServerConnection_()).WillOnce(Return(connection)); + EXPECT_CALL(factory_, createNetworkFilterChain(_, _)).WillOnce(Return(true)); + listener_callbacks1->onAccept(Network::ConnectionSocketPtr{accepted_socket}); + EXPECT_EQ(1UL, handler_->numConnections()); + + EXPECT_CALL(*listener3, onDestroy()); + EXPECT_CALL(*listener2, onDestroy()); + EXPECT_CALL(*listener1, onDestroy()); + EXPECT_CALL(*access_log_, log(_, _, _, _)); +} + TEST_F(ConnectionHandlerTest, WildcardListenerWithOriginalDstInbound) { Network::TcpListenerCallbacks* listener_callbacks1; auto listener1 = new NiceMock(); @@ -917,13 +1074,14 @@ TEST_F(ConnectionHandlerTest, ContinueOnListenerFilterTimeout) { })); Network::MockConnectionSocket* accepted_socket = new NiceMock(); Network::IoSocketHandleImpl io_handle{42}; - EXPECT_CALL(*accepted_socket, ioHandle()).WillRepeatedly(ReturnRef(io_handle)); + EXPECT_CALL(*accepted_socket, ioHandle()).WillOnce(ReturnRef(io_handle)).RetiresOnSaturation(); Event::MockTimer* timeout = new Event::MockTimer(&dispatcher_); EXPECT_CALL(*timeout, enableTimer(std::chrono::milliseconds(15000), _)); listener_callbacks->onAccept(Network::ConnectionSocketPtr{accepted_socket}); Stats::Gauge& downstream_pre_cx_active = stats_store_.gauge("downstream_pre_cx_active", Stats::Gauge::ImportMode::Accumulate); EXPECT_EQ(1UL, downstream_pre_cx_active.value()); + EXPECT_CALL(*accepted_socket, ioHandle()).WillOnce(ReturnRef(io_handle)).RetiresOnSaturation(); EXPECT_CALL(*test_filter, destroy_()); // Barrier: test_filter must be destructed before findFilterChain EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(nullptr)); @@ -938,6 +1096,12 @@ TEST_F(ConnectionHandlerTest, ContinueOnListenerFilterTimeout) { EXPECT_EQ(1UL, stats_store_.counter("no_filter_chain_match").value()); EXPECT_CALL(*listener, onDestroy()); + + // Verify the file event created by listener filter was reset. If not + // the initializeFileEvent will trigger the assertion. + io_handle.initializeFileEvent( + dispatcher_, [](uint32_t) -> void {}, Event::PlatformDefaultTriggerType, + Event::FileReadyType::Read | Event::FileReadyType::Closed); } // Timeout is disabled once the listener filters complete. @@ -965,11 +1129,12 @@ TEST_F(ConnectionHandlerTest, ListenerFilterTimeoutResetOnSuccess) { })); Network::MockConnectionSocket* accepted_socket = new NiceMock(); Network::IoSocketHandleImpl io_handle{42}; - EXPECT_CALL(*accepted_socket, ioHandle()).WillRepeatedly(ReturnRef(io_handle)); + EXPECT_CALL(*accepted_socket, ioHandle()).WillOnce(ReturnRef(io_handle)).RetiresOnSaturation(); Event::MockTimer* timeout = new Event::MockTimer(&dispatcher_); EXPECT_CALL(*timeout, enableTimer(std::chrono::milliseconds(15000), _)); listener_callbacks->onAccept(Network::ConnectionSocketPtr{accepted_socket}); + EXPECT_CALL(*accepted_socket, ioHandle()).WillOnce(ReturnRef(io_handle)).RetiresOnSaturation(); EXPECT_CALL(*test_filter, destroy_()); EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(nullptr)); EXPECT_CALL(*access_log_, log(_, _, _, _)); @@ -977,6 +1142,12 @@ TEST_F(ConnectionHandlerTest, ListenerFilterTimeoutResetOnSuccess) { listener_filter_cb->continueFilterChain(true); EXPECT_CALL(*listener, onDestroy()); + + // Verify the file event created by listener filter was reset. If not + // the initializeFileEvent will trigger the assertion. + io_handle.initializeFileEvent( + dispatcher_, [](uint32_t) -> void {}, Event::PlatformDefaultTriggerType, + Event::FileReadyType::Read | Event::FileReadyType::Closed); } // Ensure there is no timeout when the timeout is disabled with 0s. diff --git a/test/server/listener_manager_impl_test.cc b/test/server/listener_manager_impl_test.cc index fa67c82401dc0..e2bc5cdf82cb9 100644 --- a/test/server/listener_manager_impl_test.cc +++ b/test/server/listener_manager_impl_test.cc @@ -20,6 +20,7 @@ #include "source/common/init/manager_impl.h" #include "source/common/network/address_impl.h" #include "source/common/network/io_socket_handle_impl.h" +#include "source/common/network/socket_interface_impl.h" #include "source/common/network/utility.h" #include "source/common/protobuf/protobuf.h" #include "source/extensions/filters/listener/original_dst/original_dst.h" @@ -280,42 +281,6 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, EXPECT_TRUE(filter_chain->transportSocketFactory().implementsSecureTransport()); } -TEST_F(ListenerManagerImplWithRealFiltersTest, DEPRECATED_FEATURE_TEST(TlsContext)) { - const std::string yaml = TestEnvironment::substitute(R"EOF( -address: - socket_address: - address: 127.0.0.1 - port_value: 1234 -filter_chains: -- filters: [] - transport_socket: - name: tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext - common_tls_context: - tls_certificates: - - certificate_chain: - filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_uri_cert.pem" - private_key: - filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_uri_key.pem" - validation_context: - trusted_ca: - filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" - match_subject_alt_names: - exact: localhost - exact: 127.0.0.1 - )EOF", - Network::Address::IpVersion::v4); - - EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, {true})); - manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); - EXPECT_EQ(1U, manager_->listeners().size()); - - auto filter_chain = findFilterChain(1234, "127.0.0.1", "", "tls", {}, "8.8.8.8", 111); - ASSERT_NE(filter_chain, nullptr); - EXPECT_TRUE(filter_chain->transportSocketFactory().implementsSecureTransport()); -} - TEST_F(ListenerManagerImplWithRealFiltersTest, TransportSocketConnectTimeout) { const std::string yaml = R"EOF( address: @@ -1662,6 +1627,10 @@ name: foo TEST_F(ListenerManagerImplTest, BindToPortEqualToFalse) { InSequence s; + auto mock_interface = std::make_unique( + std::vector{Network::Address::IpVersion::v4}); + StackedScopedInjectableLoader new_interface(std::move(mock_interface)); + ProdListenerComponentFactory real_listener_factory(server_); EXPECT_CALL(*worker_, start(_, _)); manager_->startWorkers(guard_dog_, callback_.AsStdFunction()); @@ -1672,24 +1641,21 @@ name: foo address: 127.0.0.1 port_value: 1234 bind_to_port: false +reuse_port: false filter_chains: - filters: [] )EOF"; - auto syscall_result = os_sys_calls_actual_.socket(AF_INET, SOCK_STREAM, 0); - ASSERT_TRUE(SOCKET_VALID(syscall_result.rc_)); - ListenerHandle* listener_foo = expectListenerCreate(true, true); EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, ListenSocketCreationParams(false))) - .WillOnce(Invoke([this, &syscall_result, &real_listener_factory]( + .WillOnce(Invoke([this, &real_listener_factory]( const Network::Address::InstanceConstSharedPtr& address, Network::Socket::Type socket_type, const Network::Socket::OptionsSharedPtr& options, const ListenSocketCreationParams& params) -> Network::SocketSharedPtr { EXPECT_CALL(server_, hotRestart).Times(0); - // When bind_to_port is equal to false, create socket fd directly, and do not get socket - // fd through hot restart. - ON_CALL(os_sys_calls_, socket(AF_INET, _, 0)).WillByDefault(Return(syscall_result)); + // When bind_to_port is equal to false, the BSD socket is not created at main thread. + EXPECT_CALL(os_sys_calls_, socket(AF_INET, _, 0)).Times(0); return real_listener_factory.createListenSocket(address, socket_type, options, params); })); EXPECT_CALL(listener_foo->target_, initialize()); @@ -1697,6 +1663,60 @@ bind_to_port: false EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); } +TEST_F(ListenerManagerImplTest, UpdateBindToPortEqualToFalse) { + InSequence s; + auto mock_interface = std::make_unique( + std::vector{Network::Address::IpVersion::v4}); + StackedScopedInjectableLoader new_interface(std::move(mock_interface)); + + ProdListenerComponentFactory real_listener_factory(server_); + EXPECT_CALL(*worker_, start(_, _)); + manager_->startWorkers(guard_dog_, callback_.AsStdFunction()); + const std::string listener_foo_yaml = R"EOF( +name: foo +address: + socket_address: + address: 127.0.0.1 + port_value: 1234 +bind_to_port: false +reuse_port: false +filter_chains: +- filters: [] + )EOF"; + + ListenerHandle* listener_foo = expectListenerCreate(false, true); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, ListenSocketCreationParams(false))) + .WillOnce(Invoke([this, &real_listener_factory]( + const Network::Address::InstanceConstSharedPtr& address, + Network::Socket::Type socket_type, + const Network::Socket::OptionsSharedPtr& options, + const ListenSocketCreationParams& params) -> Network::SocketSharedPtr { + EXPECT_CALL(server_, hotRestart).Times(0); + // When bind_to_port is equal to false, the BSD socket is not created at main thread. + EXPECT_CALL(os_sys_calls_, socket(AF_INET, _, 0)).Times(0); + return real_listener_factory.createListenSocket(address, socket_type, options, params); + })); + EXPECT_CALL(*worker_, addListener(_, _, _)); + EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); + + worker_->callAddCompletion(true); + + EXPECT_CALL(*listener_foo->drain_manager_, drainClose()).WillOnce(Return(false)); + EXPECT_CALL(server_.drain_manager_, drainClose()).WillOnce(Return(false)); + EXPECT_FALSE(listener_foo->context_->drainDecision().drainClose()); + + EXPECT_CALL(*worker_, stopListener(_, _)); + EXPECT_CALL(*listener_foo->drain_manager_, startDrainSequence(_)); + + EXPECT_TRUE(manager_->removeListener("foo")); + + EXPECT_CALL(*worker_, removeListener(_, _)); + listener_foo->drain_manager_->drain_sequence_completion_(); + + EXPECT_CALL(*listener_foo, onDestroy()); + worker_->callRemovalCompletion(); +} + TEST_F(ListenerManagerImplTest, DEPRECATED_FEATURE_TEST(DeprecatedBindToPortEqualToFalse)) { InSequence s; ProdListenerComponentFactory real_listener_factory(server_); diff --git a/tools/dependency/requirements.txt b/tools/dependency/requirements.txt index e7e6d5c74ae05..9c8d43a9e9343 100644 --- a/tools/dependency/requirements.txt +++ b/tools/dependency/requirements.txt @@ -73,9 +73,9 @@ idna==2.10 \ # via # -r tools/dependency/requirements.txt # requests -packaging==20.9 \ - --hash=sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5 \ - --hash=sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a +packaging==21.0 \ + --hash=sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14 \ + --hash=sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7 # via -r tools/dependency/requirements.txt pycparser==2.20 \ --hash=sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0 \ @@ -162,9 +162,9 @@ six==1.16.0 \ --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 \ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 # via pynacl -urllib3==1.26.5 \ - --hash=sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c \ - --hash=sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098 +urllib3==1.26.6 \ + --hash=sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4 \ + --hash=sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f # via # -r tools/dependency/requirements.txt # requests diff --git a/tools/deprecate_version/requirements.txt b/tools/deprecate_version/requirements.txt index 95482317a8451..e64b21c2feb4c 100644 --- a/tools/deprecate_version/requirements.txt +++ b/tools/deprecate_version/requirements.txt @@ -125,9 +125,9 @@ smmap==4.0.0 \ # via # -r tools/deprecate_version/requirements.txt # gitdb -urllib3==1.26.5 \ - --hash=sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c \ - --hash=sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098 +urllib3==1.26.6 \ + --hash=sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4 \ + --hash=sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f # via # -r tools/deprecate_version/requirements.txt # requests diff --git a/tools/docs/requirements.txt b/tools/docs/requirements.txt index fd1e8e523334a..79091ebff34eb 100644 --- a/tools/docs/requirements.txt +++ b/tools/docs/requirements.txt @@ -32,9 +32,9 @@ colorama==0.4.4 \ --hash=sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b \ --hash=sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2 # via -r tools/docs/requirements.txt -docutils==0.16 \ - --hash=sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af \ - --hash=sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc +docutils==0.17.1 \ + --hash=sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61 \ + --hash=sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125 # via # -r tools/docs/requirements.txt # sphinx @@ -106,9 +106,9 @@ markupsafe==2.0.1 \ # via # -r tools/docs/requirements.txt # jinja2 -packaging==20.9 \ - --hash=sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5 \ - --hash=sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a +packaging==21.0 \ + --hash=sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14 \ + --hash=sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7 # via # -r tools/docs/requirements.txt # sphinx @@ -186,9 +186,9 @@ snowballstemmer==2.1.0 \ # via # -r tools/docs/requirements.txt # sphinx -sphinx-copybutton==0.3.3 \ - --hash=sha256:19850e9c1ed09c899f136ce3cfa304cb3b6d2f508528c19d8f512ccc8c66b0d4 \ - --hash=sha256:3695987d5e98e3b223471aaed8aa7491e03e9bfc48ed655a91446fd5e30b6c25 +sphinx-copybutton==0.4.0 \ + --hash=sha256:4340d33c169dac6dd82dce2c83333412aa786a42dd01a81a8decac3b130dc8b0 \ + --hash=sha256:8daed13a87afd5013c3a9af3575cc4d5bec052075ccd3db243f895c07a689386 # via -r tools/docs/requirements.txt sphinx-rtd-theme==0.5.2 \ --hash=sha256:32bd3b5d13dc8186d7a42fc816a23d32e83a4827d7d9882948e7b837c232da5a \ @@ -252,9 +252,9 @@ sphinxext-rediraffe==0.2.7 \ --hash=sha256:651dcbfae5ffda9ffd534dfb8025f36120e5efb6ea1a33f5420023862b9f725d \ --hash=sha256:9e430a52d4403847f4ffb3a8dd6dfc34a9fe43525305131f52ed899743a5fd8c # via -r tools/docs/requirements.txt -urllib3==1.26.5 \ - --hash=sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c \ - --hash=sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098 +urllib3==1.26.6 \ + --hash=sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4 \ + --hash=sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f # via # -r tools/docs/requirements.txt # requests diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index f5dad4b68e77f..73e89a709bc0a 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -797,6 +797,7 @@ megamiss mem memcmp memcpy +memoize mergeable messagename metadata diff --git a/tools/testing/requirements.txt b/tools/testing/requirements.txt index 6168ab3ee6593..748f22a0efb1b 100644 --- a/tools/testing/requirements.txt +++ b/tools/testing/requirements.txt @@ -70,9 +70,9 @@ iniconfig==1.1.1 \ # via # -r tools/testing/requirements.txt # pytest -packaging==20.9 \ - --hash=sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5 \ - --hash=sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a +packaging==21.0 \ + --hash=sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14 \ + --hash=sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7 # via # -r tools/testing/requirements.txt # pytest