UNPKG

etquia

Version:

Dummy package for the grpc-node repository

246 lines (227 loc) 8.43 kB
/* * Copyright 2021 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. */ // This is a non-public, unstable API, but it's very convenient import { loadProtosWithOptionsSync } from '@grpc/proto-loader/build/src/util'; import { experimental, logVerbosity } from '@grpc/grpc-js'; import { Any__Output } from './generated/google/protobuf/Any'; import Filter = experimental.Filter; import FilterFactory = experimental.FilterFactory; import { TypedStruct__Output as TypedStruct__Output } from './generated/xds/type/v3/TypedStruct'; import { FilterConfig__Output } from './generated/envoy/config/route/v3/FilterConfig'; import { HttpFilter__Output } from './generated/envoy/extensions/filters/network/http_connection_manager/v3/HttpFilter'; const TRACER_NAME = 'http_filter'; function trace(text: string): void { experimental.trace(logVerbosity.DEBUG, TRACER_NAME, text); } const TYPED_STRUCT_UDPA_URL = 'type.googleapis.com/udpa.type.v1.TypedStruct'; const TYPED_STRUCT_UDPA_NAME = 'udpa.type.v1.TypedStruct'; const TYPED_STRUCT_XDS_URL = 'type.googleapis.com/xds.type.v3.TypedStruct'; const TYPED_STRUCT_XDS_NAME = 'xds.type.v3.TypedStruct'; const FILTER_CONFIG_URL = 'type.googleapis.com/envoy.config.route.v3.FilterConfig'; const FILTER_CONFIG_NAME = 'envoy.config.route.v3.FilterConfig'; const resourceRoot = loadProtosWithOptionsSync([ 'udpa/type/v1/typed_struct.proto', 'xds/type/v3/typed_struct.proto', 'envoy/config/route/v3/route_components.proto'], { keepCase: true, includeDirs: [ // Paths are relative to src/build __dirname + '/../../deps/xds/', __dirname + '/../../deps/envoy-api/', __dirname + '/../../deps/protoc-gen-validate/' ], } ); export interface HttpFilterConfig { typeUrl: string; config: any; } export interface HttpFilterFactoryConstructor<FilterType extends Filter> { new(config: HttpFilterConfig, overrideConfig?: HttpFilterConfig): FilterFactory<FilterType>; } export interface HttpFilterRegistryEntry { parseTopLevelFilterConfig(encodedConfig: Any__Output): HttpFilterConfig | null; parseOverrideFilterConfig(encodedConfig: Any__Output): HttpFilterConfig | null; httpFilterConstructor: HttpFilterFactoryConstructor<Filter>; } const FILTER_REGISTRY = new Map<string, HttpFilterRegistryEntry>(); export function registerHttpFilter(typeName: string, entry: HttpFilterRegistryEntry) { trace('Registered filter with type URL ' + typeName); FILTER_REGISTRY.set(typeName, entry); } const toObjectOptions = { longs: String, enums: String, defaults: true, oneofs: true } function parseAnyMessage<MessageType>(message: Any__Output): MessageType | null { const typeName = message.type_url.substring(message.type_url.lastIndexOf('/') + 1); const messageType = resourceRoot.lookup(typeName); if (messageType) { const decodedMessage = (messageType as any).decode(message.value); return decodedMessage.$type.toObject(decodedMessage, toObjectOptions) as MessageType; } else { return null; } } export function getTopLevelFilterUrl(encodedConfig: Any__Output): string { let typeUrl: string; if (encodedConfig.type_url === TYPED_STRUCT_UDPA_URL || encodedConfig.type_url === TYPED_STRUCT_XDS_URL) { const typedStruct = parseAnyMessage<TypedStruct__Output>(encodedConfig) if (typedStruct) { return typedStruct.type_url; } else { throw new Error('Failed to parse TypedStruct'); } } else { return encodedConfig.type_url; } } export function validateTopLevelFilter(httpFilter: HttpFilter__Output): boolean { if (!httpFilter.typed_config) { trace(httpFilter.name + ' validation failed: typed_config unset'); return false; } const encodedConfig = httpFilter.typed_config; let typeUrl: string; try { typeUrl = getTopLevelFilterUrl(encodedConfig); } catch (e) { trace(httpFilter.name + ' validation failed with error ' + e.message); return false; } const registryEntry = FILTER_REGISTRY.get(typeUrl); if (registryEntry) { const parsedConfig = registryEntry.parseTopLevelFilterConfig(encodedConfig); if (parsedConfig === null) { trace(httpFilter.name + ' validation failed: config parsing failed'); } return parsedConfig !== null; } else { if (httpFilter.is_optional) { return true; } else { trace(httpFilter.name + ' validation failed: filter is not optional and registry does not contain type URL ' + typeUrl); return false; } } } export function validateOverrideFilter(encodedConfig: Any__Output): boolean { let typeUrl: string; let realConfig: Any__Output; let isOptional = false; if (encodedConfig.type_url === FILTER_CONFIG_URL) { const filterConfig = parseAnyMessage<FilterConfig__Output>(encodedConfig); if (filterConfig) { isOptional = filterConfig.is_optional; if (filterConfig.config) { realConfig = filterConfig.config; } else { trace('Override filter validation failed: FilterConfig config field is empty'); return false; } } else { trace('Override filter validation failed: failed to parse FilterConfig message'); return false; } } else { realConfig = encodedConfig; } if (realConfig.type_url === TYPED_STRUCT_UDPA_URL || realConfig.type_url === TYPED_STRUCT_XDS_URL) { const typedStruct = parseAnyMessage<TypedStruct__Output>(encodedConfig); if (typedStruct) { typeUrl = typedStruct.type_url; } else { trace('Override filter validation failed: failed to parse TypedStruct message'); return false; } } else { typeUrl = realConfig.type_url; } const registryEntry = FILTER_REGISTRY.get(typeUrl); if (registryEntry) { const parsedConfig = registryEntry.parseOverrideFilterConfig(encodedConfig); if (parsedConfig === null) { trace('Override filter validation failed: config parsing failed. Type URL: ' + typeUrl); } return parsedConfig !== null; } else { if (isOptional) { return true; } else { trace('Override filter validation failed: filter is not optional and registry does not contain type URL ' + typeUrl); return false; } } } export function parseTopLevelFilterConfig(encodedConfig: Any__Output) { let typeUrl: string; try { typeUrl = getTopLevelFilterUrl(encodedConfig); } catch (e) { return null; } const registryEntry = FILTER_REGISTRY.get(typeUrl); if (registryEntry) { return registryEntry.parseTopLevelFilterConfig(encodedConfig); } else { // Filter type URL not found in registry return null; } } export function parseOverrideFilterConfig(encodedConfig: Any__Output) { let typeUrl: string; let realConfig: Any__Output; if (encodedConfig.type_url === FILTER_CONFIG_URL) { const filterConfig = parseAnyMessage<FilterConfig__Output>(encodedConfig); if (filterConfig) { if (filterConfig.config) { realConfig = filterConfig.config; } else { return null; } } else { return null; } } else { realConfig = encodedConfig; } if (realConfig.type_url === TYPED_STRUCT_UDPA_URL || realConfig.type_url === TYPED_STRUCT_XDS_URL) { const typedStruct = parseAnyMessage<TypedStruct__Output>(encodedConfig); if (typedStruct) { typeUrl = typedStruct.type_url; } else { return null; } } else { typeUrl = realConfig.type_url; } const registryEntry = FILTER_REGISTRY.get(typeUrl); if (registryEntry) { return registryEntry.parseOverrideFilterConfig(encodedConfig); } else { return null; } } export function createHttpFilter(config: HttpFilterConfig, overrideConfig?: HttpFilterConfig): FilterFactory<Filter> | null { const registryEntry = FILTER_REGISTRY.get(config.typeUrl); if (registryEntry) { return new registryEntry.httpFilterConstructor(config, overrideConfig); } else { return null; } }