/* * Copyright 2024 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { Listener__Output } from "./generated/envoy/config/listener/v3/Listener"; import { FilterChain__Output } from "./generated/envoy/config/listener/v3/FilterChain"; import { UInt32Value__Output } from "./generated/google/protobuf/UInt32Value"; import { CidrRange__Output } from "./generated/envoy/config/core/v3/CidrRange"; import { decodeSingleResource, DOWNSTREAM_TLS_CONTEXT_TYPE_URL } from "./resources"; function nullableValueEquals(first: T | null, second: T | null, valueEquals: (a: T, b: T) => boolean): boolean { if (first === null && second === null) { return true; } if (first === null && second === null) { return false; } return valueEquals(first!, second!); } function arrayEquals(first: T[], second: T[], elementEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean { if (first.length !== second.length) { return false; } for (let i = 0; i < first.length; i++) { if (!elementEquals(first[i], second[i])) { return false; } } return true; } function uint32ValueEquals(first: UInt32Value__Output, second: UInt32Value__Output): boolean { return first.value === second.value; } function cidrRangeEquals(first: CidrRange__Output, second: CidrRange__Output): boolean { return first.address_prefix === second.address_prefix && nullableValueEquals(first.prefix_len, second.prefix_len, uint32ValueEquals); } function filterChainsEquivalent(first: FilterChain__Output, second: FilterChain__Output): boolean { if (first.filters.length !== second.filters.length) { return false; } for (let i = 0; i < first.filters.length; i++) { const firstFilter = first.filters[i]; const secondFilter = second.filters[i]; if (!firstFilter.typed_config && !secondFilter.typed_config) { continue; } if (!firstFilter.typed_config || !secondFilter.typed_config) { return false; } if (firstFilter.typed_config.type_url !== secondFilter.typed_config.type_url) { return false; } if (!firstFilter.typed_config.value.equals(secondFilter.typed_config.value)) { return false; } } if ((first.filter_chain_match === null) !== (second.filter_chain_match === null)) { return false; } if (first.filter_chain_match) { const firstMatch = first.filter_chain_match; const secondMatch = second.filter_chain_match!; if (firstMatch.address_suffix !== secondMatch.address_suffix) { return false; } if (!arrayEquals(firstMatch.application_protocols, secondMatch.application_protocols)) { return false; } if (!nullableValueEquals(firstMatch.destination_port, secondMatch.destination_port, uint32ValueEquals)) { return false; } if (!arrayEquals(firstMatch.direct_source_prefix_ranges, secondMatch.direct_source_prefix_ranges, cidrRangeEquals)) { return false; } if (!arrayEquals(firstMatch.prefix_ranges, secondMatch.prefix_ranges, cidrRangeEquals)) { return false; } if (!arrayEquals(firstMatch.server_names, secondMatch.server_names)) { return false; } if (!arrayEquals(firstMatch.source_ports, secondMatch.source_ports)) { return false; } if (!arrayEquals(firstMatch.source_prefix_ranges, secondMatch.source_prefix_ranges, cidrRangeEquals)) { return false; } if (firstMatch.source_type !== secondMatch.source_type) { return false; } if (!nullableValueEquals(firstMatch.suffix_len, secondMatch.suffix_len, uint32ValueEquals)) { return false; } if (firstMatch.transport_protocol !== secondMatch.transport_protocol) { return false; } } if ((first.transport_socket === null) !== (second.transport_socket !== null)) { return false; } if (first.transport_socket) { const firstTlsContext = decodeSingleResource(DOWNSTREAM_TLS_CONTEXT_TYPE_URL, first.transport_socket.typed_config!.value); const secondTlsContext = decodeSingleResource(DOWNSTREAM_TLS_CONTEXT_TYPE_URL, second.transport_socket!.typed_config!.value); const firstCommonContext = firstTlsContext.common_tls_context!; const secondCommonContext = secondTlsContext.common_tls_context!; if (firstCommonContext.tls_certificate_provider_instance?.instance_name !== secondCommonContext.tls_certificate_provider_instance?.instance_name) { return false; } if (firstCommonContext.validation_context_type !== secondCommonContext.validation_context_type) { return false; } switch (firstCommonContext.validation_context_type) { case 'validation_context': if (firstCommonContext.validation_context?.ca_certificate_provider_instance?.instance_name !== secondCommonContext.validation_context?.ca_certificate_provider_instance?.instance_name) { return false; } break; case 'combined_validation_context': if (firstCommonContext.combined_validation_context?.default_validation_context?.ca_certificate_provider_instance?.instance_name !== secondCommonContext.validation_context?.ca_certificate_provider_instance?.instance_name) { return false; } break; default: return false; } if (firstTlsContext.require_client_certificate?.value !== secondTlsContext.require_client_certificate?.value) { return false; } } return true; } /** * Tests whether two listener resources are equivalent with respect to the * fields that the server uses. * @param first * @param second */ export function listenersEquivalent(first: Listener__Output, second: Listener__Output): boolean { if (first.address?.socket_address?.address !== second.address?.socket_address?.address) { return false; } if (first.address?.socket_address?.port_value !== second.address?.socket_address?.port_value) { return false; } if (!nullableValueEquals(first.default_filter_chain, second.default_filter_chain, filterChainsEquivalent)) { return false; } if (first.filter_chains.length !== second.filter_chains.length) { return false; } for (let i = 0; i < first.filter_chains.length; i++) { if (!filterChainsEquivalent(first.filter_chains[i], second.filter_chains[i])) { return false; } } return true; }