Merge pull request #2465 from dancrumb/chore/#2464

Convert to eslint/prettier
This commit is contained in:
Michael Lumish 2023-06-15 15:34:56 -07:00 committed by GitHub
commit dbaaa89a08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
71 changed files with 4270 additions and 3249 deletions

View File

@ -1,11 +1,61 @@
{
"root": true,
"extends": "./node_modules/gts",
"extends": [
"eslint:recommended",
"plugin:node/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
],
"plugins": ["node", "prettier", "@typescript-eslint"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"ignorePatterns": ["**/generated/**", "**/node_modules/**", "**/build/**"],
"rules": {
"node/no-unpublished-import": ["error", {
"node/no-unpublished-import": [
"error",
{
"tryExtensions": [".ts", ".js", ".json", ".node"]
}],
}
],
"@typescript-eslint/no-unused-vars": "off",
"node/no-unpublished-require": "off"
"node/no-unpublished-require": "off",
"prettier/prettier": "error",
"block-scoped-var": "error",
"eqeqeq": "error",
"no-var": "error",
"prefer-const": "error",
"no-case-declarations": "warn",
"no-restricted-properties": [
"error",
{
"object": "describe",
"property": "only"
},
{
"object": "it",
"property": "only"
}
],
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-warning-comments": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/camelcase": "off",
"node/no-missing-import": "off",
"node/no-empty-function": "off",
"node/no-unsupported-features/es-syntax": "off",
"node/no-missing-require": "off",
"node/shebang": "off",
"no-dupe-class-members": "off",
"require-atomic-updates": "off"
}
}

View File

@ -0,0 +1,4 @@
The following files contain configuration settings that were derived from [the this commit](https://github.com/google/gts/commit/3b9ab6dd59691f77f5c5c632a44c6762ba4ef7c6) in the Google GTS repository:
- .eslintrc
- prettier.config.js
- tsconfig.json

View File

@ -1,971 +0,0 @@
{
O: [Getter/Setter],
outDir: [Getter/Setter],
'out-dir': [Getter/Setter],
_: [
'envoy/service/discovery/v2/ads.proto',
'envoy/api/v2/listener.proto',
'envoy/api/v2/route.proto',
'envoy/api/v2/cluster.proto',
'envoy/api/v2/endpoint.proto'
],
keepCase: true,
'keep-case': true,
longs: [Function: String],
enums: [Function: String],
defaults: true,
oneofs: true,
json: true,
includeDirs: [
'deps/envoy-api/',
'deps/udpa/',
'node_modules/protobufjs/',
'deps/googleapis/',
'deps/protoc-gen-validate/'
],
I: [
'deps/envoy-api/',
'deps/udpa/',
'node_modules/protobufjs/',
'deps/googleapis/',
'deps/protoc-gen-validate/'
],
'include-dirs': [
'deps/envoy-api/',
'deps/udpa/',
'node_modules/protobufjs/',
'deps/googleapis/',
'deps/protoc-gen-validate/'
],
grpcLib: '../index',
'grpc-lib': '../index',
'$0': 'node_modules/.bin/proto-loader-gen-types'
}
Processing envoy/service/discovery/v2/ads.proto
Writing src/generated//ads.d.ts
Writing src/generated//envoy/service/discovery/v2/AdsDummy.d.ts from file deps/envoy-api/envoy/service/discovery/v2/ads.proto
Writing src/generated//envoy/api/v2/DiscoveryRequest.d.ts from file deps/envoy-api/envoy/api/v2/discovery.proto
Writing src/generated//envoy/api/v2/DiscoveryResponse.d.ts from file deps/envoy-api/envoy/api/v2/discovery.proto
Writing src/generated//envoy/api/v2/DeltaDiscoveryRequest.d.ts from file deps/envoy-api/envoy/api/v2/discovery.proto
Writing src/generated//envoy/api/v2/DeltaDiscoveryResponse.d.ts from file deps/envoy-api/envoy/api/v2/discovery.proto
Writing src/generated//envoy/api/v2/Resource.d.ts from file deps/envoy-api/envoy/api/v2/discovery.proto
Writing src/generated//envoy/api/v2/core/RoutingPriority.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RequestMethod.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/TrafficDirection.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Locality.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/BuildVersion.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Extension.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Node.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Metadata.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RuntimeUInt32.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RuntimeDouble.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RuntimeFeatureFlag.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/HeaderValue.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/HeaderValueOption.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/HeaderMap.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/DataSource.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RetryPolicy.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RemoteDataSource.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/AsyncDataSource.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/TransportSocket.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RuntimeFractionalPercent.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/ControlPlane.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Pipe.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/SocketAddress.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/SocketAddress/Protocol.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/TcpKeepalive.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/BindConfig.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/Address.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/CidrRange.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/BackoffStrategy.d.ts from file deps/envoy-api/envoy/api/v2/core/backoff.proto
Writing src/generated//envoy/api/v2/core/SocketOption.d.ts from file deps/envoy-api/envoy/api/v2/core/socket_option.proto
Writing src/generated//envoy/api/v2/core/SocketOption/SocketState.d.ts from file deps/envoy-api/envoy/api/v2/core/socket_option.proto
Writing src/generated//envoy/api/v2/core/HttpUri.d.ts from file deps/envoy-api/envoy/api/v2/core/http_uri.proto
Writing src/generated//envoy/type/Percent.d.ts from file deps/envoy-api/envoy/type/percent.proto
Writing src/generated//envoy/type/FractionalPercent.d.ts from file deps/envoy-api/envoy/type/percent.proto
Writing src/generated//envoy/type/FractionalPercent/DenominatorType.d.ts from file deps/envoy-api/envoy/type/percent.proto
Writing src/generated//envoy/type/SemanticVersion.d.ts from file deps/envoy-api/envoy/type/semantic_version.proto
Writing src/generated//udpa/annotations/PackageVersionStatus.d.ts from file deps/udpa/udpa/annotations/status.proto
Writing src/generated//udpa/annotations/StatusAnnotation.d.ts from file deps/udpa/udpa/annotations/status.proto
Writing src/generated//udpa/annotations/MigrateAnnotation.d.ts from file deps/udpa/udpa/annotations/migrate.proto
Writing src/generated//udpa/annotations/FieldMigrateAnnotation.d.ts from file deps/udpa/udpa/annotations/migrate.proto
Writing src/generated//udpa/annotations/FileMigrateAnnotation.d.ts from file deps/udpa/udpa/annotations/migrate.proto
Writing src/generated//validate/FieldRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/FloatRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/DoubleRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/Int32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/Int64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/UInt32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/UInt64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/SInt32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/SInt64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/Fixed32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/Fixed64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/SFixed32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/SFixed64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/BoolRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/StringRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/KnownRegex.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/BytesRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/EnumRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/MessageRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/RepeatedRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/MapRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/AnyRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/DurationRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/TimestampRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//google/protobuf/Any.d.ts from file null
Writing src/generated//google/protobuf/Duration.d.ts from file null
Writing src/generated//google/protobuf/Struct.d.ts from file null
Writing src/generated//google/protobuf/Value.d.ts from file null
Writing src/generated//google/protobuf/NullValue.d.ts from file null
Writing src/generated//google/protobuf/ListValue.d.ts from file null
Writing src/generated//google/protobuf/DoubleValue.d.ts from file null
Writing src/generated//google/protobuf/FloatValue.d.ts from file null
Writing src/generated//google/protobuf/Int64Value.d.ts from file null
Writing src/generated//google/protobuf/UInt64Value.d.ts from file null
Writing src/generated//google/protobuf/Int32Value.d.ts from file null
Writing src/generated//google/protobuf/UInt32Value.d.ts from file null
Writing src/generated//google/protobuf/BoolValue.d.ts from file null
Writing src/generated//google/protobuf/StringValue.d.ts from file null
Writing src/generated//google/protobuf/BytesValue.d.ts from file null
Writing src/generated//google/protobuf/FileDescriptorSet.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FileDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/DescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/DescriptorProto/ExtensionRange.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/DescriptorProto/ReservedRange.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldDescriptorProto/Type.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldDescriptorProto/Label.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/OneofDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/EnumDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/EnumValueDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/ServiceDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/MethodDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FileOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FileOptions/OptimizeMode.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/MessageOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldOptions/CType.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldOptions/JSType.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/OneofOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/EnumOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/EnumValueOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/ServiceOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/MethodOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/UninterpretedOption.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/UninterpretedOption/NamePart.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/SourceCodeInfo.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/SourceCodeInfo/Location.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/GeneratedCodeInfo.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/GeneratedCodeInfo/Annotation.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/Timestamp.d.ts from file null
Writing src/generated//google/rpc/Status.d.ts from file deps/googleapis/google/rpc/status.proto
Processing envoy/api/v2/listener.proto
Writing src/generated//listener.d.ts
Writing src/generated//envoy/api/v2/Listener.d.ts from file deps/envoy-api/envoy/api/v2/listener.proto
Writing src/generated//envoy/api/v2/Listener/DrainType.d.ts from file deps/envoy-api/envoy/api/v2/listener.proto
Writing src/generated//envoy/api/v2/Listener/DeprecatedV1.d.ts from file deps/envoy-api/envoy/api/v2/listener.proto
Writing src/generated//envoy/api/v2/Listener/ConnectionBalanceConfig.d.ts from file deps/envoy-api/envoy/api/v2/listener.proto
Writing src/generated//envoy/api/v2/Listener/ConnectionBalanceConfig/ExactBalance.d.ts from file deps/envoy-api/envoy/api/v2/listener.proto
Writing src/generated//envoy/api/v2/listener/Filter.d.ts from file deps/envoy-api/envoy/api/v2/listener/listener_components.proto
Writing src/generated//envoy/api/v2/listener/FilterChainMatch.d.ts from file deps/envoy-api/envoy/api/v2/listener/listener_components.proto
Writing src/generated//envoy/api/v2/listener/FilterChainMatch/ConnectionSourceType.d.ts from file deps/envoy-api/envoy/api/v2/listener/listener_components.proto
Writing src/generated//envoy/api/v2/listener/FilterChain.d.ts from file deps/envoy-api/envoy/api/v2/listener/listener_components.proto
Writing src/generated//envoy/api/v2/listener/ListenerFilterChainMatchPredicate.d.ts from file deps/envoy-api/envoy/api/v2/listener/listener_components.proto
Writing src/generated//envoy/api/v2/listener/ListenerFilterChainMatchPredicate/MatchSet.d.ts from file deps/envoy-api/envoy/api/v2/listener/listener_components.proto
Writing src/generated//envoy/api/v2/listener/ListenerFilter.d.ts from file deps/envoy-api/envoy/api/v2/listener/listener_components.proto
Writing src/generated//envoy/api/v2/listener/UdpListenerConfig.d.ts from file deps/envoy-api/envoy/api/v2/listener/udp_listener_config.proto
Writing src/generated//envoy/api/v2/listener/ActiveRawUdpListenerConfig.d.ts from file deps/envoy-api/envoy/api/v2/listener/udp_listener_config.proto
Writing src/generated//envoy/api/v2/core/SocketOption.d.ts from file deps/envoy-api/envoy/api/v2/core/socket_option.proto
Writing src/generated//envoy/api/v2/core/SocketOption/SocketState.d.ts from file deps/envoy-api/envoy/api/v2/core/socket_option.proto
Writing src/generated//envoy/api/v2/core/RoutingPriority.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RequestMethod.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/TrafficDirection.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Locality.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/BuildVersion.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Extension.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Node.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Metadata.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RuntimeUInt32.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RuntimeDouble.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RuntimeFeatureFlag.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/HeaderValue.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/HeaderValueOption.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/HeaderMap.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/DataSource.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RetryPolicy.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RemoteDataSource.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/AsyncDataSource.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/TransportSocket.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RuntimeFractionalPercent.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/ControlPlane.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Pipe.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/SocketAddress.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/SocketAddress/Protocol.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/TcpKeepalive.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/BindConfig.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/Address.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/CidrRange.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/BackoffStrategy.d.ts from file deps/envoy-api/envoy/api/v2/core/backoff.proto
Writing src/generated//envoy/api/v2/core/HttpUri.d.ts from file deps/envoy-api/envoy/api/v2/core/http_uri.proto
Writing src/generated//envoy/api/v2/core/ApiVersion.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/ApiConfigSource.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/ApiConfigSource/ApiType.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/AggregatedConfigSource.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/SelfConfigSource.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/RateLimitSettings.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/ConfigSource.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/GrpcService.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/EnvoyGrpc.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/SslCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/GoogleLocalCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/ChannelCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/CallCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/CallCredentials/ServiceAccountJWTAccessCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/CallCredentials/GoogleIAMCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/CallCredentials/MetadataCredentialsFromPlugin.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/CallCredentials/StsService.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/auth/UpstreamTlsContext.d.ts from file deps/envoy-api/envoy/api/v2/auth/tls.proto
Writing src/generated//envoy/api/v2/auth/DownstreamTlsContext.d.ts from file deps/envoy-api/envoy/api/v2/auth/tls.proto
Writing src/generated//envoy/api/v2/auth/CommonTlsContext.d.ts from file deps/envoy-api/envoy/api/v2/auth/tls.proto
Writing src/generated//envoy/api/v2/auth/CommonTlsContext/CombinedCertificateValidationContext.d.ts from file deps/envoy-api/envoy/api/v2/auth/tls.proto
Writing src/generated//envoy/api/v2/auth/GenericSecret.d.ts from file deps/envoy-api/envoy/api/v2/auth/secret.proto
Writing src/generated//envoy/api/v2/auth/SdsSecretConfig.d.ts from file deps/envoy-api/envoy/api/v2/auth/secret.proto
Writing src/generated//envoy/api/v2/auth/Secret.d.ts from file deps/envoy-api/envoy/api/v2/auth/secret.proto
Writing src/generated//envoy/api/v2/auth/TlsParameters.d.ts from file deps/envoy-api/envoy/api/v2/auth/common.proto
Writing src/generated//envoy/api/v2/auth/TlsParameters/TlsProtocol.d.ts from file deps/envoy-api/envoy/api/v2/auth/common.proto
Writing src/generated//envoy/api/v2/auth/PrivateKeyProvider.d.ts from file deps/envoy-api/envoy/api/v2/auth/common.proto
Writing src/generated//envoy/api/v2/auth/TlsCertificate.d.ts from file deps/envoy-api/envoy/api/v2/auth/common.proto
Writing src/generated//envoy/api/v2/auth/TlsSessionTicketKeys.d.ts from file deps/envoy-api/envoy/api/v2/auth/common.proto
Writing src/generated//envoy/api/v2/auth/CertificateValidationContext.d.ts from file deps/envoy-api/envoy/api/v2/auth/common.proto
Writing src/generated//envoy/api/v2/auth/CertificateValidationContext/TrustChainVerification.d.ts from file deps/envoy-api/envoy/api/v2/auth/common.proto
Writing src/generated//envoy/api/v2/route/VirtualHost.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/VirtualHost/TlsRequirementType.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/FilterAction.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/Route.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/WeightedCluster.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/WeightedCluster/ClusterWeight.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteMatch.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteMatch/GrpcRouteMatchOptions.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteMatch/TlsContextMatchOptions.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/CorsPolicy.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction/ClusterNotFoundResponseCode.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction/InternalRedirectAction.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction/RequestMirrorPolicy.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction/HashPolicy.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction/HashPolicy/Header.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction/HashPolicy/Cookie.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction/HashPolicy/ConnectionProperties.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction/HashPolicy/QueryParameter.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction/HashPolicy/FilterState.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction/UpgradeConfig.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RetryPolicy.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RetryPolicy/RetryPriority.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RetryPolicy/RetryHostPredicate.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RetryPolicy/RetryBackOff.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/HedgePolicy.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RedirectAction.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RedirectAction/RedirectResponseCode.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/DirectResponseAction.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/Decorator.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/Tracing.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/VirtualCluster.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RateLimit.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RateLimit/Action.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RateLimit/Action/SourceCluster.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RateLimit/Action/DestinationCluster.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RateLimit/Action/RequestHeaders.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RateLimit/Action/RemoteAddress.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RateLimit/Action/GenericKey.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RateLimit/Action/HeaderValueMatch.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/HeaderMatcher.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/QueryParameterMatcher.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/config/listener/v2/ApiListener.d.ts from file deps/envoy-api/envoy/config/listener/v2/api_listener.proto
Writing src/generated//envoy/config/filter/accesslog/v2/AccessLog.d.ts from file deps/envoy-api/envoy/config/filter/accesslog/v2/accesslog.proto
Writing src/generated//envoy/config/filter/accesslog/v2/AccessLogFilter.d.ts from file deps/envoy-api/envoy/config/filter/accesslog/v2/accesslog.proto
Writing src/generated//envoy/config/filter/accesslog/v2/ComparisonFilter.d.ts from file deps/envoy-api/envoy/config/filter/accesslog/v2/accesslog.proto
Writing src/generated//envoy/config/filter/accesslog/v2/ComparisonFilter/Op.d.ts from file deps/envoy-api/envoy/config/filter/accesslog/v2/accesslog.proto
Writing src/generated//envoy/config/filter/accesslog/v2/StatusCodeFilter.d.ts from file deps/envoy-api/envoy/config/filter/accesslog/v2/accesslog.proto
Writing src/generated//envoy/config/filter/accesslog/v2/DurationFilter.d.ts from file deps/envoy-api/envoy/config/filter/accesslog/v2/accesslog.proto
Writing src/generated//envoy/config/filter/accesslog/v2/NotHealthCheckFilter.d.ts from file deps/envoy-api/envoy/config/filter/accesslog/v2/accesslog.proto
Writing src/generated//envoy/config/filter/accesslog/v2/TraceableFilter.d.ts from file deps/envoy-api/envoy/config/filter/accesslog/v2/accesslog.proto
Writing src/generated//envoy/config/filter/accesslog/v2/RuntimeFilter.d.ts from file deps/envoy-api/envoy/config/filter/accesslog/v2/accesslog.proto
Writing src/generated//envoy/config/filter/accesslog/v2/AndFilter.d.ts from file deps/envoy-api/envoy/config/filter/accesslog/v2/accesslog.proto
Writing src/generated//envoy/config/filter/accesslog/v2/OrFilter.d.ts from file deps/envoy-api/envoy/config/filter/accesslog/v2/accesslog.proto
Writing src/generated//envoy/config/filter/accesslog/v2/HeaderFilter.d.ts from file deps/envoy-api/envoy/config/filter/accesslog/v2/accesslog.proto
Writing src/generated//envoy/config/filter/accesslog/v2/ResponseFlagFilter.d.ts from file deps/envoy-api/envoy/config/filter/accesslog/v2/accesslog.proto
Writing src/generated//envoy/config/filter/accesslog/v2/GrpcStatusFilter.d.ts from file deps/envoy-api/envoy/config/filter/accesslog/v2/accesslog.proto
Writing src/generated//envoy/config/filter/accesslog/v2/GrpcStatusFilter/Status.d.ts from file deps/envoy-api/envoy/config/filter/accesslog/v2/accesslog.proto
Writing src/generated//envoy/config/filter/accesslog/v2/ExtensionFilter.d.ts from file deps/envoy-api/envoy/config/filter/accesslog/v2/accesslog.proto
Writing src/generated//envoy/type/Percent.d.ts from file deps/envoy-api/envoy/type/percent.proto
Writing src/generated//envoy/type/FractionalPercent.d.ts from file deps/envoy-api/envoy/type/percent.proto
Writing src/generated//envoy/type/FractionalPercent/DenominatorType.d.ts from file deps/envoy-api/envoy/type/percent.proto
Writing src/generated//envoy/type/SemanticVersion.d.ts from file deps/envoy-api/envoy/type/semantic_version.proto
Writing src/generated//envoy/type/Int64Range.d.ts from file deps/envoy-api/envoy/type/range.proto
Writing src/generated//envoy/type/Int32Range.d.ts from file deps/envoy-api/envoy/type/range.proto
Writing src/generated//envoy/type/DoubleRange.d.ts from file deps/envoy-api/envoy/type/range.proto
Writing src/generated//envoy/type/matcher/RegexMatcher.d.ts from file deps/envoy-api/envoy/type/matcher/regex.proto
Writing src/generated//envoy/type/matcher/RegexMatcher/GoogleRE2.d.ts from file deps/envoy-api/envoy/type/matcher/regex.proto
Writing src/generated//envoy/type/matcher/RegexMatchAndSubstitute.d.ts from file deps/envoy-api/envoy/type/matcher/regex.proto
Writing src/generated//envoy/type/matcher/StringMatcher.d.ts from file deps/envoy-api/envoy/type/matcher/string.proto
Writing src/generated//envoy/type/matcher/ListStringMatcher.d.ts from file deps/envoy-api/envoy/type/matcher/string.proto
Writing src/generated//envoy/type/tracing/v2/CustomTag.d.ts from file deps/envoy-api/envoy/type/tracing/v2/custom_tag.proto
Writing src/generated//envoy/type/tracing/v2/CustomTag/Literal.d.ts from file deps/envoy-api/envoy/type/tracing/v2/custom_tag.proto
Writing src/generated//envoy/type/tracing/v2/CustomTag/Environment.d.ts from file deps/envoy-api/envoy/type/tracing/v2/custom_tag.proto
Writing src/generated//envoy/type/tracing/v2/CustomTag/Header.d.ts from file deps/envoy-api/envoy/type/tracing/v2/custom_tag.proto
Writing src/generated//envoy/type/tracing/v2/CustomTag/Metadata.d.ts from file deps/envoy-api/envoy/type/tracing/v2/custom_tag.proto
Writing src/generated//envoy/type/metadata/v2/MetadataKey.d.ts from file deps/envoy-api/envoy/type/metadata/v2/metadata.proto
Writing src/generated//envoy/type/metadata/v2/MetadataKey/PathSegment.d.ts from file deps/envoy-api/envoy/type/metadata/v2/metadata.proto
Writing src/generated//envoy/type/metadata/v2/MetadataKind.d.ts from file deps/envoy-api/envoy/type/metadata/v2/metadata.proto
Writing src/generated//envoy/type/metadata/v2/MetadataKind/Request.d.ts from file deps/envoy-api/envoy/type/metadata/v2/metadata.proto
Writing src/generated//envoy/type/metadata/v2/MetadataKind/Route.d.ts from file deps/envoy-api/envoy/type/metadata/v2/metadata.proto
Writing src/generated//envoy/type/metadata/v2/MetadataKind/Cluster.d.ts from file deps/envoy-api/envoy/type/metadata/v2/metadata.proto
Writing src/generated//envoy/type/metadata/v2/MetadataKind/Host.d.ts from file deps/envoy-api/envoy/type/metadata/v2/metadata.proto
Writing src/generated//udpa/annotations/MigrateAnnotation.d.ts from file deps/udpa/udpa/annotations/migrate.proto
Writing src/generated//udpa/annotations/FieldMigrateAnnotation.d.ts from file deps/udpa/udpa/annotations/migrate.proto
Writing src/generated//udpa/annotations/FileMigrateAnnotation.d.ts from file deps/udpa/udpa/annotations/migrate.proto
Writing src/generated//udpa/annotations/PackageVersionStatus.d.ts from file deps/udpa/udpa/annotations/status.proto
Writing src/generated//udpa/annotations/StatusAnnotation.d.ts from file deps/udpa/udpa/annotations/status.proto
Writing src/generated//validate/FieldRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/FloatRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/DoubleRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/Int32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/Int64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/UInt32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/UInt64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/SInt32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/SInt64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/Fixed32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/Fixed64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/SFixed32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/SFixed64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/BoolRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/StringRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/KnownRegex.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/BytesRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/EnumRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/MessageRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/RepeatedRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/MapRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/AnyRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/DurationRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/TimestampRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//google/protobuf/Duration.d.ts from file null
Writing src/generated//google/protobuf/DoubleValue.d.ts from file null
Writing src/generated//google/protobuf/FloatValue.d.ts from file null
Writing src/generated//google/protobuf/Int64Value.d.ts from file null
Writing src/generated//google/protobuf/UInt64Value.d.ts from file null
Writing src/generated//google/protobuf/Int32Value.d.ts from file null
Writing src/generated//google/protobuf/UInt32Value.d.ts from file null
Writing src/generated//google/protobuf/BoolValue.d.ts from file null
Writing src/generated//google/protobuf/StringValue.d.ts from file null
Writing src/generated//google/protobuf/BytesValue.d.ts from file null
Writing src/generated//google/protobuf/Any.d.ts from file null
Writing src/generated//google/protobuf/Struct.d.ts from file null
Writing src/generated//google/protobuf/Value.d.ts from file null
Writing src/generated//google/protobuf/NullValue.d.ts from file null
Writing src/generated//google/protobuf/ListValue.d.ts from file null
Writing src/generated//google/protobuf/Timestamp.d.ts from file null
Writing src/generated//google/protobuf/FileDescriptorSet.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FileDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/DescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/DescriptorProto/ExtensionRange.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/DescriptorProto/ReservedRange.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldDescriptorProto/Type.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldDescriptorProto/Label.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/OneofDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/EnumDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/EnumValueDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/ServiceDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/MethodDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FileOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FileOptions/OptimizeMode.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/MessageOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldOptions/CType.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldOptions/JSType.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/OneofOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/EnumOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/EnumValueOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/ServiceOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/MethodOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/UninterpretedOption.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/UninterpretedOption/NamePart.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/SourceCodeInfo.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/SourceCodeInfo/Location.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/GeneratedCodeInfo.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/GeneratedCodeInfo/Annotation.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/Empty.d.ts from file null
Writing src/generated//google/api/Http.d.ts from file node_modules/protobufjs/google/api/http.proto
Writing src/generated//google/api/HttpRule.d.ts from file node_modules/protobufjs/google/api/http.proto
Writing src/generated//google/api/CustomHttpPattern.d.ts from file node_modules/protobufjs/google/api/http.proto
Processing envoy/api/v2/route.proto
Writing src/generated//route.d.ts
Writing src/generated//envoy/api/v2/RouteConfiguration.d.ts from file deps/envoy-api/envoy/api/v2/route.proto
Writing src/generated//envoy/api/v2/Vhds.d.ts from file deps/envoy-api/envoy/api/v2/route.proto
Writing src/generated//envoy/api/v2/core/ApiVersion.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/ApiConfigSource.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/ApiConfigSource/ApiType.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/AggregatedConfigSource.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/SelfConfigSource.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/RateLimitSettings.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/ConfigSource.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/RoutingPriority.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RequestMethod.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/TrafficDirection.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Locality.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/BuildVersion.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Extension.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Node.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Metadata.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RuntimeUInt32.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RuntimeDouble.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RuntimeFeatureFlag.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/HeaderValue.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/HeaderValueOption.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/HeaderMap.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/DataSource.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RetryPolicy.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RemoteDataSource.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/AsyncDataSource.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/TransportSocket.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RuntimeFractionalPercent.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/ControlPlane.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/GrpcService.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/EnvoyGrpc.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/SslCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/GoogleLocalCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/ChannelCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/CallCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/CallCredentials/ServiceAccountJWTAccessCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/CallCredentials/GoogleIAMCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/CallCredentials/MetadataCredentialsFromPlugin.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/CallCredentials/StsService.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/BackoffStrategy.d.ts from file deps/envoy-api/envoy/api/v2/core/backoff.proto
Writing src/generated//envoy/api/v2/core/HttpUri.d.ts from file deps/envoy-api/envoy/api/v2/core/http_uri.proto
Writing src/generated//envoy/api/v2/core/SocketOption.d.ts from file deps/envoy-api/envoy/api/v2/core/socket_option.proto
Writing src/generated//envoy/api/v2/core/SocketOption/SocketState.d.ts from file deps/envoy-api/envoy/api/v2/core/socket_option.proto
Writing src/generated//envoy/api/v2/core/Pipe.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/SocketAddress.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/SocketAddress/Protocol.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/TcpKeepalive.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/BindConfig.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/Address.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/CidrRange.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/route/VirtualHost.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/VirtualHost/TlsRequirementType.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/FilterAction.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/Route.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/WeightedCluster.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/WeightedCluster/ClusterWeight.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteMatch.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteMatch/GrpcRouteMatchOptions.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteMatch/TlsContextMatchOptions.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/CorsPolicy.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction/ClusterNotFoundResponseCode.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction/InternalRedirectAction.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction/RequestMirrorPolicy.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction/HashPolicy.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction/HashPolicy/Header.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction/HashPolicy/Cookie.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction/HashPolicy/ConnectionProperties.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction/HashPolicy/QueryParameter.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction/HashPolicy/FilterState.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RouteAction/UpgradeConfig.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RetryPolicy.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RetryPolicy/RetryPriority.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RetryPolicy/RetryHostPredicate.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RetryPolicy/RetryBackOff.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/HedgePolicy.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RedirectAction.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RedirectAction/RedirectResponseCode.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/DirectResponseAction.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/Decorator.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/Tracing.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/VirtualCluster.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RateLimit.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RateLimit/Action.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RateLimit/Action/SourceCluster.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RateLimit/Action/DestinationCluster.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RateLimit/Action/RequestHeaders.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RateLimit/Action/RemoteAddress.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RateLimit/Action/GenericKey.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/RateLimit/Action/HeaderValueMatch.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/HeaderMatcher.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/api/v2/route/QueryParameterMatcher.d.ts from file deps/envoy-api/envoy/api/v2/route/route_components.proto
Writing src/generated//envoy/type/matcher/RegexMatcher.d.ts from file deps/envoy-api/envoy/type/matcher/regex.proto
Writing src/generated//envoy/type/matcher/RegexMatcher/GoogleRE2.d.ts from file deps/envoy-api/envoy/type/matcher/regex.proto
Writing src/generated//envoy/type/matcher/RegexMatchAndSubstitute.d.ts from file deps/envoy-api/envoy/type/matcher/regex.proto
Writing src/generated//envoy/type/matcher/StringMatcher.d.ts from file deps/envoy-api/envoy/type/matcher/string.proto
Writing src/generated//envoy/type/matcher/ListStringMatcher.d.ts from file deps/envoy-api/envoy/type/matcher/string.proto
Writing src/generated//envoy/type/Int64Range.d.ts from file deps/envoy-api/envoy/type/range.proto
Writing src/generated//envoy/type/Int32Range.d.ts from file deps/envoy-api/envoy/type/range.proto
Writing src/generated//envoy/type/DoubleRange.d.ts from file deps/envoy-api/envoy/type/range.proto
Writing src/generated//envoy/type/tracing/v2/CustomTag.d.ts from file deps/envoy-api/envoy/type/tracing/v2/custom_tag.proto
Writing src/generated//envoy/type/tracing/v2/CustomTag/Literal.d.ts from file deps/envoy-api/envoy/type/tracing/v2/custom_tag.proto
Writing src/generated//envoy/type/tracing/v2/CustomTag/Environment.d.ts from file deps/envoy-api/envoy/type/tracing/v2/custom_tag.proto
Writing src/generated//envoy/type/tracing/v2/CustomTag/Header.d.ts from file deps/envoy-api/envoy/type/tracing/v2/custom_tag.proto
Writing src/generated//envoy/type/tracing/v2/CustomTag/Metadata.d.ts from file deps/envoy-api/envoy/type/tracing/v2/custom_tag.proto
Writing src/generated//envoy/type/SemanticVersion.d.ts from file deps/envoy-api/envoy/type/semantic_version.proto
Writing src/generated//envoy/type/Percent.d.ts from file deps/envoy-api/envoy/type/percent.proto
Writing src/generated//envoy/type/FractionalPercent.d.ts from file deps/envoy-api/envoy/type/percent.proto
Writing src/generated//envoy/type/FractionalPercent/DenominatorType.d.ts from file deps/envoy-api/envoy/type/percent.proto
Writing src/generated//envoy/type/metadata/v2/MetadataKey.d.ts from file deps/envoy-api/envoy/type/metadata/v2/metadata.proto
Writing src/generated//envoy/type/metadata/v2/MetadataKey/PathSegment.d.ts from file deps/envoy-api/envoy/type/metadata/v2/metadata.proto
Writing src/generated//envoy/type/metadata/v2/MetadataKind.d.ts from file deps/envoy-api/envoy/type/metadata/v2/metadata.proto
Writing src/generated//envoy/type/metadata/v2/MetadataKind/Request.d.ts from file deps/envoy-api/envoy/type/metadata/v2/metadata.proto
Writing src/generated//envoy/type/metadata/v2/MetadataKind/Route.d.ts from file deps/envoy-api/envoy/type/metadata/v2/metadata.proto
Writing src/generated//envoy/type/metadata/v2/MetadataKind/Cluster.d.ts from file deps/envoy-api/envoy/type/metadata/v2/metadata.proto
Writing src/generated//envoy/type/metadata/v2/MetadataKind/Host.d.ts from file deps/envoy-api/envoy/type/metadata/v2/metadata.proto
Writing src/generated//udpa/annotations/MigrateAnnotation.d.ts from file deps/udpa/udpa/annotations/migrate.proto
Writing src/generated//udpa/annotations/FieldMigrateAnnotation.d.ts from file deps/udpa/udpa/annotations/migrate.proto
Writing src/generated//udpa/annotations/FileMigrateAnnotation.d.ts from file deps/udpa/udpa/annotations/migrate.proto
Writing src/generated//udpa/annotations/PackageVersionStatus.d.ts from file deps/udpa/udpa/annotations/status.proto
Writing src/generated//udpa/annotations/StatusAnnotation.d.ts from file deps/udpa/udpa/annotations/status.proto
Writing src/generated//validate/FieldRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/FloatRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/DoubleRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/Int32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/Int64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/UInt32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/UInt64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/SInt32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/SInt64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/Fixed32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/Fixed64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/SFixed32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/SFixed64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/BoolRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/StringRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/KnownRegex.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/BytesRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/EnumRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/MessageRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/RepeatedRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/MapRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/AnyRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/DurationRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/TimestampRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//google/protobuf/DoubleValue.d.ts from file null
Writing src/generated//google/protobuf/FloatValue.d.ts from file null
Writing src/generated//google/protobuf/Int64Value.d.ts from file null
Writing src/generated//google/protobuf/UInt64Value.d.ts from file null
Writing src/generated//google/protobuf/Int32Value.d.ts from file null
Writing src/generated//google/protobuf/UInt32Value.d.ts from file null
Writing src/generated//google/protobuf/BoolValue.d.ts from file null
Writing src/generated//google/protobuf/StringValue.d.ts from file null
Writing src/generated//google/protobuf/BytesValue.d.ts from file null
Writing src/generated//google/protobuf/Duration.d.ts from file null
Writing src/generated//google/protobuf/Timestamp.d.ts from file null
Writing src/generated//google/protobuf/Any.d.ts from file null
Writing src/generated//google/protobuf/Struct.d.ts from file null
Writing src/generated//google/protobuf/Value.d.ts from file null
Writing src/generated//google/protobuf/NullValue.d.ts from file null
Writing src/generated//google/protobuf/ListValue.d.ts from file null
Writing src/generated//google/protobuf/FileDescriptorSet.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FileDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/DescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/DescriptorProto/ExtensionRange.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/DescriptorProto/ReservedRange.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldDescriptorProto/Type.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldDescriptorProto/Label.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/OneofDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/EnumDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/EnumValueDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/ServiceDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/MethodDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FileOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FileOptions/OptimizeMode.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/MessageOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldOptions/CType.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldOptions/JSType.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/OneofOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/EnumOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/EnumValueOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/ServiceOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/MethodOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/UninterpretedOption.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/UninterpretedOption/NamePart.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/SourceCodeInfo.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/SourceCodeInfo/Location.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/GeneratedCodeInfo.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/GeneratedCodeInfo/Annotation.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/Empty.d.ts from file null
Processing envoy/api/v2/cluster.proto
Writing src/generated//cluster.d.ts
Writing src/generated//envoy/api/v2/Cluster.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/Cluster/DiscoveryType.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/Cluster/LbPolicy.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/Cluster/DnsLookupFamily.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/Cluster/ClusterProtocolSelection.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/Cluster/TransportSocketMatch.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/Cluster/CustomClusterType.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/Cluster/EdsClusterConfig.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/Cluster/LbSubsetConfig.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/Cluster/LbSubsetConfig/LbSubsetFallbackPolicy.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/Cluster/LbSubsetConfig/LbSubsetSelector.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/Cluster/LbSubsetConfig/LbSubsetSelector/LbSubsetSelectorFallbackPolicy.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/Cluster/LeastRequestLbConfig.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/Cluster/RingHashLbConfig.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/Cluster/RingHashLbConfig/HashFunction.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/Cluster/OriginalDstLbConfig.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/Cluster/CommonLbConfig.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/Cluster/CommonLbConfig/ZoneAwareLbConfig.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/Cluster/CommonLbConfig/LocalityWeightedLbConfig.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/Cluster/CommonLbConfig/ConsistentHashingLbConfig.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/Cluster/RefreshRate.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/LoadBalancingPolicy.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/LoadBalancingPolicy/Policy.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/UpstreamBindConfig.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/UpstreamConnectionOptions.d.ts from file deps/envoy-api/envoy/api/v2/cluster.proto
Writing src/generated//envoy/api/v2/auth/UpstreamTlsContext.d.ts from file deps/envoy-api/envoy/api/v2/auth/tls.proto
Writing src/generated//envoy/api/v2/auth/DownstreamTlsContext.d.ts from file deps/envoy-api/envoy/api/v2/auth/tls.proto
Writing src/generated//envoy/api/v2/auth/CommonTlsContext.d.ts from file deps/envoy-api/envoy/api/v2/auth/tls.proto
Writing src/generated//envoy/api/v2/auth/CommonTlsContext/CombinedCertificateValidationContext.d.ts from file deps/envoy-api/envoy/api/v2/auth/tls.proto
Writing src/generated//envoy/api/v2/auth/TlsParameters.d.ts from file deps/envoy-api/envoy/api/v2/auth/common.proto
Writing src/generated//envoy/api/v2/auth/TlsParameters/TlsProtocol.d.ts from file deps/envoy-api/envoy/api/v2/auth/common.proto
Writing src/generated//envoy/api/v2/auth/PrivateKeyProvider.d.ts from file deps/envoy-api/envoy/api/v2/auth/common.proto
Writing src/generated//envoy/api/v2/auth/TlsCertificate.d.ts from file deps/envoy-api/envoy/api/v2/auth/common.proto
Writing src/generated//envoy/api/v2/auth/TlsSessionTicketKeys.d.ts from file deps/envoy-api/envoy/api/v2/auth/common.proto
Writing src/generated//envoy/api/v2/auth/CertificateValidationContext.d.ts from file deps/envoy-api/envoy/api/v2/auth/common.proto
Writing src/generated//envoy/api/v2/auth/CertificateValidationContext/TrustChainVerification.d.ts from file deps/envoy-api/envoy/api/v2/auth/common.proto
Writing src/generated//envoy/api/v2/auth/GenericSecret.d.ts from file deps/envoy-api/envoy/api/v2/auth/secret.proto
Writing src/generated//envoy/api/v2/auth/SdsSecretConfig.d.ts from file deps/envoy-api/envoy/api/v2/auth/secret.proto
Writing src/generated//envoy/api/v2/auth/Secret.d.ts from file deps/envoy-api/envoy/api/v2/auth/secret.proto
Writing src/generated//envoy/api/v2/core/ApiVersion.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/ApiConfigSource.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/ApiConfigSource/ApiType.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/AggregatedConfigSource.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/SelfConfigSource.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/RateLimitSettings.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/ConfigSource.d.ts from file deps/envoy-api/envoy/api/v2/core/config_source.proto
Writing src/generated//envoy/api/v2/core/HealthStatus.d.ts from file deps/envoy-api/envoy/api/v2/core/health_check.proto
Writing src/generated//envoy/api/v2/core/HealthCheck.d.ts from file deps/envoy-api/envoy/api/v2/core/health_check.proto
Writing src/generated//envoy/api/v2/core/HealthCheck/Payload.d.ts from file deps/envoy-api/envoy/api/v2/core/health_check.proto
Writing src/generated//envoy/api/v2/core/HealthCheck/HttpHealthCheck.d.ts from file deps/envoy-api/envoy/api/v2/core/health_check.proto
Writing src/generated//envoy/api/v2/core/HealthCheck/TcpHealthCheck.d.ts from file deps/envoy-api/envoy/api/v2/core/health_check.proto
Writing src/generated//envoy/api/v2/core/HealthCheck/RedisHealthCheck.d.ts from file deps/envoy-api/envoy/api/v2/core/health_check.proto
Writing src/generated//envoy/api/v2/core/HealthCheck/GrpcHealthCheck.d.ts from file deps/envoy-api/envoy/api/v2/core/health_check.proto
Writing src/generated//envoy/api/v2/core/HealthCheck/CustomHealthCheck.d.ts from file deps/envoy-api/envoy/api/v2/core/health_check.proto
Writing src/generated//envoy/api/v2/core/HealthCheck/TlsOptions.d.ts from file deps/envoy-api/envoy/api/v2/core/health_check.proto
Writing src/generated//envoy/api/v2/core/TcpProtocolOptions.d.ts from file deps/envoy-api/envoy/api/v2/core/protocol.proto
Writing src/generated//envoy/api/v2/core/UpstreamHttpProtocolOptions.d.ts from file deps/envoy-api/envoy/api/v2/core/protocol.proto
Writing src/generated//envoy/api/v2/core/HttpProtocolOptions.d.ts from file deps/envoy-api/envoy/api/v2/core/protocol.proto
Writing src/generated//envoy/api/v2/core/HttpProtocolOptions/HeadersWithUnderscoresAction.d.ts from file deps/envoy-api/envoy/api/v2/core/protocol.proto
Writing src/generated//envoy/api/v2/core/Http1ProtocolOptions.d.ts from file deps/envoy-api/envoy/api/v2/core/protocol.proto
Writing src/generated//envoy/api/v2/core/Http1ProtocolOptions/HeaderKeyFormat.d.ts from file deps/envoy-api/envoy/api/v2/core/protocol.proto
Writing src/generated//envoy/api/v2/core/Http1ProtocolOptions/HeaderKeyFormat/ProperCaseWords.d.ts from file deps/envoy-api/envoy/api/v2/core/protocol.proto
Writing src/generated//envoy/api/v2/core/Http2ProtocolOptions.d.ts from file deps/envoy-api/envoy/api/v2/core/protocol.proto
Writing src/generated//envoy/api/v2/core/Http2ProtocolOptions/SettingsParameter.d.ts from file deps/envoy-api/envoy/api/v2/core/protocol.proto
Writing src/generated//envoy/api/v2/core/GrpcProtocolOptions.d.ts from file deps/envoy-api/envoy/api/v2/core/protocol.proto
Writing src/generated//envoy/api/v2/core/Pipe.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/SocketAddress.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/SocketAddress/Protocol.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/TcpKeepalive.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/BindConfig.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/Address.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/CidrRange.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/RoutingPriority.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RequestMethod.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/TrafficDirection.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Locality.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/BuildVersion.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Extension.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Node.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Metadata.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RuntimeUInt32.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RuntimeDouble.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RuntimeFeatureFlag.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/HeaderValue.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/HeaderValueOption.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/HeaderMap.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/DataSource.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RetryPolicy.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RemoteDataSource.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/AsyncDataSource.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/TransportSocket.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RuntimeFractionalPercent.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/ControlPlane.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/GrpcService.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/EnvoyGrpc.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/SslCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/GoogleLocalCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/ChannelCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/CallCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/CallCredentials/ServiceAccountJWTAccessCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/CallCredentials/GoogleIAMCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/CallCredentials/MetadataCredentialsFromPlugin.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/CallCredentials/StsService.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/BackoffStrategy.d.ts from file deps/envoy-api/envoy/api/v2/core/backoff.proto
Writing src/generated//envoy/api/v2/core/HttpUri.d.ts from file deps/envoy-api/envoy/api/v2/core/http_uri.proto
Writing src/generated//envoy/api/v2/core/SocketOption.d.ts from file deps/envoy-api/envoy/api/v2/core/socket_option.proto
Writing src/generated//envoy/api/v2/core/SocketOption/SocketState.d.ts from file deps/envoy-api/envoy/api/v2/core/socket_option.proto
Writing src/generated//envoy/api/v2/core/EventServiceConfig.d.ts from file deps/envoy-api/envoy/api/v2/core/event_service_config.proto
Writing src/generated//envoy/api/v2/cluster/CircuitBreakers.d.ts from file deps/envoy-api/envoy/api/v2/cluster/circuit_breaker.proto
Writing src/generated//envoy/api/v2/cluster/CircuitBreakers/Thresholds.d.ts from file deps/envoy-api/envoy/api/v2/cluster/circuit_breaker.proto
Writing src/generated//envoy/api/v2/cluster/CircuitBreakers/Thresholds/RetryBudget.d.ts from file deps/envoy-api/envoy/api/v2/cluster/circuit_breaker.proto
Writing src/generated//envoy/api/v2/cluster/Filter.d.ts from file deps/envoy-api/envoy/api/v2/cluster/filter.proto
Writing src/generated//envoy/api/v2/cluster/OutlierDetection.d.ts from file deps/envoy-api/envoy/api/v2/cluster/outlier_detection.proto
Writing src/generated//envoy/api/v2/ClusterLoadAssignment.d.ts from file deps/envoy-api/envoy/api/v2/endpoint.proto
Writing src/generated//envoy/api/v2/ClusterLoadAssignment/Policy.d.ts from file deps/envoy-api/envoy/api/v2/endpoint.proto
Writing src/generated//envoy/api/v2/ClusterLoadAssignment/Policy/DropOverload.d.ts from file deps/envoy-api/envoy/api/v2/endpoint.proto
Writing src/generated//envoy/api/v2/endpoint/Endpoint.d.ts from file deps/envoy-api/envoy/api/v2/endpoint/endpoint_components.proto
Writing src/generated//envoy/api/v2/endpoint/Endpoint/HealthCheckConfig.d.ts from file deps/envoy-api/envoy/api/v2/endpoint/endpoint_components.proto
Writing src/generated//envoy/api/v2/endpoint/LbEndpoint.d.ts from file deps/envoy-api/envoy/api/v2/endpoint/endpoint_components.proto
Writing src/generated//envoy/api/v2/endpoint/LocalityLbEndpoints.d.ts from file deps/envoy-api/envoy/api/v2/endpoint/endpoint_components.proto
Writing src/generated//envoy/type/Percent.d.ts from file deps/envoy-api/envoy/type/percent.proto
Writing src/generated//envoy/type/FractionalPercent.d.ts from file deps/envoy-api/envoy/type/percent.proto
Writing src/generated//envoy/type/FractionalPercent/DenominatorType.d.ts from file deps/envoy-api/envoy/type/percent.proto
Writing src/generated//envoy/type/matcher/StringMatcher.d.ts from file deps/envoy-api/envoy/type/matcher/string.proto
Writing src/generated//envoy/type/matcher/ListStringMatcher.d.ts from file deps/envoy-api/envoy/type/matcher/string.proto
Writing src/generated//envoy/type/matcher/RegexMatcher.d.ts from file deps/envoy-api/envoy/type/matcher/regex.proto
Writing src/generated//envoy/type/matcher/RegexMatcher/GoogleRE2.d.ts from file deps/envoy-api/envoy/type/matcher/regex.proto
Writing src/generated//envoy/type/matcher/RegexMatchAndSubstitute.d.ts from file deps/envoy-api/envoy/type/matcher/regex.proto
Writing src/generated//envoy/type/CodecClientType.d.ts from file deps/envoy-api/envoy/type/http.proto
Writing src/generated//envoy/type/SemanticVersion.d.ts from file deps/envoy-api/envoy/type/semantic_version.proto
Writing src/generated//envoy/type/Int64Range.d.ts from file deps/envoy-api/envoy/type/range.proto
Writing src/generated//envoy/type/Int32Range.d.ts from file deps/envoy-api/envoy/type/range.proto
Writing src/generated//envoy/type/DoubleRange.d.ts from file deps/envoy-api/envoy/type/range.proto
Writing src/generated//udpa/annotations/MigrateAnnotation.d.ts from file deps/udpa/udpa/annotations/migrate.proto
Writing src/generated//udpa/annotations/FieldMigrateAnnotation.d.ts from file deps/udpa/udpa/annotations/migrate.proto
Writing src/generated//udpa/annotations/FileMigrateAnnotation.d.ts from file deps/udpa/udpa/annotations/migrate.proto
Writing src/generated//udpa/annotations/PackageVersionStatus.d.ts from file deps/udpa/udpa/annotations/status.proto
Writing src/generated//udpa/annotations/StatusAnnotation.d.ts from file deps/udpa/udpa/annotations/status.proto
Writing src/generated//validate/FieldRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/FloatRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/DoubleRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/Int32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/Int64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/UInt32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/UInt64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/SInt32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/SInt64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/Fixed32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/Fixed64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/SFixed32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/SFixed64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/BoolRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/StringRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/KnownRegex.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/BytesRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/EnumRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/MessageRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/RepeatedRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/MapRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/AnyRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/DurationRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/TimestampRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//google/protobuf/Any.d.ts from file null
Writing src/generated//google/protobuf/Duration.d.ts from file null
Writing src/generated//google/protobuf/Struct.d.ts from file null
Writing src/generated//google/protobuf/Value.d.ts from file null
Writing src/generated//google/protobuf/NullValue.d.ts from file null
Writing src/generated//google/protobuf/ListValue.d.ts from file null
Writing src/generated//google/protobuf/DoubleValue.d.ts from file null
Writing src/generated//google/protobuf/FloatValue.d.ts from file null
Writing src/generated//google/protobuf/Int64Value.d.ts from file null
Writing src/generated//google/protobuf/UInt64Value.d.ts from file null
Writing src/generated//google/protobuf/Int32Value.d.ts from file null
Writing src/generated//google/protobuf/UInt32Value.d.ts from file null
Writing src/generated//google/protobuf/BoolValue.d.ts from file null
Writing src/generated//google/protobuf/StringValue.d.ts from file null
Writing src/generated//google/protobuf/BytesValue.d.ts from file null
Writing src/generated//google/protobuf/Timestamp.d.ts from file null
Writing src/generated//google/protobuf/Empty.d.ts from file null
Writing src/generated//google/protobuf/FileDescriptorSet.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FileDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/DescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/DescriptorProto/ExtensionRange.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/DescriptorProto/ReservedRange.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldDescriptorProto/Type.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldDescriptorProto/Label.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/OneofDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/EnumDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/EnumValueDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/ServiceDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/MethodDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FileOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FileOptions/OptimizeMode.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/MessageOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldOptions/CType.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldOptions/JSType.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/OneofOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/EnumOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/EnumValueOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/ServiceOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/MethodOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/UninterpretedOption.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/UninterpretedOption/NamePart.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/SourceCodeInfo.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/SourceCodeInfo/Location.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/GeneratedCodeInfo.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/GeneratedCodeInfo/Annotation.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/api/Http.d.ts from file node_modules/protobufjs/google/api/http.proto
Writing src/generated//google/api/HttpRule.d.ts from file node_modules/protobufjs/google/api/http.proto
Writing src/generated//google/api/CustomHttpPattern.d.ts from file node_modules/protobufjs/google/api/http.proto
Processing envoy/api/v2/endpoint.proto
Writing src/generated//endpoint.d.ts
Writing src/generated//envoy/api/v2/ClusterLoadAssignment.d.ts from file deps/envoy-api/envoy/api/v2/endpoint.proto
Writing src/generated//envoy/api/v2/ClusterLoadAssignment/Policy.d.ts from file deps/envoy-api/envoy/api/v2/endpoint.proto
Writing src/generated//envoy/api/v2/ClusterLoadAssignment/Policy/DropOverload.d.ts from file deps/envoy-api/envoy/api/v2/endpoint.proto
Writing src/generated//envoy/api/v2/endpoint/Endpoint.d.ts from file deps/envoy-api/envoy/api/v2/endpoint/endpoint_components.proto
Writing src/generated//envoy/api/v2/endpoint/Endpoint/HealthCheckConfig.d.ts from file deps/envoy-api/envoy/api/v2/endpoint/endpoint_components.proto
Writing src/generated//envoy/api/v2/endpoint/LbEndpoint.d.ts from file deps/envoy-api/envoy/api/v2/endpoint/endpoint_components.proto
Writing src/generated//envoy/api/v2/endpoint/LocalityLbEndpoints.d.ts from file deps/envoy-api/envoy/api/v2/endpoint/endpoint_components.proto
Writing src/generated//envoy/api/v2/core/RoutingPriority.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RequestMethod.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/TrafficDirection.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Locality.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/BuildVersion.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Extension.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Node.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Metadata.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RuntimeUInt32.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RuntimeDouble.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RuntimeFeatureFlag.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/HeaderValue.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/HeaderValueOption.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/HeaderMap.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/DataSource.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RetryPolicy.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RemoteDataSource.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/AsyncDataSource.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/TransportSocket.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/RuntimeFractionalPercent.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/ControlPlane.d.ts from file deps/envoy-api/envoy/api/v2/core/base.proto
Writing src/generated//envoy/api/v2/core/Pipe.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/SocketAddress.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/SocketAddress/Protocol.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/TcpKeepalive.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/BindConfig.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/Address.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/CidrRange.d.ts from file deps/envoy-api/envoy/api/v2/core/address.proto
Writing src/generated//envoy/api/v2/core/HealthStatus.d.ts from file deps/envoy-api/envoy/api/v2/core/health_check.proto
Writing src/generated//envoy/api/v2/core/HealthCheck.d.ts from file deps/envoy-api/envoy/api/v2/core/health_check.proto
Writing src/generated//envoy/api/v2/core/HealthCheck/Payload.d.ts from file deps/envoy-api/envoy/api/v2/core/health_check.proto
Writing src/generated//envoy/api/v2/core/HealthCheck/HttpHealthCheck.d.ts from file deps/envoy-api/envoy/api/v2/core/health_check.proto
Writing src/generated//envoy/api/v2/core/HealthCheck/TcpHealthCheck.d.ts from file deps/envoy-api/envoy/api/v2/core/health_check.proto
Writing src/generated//envoy/api/v2/core/HealthCheck/RedisHealthCheck.d.ts from file deps/envoy-api/envoy/api/v2/core/health_check.proto
Writing src/generated//envoy/api/v2/core/HealthCheck/GrpcHealthCheck.d.ts from file deps/envoy-api/envoy/api/v2/core/health_check.proto
Writing src/generated//envoy/api/v2/core/HealthCheck/CustomHealthCheck.d.ts from file deps/envoy-api/envoy/api/v2/core/health_check.proto
Writing src/generated//envoy/api/v2/core/HealthCheck/TlsOptions.d.ts from file deps/envoy-api/envoy/api/v2/core/health_check.proto
Writing src/generated//envoy/api/v2/core/BackoffStrategy.d.ts from file deps/envoy-api/envoy/api/v2/core/backoff.proto
Writing src/generated//envoy/api/v2/core/HttpUri.d.ts from file deps/envoy-api/envoy/api/v2/core/http_uri.proto
Writing src/generated//envoy/api/v2/core/SocketOption.d.ts from file deps/envoy-api/envoy/api/v2/core/socket_option.proto
Writing src/generated//envoy/api/v2/core/SocketOption/SocketState.d.ts from file deps/envoy-api/envoy/api/v2/core/socket_option.proto
Writing src/generated//envoy/api/v2/core/EventServiceConfig.d.ts from file deps/envoy-api/envoy/api/v2/core/event_service_config.proto
Writing src/generated//envoy/api/v2/core/GrpcService.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/EnvoyGrpc.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/SslCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/GoogleLocalCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/ChannelCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/CallCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/CallCredentials/ServiceAccountJWTAccessCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/CallCredentials/GoogleIAMCredentials.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/CallCredentials/MetadataCredentialsFromPlugin.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/api/v2/core/GrpcService/GoogleGrpc/CallCredentials/StsService.d.ts from file deps/envoy-api/envoy/api/v2/core/grpc_service.proto
Writing src/generated//envoy/type/Percent.d.ts from file deps/envoy-api/envoy/type/percent.proto
Writing src/generated//envoy/type/FractionalPercent.d.ts from file deps/envoy-api/envoy/type/percent.proto
Writing src/generated//envoy/type/FractionalPercent/DenominatorType.d.ts from file deps/envoy-api/envoy/type/percent.proto
Writing src/generated//envoy/type/matcher/StringMatcher.d.ts from file deps/envoy-api/envoy/type/matcher/string.proto
Writing src/generated//envoy/type/matcher/ListStringMatcher.d.ts from file deps/envoy-api/envoy/type/matcher/string.proto
Writing src/generated//envoy/type/matcher/RegexMatcher.d.ts from file deps/envoy-api/envoy/type/matcher/regex.proto
Writing src/generated//envoy/type/matcher/RegexMatcher/GoogleRE2.d.ts from file deps/envoy-api/envoy/type/matcher/regex.proto
Writing src/generated//envoy/type/matcher/RegexMatchAndSubstitute.d.ts from file deps/envoy-api/envoy/type/matcher/regex.proto
Writing src/generated//envoy/type/SemanticVersion.d.ts from file deps/envoy-api/envoy/type/semantic_version.proto
Writing src/generated//envoy/type/CodecClientType.d.ts from file deps/envoy-api/envoy/type/http.proto
Writing src/generated//envoy/type/Int64Range.d.ts from file deps/envoy-api/envoy/type/range.proto
Writing src/generated//envoy/type/Int32Range.d.ts from file deps/envoy-api/envoy/type/range.proto
Writing src/generated//envoy/type/DoubleRange.d.ts from file deps/envoy-api/envoy/type/range.proto
Writing src/generated//udpa/annotations/MigrateAnnotation.d.ts from file deps/udpa/udpa/annotations/migrate.proto
Writing src/generated//udpa/annotations/FieldMigrateAnnotation.d.ts from file deps/udpa/udpa/annotations/migrate.proto
Writing src/generated//udpa/annotations/FileMigrateAnnotation.d.ts from file deps/udpa/udpa/annotations/migrate.proto
Writing src/generated//udpa/annotations/PackageVersionStatus.d.ts from file deps/udpa/udpa/annotations/status.proto
Writing src/generated//udpa/annotations/StatusAnnotation.d.ts from file deps/udpa/udpa/annotations/status.proto
Writing src/generated//validate/FieldRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/FloatRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/DoubleRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/Int32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/Int64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/UInt32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/UInt64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/SInt32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/SInt64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/Fixed32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/Fixed64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/SFixed32Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/SFixed64Rules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/BoolRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/StringRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/KnownRegex.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/BytesRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/EnumRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/MessageRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/RepeatedRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/MapRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/AnyRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/DurationRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//validate/TimestampRules.d.ts from file deps/protoc-gen-validate/validate/validate.proto
Writing src/generated//google/protobuf/Duration.d.ts from file null
Writing src/generated//google/protobuf/DoubleValue.d.ts from file null
Writing src/generated//google/protobuf/FloatValue.d.ts from file null
Writing src/generated//google/protobuf/Int64Value.d.ts from file null
Writing src/generated//google/protobuf/UInt64Value.d.ts from file null
Writing src/generated//google/protobuf/Int32Value.d.ts from file null
Writing src/generated//google/protobuf/UInt32Value.d.ts from file null
Writing src/generated//google/protobuf/BoolValue.d.ts from file null
Writing src/generated//google/protobuf/StringValue.d.ts from file null
Writing src/generated//google/protobuf/BytesValue.d.ts from file null
Writing src/generated//google/protobuf/Timestamp.d.ts from file null
Writing src/generated//google/protobuf/FileDescriptorSet.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FileDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/DescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/DescriptorProto/ExtensionRange.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/DescriptorProto/ReservedRange.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldDescriptorProto/Type.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldDescriptorProto/Label.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/OneofDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/EnumDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/EnumValueDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/ServiceDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/MethodDescriptorProto.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FileOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FileOptions/OptimizeMode.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/MessageOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldOptions/CType.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/FieldOptions/JSType.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/OneofOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/EnumOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/EnumValueOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/ServiceOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/MethodOptions.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/UninterpretedOption.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/UninterpretedOption/NamePart.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/SourceCodeInfo.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/SourceCodeInfo/Location.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/GeneratedCodeInfo.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/GeneratedCodeInfo/Annotation.d.ts from file node_modules/protobufjs/google/protobuf/descriptor.proto
Writing src/generated//google/protobuf/Any.d.ts from file null
Writing src/generated//google/protobuf/Struct.d.ts from file null
Writing src/generated//google/protobuf/Value.d.ts from file null
Writing src/generated//google/protobuf/NullValue.d.ts from file null
Writing src/generated//google/protobuf/ListValue.d.ts from file null
Writing src/generated//google/protobuf/Empty.d.ts from file null
Writing src/generated//google/api/Http.d.ts from file node_modules/protobufjs/google/api/http.proto
Writing src/generated//google/api/HttpRule.d.ts from file node_modules/protobufjs/google/api/http.proto
Writing src/generated//google/api/CustomHttpPattern.d.ts from file node_modules/protobufjs/google/api/http.proto
Success

View File

@ -22,9 +22,15 @@
"@types/ncp": "^2.0.1",
"@types/pify": "^3.0.2",
"@types/semver": "^7.3.9",
"@typescript-eslint/eslint-plugin": "^5.59.11",
"@typescript-eslint/parser": "^5.59.11",
"@typescript-eslint/typescript-estree": "^5.59.11",
"clang-format": "^1.0.55",
"eslint": "^8.42.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^4.2.1",
"execa": "^2.0.3",
"gts": "^3.1.1",
"gulp": "^4.0.2",
"gulp-mocha": "^6.0.0",
"lodash": "^4.17.4",
@ -32,10 +38,11 @@
"mocha-jenkins-reporter": "^0.4.1",
"ncp": "^2.0.0",
"pify": "^4.0.1",
"prettier": "^2.8.8",
"rimraf": "^3.0.2",
"semver": "^7.3.5",
"ts-node": "^8.3.0",
"typescript": "^4.8.4"
"ts-node": "^10.9.1",
"typescript": "^5.1.3"
},
"contributors": [
{
@ -47,11 +54,11 @@
"clean": "rimraf ./build",
"compile": "tsc -p .",
"format": "clang-format -i -style=\"{Language: JavaScript, BasedOnStyle: Google, ColumnLimit: 80}\" src/*.ts test/*.ts",
"lint": "npm run check",
"lint": "eslint src/*.ts test/*.ts",
"prepare": "npm run generate-types && npm run compile",
"test": "gulp test",
"check": "gts check src/**/*.ts",
"fix": "gts fix src/*.ts",
"check": "npm run lint",
"fix": "eslint --fix src/*.ts test/*.ts",
"pretest": "npm run generate-types && npm run generate-test-types && npm run compile",
"posttest": "npm run check && madge -c ./build/src",
"generate-types": "proto-loader-gen-types --keepCase --longs String --enums String --defaults --oneofs --includeComments --includeDirs proto/ --include-dirs test/fixtures/ -O src/generated/ --grpcLib ../index channelz.proto",

View File

@ -2,4 +2,6 @@ module.exports = {
proseWrap: 'always',
singleQuote: true,
trailingComma: 'es5',
bracketSpacing: true,
arrowParens: 'avoid',
};

View File

@ -15,8 +15,8 @@
*
*/
import { ServiceDefinition } from "./make-client";
import { Server, UntypedServiceImplementation } from "./server";
import { ServiceDefinition } from './make-client';
import { Server, UntypedServiceImplementation } from './server';
interface GetServiceDefinition {
(): ServiceDefinition;
@ -26,14 +26,20 @@ interface GetHandlers {
(): UntypedServiceImplementation;
}
const registeredAdminServices: {getServiceDefinition: GetServiceDefinition, getHandlers: GetHandlers}[] = [];
const registeredAdminServices: {
getServiceDefinition: GetServiceDefinition;
getHandlers: GetHandlers;
}[] = [];
export function registerAdminService(getServiceDefinition: GetServiceDefinition, getHandlers: GetHandlers) {
registeredAdminServices.push({getServiceDefinition, getHandlers});
export function registerAdminService(
getServiceDefinition: GetServiceDefinition,
getHandlers: GetHandlers
) {
registeredAdminServices.push({ getServiceDefinition, getHandlers });
}
export function addAdminServicesToServer(server: Server): void {
for (const {getServiceDefinition, getHandlers} of registeredAdminServices) {
for (const { getServiceDefinition, getHandlers } of registeredAdminServices) {
server.addService(getServiceDefinition(), getHandlers());
}
}
}

View File

@ -125,14 +125,14 @@ export abstract class CallCredentials {
});
}
getHeaders.then(
(headers) => {
headers => {
const metadata = new Metadata();
for (const key of Object.keys(headers)) {
metadata.add(key, headers[key]);
}
callback(null, metadata);
},
(err) => {
err => {
callback(err);
}
);
@ -152,7 +152,7 @@ class ComposedCallCredentials extends CallCredentials {
async generateMetadata(options: CallMetadataOptions): Promise<Metadata> {
const base: Metadata = new Metadata();
const generated: Metadata[] = await Promise.all(
this.creds.map((cred) => cred.generateMetadata(options))
this.creds.map(cred => cred.generateMetadata(options))
);
for (const gen of generated) {
base.merge(gen);

View File

@ -15,11 +15,11 @@
*
*/
import { CallCredentials } from "./call-credentials";
import { Status } from "./constants";
import { Deadline } from "./deadline";
import { Metadata } from "./metadata";
import { ServerSurfaceCall } from "./server-call";
import { CallCredentials } from './call-credentials';
import { Status } from './constants';
import { Deadline } from './deadline';
import { Metadata } from './metadata';
import { ServerSurfaceCall } from './server-call';
export interface CallStreamOptions {
deadline: Deadline;
@ -38,7 +38,7 @@ export interface StatusObject {
export type PartialStatusObject = Pick<StatusObject, 'code' | 'details'> & {
metadata: Metadata | null;
}
};
export const enum WriteFlags {
BufferHint = 1,
@ -118,7 +118,7 @@ export class InterceptingListenerImpl implements InterceptingListener {
onReceiveMetadata(metadata: Metadata): void {
this.processingMetadata = true;
this.listener.onReceiveMetadata(metadata, (metadata) => {
this.listener.onReceiveMetadata(metadata, metadata => {
this.processingMetadata = false;
this.nextListener.onReceiveMetadata(metadata);
this.processPendingMessage();
@ -128,9 +128,9 @@ export class InterceptingListenerImpl implements InterceptingListener {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onReceiveMessage(message: any): void {
/* If this listener processes messages asynchronously, the last message may
* be reordered with respect to the status */
* be reordered with respect to the status */
this.processingMessage = true;
this.listener.onReceiveMessage(message, (msg) => {
this.listener.onReceiveMessage(message, msg => {
this.processingMessage = false;
if (this.processingMetadata) {
this.pendingMessage = msg;
@ -142,7 +142,7 @@ export class InterceptingListenerImpl implements InterceptingListener {
});
}
onReceiveStatus(status: StatusObject): void {
this.listener.onReceiveStatus(status, (processedStatus) => {
this.listener.onReceiveStatus(status, processedStatus => {
if (this.processingMetadata || this.processingMessage) {
this.pendingStatus = processedStatus;
} else {
@ -170,4 +170,4 @@ export interface Call {
halfClose(): void;
getCallNumber(): number;
setCredentials(credentials: CallCredentials): void;
}
}

View File

@ -18,5 +18,5 @@
let nextCallNumber = 0;
export function getNextCallNumber() {
return nextCallNumber++;
}
return nextCallNumber++;
}

View File

@ -65,10 +65,8 @@ export type ClientWritableStream<RequestType> = {
/**
* A type representing the return value of a bidirectional stream method call.
*/
export type ClientDuplexStream<
RequestType,
ResponseType
> = ClientWritableStream<RequestType> & ClientReadableStream<ResponseType>;
export type ClientDuplexStream<RequestType, ResponseType> =
ClientWritableStream<RequestType> & ClientReadableStream<ResponseType>;
/**
* Construct a ServiceError from a StatusObject. This function exists primarily
@ -76,16 +74,20 @@ export type ClientDuplexStream<
* error is not necessarily a problem in gRPC itself.
* @param status
*/
export function callErrorFromStatus(status: StatusObject, callerStack: string): ServiceError {
export function callErrorFromStatus(
status: StatusObject,
callerStack: string
): ServiceError {
const message = `${status.code} ${Status[status.code]}: ${status.details}`;
const error = new Error(message);
const stack = `${error.stack}\nfor call at\n${callerStack}`;
return Object.assign(new Error(message), status, {stack});
return Object.assign(new Error(message), status, { stack });
}
export class ClientUnaryCallImpl
extends EventEmitter
implements ClientUnaryCall {
implements ClientUnaryCall
{
public call?: InterceptingCallInterface;
constructor() {
super();
@ -102,7 +104,8 @@ export class ClientUnaryCallImpl
export class ClientReadableStreamImpl<ResponseType>
extends Readable
implements ClientReadableStream<ResponseType> {
implements ClientReadableStream<ResponseType>
{
public call?: InterceptingCallInterface;
constructor(readonly deserialize: (chunk: Buffer) => ResponseType) {
super({ objectMode: true });
@ -123,7 +126,8 @@ export class ClientReadableStreamImpl<ResponseType>
export class ClientWritableStreamImpl<RequestType>
extends Writable
implements ClientWritableStream<RequestType> {
implements ClientWritableStream<RequestType>
{
public call?: InterceptingCallInterface;
constructor(readonly serialize: (value: RequestType) => Buffer) {
super({ objectMode: true });
@ -156,7 +160,8 @@ export class ClientWritableStreamImpl<RequestType>
export class ClientDuplexStreamImpl<RequestType, ResponseType>
extends Duplex
implements ClientDuplexStream<RequestType, ResponseType> {
implements ClientDuplexStream<RequestType, ResponseType>
{
public call?: InterceptingCallInterface;
constructor(
readonly serialize: (value: RequestType) => Buffer,

View File

@ -15,7 +15,12 @@
*
*/
import { ConnectionOptions, createSecureContext, PeerCertificate, SecureContext } from 'tls';
import {
ConnectionOptions,
createSecureContext,
PeerCertificate,
SecureContext,
} from 'tls';
import { CallCredentials } from './call-credentials';
import { CIPHER_SUITES, getDefaultRootsData } from './tls-helpers';
@ -137,10 +142,7 @@ export abstract class ChannelCredentials {
cert: certChain ?? undefined,
ciphers: CIPHER_SUITES,
});
return new SecureChannelCredentialsImpl(
secureContext,
verifyOptions ?? {}
);
return new SecureChannelCredentialsImpl(secureContext, verifyOptions ?? {});
}
/**
@ -153,11 +155,11 @@ export abstract class ChannelCredentials {
* @param secureContext The return value of tls.createSecureContext()
* @param verifyOptions Additional options to modify certificate verification
*/
static createFromSecureContext(secureContext: SecureContext, verifyOptions?: VerifyOptions): ChannelCredentials {
return new SecureChannelCredentialsImpl(
secureContext,
verifyOptions ?? {}
)
static createFromSecureContext(
secureContext: SecureContext,
verifyOptions?: VerifyOptions
): ChannelCredentials {
return new SecureChannelCredentialsImpl(secureContext, verifyOptions ?? {});
}
/**
@ -196,19 +198,19 @@ class SecureChannelCredentialsImpl extends ChannelCredentials {
private verifyOptions: VerifyOptions
) {
super();
this.connectionOptions = {
secureContext
this.connectionOptions = {
secureContext,
};
// Node asserts that this option is a function, so we cannot pass undefined
if (verifyOptions?.checkServerIdentity) {
this.connectionOptions.checkServerIdentity = verifyOptions.checkServerIdentity;
this.connectionOptions.checkServerIdentity =
verifyOptions.checkServerIdentity;
}
}
compose(callCredentials: CallCredentials): ChannelCredentials {
const combinedCallCredentials = this.callCredentials.compose(
callCredentials
);
const combinedCallCredentials =
this.callCredentials.compose(callCredentials);
return new ComposedChannelCredentialsImpl(this, combinedCallCredentials);
}
@ -225,9 +227,10 @@ class SecureChannelCredentialsImpl extends ChannelCredentials {
}
if (other instanceof SecureChannelCredentialsImpl) {
return (
this.secureContext === other.secureContext &&
this.verifyOptions.checkServerIdentity === other.verifyOptions.checkServerIdentity
);
this.secureContext === other.secureContext &&
this.verifyOptions.checkServerIdentity ===
other.verifyOptions.checkServerIdentity
);
} else {
return false;
}
@ -242,9 +245,8 @@ class ComposedChannelCredentialsImpl extends ChannelCredentials {
super(callCreds);
}
compose(callCredentials: CallCredentials) {
const combinedCallCredentials = this.callCredentials.compose(
callCredentials
);
const combinedCallCredentials =
this.callCredentials.compose(callCredentials);
return new ComposedChannelCredentialsImpl(
this.channelCredentials,
combinedCallCredentials

View File

@ -91,7 +91,6 @@ export interface Channel {
}
export class ChannelImplementation implements Channel {
private internalChannel: InternalChannel;
constructor(
@ -133,13 +132,17 @@ export class ChannelImplementation implements Channel {
deadline: Date | number,
callback: (error?: Error) => void
): void {
this.internalChannel.watchConnectivityState(currentState, deadline, callback);
this.internalChannel.watchConnectivityState(
currentState,
deadline,
callback
);
}
/**
* Get the channelz reference object for this channel. The returned value is
* garbage if channelz is disabled for this channel.
* @returns
* @returns
*/
getChannelzRef() {
return this.internalChannel.getChannelzRef();
@ -160,6 +163,12 @@ export class ChannelImplementation implements Channel {
'Channel#createCall: deadline must be a number or Date'
);
}
return this.internalChannel.createCall(method, deadline, host, parentCall, propagateFlags);
return this.internalChannel.createCall(
method,
deadline,
host,
parentCall,
propagateFlags
);
}
}

View File

@ -15,45 +15,55 @@
*
*/
import { isIPv4, isIPv6 } from "net";
import { ConnectivityState } from "./connectivity-state";
import { Status } from "./constants";
import { Timestamp } from "./generated/google/protobuf/Timestamp";
import { Channel as ChannelMessage } from "./generated/grpc/channelz/v1/Channel";
import { ChannelConnectivityState__Output } from "./generated/grpc/channelz/v1/ChannelConnectivityState";
import { ChannelRef as ChannelRefMessage } from "./generated/grpc/channelz/v1/ChannelRef";
import { ChannelTrace } from "./generated/grpc/channelz/v1/ChannelTrace";
import { GetChannelRequest__Output } from "./generated/grpc/channelz/v1/GetChannelRequest";
import { GetChannelResponse } from "./generated/grpc/channelz/v1/GetChannelResponse";
import { sendUnaryData, ServerUnaryCall } from "./server-call";
import { ServerRef as ServerRefMessage } from "./generated/grpc/channelz/v1/ServerRef";
import { SocketRef as SocketRefMessage } from "./generated/grpc/channelz/v1/SocketRef";
import { isTcpSubchannelAddress, SubchannelAddress } from "./subchannel-address";
import { SubchannelRef as SubchannelRefMessage } from "./generated/grpc/channelz/v1/SubchannelRef";
import { GetServerRequest__Output } from "./generated/grpc/channelz/v1/GetServerRequest";
import { GetServerResponse } from "./generated/grpc/channelz/v1/GetServerResponse";
import { Server as ServerMessage } from "./generated/grpc/channelz/v1/Server";
import { GetServersRequest__Output } from "./generated/grpc/channelz/v1/GetServersRequest";
import { GetServersResponse } from "./generated/grpc/channelz/v1/GetServersResponse";
import { GetTopChannelsRequest__Output } from "./generated/grpc/channelz/v1/GetTopChannelsRequest";
import { GetTopChannelsResponse } from "./generated/grpc/channelz/v1/GetTopChannelsResponse";
import { GetSubchannelRequest__Output } from "./generated/grpc/channelz/v1/GetSubchannelRequest";
import { GetSubchannelResponse } from "./generated/grpc/channelz/v1/GetSubchannelResponse";
import { Subchannel as SubchannelMessage } from "./generated/grpc/channelz/v1/Subchannel";
import { GetSocketRequest__Output } from "./generated/grpc/channelz/v1/GetSocketRequest";
import { GetSocketResponse } from "./generated/grpc/channelz/v1/GetSocketResponse";
import { Socket as SocketMessage } from "./generated/grpc/channelz/v1/Socket";
import { Address } from "./generated/grpc/channelz/v1/Address";
import { Security } from "./generated/grpc/channelz/v1/Security";
import { GetServerSocketsRequest__Output } from "./generated/grpc/channelz/v1/GetServerSocketsRequest";
import { GetServerSocketsResponse } from "./generated/grpc/channelz/v1/GetServerSocketsResponse";
import { ChannelzDefinition, ChannelzHandlers } from "./generated/grpc/channelz/v1/Channelz";
import { ProtoGrpcType as ChannelzProtoGrpcType } from "./generated/channelz";
import { isIPv4, isIPv6 } from 'net';
import { ConnectivityState } from './connectivity-state';
import { Status } from './constants';
import { Timestamp } from './generated/google/protobuf/Timestamp';
import { Channel as ChannelMessage } from './generated/grpc/channelz/v1/Channel';
import { ChannelConnectivityState__Output } from './generated/grpc/channelz/v1/ChannelConnectivityState';
import { ChannelRef as ChannelRefMessage } from './generated/grpc/channelz/v1/ChannelRef';
import { ChannelTrace } from './generated/grpc/channelz/v1/ChannelTrace';
import { GetChannelRequest__Output } from './generated/grpc/channelz/v1/GetChannelRequest';
import { GetChannelResponse } from './generated/grpc/channelz/v1/GetChannelResponse';
import { sendUnaryData, ServerUnaryCall } from './server-call';
import { ServerRef as ServerRefMessage } from './generated/grpc/channelz/v1/ServerRef';
import { SocketRef as SocketRefMessage } from './generated/grpc/channelz/v1/SocketRef';
import {
isTcpSubchannelAddress,
SubchannelAddress,
} from './subchannel-address';
import { SubchannelRef as SubchannelRefMessage } from './generated/grpc/channelz/v1/SubchannelRef';
import { GetServerRequest__Output } from './generated/grpc/channelz/v1/GetServerRequest';
import { GetServerResponse } from './generated/grpc/channelz/v1/GetServerResponse';
import { Server as ServerMessage } from './generated/grpc/channelz/v1/Server';
import { GetServersRequest__Output } from './generated/grpc/channelz/v1/GetServersRequest';
import { GetServersResponse } from './generated/grpc/channelz/v1/GetServersResponse';
import { GetTopChannelsRequest__Output } from './generated/grpc/channelz/v1/GetTopChannelsRequest';
import { GetTopChannelsResponse } from './generated/grpc/channelz/v1/GetTopChannelsResponse';
import { GetSubchannelRequest__Output } from './generated/grpc/channelz/v1/GetSubchannelRequest';
import { GetSubchannelResponse } from './generated/grpc/channelz/v1/GetSubchannelResponse';
import { Subchannel as SubchannelMessage } from './generated/grpc/channelz/v1/Subchannel';
import { GetSocketRequest__Output } from './generated/grpc/channelz/v1/GetSocketRequest';
import { GetSocketResponse } from './generated/grpc/channelz/v1/GetSocketResponse';
import { Socket as SocketMessage } from './generated/grpc/channelz/v1/Socket';
import { Address } from './generated/grpc/channelz/v1/Address';
import { Security } from './generated/grpc/channelz/v1/Security';
import { GetServerSocketsRequest__Output } from './generated/grpc/channelz/v1/GetServerSocketsRequest';
import { GetServerSocketsResponse } from './generated/grpc/channelz/v1/GetServerSocketsResponse';
import {
ChannelzDefinition,
ChannelzHandlers,
} from './generated/grpc/channelz/v1/Channelz';
import { ProtoGrpcType as ChannelzProtoGrpcType } from './generated/channelz';
import type { loadSync } from '@grpc/proto-loader';
import { registerAdminService } from "./admin";
import { loadPackageDefinition } from "./make-client";
import { registerAdminService } from './admin';
import { loadPackageDefinition } from './make-client';
export type TraceSeverity = 'CT_UNKNOWN' | 'CT_INFO' | 'CT_WARNING' | 'CT_ERROR';
export type TraceSeverity =
| 'CT_UNKNOWN'
| 'CT_INFO'
| 'CT_WARNING'
| 'CT_ERROR';
export interface ChannelRef {
kind: 'channel';
@ -81,28 +91,28 @@ export interface SocketRef {
function channelRefToMessage(ref: ChannelRef): ChannelRefMessage {
return {
channel_id: ref.id,
name: ref.name
name: ref.name,
};
}
function subchannelRefToMessage(ref: SubchannelRef): SubchannelRefMessage {
return {
subchannel_id: ref.id,
name: ref.name
}
name: ref.name,
};
}
function serverRefToMessage(ref: ServerRef): ServerRefMessage {
return {
server_id: ref.id
}
server_id: ref.id,
};
}
function socketRefToMessage(ref: SocketRef): SocketRefMessage {
return {
socket_id: ref.id,
name: ref.name
}
name: ref.name,
};
}
interface TraceEvent {
@ -124,20 +134,24 @@ const TARGET_RETAINED_TRACES = 32;
export class ChannelzTrace {
events: TraceEvent[] = [];
creationTimestamp: Date;
eventsLogged: number = 0;
eventsLogged = 0;
constructor() {
this.creationTimestamp = new Date();
}
addTrace(severity: TraceSeverity, description: string, child?: ChannelRef | SubchannelRef) {
addTrace(
severity: TraceSeverity,
description: string,
child?: ChannelRef | SubchannelRef
) {
const timestamp = new Date();
this.events.push({
description: description,
severity: severity,
timestamp: timestamp,
childChannel: child?.kind === 'channel' ? child : undefined,
childSubchannel: child?.kind === 'subchannel' ? child : undefined
childSubchannel: child?.kind === 'subchannel' ? child : undefined,
});
// Whenever the trace array gets too large, discard the first half
if (this.events.length >= TARGET_RETAINED_TRACES * 2) {
@ -155,35 +169,53 @@ export class ChannelzTrace {
description: event.description,
severity: event.severity,
timestamp: dateToProtoTimestamp(event.timestamp),
channel_ref: event.childChannel ? channelRefToMessage(event.childChannel) : null,
subchannel_ref: event.childSubchannel ? subchannelRefToMessage(event.childSubchannel) : null
}
})
channel_ref: event.childChannel
? channelRefToMessage(event.childChannel)
: null,
subchannel_ref: event.childSubchannel
? subchannelRefToMessage(event.childSubchannel)
: null,
};
}),
};
}
}
export class ChannelzChildrenTracker {
private channelChildren: Map<number, {ref: ChannelRef, count: number}> = new Map<number, {ref: ChannelRef, count: number}>();
private subchannelChildren: Map<number, {ref: SubchannelRef, count: number}> = new Map<number, {ref: SubchannelRef, count: number}>();
private socketChildren: Map<number, {ref: SocketRef, count: number}> = new Map<number, {ref: SocketRef, count: number}>();
private channelChildren: Map<number, { ref: ChannelRef; count: number }> =
new Map<number, { ref: ChannelRef; count: number }>();
private subchannelChildren: Map<
number,
{ ref: SubchannelRef; count: number }
> = new Map<number, { ref: SubchannelRef; count: number }>();
private socketChildren: Map<number, { ref: SocketRef; count: number }> =
new Map<number, { ref: SocketRef; count: number }>();
refChild(child: ChannelRef | SubchannelRef | SocketRef) {
switch (child.kind) {
case 'channel': {
let trackedChild = this.channelChildren.get(child.id) ?? {ref: child, count: 0};
const trackedChild = this.channelChildren.get(child.id) ?? {
ref: child,
count: 0,
};
trackedChild.count += 1;
this.channelChildren.set(child.id, trackedChild);
break;
}
case 'subchannel':{
let trackedChild = this.subchannelChildren.get(child.id) ?? {ref: child, count: 0};
case 'subchannel': {
const trackedChild = this.subchannelChildren.get(child.id) ?? {
ref: child,
count: 0,
};
trackedChild.count += 1;
this.subchannelChildren.set(child.id, trackedChild);
break;
}
case 'socket':{
let trackedChild = this.socketChildren.get(child.id) ?? {ref: child, count: 0};
case 'socket': {
const trackedChild = this.socketChildren.get(child.id) ?? {
ref: child,
count: 0,
};
trackedChild.count += 1;
this.socketChildren.set(child.id, trackedChild);
break;
@ -194,7 +226,7 @@ export class ChannelzChildrenTracker {
unrefChild(child: ChannelRef | SubchannelRef | SocketRef) {
switch (child.kind) {
case 'channel': {
let trackedChild = this.channelChildren.get(child.id);
const trackedChild = this.channelChildren.get(child.id);
if (trackedChild !== undefined) {
trackedChild.count -= 1;
if (trackedChild.count === 0) {
@ -206,7 +238,7 @@ export class ChannelzChildrenTracker {
break;
}
case 'subchannel': {
let trackedChild = this.subchannelChildren.get(child.id);
const trackedChild = this.subchannelChildren.get(child.id);
if (trackedChild !== undefined) {
trackedChild.count -= 1;
if (trackedChild.count === 0) {
@ -218,7 +250,7 @@ export class ChannelzChildrenTracker {
break;
}
case 'socket': {
let trackedChild = this.socketChildren.get(child.id);
const trackedChild = this.socketChildren.get(child.id);
if (trackedChild !== undefined) {
trackedChild.count -= 1;
if (trackedChild.count === 0) {
@ -234,25 +266,25 @@ export class ChannelzChildrenTracker {
getChildLists(): ChannelzChildren {
const channels: ChannelRef[] = [];
for (const {ref} of this.channelChildren.values()) {
for (const { ref } of this.channelChildren.values()) {
channels.push(ref);
}
const subchannels: SubchannelRef[] = [];
for (const {ref} of this.subchannelChildren.values()) {
for (const { ref } of this.subchannelChildren.values()) {
subchannels.push(ref);
}
const sockets: SocketRef[] = [];
for (const {ref} of this.socketChildren.values()) {
for (const { ref } of this.socketChildren.values()) {
sockets.push(ref);
}
return {channels, subchannels, sockets};
return { channels, subchannels, sockets };
}
}
export class ChannelzCallTracker {
callsStarted: number = 0;
callsSucceeded: number = 0;
callsFailed: number = 0;
callsStarted = 0;
callsSucceeded = 0;
callsFailed = 0;
lastCallStartedTimestamp: Date | null = null;
addCallStarted() {
@ -281,7 +313,7 @@ export interface ChannelInfo {
children: ChannelzChildren;
}
export interface SubchannelInfo extends ChannelInfo {}
export type SubchannelInfo = ChannelInfo;
export interface ServerInfo {
trace: ChannelzTrace;
@ -347,43 +379,60 @@ const subchannels: (SubchannelEntry | undefined)[] = [];
const servers: (ServerEntry | undefined)[] = [];
const sockets: (SocketEntry | undefined)[] = [];
export function registerChannelzChannel(name: string, getInfo: () => ChannelInfo, channelzEnabled: boolean): ChannelRef {
export function registerChannelzChannel(
name: string,
getInfo: () => ChannelInfo,
channelzEnabled: boolean
): ChannelRef {
const id = getNextId();
const ref: ChannelRef = {id, name, kind: 'channel'};
const ref: ChannelRef = { id, name, kind: 'channel' };
if (channelzEnabled) {
channels[id] = { ref, getInfo };
}
return ref;
}
export function registerChannelzSubchannel(name: string, getInfo:() => SubchannelInfo, channelzEnabled: boolean): SubchannelRef {
export function registerChannelzSubchannel(
name: string,
getInfo: () => SubchannelInfo,
channelzEnabled: boolean
): SubchannelRef {
const id = getNextId();
const ref: SubchannelRef = {id, name, kind: 'subchannel'};
const ref: SubchannelRef = { id, name, kind: 'subchannel' };
if (channelzEnabled) {
subchannels[id] = { ref, getInfo };
}
return ref;
}
export function registerChannelzServer(getInfo: () => ServerInfo, channelzEnabled: boolean): ServerRef {
export function registerChannelzServer(
getInfo: () => ServerInfo,
channelzEnabled: boolean
): ServerRef {
const id = getNextId();
const ref: ServerRef = {id, kind: 'server'};
const ref: ServerRef = { id, kind: 'server' };
if (channelzEnabled) {
servers[id] = { ref, getInfo };
}
return ref;
}
export function registerChannelzSocket(name: string, getInfo: () => SocketInfo, channelzEnabled: boolean): SocketRef {
export function registerChannelzSocket(
name: string,
getInfo: () => SocketInfo,
channelzEnabled: boolean
): SocketRef {
const id = getNextId();
const ref: SocketRef = {id, name, kind: 'socket'};
const ref: SocketRef = { id, name, kind: 'socket' };
if (channelzEnabled) {
sockets[id] = { ref, getInfo};
sockets[id] = { ref, getInfo };
}
return ref;
}
export function unregisterChannelzRef(ref: ChannelRef | SubchannelRef | ServerRef | SocketRef) {
export function unregisterChannelzRef(
ref: ChannelRef | SubchannelRef | ServerRef | SocketRef
) {
switch (ref.kind) {
case 'channel':
delete channels[ref.id];
@ -407,7 +456,7 @@ export function unregisterChannelzRef(ref: ChannelRef | SubchannelRef | ServerRe
*/
function parseIPv6Section(addressSection: string): [number, number] {
const numberValue = Number.parseInt(addressSection, 16);
return [numberValue / 256 | 0, numberValue % 256];
return [(numberValue / 256) | 0, numberValue % 256];
}
/**
@ -420,7 +469,9 @@ function parseIPv6Chunk(addressChunk: string): number[] {
if (addressChunk === '') {
return [];
}
const bytePairs = addressChunk.split(':').map(section => parseIPv6Section(section));
const bytePairs = addressChunk
.split(':')
.map(section => parseIPv6Section(section));
const result: number[] = [];
return result.concat(...bytePairs);
}
@ -429,11 +480,15 @@ function parseIPv6Chunk(addressChunk: string): number[] {
* Converts an IPv4 or IPv6 address from string representation to binary
* representation
* @param ipAddress an IP address in standard IPv4 or IPv6 text format
* @returns
* @returns
*/
function ipAddressStringToBuffer(ipAddress: string): Buffer | null {
if (isIPv4(ipAddress)) {
return Buffer.from(Uint8Array.from(ipAddress.split('.').map(segment => Number.parseInt(segment))));
return Buffer.from(
Uint8Array.from(
ipAddress.split('.').map(segment => Number.parseInt(segment))
)
);
} else if (isIPv6(ipAddress)) {
let leftSection: string;
let rightSection: string;
@ -447,38 +502,43 @@ function ipAddressStringToBuffer(ipAddress: string): Buffer | null {
}
const leftBuffer = Buffer.from(parseIPv6Chunk(leftSection));
const rightBuffer = Buffer.from(parseIPv6Chunk(rightSection));
const middleBuffer = Buffer.alloc(16 - leftBuffer.length - rightBuffer.length, 0);
const middleBuffer = Buffer.alloc(
16 - leftBuffer.length - rightBuffer.length,
0
);
return Buffer.concat([leftBuffer, middleBuffer, rightBuffer]);
} else {
return null;
}
}
function connectivityStateToMessage(state: ConnectivityState): ChannelConnectivityState__Output {
function connectivityStateToMessage(
state: ConnectivityState
): ChannelConnectivityState__Output {
switch (state) {
case ConnectivityState.CONNECTING:
return {
state: 'CONNECTING'
state: 'CONNECTING',
};
case ConnectivityState.IDLE:
return {
state: 'IDLE'
state: 'IDLE',
};
case ConnectivityState.READY:
return {
state: 'READY'
state: 'READY',
};
case ConnectivityState.SHUTDOWN:
return {
state: 'SHUTDOWN'
state: 'SHUTDOWN',
};
case ConnectivityState.TRANSIENT_FAILURE:
return {
state: 'TRANSIENT_FAILURE'
state: 'TRANSIENT_FAILURE',
};
default:
return {
state: 'UNKNOWN'
state: 'UNKNOWN',
};
}
}
@ -490,8 +550,8 @@ function dateToProtoTimestamp(date?: Date | null): Timestamp | null {
const millisSinceEpoch = date.getTime();
return {
seconds: (millisSinceEpoch / 1000) | 0,
nanos: (millisSinceEpoch % 1000) * 1_000_000
}
nanos: (millisSinceEpoch % 1000) * 1_000_000,
};
}
function getChannelMessage(channelEntry: ChannelEntry): ChannelMessage {
@ -504,28 +564,40 @@ function getChannelMessage(channelEntry: ChannelEntry): ChannelMessage {
calls_started: resolvedInfo.callTracker.callsStarted,
calls_succeeded: resolvedInfo.callTracker.callsSucceeded,
calls_failed: resolvedInfo.callTracker.callsFailed,
last_call_started_timestamp: dateToProtoTimestamp(resolvedInfo.callTracker.lastCallStartedTimestamp),
trace: resolvedInfo.trace.getTraceMessage()
last_call_started_timestamp: dateToProtoTimestamp(
resolvedInfo.callTracker.lastCallStartedTimestamp
),
trace: resolvedInfo.trace.getTraceMessage(),
},
channel_ref: resolvedInfo.children.channels.map(ref => channelRefToMessage(ref)),
subchannel_ref: resolvedInfo.children.subchannels.map(ref => subchannelRefToMessage(ref))
channel_ref: resolvedInfo.children.channels.map(ref =>
channelRefToMessage(ref)
),
subchannel_ref: resolvedInfo.children.subchannels.map(ref =>
subchannelRefToMessage(ref)
),
};
}
function GetChannel(call: ServerUnaryCall<GetChannelRequest__Output, GetChannelResponse>, callback: sendUnaryData<GetChannelResponse>): void {
function GetChannel(
call: ServerUnaryCall<GetChannelRequest__Output, GetChannelResponse>,
callback: sendUnaryData<GetChannelResponse>
): void {
const channelId = Number.parseInt(call.request.channel_id);
const channelEntry = channels[channelId];
if (channelEntry === undefined) {
callback({
'code': Status.NOT_FOUND,
'details': 'No channel data found for id ' + channelId
code: Status.NOT_FOUND,
details: 'No channel data found for id ' + channelId,
});
return;
}
callback(null, {channel: getChannelMessage(channelEntry)});
callback(null, { channel: getChannelMessage(channelEntry) });
}
function GetTopChannels(call: ServerUnaryCall<GetTopChannelsRequest__Output, GetTopChannelsResponse>, callback: sendUnaryData<GetTopChannelsResponse>): void {
function GetTopChannels(
call: ServerUnaryCall<GetTopChannelsRequest__Output, GetTopChannelsResponse>,
callback: sendUnaryData<GetTopChannelsResponse>
): void {
const maxResults = Number.parseInt(call.request.max_results);
const resultList: ChannelMessage[] = [];
let i = Number.parseInt(call.request.start_channel_id);
@ -541,7 +613,7 @@ function GetTopChannels(call: ServerUnaryCall<GetTopChannelsRequest__Output, Get
}
callback(null, {
channel: resultList,
end: i >= servers.length
end: i >= servers.length,
});
}
@ -553,27 +625,37 @@ function getServerMessage(serverEntry: ServerEntry): ServerMessage {
calls_started: resolvedInfo.callTracker.callsStarted,
calls_succeeded: resolvedInfo.callTracker.callsSucceeded,
calls_failed: resolvedInfo.callTracker.callsFailed,
last_call_started_timestamp: dateToProtoTimestamp(resolvedInfo.callTracker.lastCallStartedTimestamp),
trace: resolvedInfo.trace.getTraceMessage()
last_call_started_timestamp: dateToProtoTimestamp(
resolvedInfo.callTracker.lastCallStartedTimestamp
),
trace: resolvedInfo.trace.getTraceMessage(),
},
listen_socket: resolvedInfo.listenerChildren.sockets.map(ref => socketRefToMessage(ref))
listen_socket: resolvedInfo.listenerChildren.sockets.map(ref =>
socketRefToMessage(ref)
),
};
}
function GetServer(call: ServerUnaryCall<GetServerRequest__Output, GetServerResponse>, callback: sendUnaryData<GetServerResponse>): void {
function GetServer(
call: ServerUnaryCall<GetServerRequest__Output, GetServerResponse>,
callback: sendUnaryData<GetServerResponse>
): void {
const serverId = Number.parseInt(call.request.server_id);
const serverEntry = servers[serverId];
if (serverEntry === undefined) {
callback({
'code': Status.NOT_FOUND,
'details': 'No server data found for id ' + serverId
code: Status.NOT_FOUND,
details: 'No server data found for id ' + serverId,
});
return;
}
callback(null, {server: getServerMessage(serverEntry)});
callback(null, { server: getServerMessage(serverEntry) });
}
function GetServers(call: ServerUnaryCall<GetServersRequest__Output, GetServersResponse>, callback: sendUnaryData<GetServersResponse>): void {
function GetServers(
call: ServerUnaryCall<GetServersRequest__Output, GetServersResponse>,
callback: sendUnaryData<GetServersResponse>
): void {
const maxResults = Number.parseInt(call.request.max_results);
const resultList: ServerMessage[] = [];
let i = Number.parseInt(call.request.start_server_id);
@ -589,17 +671,20 @@ function GetServers(call: ServerUnaryCall<GetServersRequest__Output, GetServersR
}
callback(null, {
server: resultList,
end: i >= servers.length
end: i >= servers.length,
});
}
function GetSubchannel(call: ServerUnaryCall<GetSubchannelRequest__Output, GetSubchannelResponse>, callback: sendUnaryData<GetSubchannelResponse>): void {
function GetSubchannel(
call: ServerUnaryCall<GetSubchannelRequest__Output, GetSubchannelResponse>,
callback: sendUnaryData<GetSubchannelResponse>
): void {
const subchannelId = Number.parseInt(call.request.subchannel_id);
const subchannelEntry = subchannels[subchannelId];
if (subchannelEntry === undefined) {
callback({
'code': Status.NOT_FOUND,
'details': 'No subchannel data found for id ' + subchannelId
code: Status.NOT_FOUND,
details: 'No subchannel data found for id ' + subchannelId,
});
return;
}
@ -612,58 +697,79 @@ function GetSubchannel(call: ServerUnaryCall<GetSubchannelRequest__Output, GetSu
calls_started: resolvedInfo.callTracker.callsStarted,
calls_succeeded: resolvedInfo.callTracker.callsSucceeded,
calls_failed: resolvedInfo.callTracker.callsFailed,
last_call_started_timestamp: dateToProtoTimestamp(resolvedInfo.callTracker.lastCallStartedTimestamp),
trace: resolvedInfo.trace.getTraceMessage()
last_call_started_timestamp: dateToProtoTimestamp(
resolvedInfo.callTracker.lastCallStartedTimestamp
),
trace: resolvedInfo.trace.getTraceMessage(),
},
socket_ref: resolvedInfo.children.sockets.map(ref => socketRefToMessage(ref))
socket_ref: resolvedInfo.children.sockets.map(ref =>
socketRefToMessage(ref)
),
};
callback(null, {subchannel: subchannelMessage});
callback(null, { subchannel: subchannelMessage });
}
function subchannelAddressToAddressMessage(subchannelAddress: SubchannelAddress): Address {
function subchannelAddressToAddressMessage(
subchannelAddress: SubchannelAddress
): Address {
if (isTcpSubchannelAddress(subchannelAddress)) {
return {
address: 'tcpip_address',
tcpip_address: {
ip_address: ipAddressStringToBuffer(subchannelAddress.host) ?? undefined,
port: subchannelAddress.port
}
ip_address:
ipAddressStringToBuffer(subchannelAddress.host) ?? undefined,
port: subchannelAddress.port,
},
};
} else {
return {
address: 'uds_address',
uds_address: {
filename: subchannelAddress.path
}
filename: subchannelAddress.path,
},
};
}
}
function GetSocket(call: ServerUnaryCall<GetSocketRequest__Output, GetSocketResponse>, callback: sendUnaryData<GetSocketResponse>): void {
function GetSocket(
call: ServerUnaryCall<GetSocketRequest__Output, GetSocketResponse>,
callback: sendUnaryData<GetSocketResponse>
): void {
const socketId = Number.parseInt(call.request.socket_id);
const socketEntry = sockets[socketId];
if (socketEntry === undefined) {
callback({
'code': Status.NOT_FOUND,
'details': 'No socket data found for id ' + socketId
code: Status.NOT_FOUND,
details: 'No socket data found for id ' + socketId,
});
return;
}
const resolvedInfo = socketEntry.getInfo();
const securityMessage: Security | null = resolvedInfo.security ? {
model: 'tls',
tls: {
cipher_suite: resolvedInfo.security.cipherSuiteStandardName ? 'standard_name' : 'other_name',
standard_name: resolvedInfo.security.cipherSuiteStandardName ?? undefined,
other_name: resolvedInfo.security.cipherSuiteOtherName ?? undefined,
local_certificate: resolvedInfo.security.localCertificate ?? undefined,
remote_certificate: resolvedInfo.security.remoteCertificate ?? undefined
}
} : null;
const securityMessage: Security | null = resolvedInfo.security
? {
model: 'tls',
tls: {
cipher_suite: resolvedInfo.security.cipherSuiteStandardName
? 'standard_name'
: 'other_name',
standard_name:
resolvedInfo.security.cipherSuiteStandardName ?? undefined,
other_name: resolvedInfo.security.cipherSuiteOtherName ?? undefined,
local_certificate:
resolvedInfo.security.localCertificate ?? undefined,
remote_certificate:
resolvedInfo.security.remoteCertificate ?? undefined,
},
}
: null;
const socketMessage: SocketMessage = {
ref: socketRefToMessage(socketEntry.ref),
local: resolvedInfo.localAddress ? subchannelAddressToAddressMessage(resolvedInfo.localAddress) : null,
remote: resolvedInfo.remoteAddress ? subchannelAddressToAddressMessage(resolvedInfo.remoteAddress) : null,
local: resolvedInfo.localAddress
? subchannelAddressToAddressMessage(resolvedInfo.localAddress)
: null,
remote: resolvedInfo.remoteAddress
? subchannelAddressToAddressMessage(resolvedInfo.remoteAddress)
: null,
remote_name: resolvedInfo.remoteName ?? undefined,
security: securityMessage,
data: {
@ -671,26 +777,44 @@ function GetSocket(call: ServerUnaryCall<GetSocketRequest__Output, GetSocketResp
streams_started: resolvedInfo.streamsStarted,
streams_succeeded: resolvedInfo.streamsSucceeded,
streams_failed: resolvedInfo.streamsFailed,
last_local_stream_created_timestamp: dateToProtoTimestamp(resolvedInfo.lastLocalStreamCreatedTimestamp),
last_remote_stream_created_timestamp: dateToProtoTimestamp(resolvedInfo.lastRemoteStreamCreatedTimestamp),
last_local_stream_created_timestamp: dateToProtoTimestamp(
resolvedInfo.lastLocalStreamCreatedTimestamp
),
last_remote_stream_created_timestamp: dateToProtoTimestamp(
resolvedInfo.lastRemoteStreamCreatedTimestamp
),
messages_received: resolvedInfo.messagesReceived,
messages_sent: resolvedInfo.messagesSent,
last_message_received_timestamp: dateToProtoTimestamp(resolvedInfo.lastMessageReceivedTimestamp),
last_message_sent_timestamp: dateToProtoTimestamp(resolvedInfo.lastMessageSentTimestamp),
local_flow_control_window: resolvedInfo.localFlowControlWindow ? { value: resolvedInfo.localFlowControlWindow } : null,
remote_flow_control_window: resolvedInfo.remoteFlowControlWindow ? { value: resolvedInfo.remoteFlowControlWindow } : null,
}
last_message_received_timestamp: dateToProtoTimestamp(
resolvedInfo.lastMessageReceivedTimestamp
),
last_message_sent_timestamp: dateToProtoTimestamp(
resolvedInfo.lastMessageSentTimestamp
),
local_flow_control_window: resolvedInfo.localFlowControlWindow
? { value: resolvedInfo.localFlowControlWindow }
: null,
remote_flow_control_window: resolvedInfo.remoteFlowControlWindow
? { value: resolvedInfo.remoteFlowControlWindow }
: null,
},
};
callback(null, {socket: socketMessage});
callback(null, { socket: socketMessage });
}
function GetServerSockets(call: ServerUnaryCall<GetServerSocketsRequest__Output, GetServerSocketsResponse>, callback: sendUnaryData<GetServerSocketsResponse>): void {
function GetServerSockets(
call: ServerUnaryCall<
GetServerSocketsRequest__Output,
GetServerSocketsResponse
>,
callback: sendUnaryData<GetServerSocketsResponse>
): void {
const serverId = Number.parseInt(call.request.server_id);
const serverEntry = servers[serverId];
if (serverEntry === undefined) {
callback({
'code': Status.NOT_FOUND,
'details': 'No server data found for id ' + serverId
code: Status.NOT_FOUND,
details: 'No server data found for id ' + serverId,
});
return;
}
@ -700,7 +824,9 @@ function GetServerSockets(call: ServerUnaryCall<GetServerSocketsRequest__Output,
// If we wanted to include listener sockets in the result, this line would
// instead say
// const allSockets = resolvedInfo.listenerChildren.sockets.concat(resolvedInfo.sessionChildren.sockets).sort((ref1, ref2) => ref1.id - ref2.id);
const allSockets = resolvedInfo.sessionChildren.sockets.sort((ref1, ref2) => ref1.id - ref2.id);
const allSockets = resolvedInfo.sessionChildren.sockets.sort(
(ref1, ref2) => ref1.id - ref2.id
);
const resultList: SocketRefMessage[] = [];
let i = 0;
for (; i < allSockets.length; i++) {
@ -713,7 +839,7 @@ function GetServerSockets(call: ServerUnaryCall<GetServerSocketsRequest__Output,
}
callback(null, {
socket_ref: resultList,
end: i >= allSockets.length
end: i >= allSockets.length,
});
}
@ -725,7 +851,7 @@ export function getChannelzHandlers(): ChannelzHandlers {
GetServers,
GetSubchannel,
GetSocket,
GetServerSockets
GetServerSockets,
};
}
@ -737,22 +863,24 @@ export function getChannelzServiceDefinition(): ChannelzDefinition {
}
/* The purpose of this complexity is to avoid loading @grpc/proto-loader at
* runtime for users who will not use/enable channelz. */
const loaderLoadSync = require('@grpc/proto-loader').loadSync as typeof loadSync;
const loaderLoadSync = require('@grpc/proto-loader')
.loadSync as typeof loadSync;
const loadedProto = loaderLoadSync('channelz.proto', {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
includeDirs: [
`${__dirname}/../../proto`
]
includeDirs: [`${__dirname}/../../proto`],
});
const channelzGrpcObject = loadPackageDefinition(loadedProto) as unknown as ChannelzProtoGrpcType;
loadedChannelzDefinition = channelzGrpcObject.grpc.channelz.v1.Channelz.service;
const channelzGrpcObject = loadPackageDefinition(
loadedProto
) as unknown as ChannelzProtoGrpcType;
loadedChannelzDefinition =
channelzGrpcObject.grpc.channelz.v1.Channelz.service;
return loadedChannelzDefinition;
}
export function setup() {
registerAdminService(getChannelzServiceDefinition, getChannelzHandlers);
}
}

View File

@ -176,10 +176,10 @@ const defaultRequester: FullRequester = {
sendMessage: (message, next) => {
next(message);
},
halfClose: (next) => {
halfClose: next => {
next();
},
cancel: (next) => {
cancel: next => {
next();
},
};
@ -254,7 +254,10 @@ export class InterceptingCall implements InterceptingCallInterface {
private processPendingMessage() {
if (this.pendingMessageContext) {
this.nextCall.sendMessageWithContext(this.pendingMessageContext, this.pendingMessage);
this.nextCall.sendMessageWithContext(
this.pendingMessageContext,
this.pendingMessage
);
this.pendingMessageContext = null;
this.pendingMessage = null;
}
@ -273,13 +276,13 @@ export class InterceptingCall implements InterceptingCallInterface {
const fullInterceptingListener: InterceptingListener = {
onReceiveMetadata:
interceptingListener?.onReceiveMetadata?.bind(interceptingListener) ??
((metadata) => {}),
(metadata => {}),
onReceiveMessage:
interceptingListener?.onReceiveMessage?.bind(interceptingListener) ??
((message) => {}),
(message => {}),
onReceiveStatus:
interceptingListener?.onReceiveStatus?.bind(interceptingListener) ??
((status) => {}),
(status => {}),
};
this.processingMetadata = true;
this.requester.start(metadata, fullInterceptingListener, (md, listener) => {
@ -309,7 +312,7 @@ export class InterceptingCall implements InterceptingCallInterface {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
sendMessageWithContext(context: MessageContext, message: any): void {
this.processingMessage = true;
this.requester.sendMessage(message, (finalMessage) => {
this.requester.sendMessage(message, finalMessage => {
this.processingMessage = false;
if (this.processingMetadata) {
this.pendingMessageContext = context;
@ -391,10 +394,10 @@ class BaseInterceptingCall implements InterceptingCallInterface {
): void {
let readError: StatusObject | null = null;
this.call.start(metadata, {
onReceiveMetadata: (metadata) => {
onReceiveMetadata: metadata => {
interceptingListener?.onReceiveMetadata?.(metadata);
},
onReceiveMessage: (message) => {
onReceiveMessage: message => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let deserialized: any;
try {
@ -410,7 +413,7 @@ class BaseInterceptingCall implements InterceptingCallInterface {
}
interceptingListener?.onReceiveMessage?.(deserialized);
},
onReceiveStatus: (status) => {
onReceiveStatus: status => {
if (readError) {
interceptingListener?.onReceiveStatus?.(readError);
} else {
@ -433,7 +436,8 @@ class BaseInterceptingCall implements InterceptingCallInterface {
*/
class BaseUnaryInterceptingCall
extends BaseInterceptingCall
implements InterceptingCallInterface {
implements InterceptingCallInterface
{
// eslint-disable-next-line @typescript-eslint/no-explicit-any
constructor(call: Call, methodDefinition: ClientMethodDefinition<any, any>) {
super(call, methodDefinition);
@ -442,7 +446,7 @@ class BaseUnaryInterceptingCall
let receivedMessage = false;
const wrapperListener: InterceptingListener = {
onReceiveMetadata:
listener?.onReceiveMetadata?.bind(listener) ?? ((metadata) => {}),
listener?.onReceiveMetadata?.bind(listener) ?? (metadata => {}),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onReceiveMessage: (message: any) => {
receivedMessage = true;
@ -536,21 +540,21 @@ export function getInterceptingCall(
interceptors = ([] as Interceptor[])
.concat(
interceptorArgs.callInterceptors,
interceptorArgs.callInterceptorProviders.map((provider) =>
interceptorArgs.callInterceptorProviders.map(provider =>
provider(methodDefinition)
)
)
.filter((interceptor) => interceptor);
.filter(interceptor => interceptor);
// Filter out falsy values when providers return nothing
} else {
interceptors = ([] as Interceptor[])
.concat(
interceptorArgs.clientInterceptors,
interceptorArgs.clientInterceptorProviders.map((provider) =>
interceptorArgs.clientInterceptorProviders.map(provider =>
provider(methodDefinition)
)
)
.filter((interceptor) => interceptor);
.filter(interceptor => interceptor);
// Filter out falsy values when providers return nothing
}
const interceptorOptions = Object.assign({}, options, {
@ -565,7 +569,7 @@ export function getInterceptingCall(
* channel. */
const getCall: NextCall = interceptors.reduceRight<NextCall>(
(nextCall: NextCall, nextInterceptor: Interceptor) => {
return (currentOptions) => nextInterceptor(currentOptions, nextCall);
return currentOptions => nextInterceptor(currentOptions, nextCall);
},
(finalOptions: InterceptorOptions) =>
getBottomInterceptingCall(channel, finalOptions, methodDefinition)

View File

@ -273,21 +273,20 @@ export class Client {
options?: CallOptions | UnaryCallback<ResponseType>,
callback?: UnaryCallback<ResponseType>
): ClientUnaryCall {
const checkedArguments = this.checkOptionalUnaryResponseArguments<ResponseType>(
metadata,
options,
callback
);
const methodDefinition: ClientMethodDefinition<
RequestType,
ResponseType
> = {
path: method,
requestStream: false,
responseStream: false,
requestSerialize: serialize,
responseDeserialize: deserialize,
};
const checkedArguments =
this.checkOptionalUnaryResponseArguments<ResponseType>(
metadata,
options,
callback
);
const methodDefinition: ClientMethodDefinition<RequestType, ResponseType> =
{
path: method,
requestStream: false,
responseStream: false,
requestSerialize: serialize,
responseDeserialize: deserialize,
};
let callProperties: CallProperties<RequestType, ResponseType> = {
argument: argument,
metadata: checkedArguments.metadata,
@ -325,7 +324,7 @@ export class Client {
let receivedStatus = false;
const callerStackError = new Error();
call.start(callProperties.metadata, {
onReceiveMetadata: (metadata) => {
onReceiveMetadata: metadata => {
emitter.emit('metadata', metadata);
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -343,11 +342,16 @@ export class Client {
if (status.code === Status.OK) {
if (responseMessage === null) {
const callerStack = getErrorStackString(callerStackError);
callProperties.callback!(callErrorFromStatus({
code: Status.INTERNAL,
details: 'No message received',
metadata: status.metadata
}, callerStack));
callProperties.callback!(
callErrorFromStatus(
{
code: Status.INTERNAL,
details: 'No message received',
metadata: status.metadata,
},
callerStack
)
);
} else {
callProperties.callback!(null, responseMessage);
}
@ -399,21 +403,20 @@ export class Client {
options?: CallOptions | UnaryCallback<ResponseType>,
callback?: UnaryCallback<ResponseType>
): ClientWritableStream<RequestType> {
const checkedArguments = this.checkOptionalUnaryResponseArguments<ResponseType>(
metadata,
options,
callback
);
const methodDefinition: ClientMethodDefinition<
RequestType,
ResponseType
> = {
path: method,
requestStream: true,
responseStream: false,
requestSerialize: serialize,
responseDeserialize: deserialize,
};
const checkedArguments =
this.checkOptionalUnaryResponseArguments<ResponseType>(
metadata,
options,
callback
);
const methodDefinition: ClientMethodDefinition<RequestType, ResponseType> =
{
path: method,
requestStream: true,
responseStream: false,
requestSerialize: serialize,
responseDeserialize: deserialize,
};
let callProperties: CallProperties<RequestType, ResponseType> = {
metadata: checkedArguments.metadata,
call: new ClientWritableStreamImpl<RequestType>(serialize),
@ -427,7 +430,8 @@ export class Client {
callProperties
) as CallProperties<RequestType, ResponseType>;
}
const emitter: ClientWritableStream<RequestType> = callProperties.call as ClientWritableStream<RequestType>;
const emitter: ClientWritableStream<RequestType> =
callProperties.call as ClientWritableStream<RequestType>;
const interceptorArgs: InterceptorArguments = {
clientInterceptors: this[INTERCEPTOR_SYMBOL],
clientInterceptorProviders: this[INTERCEPTOR_PROVIDER_SYMBOL],
@ -450,7 +454,7 @@ export class Client {
let receivedStatus = false;
const callerStackError = new Error();
call.start(callProperties.metadata, {
onReceiveMetadata: (metadata) => {
onReceiveMetadata: metadata => {
emitter.emit('metadata', metadata);
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -468,11 +472,16 @@ export class Client {
if (status.code === Status.OK) {
if (responseMessage === null) {
const callerStack = getErrorStackString(callerStackError);
callProperties.callback!(callErrorFromStatus({
code: Status.INTERNAL,
details: 'No message received',
metadata: status.metadata
}, callerStack));
callProperties.callback!(
callErrorFromStatus(
{
code: Status.INTERNAL,
details: 'No message received',
metadata: status.metadata,
},
callerStack
)
);
} else {
callProperties.callback!(null, responseMessage);
}
@ -534,16 +543,14 @@ export class Client {
options?: CallOptions
): ClientReadableStream<ResponseType> {
const checkedArguments = this.checkMetadataAndOptions(metadata, options);
const methodDefinition: ClientMethodDefinition<
RequestType,
ResponseType
> = {
path: method,
requestStream: false,
responseStream: true,
requestSerialize: serialize,
responseDeserialize: deserialize,
};
const methodDefinition: ClientMethodDefinition<RequestType, ResponseType> =
{
path: method,
requestStream: false,
responseStream: true,
requestSerialize: serialize,
responseDeserialize: deserialize,
};
let callProperties: CallProperties<RequestType, ResponseType> = {
argument: argument,
metadata: checkedArguments.metadata,
@ -557,7 +564,8 @@ export class Client {
callProperties
) as CallProperties<RequestType, ResponseType>;
}
const stream: ClientReadableStream<ResponseType> = callProperties.call as ClientReadableStream<ResponseType>;
const stream: ClientReadableStream<ResponseType> =
callProperties.call as ClientReadableStream<ResponseType>;
const interceptorArgs: InterceptorArguments = {
clientInterceptors: this[INTERCEPTOR_SYMBOL],
clientInterceptorProviders: this[INTERCEPTOR_PROVIDER_SYMBOL],
@ -625,16 +633,14 @@ export class Client {
options?: CallOptions
): ClientDuplexStream<RequestType, ResponseType> {
const checkedArguments = this.checkMetadataAndOptions(metadata, options);
const methodDefinition: ClientMethodDefinition<
RequestType,
ResponseType
> = {
path: method,
requestStream: true,
responseStream: true,
requestSerialize: serialize,
responseDeserialize: deserialize,
};
const methodDefinition: ClientMethodDefinition<RequestType, ResponseType> =
{
path: method,
requestStream: true,
responseStream: true,
requestSerialize: serialize,
responseDeserialize: deserialize,
};
let callProperties: CallProperties<RequestType, ResponseType> = {
metadata: checkedArguments.metadata,
call: new ClientDuplexStreamImpl<RequestType, ResponseType>(
@ -650,10 +656,8 @@ export class Client {
callProperties
) as CallProperties<RequestType, ResponseType>;
}
const stream: ClientDuplexStream<
RequestType,
ResponseType
> = callProperties.call as ClientDuplexStream<RequestType, ResponseType>;
const stream: ClientDuplexStream<RequestType, ResponseType> =
callProperties.call as ClientDuplexStream<RequestType, ResponseType>;
const interceptorArgs: InterceptorArguments = {
clientInterceptors: this[INTERCEPTOR_SYMBOL],
clientInterceptorProviders: this[INTERCEPTOR_PROVIDER_SYMBOL],

View File

@ -18,5 +18,5 @@
export enum CompressionAlgorithms {
identity = 0,
deflate = 1,
gzip = 2
};
gzip = 2,
}

View File

@ -26,9 +26,13 @@ import { BaseFilter, Filter, FilterFactory } from './filter';
import * as logging from './logging';
import { Metadata, MetadataValue } from './metadata';
const isCompressionAlgorithmKey = (key: number): key is CompressionAlgorithms => {
return typeof key === 'number' && typeof CompressionAlgorithms[key] === 'string';
}
const isCompressionAlgorithmKey = (
key: number
): key is CompressionAlgorithms => {
return (
typeof key === 'number' && typeof CompressionAlgorithms[key] === 'string'
);
};
type CompressionAlgorithm = keyof typeof CompressionAlgorithms;
@ -183,14 +187,21 @@ export class CompressionFilter extends BaseFilter implements Filter {
private receiveCompression: CompressionHandler = new IdentityHandler();
private currentCompressionAlgorithm: CompressionAlgorithm = 'identity';
constructor(channelOptions: ChannelOptions, private sharedFilterConfig: SharedCompressionFilterConfig) {
constructor(
channelOptions: ChannelOptions,
private sharedFilterConfig: SharedCompressionFilterConfig
) {
super();
const compressionAlgorithmKey = channelOptions['grpc.default_compression_algorithm'];
const compressionAlgorithmKey =
channelOptions['grpc.default_compression_algorithm'];
if (compressionAlgorithmKey !== undefined) {
if (isCompressionAlgorithmKey(compressionAlgorithmKey)) {
const clientSelectedEncoding = CompressionAlgorithms[compressionAlgorithmKey] as CompressionAlgorithm;
const serverSupportedEncodings = sharedFilterConfig.serverSupportedEncodingHeader?.split(',');
const clientSelectedEncoding = CompressionAlgorithms[
compressionAlgorithmKey
] as CompressionAlgorithm;
const serverSupportedEncodings =
sharedFilterConfig.serverSupportedEncodingHeader?.split(',');
/**
* There are two possible situations here:
* 1) We don't have any info yet from the server about what compression it supports
@ -198,12 +209,20 @@ export class CompressionFilter extends BaseFilter implements Filter {
* 2) We've previously received a response from the server including a grpc-accept-encoding header
* In that case we only want to use the encoding chosen by the client if the server supports it
*/
if (!serverSupportedEncodings || serverSupportedEncodings.includes(clientSelectedEncoding)) {
if (
!serverSupportedEncodings ||
serverSupportedEncodings.includes(clientSelectedEncoding)
) {
this.currentCompressionAlgorithm = clientSelectedEncoding;
this.sendCompression = getCompressionHandler(this.currentCompressionAlgorithm);
this.sendCompression = getCompressionHandler(
this.currentCompressionAlgorithm
);
}
} else {
logging.log(LogVerbosity.ERROR, `Invalid value provided for grpc.default_compression_algorithm option: ${compressionAlgorithmKey}`);
logging.log(
LogVerbosity.ERROR,
`Invalid value provided for grpc.default_compression_algorithm option: ${compressionAlgorithmKey}`
);
}
}
}
@ -235,12 +254,18 @@ export class CompressionFilter extends BaseFilter implements Filter {
/* Check to see if the compression we're using to send messages is supported by the server
* If not, reset the sendCompression filter and have it use the default IdentityHandler */
const serverSupportedEncodingsHeader = metadata.get('grpc-accept-encoding')[0] as string | undefined;
const serverSupportedEncodingsHeader = metadata.get(
'grpc-accept-encoding'
)[0] as string | undefined;
if (serverSupportedEncodingsHeader) {
this.sharedFilterConfig.serverSupportedEncodingHeader = serverSupportedEncodingsHeader;
const serverSupportedEncodings = serverSupportedEncodingsHeader.split(',');
this.sharedFilterConfig.serverSupportedEncodingHeader =
serverSupportedEncodingsHeader;
const serverSupportedEncodings =
serverSupportedEncodingsHeader.split(',');
if (!serverSupportedEncodings.includes(this.currentCompressionAlgorithm)) {
if (
!serverSupportedEncodings.includes(this.currentCompressionAlgorithm)
) {
this.sendCompression = new IdentityHandler();
this.currentCompressionAlgorithm = 'identity';
}
@ -280,9 +305,13 @@ export class CompressionFilter extends BaseFilter implements Filter {
}
export class CompressionFilterFactory
implements FilterFactory<CompressionFilter> {
private sharedFilterConfig: SharedCompressionFilterConfig = {};
constructor(private readonly channel: Channel, private readonly options: ChannelOptions) {}
implements FilterFactory<CompressionFilter>
{
private sharedFilterConfig: SharedCompressionFilterConfig = {};
constructor(
private readonly channel: Channel,
private readonly options: ChannelOptions
) {}
createFilter(): CompressionFilter {
return new CompressionFilter(this.options, this.sharedFilterConfig);
}

View File

@ -25,16 +25,19 @@ const INAPPROPRIATE_CONTROL_PLANE_CODES: Status[] = [
Status.FAILED_PRECONDITION,
Status.ABORTED,
Status.OUT_OF_RANGE,
Status.DATA_LOSS
]
Status.DATA_LOSS,
];
export function restrictControlPlaneStatusCode(code: Status, details: string): {code: Status, details: string} {
export function restrictControlPlaneStatusCode(
code: Status,
details: string
): { code: Status; details: string } {
if (INAPPROPRIATE_CONTROL_PLANE_CODES.includes(code)) {
return {
code: Status.INTERNAL,
details: `Invalid status from control plane: ${code} ${Status[code]} ${details}`
}
details: `Invalid status from control plane: ${code} ${Status[code]} ${details}`,
};
} else {
return {code, details};
return { code, details };
}
}
}

View File

@ -48,7 +48,7 @@ export function getDeadlineTimeoutString(deadline: Deadline) {
return String(Math.ceil(amount)) + unit;
}
}
throw new Error('Deadline is too far in the future')
throw new Error('Deadline is too far in the future');
}
/**
@ -65,7 +65,7 @@ const MAX_TIMEOUT_TIME = 2147483647;
* immediately, represented by a value of 0. For any deadline more than
* MAX_TIMEOUT_TIME milliseconds in the future, a timer cannot be set that will
* end at that time, so it is treated as infinitely far in the future.
* @param deadline
* @param deadline
* @returns
*/
export function getRelativeTimeout(deadline: Deadline) {
@ -75,7 +75,7 @@ export function getRelativeTimeout(deadline: Deadline) {
if (timeout < 0) {
return 0;
} else if (timeout > MAX_TIMEOUT_TIME) {
return Infinity
return Infinity;
} else {
return timeout;
}
@ -92,4 +92,4 @@ export function deadlineToString(deadline: Deadline): string {
return dateDeadline.toISOString();
}
}
}
}

View File

@ -23,7 +23,7 @@ export interface Duration {
export function msToDuration(millis: number): Duration {
return {
seconds: (millis / 1000) | 0,
nanos: (millis % 1000) * 1_000_000 | 0
nanos: ((millis % 1000) * 1_000_000) | 0,
};
}
@ -32,5 +32,5 @@ export function durationToMs(duration: Duration): number {
}
export function isDuration(value: any): value is Duration {
return (typeof value.seconds === 'number') && (typeof value.nanos === 'number');
}
return typeof value.seconds === 'number' && typeof value.nanos === 'number';
}

View File

@ -34,4 +34,4 @@ export function getErrorCode(error: unknown): number | null {
} else {
return null;
}
}
}

View File

@ -4,7 +4,7 @@ export {
ResolverListener,
registerResolver,
ConfigSelector,
createResolver
createResolver,
} from './resolver';
export { GrpcUri, uriToString } from './uri-parser';
export { Duration, durationToMs } from './duration';
@ -36,5 +36,13 @@ export { Call as CallStream } from './call-interface';
export { Filter, BaseFilter, FilterFactory } from './filter';
export { FilterStackFactory } from './filter-stack';
export { registerAdminService } from './admin';
export { SubchannelInterface, BaseSubchannelWrapper, ConnectivityStateListener } from './subchannel-interface';
export { OutlierDetectionLoadBalancingConfig, SuccessRateEjectionConfig, FailurePercentageEjectionConfig } from './load-balancer-outlier-detection';
export {
SubchannelInterface,
BaseSubchannelWrapper,
ConnectivityStateListener,
} from './subchannel-interface';
export {
OutlierDetectionLoadBalancingConfig,
SuccessRateEjectionConfig,
FailurePercentageEjectionConfig,
} from './load-balancer-outlier-detection';

View File

@ -94,7 +94,7 @@ export class FilterStackFactory implements FilterFactory<FilterStack> {
createFilter(): FilterStack {
return new FilterStack(
this.factories.map((factory) => factory.createFilter())
this.factories.map(factory => factory.createFilter())
);
}
}

View File

@ -206,11 +206,11 @@ export function getProxiedConnection(
if ('grpc.http_connect_creds' in channelOptions) {
headers['Proxy-Authorization'] =
'Basic ' +
Buffer.from(
channelOptions['grpc.http_connect_creds'] as string
).toString('base64');
Buffer.from(channelOptions['grpc.http_connect_creds'] as string).toString(
'base64'
);
}
options.headers = headers
options.headers = headers;
const proxyAddressString = subchannelAddressToString(address);
trace('Using proxy ' + proxyAddressString + ' to connect to ' + options.path);
return new Promise<ProxyConnectionResult>((resolve, reject) => {
@ -252,12 +252,14 @@ export function getProxiedConnection(
}
);
cts.on('error', (error: Error) => {
trace('Failed to establish a TLS connection to ' +
options.path +
' through proxy ' +
proxyAddressString +
' with error ' +
error.message);
trace(
'Failed to establish a TLS connection to ' +
options.path +
' through proxy ' +
proxyAddressString +
' with error ' +
error.message
);
reject();
});
} else {
@ -285,7 +287,7 @@ export function getProxiedConnection(
reject();
}
});
request.once('error', (err) => {
request.once('error', err => {
request.removeAllListeners();
log(
LogVerbosity.ERROR,

View File

@ -128,7 +128,7 @@ export {
Status as status,
ConnectivityState as connectivityState,
Propagate as propagate,
CompressionAlgorithms as compressionAlgorithms
CompressionAlgorithms as compressionAlgorithms,
// TODO: Other constants as well
};
@ -248,21 +248,18 @@ export {
InterceptorProvider,
InterceptingCall,
InterceptorConfigurationError,
NextCall
NextCall,
} from './client-interceptors';
export {
GrpcObject,
ServiceClientConstructor,
ProtobufTypeDefinition
ProtobufTypeDefinition,
} from './make-client';
export { ChannelOptions } from './channel-options';
export {
getChannelzServiceDefinition,
getChannelzHandlers
} from './channelz';
export { getChannelzServiceDefinition, getChannelzHandlers } from './channelz';
export { addAdminServicesToServer } from './admin';
@ -281,7 +278,11 @@ import { Deadline } from './deadline';
const clientVersion = require('../../package.json').version;
(() => {
logging.trace(LogVerbosity.DEBUG, 'index', 'Loading @grpc/grpc-js version ' + clientVersion);
logging.trace(
LogVerbosity.DEBUG,
'index',
'Loading @grpc/grpc-js version ' + clientVersion
);
resolver_dns.setup();
resolver_uds.setup();
resolver_ip.setup();

View File

@ -40,18 +40,45 @@ import { ServerSurfaceCall } from './server-call';
import { Filter } from './filter';
import { ConnectivityState } from './connectivity-state';
import { ChannelInfo, ChannelRef, ChannelzCallTracker, ChannelzChildrenTracker, ChannelzTrace, registerChannelzChannel, SubchannelRef, unregisterChannelzRef } from './channelz';
import {
ChannelInfo,
ChannelRef,
ChannelzCallTracker,
ChannelzChildrenTracker,
ChannelzTrace,
registerChannelzChannel,
SubchannelRef,
unregisterChannelzRef,
} from './channelz';
import { Subchannel } from './subchannel';
import { LoadBalancingCall } from './load-balancing-call';
import { CallCredentials } from './call-credentials';
import { Call, CallStreamOptions, InterceptingListener, MessageContext, StatusObject } from './call-interface';
import {
Call,
CallStreamOptions,
InterceptingListener,
MessageContext,
StatusObject,
} from './call-interface';
import { SubchannelCall } from './subchannel-call';
import { Deadline, deadlineToString, getDeadlineTimeoutString } from './deadline';
import {
Deadline,
deadlineToString,
getDeadlineTimeoutString,
} from './deadline';
import { ResolvingCall } from './resolving-call';
import { getNextCallNumber } from './call-number';
import { restrictControlPlaneStatusCode } from './control-plane-status';
import { MessageBufferTracker, RetryingCall, RetryThrottler } from './retrying-call';
import { BaseSubchannelWrapper, ConnectivityStateListener, SubchannelInterface } from './subchannel-interface';
import {
MessageBufferTracker,
RetryingCall,
RetryThrottler,
} from './retrying-call';
import {
BaseSubchannelWrapper,
ConnectivityStateListener,
SubchannelInterface,
} from './subchannel-interface';
/**
* See https://nodejs.org/api/timers.html#timers_setinterval_callback_delay_args
@ -78,19 +105,33 @@ interface ErrorConfigResult {
error: StatusObject;
}
type GetConfigResult = NoneConfigResult | SuccessConfigResult | ErrorConfigResult;
type GetConfigResult =
| NoneConfigResult
| SuccessConfigResult
| ErrorConfigResult;
const RETRY_THROTTLER_MAP: Map<string, RetryThrottler> = new Map();
const DEFAULT_RETRY_BUFFER_SIZE_BYTES = 1<<24; // 16 MB
const DEFAULT_PER_RPC_RETRY_BUFFER_SIZE_BYTES = 1<<20; // 1 MB
const DEFAULT_RETRY_BUFFER_SIZE_BYTES = 1 << 24; // 16 MB
const DEFAULT_PER_RPC_RETRY_BUFFER_SIZE_BYTES = 1 << 20; // 1 MB
class ChannelSubchannelWrapper extends BaseSubchannelWrapper implements SubchannelInterface {
class ChannelSubchannelWrapper
extends BaseSubchannelWrapper
implements SubchannelInterface
{
private refCount = 0;
private subchannelStateListener: ConnectivityStateListener;
constructor(childSubchannel: SubchannelInterface, private channel: InternalChannel) {
constructor(
childSubchannel: SubchannelInterface,
private channel: InternalChannel
) {
super(childSubchannel);
this.subchannelStateListener = (subchannel, previousState, newState, keepaliveTime) => {
this.subchannelStateListener = (
subchannel,
previousState,
newState,
keepaliveTime
) => {
channel.throttleKeepalive(keepaliveTime);
};
childSubchannel.addConnectivityStateListener(this.subchannelStateListener);
@ -112,7 +153,6 @@ class ChannelSubchannelWrapper extends BaseSubchannelWrapper implements Subchann
}
export class InternalChannel {
private resolvingLoadBalancer: ResolvingLoadBalancer;
private subchannelPool: SubchannelPool;
private connectivityState: ConnectivityState = ConnectivityState.IDLE;
@ -196,7 +236,11 @@ export class InternalChannel {
}
this.channelzTrace = new ChannelzTrace();
this.channelzRef = registerChannelzChannel(target, () => this.getChannelzInfo(), this.channelzEnabled);
this.channelzRef = registerChannelzChannel(
target,
() => this.getChannelzInfo(),
this.channelzEnabled
);
if (this.channelzEnabled) {
this.channelzTrace.addTrace('CT_INFO', 'Channel created');
}
@ -217,7 +261,8 @@ export class InternalChannel {
);
this.retryBufferTracker = new MessageBufferTracker(
options['grpc.retry_buffer_size'] ?? DEFAULT_RETRY_BUFFER_SIZE_BYTES,
options['grpc.per_rpc_retry_buffer_size'] ?? DEFAULT_PER_RPC_RETRY_BUFFER_SIZE_BYTES
options['grpc.per_rpc_retry_buffer_size'] ??
DEFAULT_PER_RPC_RETRY_BUFFER_SIZE_BYTES
);
this.keepaliveTime = options['grpc.keepalive_time_ms'] ?? -1;
const channelControlHelper: ChannelControlHelper = {
@ -233,9 +278,16 @@ export class InternalChannel {
);
subchannel.throttleKeepalive(this.keepaliveTime);
if (this.channelzEnabled) {
this.channelzTrace.addTrace('CT_INFO', 'Created subchannel or used existing subchannel', subchannel.getChannelzRef());
this.channelzTrace.addTrace(
'CT_INFO',
'Created subchannel or used existing subchannel',
subchannel.getChannelzRef()
);
}
const wrappedSubchannel = new ChannelSubchannelWrapper(subchannel, this);
const wrappedSubchannel = new ChannelSubchannelWrapper(
subchannel,
this
);
this.wrappedSubchannels.add(wrappedSubchannel);
return wrappedSubchannel;
},
@ -264,7 +316,7 @@ export class InternalChannel {
if (this.channelzEnabled) {
this.childrenTracker.unrefChild(child);
}
}
},
};
this.resolvingLoadBalancer = new ResolvingLoadBalancer(
this.target,
@ -272,12 +324,22 @@ export class InternalChannel {
options,
(serviceConfig, configSelector) => {
if (serviceConfig.retryThrottling) {
RETRY_THROTTLER_MAP.set(this.getTarget(), new RetryThrottler(serviceConfig.retryThrottling.maxTokens, serviceConfig.retryThrottling.tokenRatio, RETRY_THROTTLER_MAP.get(this.getTarget())));
RETRY_THROTTLER_MAP.set(
this.getTarget(),
new RetryThrottler(
serviceConfig.retryThrottling.maxTokens,
serviceConfig.retryThrottling.tokenRatio,
RETRY_THROTTLER_MAP.get(this.getTarget())
)
);
} else {
RETRY_THROTTLER_MAP.delete(this.getTarget());
}
if (this.channelzEnabled) {
this.channelzTrace.addTrace('CT_INFO', 'Address resolution succeeded');
this.channelzTrace.addTrace(
'CT_INFO',
'Address resolution succeeded'
);
}
this.configSelector = configSelector;
this.currentResolutionError = null;
@ -292,17 +354,28 @@ export class InternalChannel {
}
this.configSelectionQueue = [];
});
},
(status) => {
status => {
if (this.channelzEnabled) {
this.channelzTrace.addTrace('CT_WARNING', 'Address resolution failed with code ' + status.code + ' and details "' + status.details + '"');
this.channelzTrace.addTrace(
'CT_WARNING',
'Address resolution failed with code ' +
status.code +
' and details "' +
status.details +
'"'
);
}
if (this.configSelectionQueue.length > 0) {
this.trace('Name resolution failed with calls queued for config selection');
this.trace(
'Name resolution failed with calls queued for config selection'
);
}
if (this.configSelector === null) {
this.currentResolutionError = {...restrictControlPlaneStatusCode(status.code, status.details), metadata: status.metadata};
this.currentResolutionError = {
...restrictControlPlaneStatusCode(status.code, status.details),
metadata: status.metadata,
};
}
const localQueue = this.configSelectionQueue;
this.configSelectionQueue = [];
@ -316,9 +389,20 @@ export class InternalChannel {
new MaxMessageSizeFilterFactory(this.options),
new CompressionFilterFactory(this, this.options),
]);
this.trace('Channel constructed with options ' + JSON.stringify(options, undefined, 2));
this.trace(
'Channel constructed with options ' +
JSON.stringify(options, undefined, 2)
);
const error = new Error();
trace(LogVerbosity.DEBUG, 'channel_stacktrace', '(' + this.channelzRef.id + ') ' + 'Channel constructed \n' + error.stack?.substring(error.stack.indexOf('\n')+1));
trace(
LogVerbosity.DEBUG,
'channel_stacktrace',
'(' +
this.channelzRef.id +
') ' +
'Channel constructed \n' +
error.stack?.substring(error.stack.indexOf('\n') + 1)
);
}
private getChannelzInfo(): ChannelInfo {
@ -327,12 +411,16 @@ export class InternalChannel {
state: this.connectivityState,
trace: this.channelzTrace,
callTracker: this.callTracker,
children: this.childrenTracker.getChildLists()
children: this.childrenTracker.getChildLists(),
};
}
private trace(text: string, verbosityOverride?: LogVerbosity) {
trace(verbosityOverride ?? LogVerbosity.DEBUG, 'channel', '(' + this.channelzRef.id + ') ' + uriToString(this.target) + ' ' + text);
trace(
verbosityOverride ?? LogVerbosity.DEBUG,
'channel',
'(' + this.channelzRef.id + ') ' + uriToString(this.target) + ' ' + text
);
}
private callRefTimerRef() {
@ -365,7 +453,7 @@ export class InternalChannel {
watcherObject: ConnectivityStateWatcher
) {
const watcherIndex = this.connectivityStateWatchers.findIndex(
(value) => value === watcherObject
value => value === watcherObject
);
if (watcherIndex >= 0) {
this.connectivityStateWatchers.splice(watcherIndex, 1);
@ -376,7 +464,9 @@ export class InternalChannel {
trace(
LogVerbosity.DEBUG,
'connectivity_state',
'(' + this.channelzRef.id + ') ' +
'(' +
this.channelzRef.id +
') ' +
uriToString(this.target) +
' ' +
ConnectivityState[this.connectivityState] +
@ -384,7 +474,12 @@ export class InternalChannel {
ConnectivityState[newState]
);
if (this.channelzEnabled) {
this.channelzTrace.addTrace('CT_INFO', ConnectivityState[this.connectivityState] + ' -> ' + ConnectivityState[newState]);
this.channelzTrace.addTrace(
'CT_INFO',
ConnectivityState[this.connectivityState] +
' -> ' +
ConnectivityState[newState]
);
}
this.connectivityState = newState;
const watchersCopy = this.connectivityStateWatchers.slice();
@ -415,8 +510,11 @@ export class InternalChannel {
this.wrappedSubchannels.delete(wrappedSubchannel);
}
doPick(metadata: Metadata, extraPickInfo: {[key: string]: string}) {
return this.currentPicker.pick({metadata: metadata, extraPickInfo: extraPickInfo});
doPick(metadata: Metadata, extraPickInfo: { [key: string]: string }) {
return this.currentPicker.pick({
metadata: metadata,
extraPickInfo: extraPickInfo,
});
}
queueCallForPick(call: LoadBalancingCall) {
@ -429,18 +527,18 @@ export class InternalChannel {
if (this.configSelector) {
return {
type: 'SUCCESS',
config: this.configSelector(method, metadata)
config: this.configSelector(method, metadata),
};
} else {
if (this.currentResolutionError) {
return {
type: 'ERROR',
error: this.currentResolutionError
}
error: this.currentResolutionError,
};
} else {
return {
type: 'NONE'
}
type: 'NONE',
};
}
}
}
@ -459,13 +557,17 @@ export class InternalChannel {
): LoadBalancingCall {
const callNumber = getNextCallNumber();
this.trace(
'createLoadBalancingCall [' +
callNumber +
'] method="' +
method +
'"'
'createLoadBalancingCall [' + callNumber + '] method="' + method + '"'
);
return new LoadBalancingCall(
this,
callConfig,
method,
host,
credentials,
deadline,
callNumber
);
return new LoadBalancingCall(this, callConfig, method, host, credentials, deadline, callNumber);
}
createRetryingCall(
@ -477,13 +579,19 @@ export class InternalChannel {
): RetryingCall {
const callNumber = getNextCallNumber();
this.trace(
'createRetryingCall [' +
callNumber +
'] method="' +
method +
'"'
'createRetryingCall [' + callNumber + '] method="' + method + '"'
);
return new RetryingCall(
this,
callConfig,
method,
host,
credentials,
deadline,
callNumber,
this.retryBufferTracker,
RETRY_THROTTLER_MAP.get(this.getTarget())
);
return new RetryingCall(this, callConfig, method, host, credentials, deadline, callNumber, this.retryBufferTracker, RETRY_THROTTLER_MAP.get(this.getTarget()))
}
createInnerCall(
@ -495,9 +603,21 @@ export class InternalChannel {
): Call {
// Create a RetryingCall if retries are enabled
if (this.options['grpc.enable_retries'] === 0) {
return this.createLoadBalancingCall(callConfig, method, host, credentials, deadline);
return this.createLoadBalancingCall(
callConfig,
method,
host,
credentials,
deadline
);
} else {
return this.createRetryingCall(callConfig, method, host, credentials, deadline);
return this.createRetryingCall(
callConfig,
method,
host,
credentials,
deadline
);
}
}
@ -524,7 +644,14 @@ export class InternalChannel {
parentCall: parentCall,
};
const call = new ResolvingCall(this, method, finalOptions, this.filterStackFactory.clone(), this.credentials._getCallCredentials(), callNumber);
const call = new ResolvingCall(
this,
method,
finalOptions,
this.filterStackFactory.clone(),
this.credentials._getCallCredentials(),
callNumber
);
if (this.channelzEnabled) {
this.callTracker.addCallStarted();
@ -537,7 +664,6 @@ export class InternalChannel {
});
}
return call;
}
close() {
@ -601,7 +727,7 @@ export class InternalChannel {
/**
* Get the channelz reference object for this channel. The returned value is
* garbage if channelz is disabled for this channel.
* @returns
* @returns
*/
getChannelzRef() {
return this.channelzRef;
@ -625,6 +751,12 @@ export class InternalChannel {
if (this.connectivityState === ConnectivityState.SHUTDOWN) {
throw new Error('Channel has been shut down');
}
return this.createResolvingCall(method, deadline, host, parentCall, propagateFlags);
return this.createResolvingCall(
method,
deadline,
host,
parentCall,
propagateFlags
);
}
}

View File

@ -75,7 +75,7 @@ export class ChildLoadBalancerHandler implements LoadBalancer {
removeChannelzChild(child: ChannelRef | SubchannelRef) {
this.parent.channelControlHelper.removeChannelzChild(child);
}
private calledByPendingChild(): boolean {
return this.child === this.parent.pendingChild;
}
@ -86,7 +86,10 @@ export class ChildLoadBalancerHandler implements LoadBalancer {
constructor(private readonly channelControlHelper: ChannelControlHelper) {}
protected configUpdateRequiresNewPolicyInstance(oldConfig: LoadBalancingConfig, newConfig: LoadBalancingConfig): boolean {
protected configUpdateRequiresNewPolicyInstance(
oldConfig: LoadBalancingConfig,
newConfig: LoadBalancingConfig
): boolean {
return oldConfig.getLoadBalancerName() !== newConfig.getLoadBalancerName();
}

View File

@ -15,18 +15,41 @@
*
*/
import { ChannelOptions } from "./channel-options";
import { ConnectivityState } from "./connectivity-state";
import { LogVerbosity, Status } from "./constants";
import { durationToMs, isDuration, msToDuration } from "./duration";
import { ChannelControlHelper, createChildChannelControlHelper, registerLoadBalancerType } from "./experimental";
import { BaseFilter, Filter, FilterFactory } from "./filter";
import { getFirstUsableConfig, LoadBalancer, LoadBalancingConfig, validateLoadBalancingConfig } from "./load-balancer";
import { ChildLoadBalancerHandler } from "./load-balancer-child-handler";
import { PickArgs, Picker, PickResult, PickResultType, QueuePicker, UnavailablePicker } from "./picker";
import { Subchannel } from "./subchannel";
import { SubchannelAddress, subchannelAddressToString } from "./subchannel-address";
import { BaseSubchannelWrapper, ConnectivityStateListener, SubchannelInterface } from "./subchannel-interface";
import { ChannelOptions } from './channel-options';
import { ConnectivityState } from './connectivity-state';
import { LogVerbosity, Status } from './constants';
import { durationToMs, isDuration, msToDuration } from './duration';
import {
ChannelControlHelper,
createChildChannelControlHelper,
registerLoadBalancerType,
} from './experimental';
import { BaseFilter, Filter, FilterFactory } from './filter';
import {
getFirstUsableConfig,
LoadBalancer,
LoadBalancingConfig,
validateLoadBalancingConfig,
} from './load-balancer';
import { ChildLoadBalancerHandler } from './load-balancer-child-handler';
import {
PickArgs,
Picker,
PickResult,
PickResultType,
QueuePicker,
UnavailablePicker,
} from './picker';
import { Subchannel } from './subchannel';
import {
SubchannelAddress,
subchannelAddressToString,
} from './subchannel-address';
import {
BaseSubchannelWrapper,
ConnectivityStateListener,
SubchannelInterface,
} from './subchannel-interface';
import * as logging from './logging';
const TRACER_NAME = 'outlier_detection';
@ -37,7 +60,8 @@ function trace(text: string): void {
const TYPE_NAME = 'outlier_detection';
const OUTLIER_DETECTION_ENABLED = (process.env.GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION ?? 'true') === 'true';
const OUTLIER_DETECTION_ENABLED =
(process.env.GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION ?? 'true') === 'true';
export interface SuccessRateEjectionConfig {
readonly stdev_factor: number;
@ -57,33 +81,66 @@ const defaultSuccessRateEjectionConfig: SuccessRateEjectionConfig = {
stdev_factor: 1900,
enforcement_percentage: 100,
minimum_hosts: 5,
request_volume: 100
request_volume: 100,
};
const defaultFailurePercentageEjectionConfig: FailurePercentageEjectionConfig = {
threshold: 85,
enforcement_percentage: 100,
minimum_hosts: 5,
request_volume: 50
}
const defaultFailurePercentageEjectionConfig: FailurePercentageEjectionConfig =
{
threshold: 85,
enforcement_percentage: 100,
minimum_hosts: 5,
request_volume: 50,
};
type TypeofValues = 'object' | 'boolean' | 'function' | 'number' | 'string' | 'undefined';
type TypeofValues =
| 'object'
| 'boolean'
| 'function'
| 'number'
| 'string'
| 'undefined';
function validateFieldType(obj: any, fieldName: string, expectedType: TypeofValues, objectName?: string) {
function validateFieldType(
obj: any,
fieldName: string,
expectedType: TypeofValues,
objectName?: string
) {
if (fieldName in obj && typeof obj[fieldName] !== expectedType) {
const fullFieldName = objectName ? `${objectName}.${fieldName}` : fieldName;
throw new Error(`outlier detection config ${fullFieldName} parse error: expected ${expectedType}, got ${typeof obj[fieldName]}`);
throw new Error(
`outlier detection config ${fullFieldName} parse error: expected ${expectedType}, got ${typeof obj[
fieldName
]}`
);
}
}
function validatePositiveDuration(obj: any, fieldName: string, objectName?: string) {
function validatePositiveDuration(
obj: any,
fieldName: string,
objectName?: string
) {
const fullFieldName = objectName ? `${objectName}.${fieldName}` : fieldName;
if (fieldName in obj) {
if (!isDuration(obj[fieldName])) {
throw new Error(`outlier detection config ${fullFieldName} parse error: expected Duration, got ${typeof obj[fieldName]}`);
throw new Error(
`outlier detection config ${fullFieldName} parse error: expected Duration, got ${typeof obj[
fieldName
]}`
);
}
if (!(obj[fieldName].seconds >= 0 && obj[fieldName].seconds <= 315_576_000_000 && obj[fieldName].nanos >= 0 && obj[fieldName].nanos <= 999_999_999)) {
throw new Error(`outlier detection config ${fullFieldName} parse error: values out of range for non-negative Duaration`);
if (
!(
obj[fieldName].seconds >= 0 &&
obj[fieldName].seconds <= 315_576_000_000 &&
obj[fieldName].nanos >= 0 &&
obj[fieldName].nanos <= 999_999_999
)
) {
throw new Error(
`outlier detection config ${fullFieldName} parse error: values out of range for non-negative Duaration`
);
}
}
}
@ -92,11 +149,15 @@ function validatePercentage(obj: any, fieldName: string, objectName?: string) {
const fullFieldName = objectName ? `${objectName}.${fieldName}` : fieldName;
validateFieldType(obj, fieldName, 'number', objectName);
if (fieldName in obj && !(obj[fieldName] >= 0 && obj[fieldName] <= 100)) {
throw new Error(`outlier detection config ${fullFieldName} parse error: value out of range for percentage (0-100)`);
throw new Error(
`outlier detection config ${fullFieldName} parse error: value out of range for percentage (0-100)`
);
}
}
export class OutlierDetectionLoadBalancingConfig implements LoadBalancingConfig {
export class OutlierDetectionLoadBalancingConfig
implements LoadBalancingConfig
{
private readonly intervalMs: number;
private readonly baseEjectionTimeMs: number;
private readonly maxEjectionTimeMs: number;
@ -117,8 +178,15 @@ export class OutlierDetectionLoadBalancingConfig implements LoadBalancingConfig
this.baseEjectionTimeMs = baseEjectionTimeMs ?? 30_000;
this.maxEjectionTimeMs = maxEjectionTimeMs ?? 300_000;
this.maxEjectionPercent = maxEjectionPercent ?? 10;
this.successRateEjection = successRateEjection ? {...defaultSuccessRateEjectionConfig, ...successRateEjection} : null;
this.failurePercentageEjection = failurePercentageEjection ? {...defaultFailurePercentageEjectionConfig, ...failurePercentageEjection}: null;
this.successRateEjection = successRateEjection
? { ...defaultSuccessRateEjectionConfig, ...successRateEjection }
: null;
this.failurePercentageEjection = failurePercentageEjection
? {
...defaultFailurePercentageEjectionConfig,
...failurePercentageEjection,
}
: null;
}
getLoadBalancerName(): string {
return TYPE_NAME;
@ -131,7 +199,7 @@ export class OutlierDetectionLoadBalancingConfig implements LoadBalancingConfig
max_ejection_percent: this.maxEjectionPercent,
success_rate_ejection: this.successRateEjection,
failure_percentage_ejection: this.failurePercentageEjection,
child_policy: this.childPolicy.map(policy => policy.toJsonObject())
child_policy: this.childPolicy.map(policy => policy.toJsonObject()),
};
}
@ -157,8 +225,18 @@ export class OutlierDetectionLoadBalancingConfig implements LoadBalancingConfig
return this.childPolicy;
}
copyWithChildPolicy(childPolicy: LoadBalancingConfig[]): OutlierDetectionLoadBalancingConfig {
return new OutlierDetectionLoadBalancingConfig(this.intervalMs, this.baseEjectionTimeMs, this.maxEjectionTimeMs, this.maxEjectionPercent, this.successRateEjection, this.failurePercentageEjection, childPolicy);
copyWithChildPolicy(
childPolicy: LoadBalancingConfig[]
): OutlierDetectionLoadBalancingConfig {
return new OutlierDetectionLoadBalancingConfig(
this.intervalMs,
this.baseEjectionTimeMs,
this.maxEjectionTimeMs,
this.maxEjectionPercent,
this.successRateEjection,
this.failurePercentageEjection,
childPolicy
);
}
static createFromJson(obj: any): OutlierDetectionLoadBalancingConfig {
@ -168,21 +246,62 @@ export class OutlierDetectionLoadBalancingConfig implements LoadBalancingConfig
validatePercentage(obj, 'max_ejection_percent');
if ('success_rate_ejection' in obj) {
if (typeof obj.success_rate_ejection !== 'object') {
throw new Error('outlier detection config success_rate_ejection must be an object');
throw new Error(
'outlier detection config success_rate_ejection must be an object'
);
}
validateFieldType(obj.success_rate_ejection, 'stdev_factor', 'number', 'success_rate_ejection');
validatePercentage(obj.success_rate_ejection, 'enforcement_percentage', 'success_rate_ejection');
validateFieldType(obj.success_rate_ejection, 'minimum_hosts', 'number', 'success_rate_ejection');
validateFieldType(obj.success_rate_ejection, 'request_volume', 'number', 'success_rate_ejection');
validateFieldType(
obj.success_rate_ejection,
'stdev_factor',
'number',
'success_rate_ejection'
);
validatePercentage(
obj.success_rate_ejection,
'enforcement_percentage',
'success_rate_ejection'
);
validateFieldType(
obj.success_rate_ejection,
'minimum_hosts',
'number',
'success_rate_ejection'
);
validateFieldType(
obj.success_rate_ejection,
'request_volume',
'number',
'success_rate_ejection'
);
}
if ('failure_percentage_ejection' in obj) {
if (typeof obj.failure_percentage_ejection !== 'object') {
throw new Error('outlier detection config failure_percentage_ejection must be an object');
throw new Error(
'outlier detection config failure_percentage_ejection must be an object'
);
}
validatePercentage(obj.failure_percentage_ejection, 'threshold', 'failure_percentage_ejection');
validatePercentage(obj.failure_percentage_ejection, 'enforcement_percentage', 'failure_percentage_ejection');
validateFieldType(obj.failure_percentage_ejection, 'minimum_hosts', 'number', 'failure_percentage_ejection');
validateFieldType(obj.failure_percentage_ejection, 'request_volume', 'number', 'failure_percentage_ejection');
validatePercentage(
obj.failure_percentage_ejection,
'threshold',
'failure_percentage_ejection'
);
validatePercentage(
obj.failure_percentage_ejection,
'enforcement_percentage',
'failure_percentage_ejection'
);
validateFieldType(
obj.failure_percentage_ejection,
'minimum_hosts',
'number',
'failure_percentage_ejection'
);
validateFieldType(
obj.failure_percentage_ejection,
'request_volume',
'number',
'failure_percentage_ejection'
);
}
return new OutlierDetectionLoadBalancingConfig(
@ -197,22 +316,30 @@ export class OutlierDetectionLoadBalancingConfig implements LoadBalancingConfig
}
}
class OutlierDetectionSubchannelWrapper extends BaseSubchannelWrapper implements SubchannelInterface {
class OutlierDetectionSubchannelWrapper
extends BaseSubchannelWrapper
implements SubchannelInterface
{
private childSubchannelState: ConnectivityState;
private stateListeners: ConnectivityStateListener[] = [];
private ejected: boolean = false;
private refCount: number = 0;
constructor(childSubchannel: SubchannelInterface, private mapEntry?: MapEntry) {
private ejected = false;
private refCount = 0;
constructor(
childSubchannel: SubchannelInterface,
private mapEntry?: MapEntry
) {
super(childSubchannel);
this.childSubchannelState = childSubchannel.getConnectivityState();
childSubchannel.addConnectivityStateListener((subchannel, previousState, newState, keepaliveTime) => {
this.childSubchannelState = newState;
if (!this.ejected) {
for (const listener of this.stateListeners) {
listener(this, previousState, newState, keepaliveTime);
childSubchannel.addConnectivityStateListener(
(subchannel, previousState, newState, keepaliveTime) => {
this.childSubchannelState = newState;
if (!this.ejected) {
for (const listener of this.stateListeners) {
listener(this, previousState, newState, keepaliveTime);
}
}
}
});
);
}
getConnectivityState(): ConnectivityState {
@ -265,14 +392,24 @@ class OutlierDetectionSubchannelWrapper extends BaseSubchannelWrapper implements
eject() {
this.ejected = true;
for (const listener of this.stateListeners) {
listener(this, this.childSubchannelState, ConnectivityState.TRANSIENT_FAILURE, -1);
listener(
this,
this.childSubchannelState,
ConnectivityState.TRANSIENT_FAILURE,
-1
);
}
}
uneject() {
this.ejected = false;
for (const listener of this.stateListeners) {
listener(this, ConnectivityState.TRANSIENT_FAILURE, this.childSubchannelState, -1);
listener(
this,
ConnectivityState.TRANSIENT_FAILURE,
this.childSubchannelState,
-1
);
}
}
@ -293,8 +430,8 @@ interface CallCountBucket {
function createEmptyBucket(): CallCountBucket {
return {
success: 0,
failure: 0
}
failure: 0,
};
}
class CallCounter {
@ -330,7 +467,8 @@ class OutlierDetectionPicker implements Picker {
pick(pickArgs: PickArgs): PickResult {
const wrappedPick = this.wrappedPicker.pick(pickArgs);
if (wrappedPick.pickResultType === PickResultType.COMPLETE) {
const subchannelWrapper = wrappedPick.subchannel as OutlierDetectionSubchannelWrapper;
const subchannelWrapper =
wrappedPick.subchannel as OutlierDetectionSubchannelWrapper;
const mapEntry = subchannelWrapper.getMapEntry();
if (mapEntry) {
let onCallEnded = wrappedPick.onCallEnded;
@ -347,19 +485,18 @@ class OutlierDetectionPicker implements Picker {
return {
...wrappedPick,
subchannel: subchannelWrapper.getWrappedSubchannel(),
onCallEnded: onCallEnded
onCallEnded: onCallEnded,
};
} else {
return {
...wrappedPick,
subchannel: subchannelWrapper.getWrappedSubchannel()
}
subchannel: subchannelWrapper.getWrappedSubchannel(),
};
}
} else {
return wrappedPick;
}
}
}
export class OutlierDetectionLoadBalancer implements LoadBalancer {
@ -370,34 +507,52 @@ export class OutlierDetectionLoadBalancer implements LoadBalancer {
private timerStartTime: Date | null = null;
constructor(channelControlHelper: ChannelControlHelper) {
this.childBalancer = new ChildLoadBalancerHandler(createChildChannelControlHelper(channelControlHelper, {
createSubchannel: (subchannelAddress: SubchannelAddress, subchannelArgs: ChannelOptions) => {
const originalSubchannel = channelControlHelper.createSubchannel(subchannelAddress, subchannelArgs);
const mapEntry = this.addressMap.get(subchannelAddressToString(subchannelAddress));
const subchannelWrapper = new OutlierDetectionSubchannelWrapper(originalSubchannel, mapEntry);
if (mapEntry?.currentEjectionTimestamp !== null) {
// If the address is ejected, propagate that to the new subchannel wrapper
subchannelWrapper.eject();
}
mapEntry?.subchannelWrappers.push(subchannelWrapper);
return subchannelWrapper;
},
updateState: (connectivityState: ConnectivityState, picker: Picker) => {
if (connectivityState === ConnectivityState.READY) {
channelControlHelper.updateState(connectivityState, new OutlierDetectionPicker(picker, this.isCountingEnabled()));
} else {
channelControlHelper.updateState(connectivityState, picker);
}
}
}));
this.childBalancer = new ChildLoadBalancerHandler(
createChildChannelControlHelper(channelControlHelper, {
createSubchannel: (
subchannelAddress: SubchannelAddress,
subchannelArgs: ChannelOptions
) => {
const originalSubchannel = channelControlHelper.createSubchannel(
subchannelAddress,
subchannelArgs
);
const mapEntry = this.addressMap.get(
subchannelAddressToString(subchannelAddress)
);
const subchannelWrapper = new OutlierDetectionSubchannelWrapper(
originalSubchannel,
mapEntry
);
if (mapEntry?.currentEjectionTimestamp !== null) {
// If the address is ejected, propagate that to the new subchannel wrapper
subchannelWrapper.eject();
}
mapEntry?.subchannelWrappers.push(subchannelWrapper);
return subchannelWrapper;
},
updateState: (connectivityState: ConnectivityState, picker: Picker) => {
if (connectivityState === ConnectivityState.READY) {
channelControlHelper.updateState(
connectivityState,
new OutlierDetectionPicker(picker, this.isCountingEnabled())
);
} else {
channelControlHelper.updateState(connectivityState, picker);
}
},
})
);
this.ejectionTimer = setInterval(() => {}, 0);
clearInterval(this.ejectionTimer);
}
private isCountingEnabled(): boolean {
return this.latestConfig !== null &&
(this.latestConfig.getSuccessRateEjectionConfig() !== null ||
this.latestConfig.getFailurePercentageEjectionConfig() !== null);
return (
this.latestConfig !== null &&
(this.latestConfig.getSuccessRateEjectionConfig() !== null ||
this.latestConfig.getFailurePercentageEjectionConfig() !== null)
);
}
private getCurrentEjectionPercent() {
@ -422,23 +577,41 @@ export class OutlierDetectionLoadBalancer implements LoadBalancer {
// Step 1
const targetRequestVolume = successRateConfig.request_volume;
let addresesWithTargetVolume = 0;
const successRates: number[] = []
const successRates: number[] = [];
for (const [address, mapEntry] of this.addressMap) {
const successes = mapEntry.counter.getLastSuccesses();
const failures = mapEntry.counter.getLastFailures();
trace('Stats for ' + address + ': successes=' + successes + ' failures=' + failures + ' targetRequestVolume=' + targetRequestVolume);
trace(
'Stats for ' +
address +
': successes=' +
successes +
' failures=' +
failures +
' targetRequestVolume=' +
targetRequestVolume
);
if (successes + failures >= targetRequestVolume) {
addresesWithTargetVolume += 1;
successRates.push(successes/(successes + failures));
successRates.push(successes / (successes + failures));
}
}
trace('Found ' + addresesWithTargetVolume + ' success rate candidates; currentEjectionPercent=' + this.getCurrentEjectionPercent() + ' successRates=[' + successRates + ']');
trace(
'Found ' +
addresesWithTargetVolume +
' success rate candidates; currentEjectionPercent=' +
this.getCurrentEjectionPercent() +
' successRates=[' +
successRates +
']'
);
if (addresesWithTargetVolume < successRateConfig.minimum_hosts) {
return;
}
// Step 2
const successRateMean = successRates.reduce((a, b) => a + b) / successRates.length;
const successRateMean =
successRates.reduce((a, b) => a + b) / successRates.length;
let successRateDeviationSum = 0;
for (const rate of successRates) {
const deviation = rate - successRateMean;
@ -446,13 +619,20 @@ export class OutlierDetectionLoadBalancer implements LoadBalancer {
}
const successRateVariance = successRateDeviationSum / successRates.length;
const successRateStdev = Math.sqrt(successRateVariance);
const ejectionThreshold = successRateMean - successRateStdev * (successRateConfig.stdev_factor / 1000);
trace('stdev=' + successRateStdev + ' ejectionThreshold=' + ejectionThreshold);
const ejectionThreshold =
successRateMean -
successRateStdev * (successRateConfig.stdev_factor / 1000);
trace(
'stdev=' + successRateStdev + ' ejectionThreshold=' + ejectionThreshold
);
// Step 3
for (const [address, mapEntry] of this.addressMap.entries()) {
// Step 3.i
if (this.getCurrentEjectionPercent() >= this.latestConfig.getMaxEjectionPercent()) {
if (
this.getCurrentEjectionPercent() >=
this.latestConfig.getMaxEjectionPercent()
) {
break;
}
// Step 3.ii
@ -466,7 +646,14 @@ export class OutlierDetectionLoadBalancer implements LoadBalancer {
trace('Checking candidate ' + address + ' successRate=' + successRate);
if (successRate < ejectionThreshold) {
const randomNumber = Math.random() * 100;
trace('Candidate ' + address + ' randomNumber=' + randomNumber + ' enforcement_percentage=' + successRateConfig.enforcement_percentage);
trace(
'Candidate ' +
address +
' randomNumber=' +
randomNumber +
' enforcement_percentage=' +
successRateConfig.enforcement_percentage
);
if (randomNumber < successRateConfig.enforcement_percentage) {
trace('Ejecting candidate ' + address);
this.eject(mapEntry, ejectionTimestamp);
@ -479,11 +666,17 @@ export class OutlierDetectionLoadBalancer implements LoadBalancer {
if (!this.latestConfig) {
return;
}
const failurePercentageConfig = this.latestConfig.getFailurePercentageEjectionConfig()
const failurePercentageConfig =
this.latestConfig.getFailurePercentageEjectionConfig();
if (!failurePercentageConfig) {
return;
}
trace('Running failure percentage check. threshold=' + failurePercentageConfig.threshold + ' request volume threshold=' + failurePercentageConfig.request_volume);
trace(
'Running failure percentage check. threshold=' +
failurePercentageConfig.threshold +
' request volume threshold=' +
failurePercentageConfig.request_volume
);
// Step 1
let addressesWithTargetVolume = 0;
for (const mapEntry of this.addressMap.values()) {
@ -496,11 +689,14 @@ export class OutlierDetectionLoadBalancer implements LoadBalancer {
if (addressesWithTargetVolume < failurePercentageConfig.minimum_hosts) {
return;
}
// Step 2
for (const [address, mapEntry] of this.addressMap.entries()) {
// Step 2.i
if (this.getCurrentEjectionPercent() >= this.latestConfig.getMaxEjectionPercent()) {
if (
this.getCurrentEjectionPercent() >=
this.latestConfig.getMaxEjectionPercent()
) {
break;
}
// Step 2.ii
@ -514,7 +710,14 @@ export class OutlierDetectionLoadBalancer implements LoadBalancer {
const failurePercentage = (failures * 100) / (failures + successes);
if (failurePercentage > failurePercentageConfig.threshold) {
const randomNumber = Math.random() * 100;
trace('Candidate ' + address + ' randomNumber=' + randomNumber + ' enforcement_percentage=' + failurePercentageConfig.enforcement_percentage);
trace(
'Candidate ' +
address +
' randomNumber=' +
randomNumber +
' enforcement_percentage=' +
failurePercentageConfig.enforcement_percentage
);
if (randomNumber < failurePercentageConfig.enforcement_percentage) {
trace('Ejecting candidate ' + address);
this.eject(mapEntry, ejectionTimestamp);
@ -572,8 +775,16 @@ export class OutlierDetectionLoadBalancer implements LoadBalancer {
} else {
const baseEjectionTimeMs = this.latestConfig.getBaseEjectionTimeMs();
const maxEjectionTimeMs = this.latestConfig.getMaxEjectionTimeMs();
const returnTime = new Date(mapEntry.currentEjectionTimestamp.getTime());
returnTime.setMilliseconds(returnTime.getMilliseconds() + Math.min(baseEjectionTimeMs * mapEntry.ejectionTimeMultiplier, Math.max(baseEjectionTimeMs, maxEjectionTimeMs)));
const returnTime = new Date(
mapEntry.currentEjectionTimestamp.getTime()
);
returnTime.setMilliseconds(
returnTime.getMilliseconds() +
Math.min(
baseEjectionTimeMs * mapEntry.ejectionTimeMultiplier,
Math.max(baseEjectionTimeMs, maxEjectionTimeMs)
)
);
if (returnTime < new Date()) {
trace('Unejecting ' + address);
this.uneject(mapEntry);
@ -582,7 +793,11 @@ export class OutlierDetectionLoadBalancer implements LoadBalancer {
}
}
updateAddressList(addressList: SubchannelAddress[], lbConfig: LoadBalancingConfig, attributes: { [key: string]: unknown; }): void {
updateAddressList(
addressList: SubchannelAddress[],
lbConfig: LoadBalancingConfig,
attributes: { [key: string]: unknown }
): void {
if (!(lbConfig instanceof OutlierDetectionLoadBalancingConfig)) {
return;
}
@ -597,7 +812,7 @@ export class OutlierDetectionLoadBalancer implements LoadBalancer {
counter: new CallCounter(),
currentEjectionTimestamp: null,
ejectionTimeMultiplier: 0,
subchannelWrappers: []
subchannelWrappers: [],
});
}
}
@ -613,11 +828,16 @@ export class OutlierDetectionLoadBalancer implements LoadBalancer {
);
this.childBalancer.updateAddressList(addressList, childPolicy, attributes);
if (lbConfig.getSuccessRateEjectionConfig() || lbConfig.getFailurePercentageEjectionConfig()) {
if (
lbConfig.getSuccessRateEjectionConfig() ||
lbConfig.getFailurePercentageEjectionConfig()
) {
if (this.timerStartTime) {
trace('Previous timer existed. Replacing timer');
clearTimeout(this.ejectionTimer);
const remainingDelay = lbConfig.getIntervalMs() - ((new Date()).getTime() - this.timerStartTime.getTime());
const remainingDelay =
lbConfig.getIntervalMs() -
(new Date().getTime() - this.timerStartTime.getTime());
this.startTimer(remainingDelay);
} else {
trace('Starting new timer');
@ -654,6 +874,10 @@ export class OutlierDetectionLoadBalancer implements LoadBalancer {
export function setup() {
if (OUTLIER_DETECTION_ENABLED) {
registerLoadBalancerType(TYPE_NAME, OutlierDetectionLoadBalancer, OutlierDetectionLoadBalancingConfig);
registerLoadBalancerType(
TYPE_NAME,
OutlierDetectionLoadBalancer,
OutlierDetectionLoadBalancingConfig
);
}
}
}

View File

@ -38,7 +38,10 @@ import {
} from './subchannel-address';
import * as logging from './logging';
import { LogVerbosity } from './constants';
import { SubchannelInterface, ConnectivityStateListener } from './subchannel-interface';
import {
SubchannelInterface,
ConnectivityStateListener,
} from './subchannel-interface';
const TRACER_NAME = 'pick_first';
@ -86,7 +89,7 @@ class PickFirstPicker implements Picker {
subchannel: this.subchannel,
status: null,
onCallStarted: null,
onCallEnded: null
onCallEnded: null,
};
}
}
@ -169,7 +172,8 @@ export class PickFirstLoadBalancer implements LoadBalancer {
* connecting to the next one instead of waiting for the connection
* delay timer. */
if (
subchannel.getRealSubchannel() === this.subchannels[this.currentSubchannelIndex].getRealSubchannel() &&
subchannel.getRealSubchannel() ===
this.subchannels[this.currentSubchannelIndex].getRealSubchannel() &&
newState === ConnectivityState.TRANSIENT_FAILURE
) {
this.startNextSubchannelConnecting();
@ -232,7 +236,9 @@ export class PickFirstLoadBalancer implements LoadBalancer {
subchannel.removeConnectivityStateListener(
this.pickedSubchannelStateListener
);
this.channelControlHelper.removeChannelzChild(subchannel.getChannelzRef());
this.channelControlHelper.removeChannelzChild(
subchannel.getChannelzRef()
);
if (this.subchannels.length > 0) {
if (this.triedAllSubchannels) {
let newLBState: ConnectivityState;
@ -344,7 +350,9 @@ export class PickFirstLoadBalancer implements LoadBalancer {
for (const subchannel of this.subchannels) {
subchannel.removeConnectivityStateListener(this.subchannelStateListener);
subchannel.unref();
this.channelControlHelper.removeChannelzChild(subchannel.getChannelzRef());
this.channelControlHelper.removeChannelzChild(
subchannel.getChannelzRef()
);
}
this.currentSubchannelIndex = 0;
this.subchannelStateCounts = {
@ -368,11 +376,11 @@ export class PickFirstLoadBalancer implements LoadBalancer {
this.resetSubchannelList();
trace(
'Connect to address list ' +
this.latestAddressList.map((address) =>
this.latestAddressList.map(address =>
subchannelAddressToString(address)
)
);
this.subchannels = this.latestAddressList.map((address) =>
this.subchannels = this.latestAddressList.map(address =>
this.channelControlHelper.createSubchannel(address, {})
);
for (const subchannel of this.subchannels) {
@ -422,7 +430,9 @@ export class PickFirstLoadBalancer implements LoadBalancer {
this.subchannels.length === 0 ||
this.latestAddressList.length !== addressList.length ||
!this.latestAddressList.every(
(value, index) => addressList[index] && subchannelAddressEqual(addressList[index], value)
(value, index) =>
addressList[index] &&
subchannelAddressEqual(addressList[index], value)
)
) {
this.latestAddressList = addressList;
@ -463,7 +473,9 @@ export class PickFirstLoadBalancer implements LoadBalancer {
currentPick.removeConnectivityStateListener(
this.pickedSubchannelStateListener
);
this.channelControlHelper.removeChannelzChild(currentPick.getChannelzRef());
this.channelControlHelper.removeChannelzChild(
currentPick.getChannelzRef()
);
}
}

View File

@ -36,7 +36,10 @@ import {
} from './subchannel-address';
import * as logging from './logging';
import { LogVerbosity } from './constants';
import { ConnectivityStateListener, SubchannelInterface } from './subchannel-interface';
import {
ConnectivityStateListener,
SubchannelInterface,
} from './subchannel-interface';
const TRACER_NAME = 'round_robin';
@ -79,7 +82,7 @@ class RoundRobinPicker implements Picker {
subchannel: pickedSubchannel,
status: null,
onCallStarted: null,
onCallEnded: null
onCallEnded: null,
};
}
@ -121,13 +124,15 @@ export class RoundRobinLoadBalancer implements LoadBalancer {
}
private countSubchannelsWithState(state: ConnectivityState) {
return this.subchannels.filter(subchannel => subchannel.getConnectivityState() === state).length;
return this.subchannels.filter(
subchannel => subchannel.getConnectivityState() === state
).length;
}
private calculateAndUpdateState() {
if (this.countSubchannelsWithState(ConnectivityState.READY) > 0) {
const readySubchannels = this.subchannels.filter(
(subchannel) =>
subchannel =>
subchannel.getConnectivityState() === ConnectivityState.READY
);
let index = 0;
@ -143,7 +148,9 @@ export class RoundRobinLoadBalancer implements LoadBalancer {
ConnectivityState.READY,
new RoundRobinPicker(readySubchannels, index)
);
} else if (this.countSubchannelsWithState(ConnectivityState.CONNECTING) > 0) {
} else if (
this.countSubchannelsWithState(ConnectivityState.CONNECTING) > 0
) {
this.updateState(ConnectivityState.CONNECTING, new QueuePicker(this));
} else if (
this.countSubchannelsWithState(ConnectivityState.TRANSIENT_FAILURE) > 0
@ -176,7 +183,9 @@ export class RoundRobinLoadBalancer implements LoadBalancer {
for (const subchannel of this.subchannels) {
subchannel.removeConnectivityStateListener(this.subchannelStateListener);
subchannel.unref();
this.channelControlHelper.removeChannelzChild(subchannel.getChannelzRef());
this.channelControlHelper.removeChannelzChild(
subchannel.getChannelzRef()
);
}
this.subchannels = [];
}
@ -188,9 +197,9 @@ export class RoundRobinLoadBalancer implements LoadBalancer {
this.resetSubchannelList();
trace(
'Connect to address list ' +
addressList.map((address) => subchannelAddressToString(address))
addressList.map(address => subchannelAddressToString(address))
);
this.subchannels = addressList.map((address) =>
this.subchannels = addressList.map(address =>
this.channelControlHelper.createSubchannel(address, {})
);
for (const subchannel of this.subchannels) {

View File

@ -58,16 +58,28 @@ export interface ChannelControlHelper {
* parent while letting others pass through to the parent unmodified. This
* allows other code to create these children without needing to know about
* all of the methods to be passed through.
* @param parent
* @param overrides
* @param parent
* @param overrides
*/
export function createChildChannelControlHelper(parent: ChannelControlHelper, overrides: Partial<ChannelControlHelper>): ChannelControlHelper {
export function createChildChannelControlHelper(
parent: ChannelControlHelper,
overrides: Partial<ChannelControlHelper>
): ChannelControlHelper {
return {
createSubchannel: overrides.createSubchannel?.bind(overrides) ?? parent.createSubchannel.bind(parent),
updateState: overrides.updateState?.bind(overrides) ?? parent.updateState.bind(parent),
requestReresolution: overrides.requestReresolution?.bind(overrides) ?? parent.requestReresolution.bind(parent),
addChannelzChild: overrides.addChannelzChild?.bind(overrides) ?? parent.addChannelzChild.bind(parent),
removeChannelzChild: overrides.removeChannelzChild?.bind(overrides) ?? parent.removeChannelzChild.bind(parent)
createSubchannel:
overrides.createSubchannel?.bind(overrides) ??
parent.createSubchannel.bind(parent),
updateState:
overrides.updateState?.bind(overrides) ?? parent.updateState.bind(parent),
requestReresolution:
overrides.requestReresolution?.bind(overrides) ??
parent.requestReresolution.bind(parent),
addChannelzChild:
overrides.addChannelzChild?.bind(overrides) ??
parent.addChannelzChild.bind(parent),
removeChannelzChild:
overrides.removeChannelzChild?.bind(overrides) ??
parent.removeChannelzChild.bind(parent),
};
}

View File

@ -15,20 +15,25 @@
*
*/
import { CallCredentials } from "./call-credentials";
import { Call, InterceptingListener, MessageContext, StatusObject } from "./call-interface";
import { SubchannelCall } from "./subchannel-call";
import { ConnectivityState } from "./connectivity-state";
import { LogVerbosity, Status } from "./constants";
import { Deadline, getDeadlineTimeoutString } from "./deadline";
import { FilterStack, FilterStackFactory } from "./filter-stack";
import { InternalChannel } from "./internal-channel";
import { Metadata } from "./metadata";
import { PickResultType } from "./picker";
import { CallConfig } from "./resolver";
import { splitHostPort } from "./uri-parser";
import { CallCredentials } from './call-credentials';
import {
Call,
InterceptingListener,
MessageContext,
StatusObject,
} from './call-interface';
import { SubchannelCall } from './subchannel-call';
import { ConnectivityState } from './connectivity-state';
import { LogVerbosity, Status } from './constants';
import { Deadline, getDeadlineTimeoutString } from './deadline';
import { FilterStack, FilterStackFactory } from './filter-stack';
import { InternalChannel } from './internal-channel';
import { Metadata } from './metadata';
import { PickResultType } from './picker';
import { CallConfig } from './resolver';
import { splitHostPort } from './uri-parser';
import * as logging from './logging';
import { restrictControlPlaneStatusCode } from "./control-plane-status";
import { restrictControlPlaneStatusCode } from './control-plane-status';
import * as http2 from 'http2';
const TRACER_NAME = 'load_balancing_call';
@ -39,14 +44,16 @@ export interface StatusObjectWithProgress extends StatusObject {
progress: RpcProgress;
}
export interface LoadBalancingCallInterceptingListener extends InterceptingListener {
export interface LoadBalancingCallInterceptingListener
extends InterceptingListener {
onReceiveStatus(status: StatusObjectWithProgress): void;
}
export class LoadBalancingCall implements Call {
private child: SubchannelCall | null = null;
private readPending = false;
private pendingMessage: {context: MessageContext, message: Buffer} | null = null;
private pendingMessage: { context: MessageContext; message: Buffer } | null =
null;
private pendingHalfClose = false;
private pendingChildStatus: StatusObject | null = null;
private ended = false;
@ -58,7 +65,7 @@ export class LoadBalancingCall implements Call {
private readonly channel: InternalChannel,
private readonly callConfig: CallConfig,
private readonly methodName: string,
private readonly host : string,
private readonly host: string,
private readonly credentials: CallCredentials,
private readonly deadline: Deadline,
private readonly callNumber: number
@ -88,8 +95,14 @@ export class LoadBalancingCall implements Call {
private outputStatus(status: StatusObject, progress: RpcProgress) {
if (!this.ended) {
this.ended = true;
this.trace('ended with status: code=' + status.code + ' details="' + status.details + '"');
const finalStatus = {...status, progress};
this.trace(
'ended with status: code=' +
status.code +
' details="' +
status.details +
'"'
);
const finalStatus = { ...status, progress };
this.listener?.onReceiveStatus(finalStatus);
this.onCallEnded?.(finalStatus.code);
}
@ -102,11 +115,17 @@ export class LoadBalancingCall implements Call {
if (!this.metadata) {
throw new Error('doPick called before start');
}
this.trace('Pick called')
const pickResult = this.channel.doPick(this.metadata, this.callConfig.pickInformation);
const subchannelString = pickResult.subchannel ?
'(' + pickResult.subchannel.getChannelzRef().id + ') ' + pickResult.subchannel.getAddress() :
'' + pickResult.subchannel;
this.trace('Pick called');
const pickResult = this.channel.doPick(
this.metadata,
this.callConfig.pickInformation
);
const subchannelString = pickResult.subchannel
? '(' +
pickResult.subchannel.getChannelzRef().id +
') ' +
pickResult.subchannel.getAddress()
: '' + pickResult.subchannel;
this.trace(
'Pick result: ' +
PickResultType[pickResult.pickResultType] +
@ -119,111 +138,147 @@ export class LoadBalancingCall implements Call {
);
switch (pickResult.pickResultType) {
case PickResultType.COMPLETE:
this.credentials.generateMetadata({service_url: this.serviceUrl}).then(
(credsMetadata) => {
const finalMetadata = this.metadata!.clone();
finalMetadata.merge(credsMetadata);
if (finalMetadata.get('authorization').length > 1) {
this.credentials
.generateMetadata({ service_url: this.serviceUrl })
.then(
credsMetadata => {
const finalMetadata = this.metadata!.clone();
finalMetadata.merge(credsMetadata);
if (finalMetadata.get('authorization').length > 1) {
this.outputStatus(
{
code: Status.INTERNAL,
details:
'"authorization" metadata cannot have multiple values',
metadata: new Metadata(),
},
'PROCESSED'
);
}
if (
pickResult.subchannel!.getConnectivityState() !==
ConnectivityState.READY
) {
this.trace(
'Picked subchannel ' +
subchannelString +
' has state ' +
ConnectivityState[
pickResult.subchannel!.getConnectivityState()
] +
' after getting credentials metadata. Retrying pick'
);
this.doPick();
return;
}
if (this.deadline !== Infinity) {
finalMetadata.set(
'grpc-timeout',
getDeadlineTimeoutString(this.deadline)
);
}
try {
this.child = pickResult
.subchannel!.getRealSubchannel()
.createCall(finalMetadata, this.host, this.methodName, {
onReceiveMetadata: metadata => {
this.trace('Received metadata');
this.listener!.onReceiveMetadata(metadata);
},
onReceiveMessage: message => {
this.trace('Received message');
this.listener!.onReceiveMessage(message);
},
onReceiveStatus: status => {
this.trace('Received status');
if (
status.rstCode ===
http2.constants.NGHTTP2_REFUSED_STREAM
) {
this.outputStatus(status, 'REFUSED');
} else {
this.outputStatus(status, 'PROCESSED');
}
},
});
} catch (error) {
this.trace(
'Failed to start call on picked subchannel ' +
subchannelString +
' with error ' +
(error as Error).message
);
this.outputStatus(
{
code: Status.INTERNAL,
details:
'Failed to start HTTP/2 stream with error ' +
(error as Error).message,
metadata: new Metadata(),
},
'NOT_STARTED'
);
return;
}
this.callConfig.onCommitted?.();
pickResult.onCallStarted?.();
this.onCallEnded = pickResult.onCallEnded;
this.trace(
'Created child call [' + this.child.getCallNumber() + ']'
);
if (this.readPending) {
this.child.startRead();
}
if (this.pendingMessage) {
this.child.sendMessageWithContext(
this.pendingMessage.context,
this.pendingMessage.message
);
}
if (this.pendingHalfClose) {
this.child.halfClose();
}
},
(error: Error & { code: number }) => {
// We assume the error code isn't 0 (Status.OK)
const { code, details } = restrictControlPlaneStatusCode(
typeof error.code === 'number' ? error.code : Status.UNKNOWN,
`Getting metadata from plugin failed with error: ${error.message}`
);
this.outputStatus(
{
code: Status.INTERNAL,
details: '"authorization" metadata cannot have multiple values',
metadata: new Metadata()
code: code,
details: details,
metadata: new Metadata(),
},
'PROCESSED'
);
}
if (pickResult.subchannel!.getConnectivityState() !== ConnectivityState.READY) {
this.trace(
'Picked subchannel ' +
subchannelString +
' has state ' +
ConnectivityState[pickResult.subchannel!.getConnectivityState()] +
' after getting credentials metadata. Retrying pick'
);
this.doPick();
return;
}
if (this.deadline !== Infinity) {
finalMetadata.set('grpc-timeout', getDeadlineTimeoutString(this.deadline));
}
try {
this.child = pickResult.subchannel!.getRealSubchannel().createCall(finalMetadata, this.host, this.methodName, {
onReceiveMetadata: metadata => {
this.trace('Received metadata');
this.listener!.onReceiveMetadata(metadata);
},
onReceiveMessage: message => {
this.trace('Received message');
this.listener!.onReceiveMessage(message);
},
onReceiveStatus: status => {
this.trace('Received status');
if (status.rstCode === http2.constants.NGHTTP2_REFUSED_STREAM) {
this.outputStatus(status, 'REFUSED');
} else {
this.outputStatus(status, 'PROCESSED');
}
}
});
} catch (error) {
this.trace(
'Failed to start call on picked subchannel ' +
subchannelString +
' with error ' +
(error as Error).message
);
this.outputStatus(
{
code: Status.INTERNAL,
details: 'Failed to start HTTP/2 stream with error ' + (error as Error).message,
metadata: new Metadata()
},
'NOT_STARTED'
);
return;
}
this.callConfig.onCommitted?.();
pickResult.onCallStarted?.();
this.onCallEnded = pickResult.onCallEnded;
this.trace('Created child call [' + this.child.getCallNumber() + ']');
if (this.readPending) {
this.child.startRead();
}
if (this.pendingMessage) {
this.child.sendMessageWithContext(this.pendingMessage.context, this.pendingMessage.message);
}
if (this.pendingHalfClose) {
this.child.halfClose();
}
}, (error: Error & { code: number }) => {
// We assume the error code isn't 0 (Status.OK)
const {code, details} = restrictControlPlaneStatusCode(
typeof error.code === 'number' ? error.code : Status.UNKNOWN,
`Getting metadata from plugin failed with error: ${error.message}`
)
this.outputStatus(
{
code: code,
details: details,
metadata: new Metadata()
},
'PROCESSED'
);
}
);
);
break;
case PickResultType.DROP:
const {code, details} = restrictControlPlaneStatusCode(pickResult.status!.code, pickResult.status!.details);
this.outputStatus({code, details, metadata: pickResult.status!.metadata}, 'DROP');
const { code, details } = restrictControlPlaneStatusCode(
pickResult.status!.code,
pickResult.status!.details
);
this.outputStatus(
{ code, details, metadata: pickResult.status!.metadata },
'DROP'
);
break;
case PickResultType.TRANSIENT_FAILURE:
if (this.metadata.getOptions().waitForReady) {
this.channel.queueCallForPick(this);
} else {
const {code, details} = restrictControlPlaneStatusCode(pickResult.status!.code, pickResult.status!.details);
this.outputStatus({code, details, metadata: pickResult.status!.metadata}, 'PROCESSED');
const { code, details } = restrictControlPlaneStatusCode(
pickResult.status!.code,
pickResult.status!.details
);
this.outputStatus(
{ code, details, metadata: pickResult.status!.metadata },
'PROCESSED'
);
}
break;
case PickResultType.QUEUE:
@ -232,14 +287,22 @@ export class LoadBalancingCall implements Call {
}
cancelWithStatus(status: Status, details: string): void {
this.trace('cancelWithStatus code: ' + status + ' details: "' + details + '"');
this.trace(
'cancelWithStatus code: ' + status + ' details: "' + details + '"'
);
this.child?.cancelWithStatus(status, details);
this.outputStatus({code: status, details: details, metadata: new Metadata()}, 'PROCESSED');
this.outputStatus(
{ code: status, details: details, metadata: new Metadata() },
'PROCESSED'
);
}
getPeer(): string {
return this.child?.getPeer() ?? this.channel.getTarget();
}
start(metadata: Metadata, listener: LoadBalancingCallInterceptingListener): void {
start(
metadata: Metadata,
listener: LoadBalancingCallInterceptingListener
): void {
this.trace('start called');
this.listener = listener;
this.metadata = metadata;
@ -250,7 +313,7 @@ export class LoadBalancingCall implements Call {
if (this.child) {
this.child.sendMessageWithContext(context, message);
} else {
this.pendingMessage = {context, message};
this.pendingMessage = { context, message };
}
}
startRead(): void {
@ -270,10 +333,10 @@ export class LoadBalancingCall implements Call {
}
}
setCredentials(credentials: CallCredentials): void {
throw new Error("Method not implemented.");
throw new Error('Method not implemented.');
}
getCallNumber(): number {
return this.callNumber;
}
}
}

View File

@ -27,7 +27,7 @@ const DEFAULT_LOGGER: Partial<Console> = {
debug: (message?: any, ...optionalParams: any[]) => {
console.error('D ' + message, ...optionalParams);
},
}
};
let _logger: Partial<Console> = DEFAULT_LOGGER;
let _logVerbosity: LogVerbosity = LogVerbosity.ERROR;
@ -114,6 +114,7 @@ export function trace(
}
export function isTracerEnabled(tracer: string): boolean {
return !disabledTracers.has(tracer) &&
(allEnabled || enabledTracers.has(tracer));
return (
!disabledTracers.has(tracer) && (allEnabled || enabledTracers.has(tracer))
);
}

View File

@ -132,7 +132,7 @@ export function makeClientConstructor(
[methodName: string]: Function;
}
Object.keys(methods).forEach((name) => {
Object.keys(methods).forEach(name => {
if (isPrototypePolluted(name)) {
return;
}

View File

@ -28,9 +28,7 @@ import { Metadata } from './metadata';
export class MaxMessageSizeFilter extends BaseFilter implements Filter {
private maxSendMessageSize: number = DEFAULT_MAX_SEND_MESSAGE_LENGTH;
private maxReceiveMessageSize: number = DEFAULT_MAX_RECEIVE_MESSAGE_LENGTH;
constructor(
private readonly options: ChannelOptions
) {
constructor(private readonly options: ChannelOptions) {
super();
if ('grpc.max_send_message_length' in options) {
this.maxSendMessageSize = options['grpc.max_send_message_length']!;
@ -51,7 +49,7 @@ export class MaxMessageSizeFilter extends BaseFilter implements Filter {
throw {
code: Status.RESOURCE_EXHAUSTED,
details: `Sent message larger than max (${concreteMessage.message.length} vs. ${this.maxSendMessageSize})`,
metadata: new Metadata()
metadata: new Metadata(),
};
} else {
return concreteMessage;
@ -70,7 +68,7 @@ export class MaxMessageSizeFilter extends BaseFilter implements Filter {
throw {
code: Status.RESOURCE_EXHAUSTED,
details: `Received message larger than max (${concreteMessage.length} vs. ${this.maxReceiveMessageSize})`,
metadata: new Metadata()
metadata: new Metadata(),
};
} else {
return concreteMessage;
@ -80,7 +78,8 @@ export class MaxMessageSizeFilter extends BaseFilter implements Filter {
}
export class MaxMessageSizeFilterFactory
implements FilterFactory<MaxMessageSizeFilter> {
implements FilterFactory<MaxMessageSizeFilter>
{
constructor(private readonly options: ChannelOptions) {}
createFilter(): MaxMessageSizeFilter {

View File

@ -118,7 +118,8 @@ export class Metadata {
key = normalizeKey(key);
validate(key, value);
const existingValue: MetadataValue[] | undefined = this.internalRepr.get(key);
const existingValue: MetadataValue[] | undefined =
this.internalRepr.get(key);
if (existingValue === undefined) {
this.internalRepr.set(key, [value]);
@ -174,7 +175,7 @@ export class Metadata {
const newInternalRepr = newMetadata.internalRepr;
for (const [key, value] of this.internalRepr) {
const clonedValue: MetadataValue[] = value.map((v) => {
const clonedValue: MetadataValue[] = value.map(v => {
if (Buffer.isBuffer(v)) {
return Buffer.from(v);
} else {
@ -264,12 +265,12 @@ export class Metadata {
try {
if (isBinaryKey(key)) {
if (Array.isArray(values)) {
values.forEach((value) => {
values.forEach(value => {
result.add(key, Buffer.from(value, 'base64'));
});
} else if (values !== undefined) {
if (isCustomMetadata(key)) {
values.split(',').forEach((v) => {
values.split(',').forEach(v => {
result.add(key, Buffer.from(v.trim(), 'base64'));
});
} else {
@ -278,7 +279,7 @@ export class Metadata {
}
} else {
if (Array.isArray(values)) {
values.forEach((value) => {
values.forEach(value => {
result.add(key, value);
});
} else if (values !== undefined) {
@ -286,7 +287,9 @@ export class Metadata {
}
}
} catch (error) {
const message = `Failed to add metadata entry ${key}: ${values}. ${getErrorMessage(error)}. For more information see https://github.com/grpc/grpc-node/issues/1173`;
const message = `Failed to add metadata entry ${key}: ${values}. ${getErrorMessage(
error
)}. For more information see https://github.com/grpc/grpc-node/issues/1173`;
log(LogVerbosity.ERROR, message);
}
}
@ -296,5 +299,5 @@ export class Metadata {
}
const bufToString = (val: string | Buffer): string => {
return Buffer.isBuffer(val) ? val.toString('base64') : val
return Buffer.isBuffer(val) ? val.toString('base64') : val;
};

View File

@ -37,8 +37,15 @@ export interface IntermediateObjectWritable<T> extends Writable {
write(chunk: any & T, encoding?: any, cb?: WriteCallback): boolean;
setDefaultEncoding(encoding: string): this;
end(): ReturnType<Writable['end']> extends Writable ? this : void;
end(chunk: any & T, cb?: Function): ReturnType<Writable['end']> extends Writable ? this : void;
end(chunk: any & T, encoding?: any, cb?: Function): ReturnType<Writable['end']> extends Writable ? this : void;
end(
chunk: any & T,
cb?: Function
): ReturnType<Writable['end']> extends Writable ? this : void;
end(
chunk: any & T,
encoding?: any,
cb?: Function
): ReturnType<Writable['end']> extends Writable ? this : void;
}
export interface ObjectWritable<T> extends IntermediateObjectWritable<T> {
@ -47,6 +54,13 @@ export interface ObjectWritable<T> extends IntermediateObjectWritable<T> {
write(chunk: T, encoding?: any, cb?: Function): boolean;
setDefaultEncoding(encoding: string): this;
end(): ReturnType<Writable['end']> extends Writable ? this : void;
end(chunk: T, cb?: Function): ReturnType<Writable['end']> extends Writable ? this : void;
end(chunk: T, encoding?: any, cb?: Function): ReturnType<Writable['end']> extends Writable ? this : void;
end(
chunk: T,
cb?: Function
): ReturnType<Writable['end']> extends Writable ? this : void;
end(
chunk: T,
encoding?: any,
cb?: Function
): ReturnType<Writable['end']> extends Writable ? this : void;
}

View File

@ -114,7 +114,7 @@ export class UnavailablePicker implements Picker {
subchannel: null,
status: this.status,
onCallStarted: null,
onCallEnded: null
onCallEnded: null,
};
}
}
@ -143,7 +143,7 @@ export class QueuePicker {
subchannel: null,
status: null,
onCallStarted: null,
onCallEnded: null
onCallEnded: null,
};
}
}

View File

@ -61,7 +61,7 @@ function mergeArrays<T>(...arrays: T[][]): T[] {
i <
Math.max.apply(
null,
arrays.map((array) => array.length)
arrays.map(array => array.length)
);
i++
) {
@ -137,7 +137,7 @@ class DnsResolver implements Resolver {
details: `Name resolution failed for target ${uriToString(this.target)}`,
metadata: new Metadata(),
};
const backoffOptions: BackoffOptions = {
initialDelay: channelOptions['grpc.initial_reconnect_backoff_ms'],
maxDelay: channelOptions['grpc.max_reconnect_backoff_ms'],
@ -150,7 +150,9 @@ class DnsResolver implements Resolver {
}, backoffOptions);
this.backoff.unref();
this.minTimeBetweenResolutionsMs = channelOptions['grpc.dns_min_time_between_resolutions_ms'] ?? DEFAULT_MIN_TIME_BETWEEN_RESOLUTIONS_MS;
this.minTimeBetweenResolutionsMs =
channelOptions['grpc.dns_min_time_between_resolutions_ms'] ??
DEFAULT_MIN_TIME_BETWEEN_RESOLUTIONS_MS;
this.nextResolutionTimer = setTimeout(() => {}, 0);
clearTimeout(this.nextResolutionTimer);
}
@ -204,24 +206,23 @@ class DnsResolver implements Resolver {
* error is indistinguishable from other kinds of errors */
this.pendingLookupPromise = dnsLookupPromise(hostname, { all: true });
this.pendingLookupPromise.then(
(addressList) => {
addressList => {
this.pendingLookupPromise = null;
this.backoff.reset();
this.backoff.stop();
const ip4Addresses: dns.LookupAddress[] = addressList.filter(
(addr) => addr.family === 4
addr => addr.family === 4
);
const ip6Addresses: dns.LookupAddress[] = addressList.filter(
(addr) => addr.family === 6
addr => addr.family === 6
);
this.latestLookupResult = mergeArrays(ip6Addresses, ip4Addresses).map(
addr => ({ host: addr.address, port: +this.port! })
);
this.latestLookupResult = mergeArrays(
ip6Addresses,
ip4Addresses
).map((addr) => ({ host: addr.address, port: +this.port! }));
const allAddressesString: string =
'[' +
this.latestLookupResult
.map((addr) => addr.host + ':' + addr.port)
.map(addr => addr.host + ':' + addr.port)
.join(',') +
']';
trace(
@ -246,7 +247,7 @@ class DnsResolver implements Resolver {
{}
);
},
(err) => {
err => {
trace(
'Resolution error for target ' +
uriToString(this.target) +
@ -266,7 +267,7 @@ class DnsResolver implements Resolver {
* lookup fails */
this.pendingTxtPromise = resolveTxtPromise(hostname);
this.pendingTxtPromise.then(
(txtRecord) => {
txtRecord => {
this.pendingTxtPromise = null;
try {
this.latestServiceConfig = extractAndSelectServiceConfig(
@ -294,7 +295,7 @@ class DnsResolver implements Resolver {
);
}
},
(err) => {
err => {
/* If TXT lookup fails we should do nothing, which means that we
* continue to use the result of the most recent successful lookup,
* or the default null config object if there has never been a

View File

@ -15,22 +15,35 @@
*
*/
import { CallCredentials } from "./call-credentials";
import { Call, CallStreamOptions, InterceptingListener, MessageContext, StatusObject } from "./call-interface";
import { LogVerbosity, Propagate, Status } from "./constants";
import { Deadline, deadlineToString, getDeadlineTimeoutString, getRelativeTimeout, minDeadline } from "./deadline";
import { FilterStack, FilterStackFactory } from "./filter-stack";
import { InternalChannel } from "./internal-channel";
import { Metadata } from "./metadata";
import { CallCredentials } from './call-credentials';
import {
Call,
CallStreamOptions,
InterceptingListener,
MessageContext,
StatusObject,
} from './call-interface';
import { LogVerbosity, Propagate, Status } from './constants';
import {
Deadline,
deadlineToString,
getDeadlineTimeoutString,
getRelativeTimeout,
minDeadline,
} from './deadline';
import { FilterStack, FilterStackFactory } from './filter-stack';
import { InternalChannel } from './internal-channel';
import { Metadata } from './metadata';
import * as logging from './logging';
import { restrictControlPlaneStatusCode } from "./control-plane-status";
import { restrictControlPlaneStatusCode } from './control-plane-status';
const TRACER_NAME = 'resolving_call';
export class ResolvingCall implements Call {
private child: Call | null = null;
private readPending = false;
private pendingMessage: {context: MessageContext, message: Buffer} | null = null;
private pendingMessage: { context: MessageContext; message: Buffer } | null =
null;
private pendingHalfClose = false;
private ended = false;
private readFilterPending = false;
@ -61,8 +74,14 @@ export class ResolvingCall implements Call {
});
}
if (options.flags & Propagate.DEADLINE) {
this.trace('Propagating deadline from parent: ' + options.parentCall.getDeadline());
this.deadline = minDeadline(this.deadline, options.parentCall.getDeadline());
this.trace(
'Propagating deadline from parent: ' +
options.parentCall.getDeadline()
);
this.deadline = minDeadline(
this.deadline,
options.parentCall.getDeadline()
);
}
}
this.trace('Created');
@ -84,11 +103,8 @@ export class ResolvingCall implements Call {
if (timeout !== Infinity) {
this.trace('Deadline will be reached in ' + timeout + 'ms');
const handleDeadline = () => {
this.cancelWithStatus(
Status.DEADLINE_EXCEEDED,
'Deadline exceeded'
);
}
this.cancelWithStatus(Status.DEADLINE_EXCEEDED, 'Deadline exceeded');
};
if (timeout <= 0) {
process.nextTick(handleDeadline);
} else {
@ -105,7 +121,13 @@ export class ResolvingCall implements Call {
}
clearTimeout(this.deadlineTimer);
const filteredStatus = this.filterStack.receiveTrailers(status);
this.trace('ended with status: code=' + filteredStatus.code + ' details="' + filteredStatus.details + '"');
this.trace(
'ended with status: code=' +
filteredStatus.code +
' details="' +
filteredStatus.details +
'"'
);
this.statusWatchers.forEach(watcher => watcher(filteredStatus));
process.nextTick(() => {
this.listener?.onReceiveStatus(filteredStatus);
@ -119,15 +141,20 @@ export class ResolvingCall implements Call {
}
const child = this.child;
this.writeFilterPending = true;
this.filterStack!.sendMessage(Promise.resolve({message: message, flags: context.flags})).then((filteredMessage) => {
this.writeFilterPending = false;
child.sendMessageWithContext(context, filteredMessage.message);
if (this.pendingHalfClose) {
child.halfClose();
this.filterStack!.sendMessage(
Promise.resolve({ message: message, flags: context.flags })
).then(
filteredMessage => {
this.writeFilterPending = false;
child.sendMessageWithContext(context, filteredMessage.message);
if (this.pendingHalfClose) {
child.halfClose();
}
},
(status: StatusObject) => {
this.cancelWithStatus(status.code, status.details);
}
}, (status: StatusObject) => {
this.cancelWithStatus(status.code, status.details);
});
);
}
getConfig(): void {
@ -152,11 +179,14 @@ export class ResolvingCall implements Call {
// configResult.type === 'SUCCESS'
const config = configResult.config;
if (config.status !== Status.OK) {
const {code, details} = restrictControlPlaneStatusCode(config.status, 'Failed to route call to method ' + this.method);
const { code, details } = restrictControlPlaneStatusCode(
config.status,
'Failed to route call to method ' + this.method
);
this.outputStatus({
code: code,
details: details,
metadata: new Metadata()
metadata: new Metadata(),
});
return;
}
@ -176,48 +206,65 @@ export class ResolvingCall implements Call {
this.filterStackFactory.push(config.dynamicFilterFactories);
this.filterStack = this.filterStackFactory.createFilter();
this.filterStack.sendMetadata(Promise.resolve(this.metadata)).then(filteredMetadata => {
this.child = this.channel.createInnerCall(config, this.method, this.host, this.credentials, this.deadline);
this.trace('Created child [' + this.child.getCallNumber() + ']')
this.child.start(filteredMetadata, {
onReceiveMetadata: metadata => {
this.trace('Received metadata')
this.listener!.onReceiveMetadata(this.filterStack!.receiveMetadata(metadata));
},
onReceiveMessage: message => {
this.trace('Received message');
this.readFilterPending = true;
this.filterStack!.receiveMessage(message).then(filteredMesssage => {
this.trace('Finished filtering received message');
this.readFilterPending = false;
this.listener!.onReceiveMessage(filteredMesssage);
if (this.pendingChildStatus) {
this.outputStatus(this.pendingChildStatus);
this.filterStack.sendMetadata(Promise.resolve(this.metadata)).then(
filteredMetadata => {
this.child = this.channel.createInnerCall(
config,
this.method,
this.host,
this.credentials,
this.deadline
);
this.trace('Created child [' + this.child.getCallNumber() + ']');
this.child.start(filteredMetadata, {
onReceiveMetadata: metadata => {
this.trace('Received metadata');
this.listener!.onReceiveMetadata(
this.filterStack!.receiveMetadata(metadata)
);
},
onReceiveMessage: message => {
this.trace('Received message');
this.readFilterPending = true;
this.filterStack!.receiveMessage(message).then(
filteredMesssage => {
this.trace('Finished filtering received message');
this.readFilterPending = false;
this.listener!.onReceiveMessage(filteredMesssage);
if (this.pendingChildStatus) {
this.outputStatus(this.pendingChildStatus);
}
},
(status: StatusObject) => {
this.cancelWithStatus(status.code, status.details);
}
);
},
onReceiveStatus: status => {
this.trace('Received status');
if (this.readFilterPending) {
this.pendingChildStatus = status;
} else {
this.outputStatus(status);
}
}, (status: StatusObject) => {
this.cancelWithStatus(status.code, status.details);
});
},
onReceiveStatus: status => {
this.trace('Received status');
if (this.readFilterPending) {
this.pendingChildStatus = status;
} else {
this.outputStatus(status);
}
},
});
if (this.readPending) {
this.child.startRead();
}
});
if (this.readPending) {
this.child.startRead();
if (this.pendingMessage) {
this.sendMessageOnChild(
this.pendingMessage.context,
this.pendingMessage.message
);
} else if (this.pendingHalfClose) {
this.child.halfClose();
}
},
(status: StatusObject) => {
this.outputStatus(status);
}
if (this.pendingMessage) {
this.sendMessageOnChild(this.pendingMessage.context, this.pendingMessage.message);
} else if (this.pendingHalfClose) {
this.child.halfClose();
}
}, (status: StatusObject) => {
this.outputStatus(status);
})
);
}
reportResolverError(status: StatusObject) {
@ -228,9 +275,15 @@ export class ResolvingCall implements Call {
}
}
cancelWithStatus(status: Status, details: string): void {
this.trace('cancelWithStatus code: ' + status + ' details: "' + details + '"');
this.trace(
'cancelWithStatus code: ' + status + ' details: "' + details + '"'
);
this.child?.cancelWithStatus(status, details);
this.outputStatus({code: status, details: details, metadata: new Metadata()});
this.outputStatus({
code: status,
details: details,
metadata: new Metadata(),
});
}
getPeer(): string {
return this.child?.getPeer() ?? this.channel.getTarget();
@ -246,7 +299,7 @@ export class ResolvingCall implements Call {
if (this.child) {
this.sendMessageOnChild(context, message);
} else {
this.pendingMessage = {context, message};
this.pendingMessage = { context, message };
}
}
startRead(): void {
@ -276,4 +329,4 @@ export class ResolvingCall implements Call {
getCallNumber(): number {
return this.callNumber;
}
}
}

View File

@ -53,7 +53,7 @@ function getDefaultConfigSelector(
methodName: string,
metadata: Metadata
) {
const splitName = methodName.split('/').filter((x) => x.length > 0);
const splitName = methodName.split('/').filter(x => x.length > 0);
const service = splitName[0] ?? '';
const method = splitName[1] ?? '';
if (serviceConfig && serviceConfig.methodConfig) {
@ -67,7 +67,7 @@ function getDefaultConfigSelector(
methodConfig: methodConfig,
pickInformation: {},
status: Status.OK,
dynamicFilterFactories: []
dynamicFilterFactories: [],
};
}
}
@ -77,7 +77,7 @@ function getDefaultConfigSelector(
methodConfig: { name: [] },
pickInformation: {},
status: Status.OK,
dynamicFilterFactories: []
dynamicFilterFactories: [],
};
};
}
@ -153,9 +153,8 @@ export class ResolvingLoadBalancer implements LoadBalancer {
}
this.updateState(ConnectivityState.IDLE, new QueuePicker(this));
this.childLoadBalancer = new ChildLoadBalancerHandler({
createSubchannel: channelControlHelper.createSubchannel.bind(
channelControlHelper
),
createSubchannel:
channelControlHelper.createSubchannel.bind(channelControlHelper),
requestReresolution: () => {
/* If the backoffTimeout is running, we're still backing off from
* making resolve requests, so we shouldn't make another one here.
@ -172,12 +171,10 @@ export class ResolvingLoadBalancer implements LoadBalancer {
this.latestChildPicker = picker;
this.updateState(newState, picker);
},
addChannelzChild: channelControlHelper.addChannelzChild.bind(
channelControlHelper
),
removeChannelzChild: channelControlHelper.removeChannelzChild.bind(
channelControlHelper
)
addChannelzChild:
channelControlHelper.addChannelzChild.bind(channelControlHelper),
removeChannelzChild:
channelControlHelper.removeChannelzChild.bind(channelControlHelper),
});
this.innerResolver = createResolver(
target,
@ -299,7 +296,10 @@ export class ResolvingLoadBalancer implements LoadBalancer {
}
exitIdle() {
if (this.currentState === ConnectivityState.IDLE || this.currentState === ConnectivityState.TRANSIENT_FAILURE) {
if (
this.currentState === ConnectivityState.IDLE ||
this.currentState === ConnectivityState.TRANSIENT_FAILURE
) {
if (this.backoffTimeout.isRunning()) {
this.continueResolving = true;
} else {

View File

@ -15,25 +15,41 @@
*
*/
import { CallCredentials } from "./call-credentials";
import { LogVerbosity, Status } from "./constants";
import { Deadline } from "./deadline";
import { Metadata } from "./metadata";
import { CallConfig } from "./resolver";
import { CallCredentials } from './call-credentials';
import { LogVerbosity, Status } from './constants';
import { Deadline } from './deadline';
import { Metadata } from './metadata';
import { CallConfig } from './resolver';
import * as logging from './logging';
import { Call, InterceptingListener, MessageContext, StatusObject, WriteCallback, WriteObject } from "./call-interface";
import { LoadBalancingCall, StatusObjectWithProgress } from "./load-balancing-call";
import { InternalChannel } from "./internal-channel";
import {
Call,
InterceptingListener,
MessageContext,
StatusObject,
WriteCallback,
WriteObject,
} from './call-interface';
import {
LoadBalancingCall,
StatusObjectWithProgress,
} from './load-balancing-call';
import { InternalChannel } from './internal-channel';
const TRACER_NAME = 'retrying_call';
export class RetryThrottler {
private tokens: number;
constructor(private readonly maxTokens: number, private readonly tokenRatio: number, previousRetryThrottler?: RetryThrottler) {
constructor(
private readonly maxTokens: number,
private readonly tokenRatio: number,
previousRetryThrottler?: RetryThrottler
) {
if (previousRetryThrottler) {
/* When carrying over tokens from a previous config, rescale them to the
* new max value */
this.tokens = previousRetryThrottler.tokens * (maxTokens / previousRetryThrottler.maxTokens);
this.tokens =
previousRetryThrottler.tokens *
(maxTokens / previousRetryThrottler.maxTokens);
} else {
this.tokens = maxTokens;
}
@ -53,14 +69,17 @@ export class RetryThrottler {
}
export class MessageBufferTracker {
private totalAllocated: number = 0;
private totalAllocated = 0;
private allocatedPerCall: Map<number, number> = new Map<number, number>();
constructor(private totalLimit: number, private limitPerCall: number) {}
allocate(size: number, callId: number): boolean {
const currentPerCall = this.allocatedPerCall.get(callId) ?? 0;
if (this.limitPerCall - currentPerCall < size || this.totalLimit - this.totalAllocated < size) {
if (
this.limitPerCall - currentPerCall < size ||
this.totalLimit - this.totalAllocated < size
) {
return false;
}
this.allocatedPerCall.set(callId, currentPerCall + size);
@ -70,12 +89,16 @@ export class MessageBufferTracker {
free(size: number, callId: number) {
if (this.totalAllocated < size) {
throw new Error(`Invalid buffer allocation state: call ${callId} freed ${size} > total allocated ${this.totalAllocated}`);
throw new Error(
`Invalid buffer allocation state: call ${callId} freed ${size} > total allocated ${this.totalAllocated}`
);
}
this.totalAllocated -= size;
const currentPerCall = this.allocatedPerCall.get(callId) ?? 0;
if (currentPerCall < size) {
throw new Error(`Invalid buffer allocation state: call ${callId} freed ${size} > allocated for call ${currentPerCall}`);
throw new Error(
`Invalid buffer allocation state: call ${callId} freed ${size} > allocated for call ${currentPerCall}`
);
}
this.allocatedPerCall.set(callId, currentPerCall - size);
}
@ -83,7 +106,9 @@ export class MessageBufferTracker {
freeAll(callId: number) {
const currentPerCall = this.allocatedPerCall.get(callId) ?? 0;
if (this.totalAllocated < currentPerCall) {
throw new Error(`Invalid buffer allocation state: call ${callId} allocated ${currentPerCall} > total allocated ${this.totalAllocated}`);
throw new Error(
`Invalid buffer allocation state: call ${callId} allocated ${currentPerCall} > total allocated ${this.totalAllocated}`
);
}
this.totalAllocated -= currentPerCall;
this.allocatedPerCall.delete(callId);
@ -164,11 +189,11 @@ export class RetryingCall implements Call {
* be no new child calls.
*/
private readStarted = false;
private transparentRetryUsed: boolean = false;
private transparentRetryUsed = false;
/**
* Number of attempts so far
*/
private attempts: number = 0;
private attempts = 0;
private hedgingTimer: NodeJS.Timer | null = null;
private committedCallIndex: number | null = null;
private initialRetryBackoffSec = 0;
@ -187,7 +212,12 @@ export class RetryingCall implements Call {
if (callConfig.methodConfig.retryPolicy) {
this.state = 'RETRY';
const retryPolicy = callConfig.methodConfig.retryPolicy;
this.nextRetryBackoffSec = this.initialRetryBackoffSec = Number(retryPolicy.initialBackoff.substring(0, retryPolicy.initialBackoff.length - 1));
this.nextRetryBackoffSec = this.initialRetryBackoffSec = Number(
retryPolicy.initialBackoff.substring(
0,
retryPolicy.initialBackoff.length - 1
)
);
} else if (callConfig.methodConfig.hedgingPolicy) {
this.state = 'HEDGING';
} else {
@ -207,7 +237,13 @@ export class RetryingCall implements Call {
}
private reportStatus(statusObject: StatusObject) {
this.trace('ended with status: code=' + statusObject.code + ' details="' + statusObject.details + '"');
this.trace(
'ended with status: code=' +
statusObject.code +
' details="' +
statusObject.details +
'"'
);
this.bufferTracker.freeAll(this.callNumber);
this.writeBufferOffset = this.writeBufferOffset + this.writeBuffer.length;
this.writeBuffer = [];
@ -216,15 +252,17 @@ export class RetryingCall implements Call {
this.listener?.onReceiveStatus({
code: statusObject.code,
details: statusObject.details,
metadata: statusObject.metadata
metadata: statusObject.metadata,
});
});
}
cancelWithStatus(status: Status, details: string): void {
this.trace('cancelWithStatus code: ' + status + ' details: "' + details + '"');
this.reportStatus({code: status, details, metadata: new Metadata()});
for (const {call} of this.underlyingCalls) {
this.trace(
'cancelWithStatus code: ' + status + ' details: "' + details + '"'
);
this.reportStatus({ code: status, details, metadata: new Metadata() });
for (const { call } of this.underlyingCalls) {
call.cancelWithStatus(status, details);
}
}
@ -237,7 +275,12 @@ export class RetryingCall implements Call {
}
private getBufferEntry(messageIndex: number): WriteBufferEntry {
return this.writeBuffer[messageIndex - this.writeBufferOffset] ?? {entryType: 'FREED', allocated: false};
return (
this.writeBuffer[messageIndex - this.writeBufferOffset] ?? {
entryType: 'FREED',
allocated: false,
}
);
}
private getNextBufferIndex() {
@ -248,14 +291,24 @@ export class RetryingCall implements Call {
if (this.state !== 'COMMITTED') {
return;
}
const earliestNeededMessageIndex = this.underlyingCalls[this.committedCallIndex!].nextMessageToSend;
for (let messageIndex = this.writeBufferOffset; messageIndex < earliestNeededMessageIndex; messageIndex++) {
const earliestNeededMessageIndex =
this.underlyingCalls[this.committedCallIndex!].nextMessageToSend;
for (
let messageIndex = this.writeBufferOffset;
messageIndex < earliestNeededMessageIndex;
messageIndex++
) {
const bufferEntry = this.getBufferEntry(messageIndex);
if (bufferEntry.allocated) {
this.bufferTracker.free(bufferEntry.message!.message.length, this.callNumber);
this.bufferTracker.free(
bufferEntry.message!.message.length,
this.callNumber
);
}
}
this.writeBuffer = this.writeBuffer.slice(earliestNeededMessageIndex - this.writeBufferOffset);
this.writeBuffer = this.writeBuffer.slice(
earliestNeededMessageIndex - this.writeBufferOffset
);
this.writeBufferOffset = earliestNeededMessageIndex;
}
@ -266,7 +319,12 @@ export class RetryingCall implements Call {
if (this.underlyingCalls[index].state === 'COMPLETED') {
return;
}
this.trace('Committing call [' + this.underlyingCalls[index].call.getCallNumber() + '] at index ' + index);
this.trace(
'Committing call [' +
this.underlyingCalls[index].call.getCallNumber() +
'] at index ' +
index
);
this.state = 'COMMITTED';
this.committedCallIndex = index;
for (let i = 0; i < this.underlyingCalls.length; i++) {
@ -277,7 +335,10 @@ export class RetryingCall implements Call {
continue;
}
this.underlyingCalls[i].state = 'COMPLETED';
this.underlyingCalls[i].call.cancelWithStatus(Status.CANCELLED, 'Discarded in favor of other hedged attempt');
this.underlyingCalls[i].call.cancelWithStatus(
Status.CANCELLED,
'Discarded in favor of other hedged attempt'
);
}
this.clearSentMessages();
}
@ -289,7 +350,10 @@ export class RetryingCall implements Call {
let mostMessages = -1;
let callWithMostMessages = -1;
for (const [index, childCall] of this.underlyingCalls.entries()) {
if (childCall.state === 'ACTIVE' && childCall.nextMessageToSend > mostMessages) {
if (
childCall.state === 'ACTIVE' &&
childCall.nextMessageToSend > mostMessages
) {
mostMessages = childCall.nextMessageToSend;
callWithMostMessages = index;
}
@ -304,7 +368,11 @@ export class RetryingCall implements Call {
}
private isStatusCodeInList(list: (Status | string)[], code: Status) {
return list.some((value => value === code || value.toString().toLowerCase() === Status[code].toLowerCase()));
return list.some(
value =>
value === code ||
value.toString().toLowerCase() === Status[code].toLowerCase()
);
}
private getNextRetryBackoffMs() {
@ -313,12 +381,20 @@ export class RetryingCall implements Call {
return 0;
}
const nextBackoffMs = Math.random() * this.nextRetryBackoffSec * 1000;
const maxBackoffSec = Number(retryPolicy.maxBackoff.substring(0, retryPolicy.maxBackoff.length - 1));
this.nextRetryBackoffSec = Math.min(this.nextRetryBackoffSec * retryPolicy.backoffMultiplier, maxBackoffSec);
return nextBackoffMs
const maxBackoffSec = Number(
retryPolicy.maxBackoff.substring(0, retryPolicy.maxBackoff.length - 1)
);
this.nextRetryBackoffSec = Math.min(
this.nextRetryBackoffSec * retryPolicy.backoffMultiplier,
maxBackoffSec
);
return nextBackoffMs;
}
private maybeRetryCall(pushback: number | null, callback: (retried: boolean) => void) {
private maybeRetryCall(
pushback: number | null,
callback: (retried: boolean) => void
) {
if (this.state !== 'RETRY') {
callback(false);
return;
@ -362,7 +438,11 @@ export class RetryingCall implements Call {
return count;
}
private handleProcessedStatus(status: StatusObject, callIndex: number, pushback: number | null) {
private handleProcessedStatus(
status: StatusObject,
callIndex: number,
pushback: number | null
) {
switch (this.state) {
case 'COMMITTED':
case 'TRANSPARENT_ONLY':
@ -370,7 +450,13 @@ export class RetryingCall implements Call {
this.reportStatus(status);
break;
case 'HEDGING':
if (this.isStatusCodeInList(this.callConfig!.methodConfig.hedgingPolicy!.nonFatalStatusCodes ?? [], status.code)) {
if (
this.isStatusCodeInList(
this.callConfig!.methodConfig.hedgingPolicy!.nonFatalStatusCodes ??
[],
status.code
)
) {
this.retryThrottler?.addCallFailed();
let delayMs: number;
if (pushback === null) {
@ -397,9 +483,14 @@ export class RetryingCall implements Call {
}
break;
case 'RETRY':
if (this.isStatusCodeInList(this.callConfig!.methodConfig.retryPolicy!.retryableStatusCodes, status.code)) {
if (
this.isStatusCodeInList(
this.callConfig!.methodConfig.retryPolicy!.retryableStatusCodes,
status.code
)
) {
this.retryThrottler?.addCallFailed();
this.maybeRetryCall(pushback, (retried) => {
this.maybeRetryCall(pushback, retried => {
if (!retried) {
this.commitCall(callIndex);
this.reportStatus(status);
@ -425,11 +516,23 @@ export class RetryingCall implements Call {
}
}
private handleChildStatus(status: StatusObjectWithProgress, callIndex: number) {
private handleChildStatus(
status: StatusObjectWithProgress,
callIndex: number
) {
if (this.underlyingCalls[callIndex].state === 'COMPLETED') {
return;
}
this.trace('state=' + this.state + ' handling status with progress ' + status.progress + ' from child [' + this.underlyingCalls[callIndex].call.getCallNumber() + '] in state ' + this.underlyingCalls[callIndex].state);
this.trace(
'state=' +
this.state +
' handling status with progress ' +
status.progress +
' from child [' +
this.underlyingCalls[callIndex].call.getCallNumber() +
'] in state ' +
this.underlyingCalls[callIndex].state
);
this.underlyingCalls[callIndex].state = 'COMPLETED';
if (status.code === Status.OK) {
this.retryThrottler?.addCallSucceeded();
@ -454,7 +557,7 @@ export class RetryingCall implements Call {
} else {
this.transparentRetryUsed = true;
this.startNewAttempt();
};
}
break;
case 'DROP':
this.commitCall(callIndex);
@ -497,7 +600,9 @@ export class RetryingCall implements Call {
return;
}
const hedgingDelayString = hedgingPolicy.hedgingDelay ?? '0s';
const hedgingDelaySec = Number(hedgingDelayString.substring(0, hedgingDelayString.length - 1));
const hedgingDelaySec = Number(
hedgingDelayString.substring(0, hedgingDelayString.length - 1)
);
this.hedgingTimer = setTimeout(() => {
this.maybeStartHedgingAttempt();
}, hedgingDelaySec * 1000);
@ -505,42 +610,72 @@ export class RetryingCall implements Call {
}
private startNewAttempt() {
const child = this.channel.createLoadBalancingCall(this.callConfig, this.methodName, this.host, this.credentials, this.deadline);
this.trace('Created child call [' + child.getCallNumber() + '] for attempt ' + this.attempts);
const child = this.channel.createLoadBalancingCall(
this.callConfig,
this.methodName,
this.host,
this.credentials,
this.deadline
);
this.trace(
'Created child call [' +
child.getCallNumber() +
'] for attempt ' +
this.attempts
);
const index = this.underlyingCalls.length;
this.underlyingCalls.push({state: 'ACTIVE', call: child, nextMessageToSend: 0});
this.underlyingCalls.push({
state: 'ACTIVE',
call: child,
nextMessageToSend: 0,
});
const previousAttempts = this.attempts - 1;
const initialMetadata = this.initialMetadata!.clone();
if (previousAttempts > 0) {
initialMetadata.set(PREVIONS_RPC_ATTEMPTS_METADATA_KEY, `${previousAttempts}`);
initialMetadata.set(
PREVIONS_RPC_ATTEMPTS_METADATA_KEY,
`${previousAttempts}`
);
}
let receivedMetadata = false;
child.start(initialMetadata, {
onReceiveMetadata: metadata => {
this.trace('Received metadata from child [' + child.getCallNumber() + ']');
this.trace(
'Received metadata from child [' + child.getCallNumber() + ']'
);
this.commitCall(index);
receivedMetadata = true;
if (previousAttempts > 0) {
metadata.set(PREVIONS_RPC_ATTEMPTS_METADATA_KEY, `${previousAttempts}`);
metadata.set(
PREVIONS_RPC_ATTEMPTS_METADATA_KEY,
`${previousAttempts}`
);
}
if (this.underlyingCalls[index].state === 'ACTIVE') {
this.listener!.onReceiveMetadata(metadata);
}
},
onReceiveMessage: message => {
this.trace('Received message from child [' + child.getCallNumber() + ']');
this.trace(
'Received message from child [' + child.getCallNumber() + ']'
);
this.commitCall(index);
if (this.underlyingCalls[index].state === 'ACTIVE') {
this.listener!.onReceiveMessage(message);
}
},
onReceiveStatus: status => {
this.trace('Received status from child [' + child.getCallNumber() + ']');
this.trace(
'Received status from child [' + child.getCallNumber() + ']'
);
if (!receivedMetadata && previousAttempts > 0) {
status.metadata.set(PREVIONS_RPC_ATTEMPTS_METADATA_KEY, `${previousAttempts}`);
status.metadata.set(
PREVIONS_RPC_ATTEMPTS_METADATA_KEY,
`${previousAttempts}`
);
}
this.handleChildStatus(status, index);
}
},
});
this.sendNextChildMessage(index);
if (this.readStarted) {
@ -575,12 +710,15 @@ export class RetryingCall implements Call {
const bufferEntry = this.getBufferEntry(childCall.nextMessageToSend);
switch (bufferEntry.entryType) {
case 'MESSAGE':
childCall.call.sendMessageWithContext({
callback: (error) => {
// Ignore error
this.handleChildWriteCompleted(childIndex);
}
}, bufferEntry.message!.message);
childCall.call.sendMessageWithContext(
{
callback: error => {
// Ignore error
this.handleChildWriteCompleted(childIndex);
},
},
bufferEntry.message!.message
);
break;
case 'HALF_CLOSE':
childCall.nextMessageToSend += 1;
@ -603,19 +741,25 @@ export class RetryingCall implements Call {
const bufferEntry: WriteBufferEntry = {
entryType: 'MESSAGE',
message: writeObj,
allocated: this.bufferTracker.allocate(message.length, this.callNumber)
allocated: this.bufferTracker.allocate(message.length, this.callNumber),
};
this.writeBuffer.push(bufferEntry);
if (bufferEntry.allocated) {
context.callback?.();
for (const [callIndex, call] of this.underlyingCalls.entries()) {
if (call.state === 'ACTIVE' && call.nextMessageToSend === messageIndex) {
call.call.sendMessageWithContext({
callback: (error) => {
// Ignore error
this.handleChildWriteCompleted(callIndex);
}
}, message);
if (
call.state === 'ACTIVE' &&
call.nextMessageToSend === messageIndex
) {
call.call.sendMessageWithContext(
{
callback: error => {
// Ignore error
this.handleChildWriteCompleted(callIndex);
},
},
message
);
}
}
} else {
@ -625,14 +769,17 @@ export class RetryingCall implements Call {
return;
}
const call = this.underlyingCalls[this.committedCallIndex];
bufferEntry.callback = context.callback;
bufferEntry.callback = context.callback;
if (call.state === 'ACTIVE' && call.nextMessageToSend === messageIndex) {
call.call.sendMessageWithContext({
callback: (error) => {
// Ignore error
this.handleChildWriteCompleted(this.committedCallIndex!);
}
}, message);
call.call.sendMessageWithContext(
{
callback: error => {
// Ignore error
this.handleChildWriteCompleted(this.committedCallIndex!);
},
},
message
);
}
}
}
@ -650,17 +797,20 @@ export class RetryingCall implements Call {
const halfCloseIndex = this.getNextBufferIndex();
this.writeBuffer.push({
entryType: 'HALF_CLOSE',
allocated: false
allocated: false,
});
for (const call of this.underlyingCalls) {
if (call?.state === 'ACTIVE' && call.nextMessageToSend === halfCloseIndex) {
if (
call?.state === 'ACTIVE' &&
call.nextMessageToSend === halfCloseIndex
) {
call.nextMessageToSend += 1;
call.call.halfClose();
}
}
}
setCredentials(newCredentials: CallCredentials): void {
throw new Error("Method not implemented.");
throw new Error('Method not implemented.');
}
getMethod(): string {
return this.methodName;
@ -668,4 +818,4 @@ export class RetryingCall implements Call {
getHost(): string {
return this.host;
}
}
}

View File

@ -68,7 +68,7 @@ const defaultCompressionHeaders = {
// once compression is integrated.
[GRPC_ACCEPT_ENCODING_HEADER]: 'identity,deflate,gzip',
[GRPC_ENCODING_HEADER]: 'identity',
}
};
const defaultResponseHeaders = {
[http2.constants.HTTP2_HEADER_STATUS]: http2.constants.HTTP_STATUS_OK,
[http2.constants.HTTP2_HEADER_CONTENT_TYPE]: 'application/grpc+proto',
@ -199,7 +199,7 @@ export class ServerWritableStreamImpl<RequestType, ResponseType>
this.trailingMetadata = new Metadata();
this.call.setupSurfaceCall(this);
this.on('error', (err) => {
this.on('error', err => {
this.call.sendError(err);
this.end();
});
@ -237,7 +237,7 @@ export class ServerWritableStreamImpl<RequestType, ResponseType>
} catch (err) {
this.emit('error', {
details: getErrorMessage(err),
code: Status.INTERNAL
code: Status.INTERNAL,
});
}
@ -283,7 +283,7 @@ export class ServerDuplexStreamImpl<RequestType, ResponseType>
this.call.setupSurfaceCall(this);
this.call.setupReadable(this, encoding);
this.on('error', (err) => {
this.on('error', err => {
this.call.sendError(err);
this.end();
});
@ -502,7 +502,11 @@ export class Http2ServerCallStream<
this.metadataSent = true;
const custom = customMetadata ? customMetadata.toHttp2Headers() : null;
// TODO(cjihrig): Include compression headers.
const headers = { ...defaultResponseHeaders, ...defaultCompressionHeaders, ...custom };
const headers = {
...defaultResponseHeaders,
...defaultCompressionHeaders,
...custom,
};
this.stream.respond(headers, defaultResponseOptions);
}
@ -559,6 +563,8 @@ export class Http2ServerCallStream<
const { stream } = this;
let receivedLength = 0;
// eslint-disable-next-line @typescript-eslint/no-this-alias
const call = this;
const body: Buffer[] = [];
const limit = this.maxReceiveMessageSize;
@ -595,7 +601,10 @@ export class Http2ServerCallStream<
}
if (receivedLength === 0) {
next({ code: Status.INTERNAL, details: 'received empty unary message' })
next({
code: Status.INTERNAL,
details: 'received empty unary message',
});
return;
}
@ -615,29 +624,33 @@ export class Http2ServerCallStream<
}
decompressedMessage.then(
(decompressed) => call.safeDeserializeMessage(decompressed, next),
(err: any) => next(
err.code
? err
: {
code: Status.INTERNAL,
details: `Received "grpc-encoding" header "${encoding}" but ${encoding} decompression failed`,
}
)
)
decompressed => call.safeDeserializeMessage(decompressed, next),
(err: any) =>
next(
err.code
? err
: {
code: Status.INTERNAL,
details: `Received "grpc-encoding" header "${encoding}" but ${encoding} decompression failed`,
}
)
);
}
}
private safeDeserializeMessage(
buffer: Buffer,
next: (err: Partial<ServerStatusResponse> | null, request?: RequestType) => void
next: (
err: Partial<ServerStatusResponse> | null,
request?: RequestType
) => void
) {
try {
next(null, this.deserializeMessage(buffer));
} catch (err) {
next({
details: getErrorMessage(err),
code: Status.INTERNAL
code: Status.INTERNAL,
});
}
}
@ -688,7 +701,7 @@ export class Http2ServerCallStream<
} catch (err) {
this.sendError({
details: getErrorMessage(err),
code: Status.INTERNAL
code: Status.INTERNAL,
});
}
}
@ -720,7 +733,7 @@ export class Http2ServerCallStream<
[GRPC_MESSAGE_HEADER]: encodeURI(statusObj.details),
...statusObj.metadata?.toHttp2Headers(),
};
this.stream.sendTrailers(trailersToSend);
this.statusSent = true;
});
@ -734,7 +747,7 @@ export class Http2ServerCallStream<
...defaultResponseHeaders,
...statusObj.metadata?.toHttp2Headers(),
};
this.stream.respond(trailersToSend, {endStream: true});
this.stream.respond(trailersToSend, { endStream: true });
this.statusSent = true;
}
}
@ -790,12 +803,12 @@ export class Http2ServerCallStream<
}
setupSurfaceCall(call: ServerSurfaceCall) {
this.once('cancelled', (reason) => {
this.once('cancelled', reason => {
call.cancelled = true;
call.emit('cancelled', reason);
});
this.once('callEnd', (status) => call.emit('callEnd', status));
this.once('callEnd', status => call.emit('callEnd', status));
}
setupReadable(
@ -931,12 +944,12 @@ export class Http2ServerCallStream<
this.bufferedMessages.length = 0;
let code = getErrorCode(error);
if (code === null || code < Status.OK || code > Status.UNAUTHENTICATED) {
code = Status.INTERNAL
code = Status.INTERNAL;
}
readable.emit('error', {
details: getErrorMessage(error),
code: code
code: code,
});
}

View File

@ -59,17 +59,27 @@ import {
stringToSubchannelAddress,
} from './subchannel-address';
import { parseUri } from './uri-parser';
import { ChannelzCallTracker, ChannelzChildrenTracker, ChannelzTrace, registerChannelzServer, registerChannelzSocket, ServerInfo, ServerRef, SocketInfo, SocketRef, TlsInfo, unregisterChannelzRef } from './channelz';
import {
ChannelzCallTracker,
ChannelzChildrenTracker,
ChannelzTrace,
registerChannelzServer,
registerChannelzSocket,
ServerInfo,
ServerRef,
SocketInfo,
SocketRef,
TlsInfo,
unregisterChannelzRef,
} from './channelz';
import { CipherNameAndProtocol, TLSSocket } from 'tls';
import { getErrorCode, getErrorMessage } from './error';
const UNLIMITED_CONNECTION_AGE_MS = ~(1<<31);
const KEEPALIVE_MAX_TIME_MS = ~(1<<31);
const UNLIMITED_CONNECTION_AGE_MS = ~(1 << 31);
const KEEPALIVE_MAX_TIME_MS = ~(1 << 31);
const KEEPALIVE_TIMEOUT_MS = 20000;
const {
HTTP2_HEADER_PATH
} = http2.constants
const { HTTP2_HEADER_PATH } = http2.constants;
const TRACER_NAME = 'server';
@ -101,9 +111,8 @@ export interface UntypedServiceImplementation {
}
function getDefaultHandler(handlerType: HandlerType, methodName: string) {
const unimplementedStatusResponse = getUnimplementedStatusResponse(
methodName
);
const unimplementedStatusResponse =
getUnimplementedStatusResponse(methodName);
switch (handlerType) {
case 'unary':
return (
@ -146,7 +155,10 @@ interface ChannelzListenerInfo {
}
export class Server {
private http2ServerList: { server: (http2.Http2Server | http2.Http2SecureServer), channelzRef: SocketRef }[] = [];
private http2ServerList: {
server: http2.Http2Server | http2.Http2SecureServer;
channelzRef: SocketRef;
}[] = [];
private handlers: Map<string, UntypedHandler> = new Map<
string,
@ -155,7 +167,7 @@ export class Server {
private sessions = new Map<http2.ServerHttp2Session, ChannelzSessionInfo>();
private started = false;
private options: ChannelOptions;
private serverAddressString: string = 'null'
private serverAddressString = 'null';
// Channelz Info
private readonly channelzEnabled: boolean = true;
@ -176,14 +188,22 @@ export class Server {
if (this.options['grpc.enable_channelz'] === 0) {
this.channelzEnabled = false;
}
this.channelzRef = registerChannelzServer(() => this.getChannelzInfo(), this.channelzEnabled);
this.channelzRef = registerChannelzServer(
() => this.getChannelzInfo(),
this.channelzEnabled
);
if (this.channelzEnabled) {
this.channelzTrace.addTrace('CT_INFO', 'Server created');
}
this.maxConnectionAgeMs = this.options['grpc.max_connection_age_ms'] ?? UNLIMITED_CONNECTION_AGE_MS;
this.maxConnectionAgeGraceMs = this.options['grpc.max_connection_age_grace_ms'] ?? UNLIMITED_CONNECTION_AGE_MS;
this.keepaliveTimeMs = this.options['grpc.keepalive_time_ms'] ?? KEEPALIVE_MAX_TIME_MS;
this.keepaliveTimeoutMs = this.options['grpc.keepalive_timeout_ms'] ?? KEEPALIVE_TIMEOUT_MS;
this.maxConnectionAgeMs =
this.options['grpc.max_connection_age_ms'] ?? UNLIMITED_CONNECTION_AGE_MS;
this.maxConnectionAgeGraceMs =
this.options['grpc.max_connection_age_grace_ms'] ??
UNLIMITED_CONNECTION_AGE_MS;
this.keepaliveTimeMs =
this.options['grpc.keepalive_time_ms'] ?? KEEPALIVE_MAX_TIME_MS;
this.keepaliveTimeoutMs =
this.options['grpc.keepalive_timeout_ms'] ?? KEEPALIVE_TIMEOUT_MS;
this.trace('Server constructed');
}
@ -192,27 +212,46 @@ export class Server {
trace: this.channelzTrace,
callTracker: this.callTracker,
listenerChildren: this.listenerChildrenTracker.getChildLists(),
sessionChildren: this.sessionChildrenTracker.getChildLists()
sessionChildren: this.sessionChildrenTracker.getChildLists(),
};
}
private getChannelzSessionInfoGetter(session: http2.ServerHttp2Session): () => SocketInfo {
private getChannelzSessionInfoGetter(
session: http2.ServerHttp2Session
): () => SocketInfo {
return () => {
const sessionInfo = this.sessions.get(session)!;
const sessionSocket = session.socket;
const remoteAddress = sessionSocket.remoteAddress ? stringToSubchannelAddress(sessionSocket.remoteAddress, sessionSocket.remotePort) : null;
const localAddress = sessionSocket.localAddress ? stringToSubchannelAddress(sessionSocket.localAddress!, sessionSocket.localPort) : null;
const remoteAddress = sessionSocket.remoteAddress
? stringToSubchannelAddress(
sessionSocket.remoteAddress,
sessionSocket.remotePort
)
: null;
const localAddress = sessionSocket.localAddress
? stringToSubchannelAddress(
sessionSocket.localAddress!,
sessionSocket.localPort
)
: null;
let tlsInfo: TlsInfo | null;
if (session.encrypted) {
const tlsSocket: TLSSocket = sessionSocket as TLSSocket;
const cipherInfo: CipherNameAndProtocol & {standardName?: string} = tlsSocket.getCipher();
const cipherInfo: CipherNameAndProtocol & { standardName?: string } =
tlsSocket.getCipher();
const certificate = tlsSocket.getCertificate();
const peerCertificate = tlsSocket.getPeerCertificate();
tlsInfo = {
cipherSuiteStandardName: cipherInfo.standardName ?? null,
cipherSuiteOtherName: cipherInfo.standardName ? null : cipherInfo.name,
localCertificate: (certificate && 'raw' in certificate) ? certificate.raw : null,
remoteCertificate: (peerCertificate && 'raw' in peerCertificate) ? peerCertificate.raw : null
cipherSuiteOtherName: cipherInfo.standardName
? null
: cipherInfo.name,
localCertificate:
certificate && 'raw' in certificate ? certificate.raw : null,
remoteCertificate:
peerCertificate && 'raw' in peerCertificate
? peerCertificate.raw
: null,
};
} else {
tlsInfo = null;
@ -229,20 +268,24 @@ export class Server {
messagesReceived: sessionInfo.messagesReceived,
keepAlivesSent: 0,
lastLocalStreamCreatedTimestamp: null,
lastRemoteStreamCreatedTimestamp: sessionInfo.streamTracker.lastCallStartedTimestamp,
lastRemoteStreamCreatedTimestamp:
sessionInfo.streamTracker.lastCallStartedTimestamp,
lastMessageSentTimestamp: sessionInfo.lastMessageSentTimestamp,
lastMessageReceivedTimestamp: sessionInfo.lastMessageReceivedTimestamp,
localFlowControlWindow: session.state.localWindowSize ?? null,
remoteFlowControlWindow: session.state.remoteWindowSize ?? null
remoteFlowControlWindow: session.state.remoteWindowSize ?? null,
};
return socketInfo;
};
}
private trace(text: string): void {
logging.trace(LogVerbosity.DEBUG, TRACER_NAME, '(' + this.channelzRef.id + ') ' + text);
logging.trace(
LogVerbosity.DEBUG,
TRACER_NAME,
'(' + this.channelzRef.id + ') ' + text
);
}
addProtoService(): never {
throw new Error('Not implemented. Use addService() instead');
@ -267,7 +310,7 @@ export class Server {
throw new Error('Cannot add an empty service to a server');
}
serviceKeys.forEach((name) => {
serviceKeys.forEach(name => {
const attrs = service[name];
let methodType: HandlerType;
@ -318,7 +361,7 @@ export class Server {
}
const serviceKeys = Object.keys(service);
serviceKeys.forEach((name) => {
serviceKeys.forEach(name => {
const attrs = service[name];
this.unregister(attrs.path);
});
@ -362,9 +405,8 @@ export class Server {
maxSendHeaderBlockLength: Number.MAX_SAFE_INTEGER,
};
if ('grpc-node.max_session_memory' in this.options) {
serverOptions.maxSessionMemory = this.options[
'grpc-node.max_session_memory'
];
serverOptions.maxSessionMemory =
this.options['grpc-node.max_session_memory'];
} else {
/* By default, set a very large max session memory limit, to effectively
* disable enforcement of the limit. Some testing indicates that Node's
@ -380,7 +422,7 @@ export class Server {
const deferredCallback = (error: Error | null, port: number) => {
process.nextTick(() => callback(error, port));
}
};
const setupServer = (): http2.Http2Server | http2.Http2SecureServer => {
let http2Server: http2.Http2Server | http2.Http2SecureServer;
@ -394,7 +436,9 @@ export class Server {
/* These errors need to be handled by the user of Http2SecureServer,
* according to https://github.com/nodejs/node/issues/35824 */
socket.on('error', (e: Error) => {
this.trace('An incoming TLS connection closed with error: ' + e.message);
this.trace(
'An incoming TLS connection closed with error: ' + e.message
);
});
});
} else {
@ -415,8 +459,10 @@ export class Server {
return Promise.resolve({ port: portNum, count: previousCount });
}
return Promise.all(
addressList.map((address) => {
this.trace('Attempting to bind ' + subchannelAddressToString(address));
addressList.map(address => {
this.trace(
'Attempting to bind ' + subchannelAddressToString(address)
);
let addr: SubchannelAddress;
if (isTcpSubchannelAddress(address)) {
addr = {
@ -430,9 +476,14 @@ export class Server {
const http2Server = setupServer();
return new Promise<number | Error>((resolve, reject) => {
const onError = (err: Error) => {
this.trace('Failed to bind ' + subchannelAddressToString(address) + ' with error ' + err.message);
this.trace(
'Failed to bind ' +
subchannelAddressToString(address) +
' with error ' +
err.message
);
resolve(err);
}
};
http2Server.once('error', onError);
@ -441,46 +492,60 @@ export class Server {
let boundSubchannelAddress: SubchannelAddress;
if (typeof boundAddress === 'string') {
boundSubchannelAddress = {
path: boundAddress
path: boundAddress,
};
} else {
boundSubchannelAddress = {
host: boundAddress.address,
port: boundAddress.port
}
}
let channelzRef: SocketRef;
channelzRef = registerChannelzSocket(subchannelAddressToString(boundSubchannelAddress), () => {
return {
localAddress: boundSubchannelAddress,
remoteAddress: null,
security: null,
remoteName: null,
streamsStarted: 0,
streamsSucceeded: 0,
streamsFailed: 0,
messagesSent: 0,
messagesReceived: 0,
keepAlivesSent: 0,
lastLocalStreamCreatedTimestamp: null,
lastRemoteStreamCreatedTimestamp: null,
lastMessageSentTimestamp: null,
lastMessageReceivedTimestamp: null,
localFlowControlWindow: null,
remoteFlowControlWindow: null
port: boundAddress.port,
};
}, this.channelzEnabled);
}
const channelzRef = registerChannelzSocket(
subchannelAddressToString(boundSubchannelAddress),
() => {
return {
localAddress: boundSubchannelAddress,
remoteAddress: null,
security: null,
remoteName: null,
streamsStarted: 0,
streamsSucceeded: 0,
streamsFailed: 0,
messagesSent: 0,
messagesReceived: 0,
keepAlivesSent: 0,
lastLocalStreamCreatedTimestamp: null,
lastRemoteStreamCreatedTimestamp: null,
lastMessageSentTimestamp: null,
lastMessageReceivedTimestamp: null,
localFlowControlWindow: null,
remoteFlowControlWindow: null,
};
},
this.channelzEnabled
);
if (this.channelzEnabled) {
this.listenerChildrenTracker.refChild(channelzRef);
}
this.http2ServerList.push({server: http2Server, channelzRef: channelzRef});
this.trace('Successfully bound ' + subchannelAddressToString(boundSubchannelAddress));
resolve('port' in boundSubchannelAddress ? boundSubchannelAddress.port : portNum);
this.http2ServerList.push({
server: http2Server,
channelzRef: channelzRef,
});
this.trace(
'Successfully bound ' +
subchannelAddressToString(boundSubchannelAddress)
);
resolve(
'port' in boundSubchannelAddress
? boundSubchannelAddress.port
: portNum
);
http2Server.removeListener('error', onError);
});
});
})
).then((results) => {
).then(results => {
let count = 0;
for (const result of results) {
if (typeof result === 'number') {
@ -509,9 +574,14 @@ export class Server {
const http2Server = setupServer();
return new Promise<BindResult>((resolve, reject) => {
const onError = (err: Error) => {
this.trace('Failed to bind ' + subchannelAddressToString(address) + ' with error ' + err.message);
this.trace(
'Failed to bind ' +
subchannelAddressToString(address) +
' with error ' +
err.message
);
resolve(bindWildcardPort(addressList.slice(1)));
}
};
http2Server.once('error', onError);
@ -519,41 +589,44 @@ export class Server {
const boundAddress = http2Server.address() as AddressInfo;
const boundSubchannelAddress: SubchannelAddress = {
host: boundAddress.address,
port: boundAddress.port
port: boundAddress.port,
};
let channelzRef: SocketRef;
channelzRef = registerChannelzSocket(subchannelAddressToString(boundSubchannelAddress), () => {
return {
localAddress: boundSubchannelAddress,
remoteAddress: null,
security: null,
remoteName: null,
streamsStarted: 0,
streamsSucceeded: 0,
streamsFailed: 0,
messagesSent: 0,
messagesReceived: 0,
keepAlivesSent: 0,
lastLocalStreamCreatedTimestamp: null,
lastRemoteStreamCreatedTimestamp: null,
lastMessageSentTimestamp: null,
lastMessageReceivedTimestamp: null,
localFlowControlWindow: null,
remoteFlowControlWindow: null
};
}, this.channelzEnabled);
const channelzRef = registerChannelzSocket(
subchannelAddressToString(boundSubchannelAddress),
() => {
return {
localAddress: boundSubchannelAddress,
remoteAddress: null,
security: null,
remoteName: null,
streamsStarted: 0,
streamsSucceeded: 0,
streamsFailed: 0,
messagesSent: 0,
messagesReceived: 0,
keepAlivesSent: 0,
lastLocalStreamCreatedTimestamp: null,
lastRemoteStreamCreatedTimestamp: null,
lastMessageSentTimestamp: null,
lastMessageReceivedTimestamp: null,
localFlowControlWindow: null,
remoteFlowControlWindow: null,
};
},
this.channelzEnabled
);
if (this.channelzEnabled) {
this.listenerChildrenTracker.refChild(channelzRef);
}
this.http2ServerList.push({server: http2Server, channelzRef: channelzRef});
this.trace('Successfully bound ' + subchannelAddressToString(boundSubchannelAddress));
resolve(
bindSpecificPort(
addressList.slice(1),
boundAddress.port,
1
)
this.http2ServerList.push({
server: http2Server,
channelzRef: channelzRef,
});
this.trace(
'Successfully bound ' +
subchannelAddressToString(boundSubchannelAddress)
);
resolve(bindSpecificPort(addressList.slice(1), boundAddress.port, 1));
http2Server.removeListener('error', onError);
});
});
@ -568,7 +641,10 @@ export class Server {
// We only want one resolution result. Discard all future results
resolverListener.onSuccessfulResolution = () => {};
if (addressList.length === 0) {
deferredCallback(new Error(`No addresses resolved for port ${port}`), 0);
deferredCallback(
new Error(`No addresses resolved for port ${port}`),
0
);
return;
}
let bindResultPromise: Promise<BindResult>;
@ -587,7 +663,7 @@ export class Server {
bindResultPromise = bindSpecificPort(addressList, 1, 0);
}
bindResultPromise.then(
(bindResult) => {
bindResult => {
if (bindResult.count === 0) {
const errorString = `No address added out of total ${addressList.length} resolved`;
logging.log(LogVerbosity.ERROR, errorString);
@ -602,14 +678,14 @@ export class Server {
deferredCallback(null, bindResult.port);
}
},
(error) => {
error => {
const errorString = `No address added out of total ${addressList.length} resolved`;
logging.log(LogVerbosity.ERROR, errorString);
deferredCallback(new Error(errorString), 0);
}
);
},
onError: (error) => {
onError: error => {
deferredCallback(new Error(error.details), 0);
},
};
@ -621,7 +697,8 @@ export class Server {
forceShutdown(): void {
// Close the server if it is still running.
for (const {server: http2Server, channelzRef: ref} of this.http2ServerList) {
for (const { server: http2Server, channelzRef: ref } of this
.http2ServerList) {
if (http2Server.listening) {
http2Server.close(() => {
if (this.channelzEnabled) {
@ -677,7 +754,7 @@ export class Server {
if (
this.http2ServerList.length === 0 ||
this.http2ServerList.every(
({server: http2Server}) => http2Server.listening !== true
({ server: http2Server }) => http2Server.listening !== true
)
) {
throw new Error('server must be bound in order to start');
@ -712,7 +789,8 @@ export class Server {
// Close the server if necessary.
this.started = false;
for (const {server: http2Server, channelzRef: ref} of this.http2ServerList) {
for (const { server: http2Server, channelzRef: ref } of this
.http2ServerList) {
if (http2Server.listening) {
pendingChecks++;
http2Server.close(() => {
@ -743,13 +821,16 @@ export class Server {
/**
* Get the channelz reference object for this server. The returned value is
* garbage if channelz is disabled for this server.
* @returns
* @returns
*/
getChannelzRef() {
return this.channelzRef;
}
private _verifyContentType(stream: http2.ServerHttp2Stream, headers: http2.IncomingHttpHeaders): boolean {
private _verifyContentType(
stream: http2.ServerHttp2Stream,
headers: http2.IncomingHttpHeaders
): boolean {
const contentType = headers[http2.constants.HTTP2_HEADER_CONTENT_TYPE];
if (
@ -763,20 +844,22 @@ export class Server {
},
{ endStream: true }
);
return false
return false;
}
return true
return true;
}
private _retrieveHandler(headers: http2.IncomingHttpHeaders): Handler<any, any> {
const path = headers[HTTP2_HEADER_PATH] as string
private _retrieveHandler(
headers: http2.IncomingHttpHeaders
): Handler<any, any> {
const path = headers[HTTP2_HEADER_PATH] as string;
this.trace(
'Received call to method ' +
path +
' at address ' +
this.serverAddressString
path +
' at address ' +
this.serverAddressString
);
const handler = this.handlers.get(path);
@ -784,59 +867,68 @@ export class Server {
if (handler === undefined) {
this.trace(
'No handler registered for method ' +
path +
'. Sending UNIMPLEMENTED status.'
path +
'. Sending UNIMPLEMENTED status.'
);
throw getUnimplementedStatusResponse(path);
}
return handler
return handler;
}
private _respondWithError<T extends Partial<ServiceError>>(
err: T,
stream: http2.ServerHttp2Stream,
err: T,
stream: http2.ServerHttp2Stream,
channelzSessionInfo: ChannelzSessionInfo | null = null
) {
const call = new Http2ServerCallStream(stream, null!, this.options);
if (err.code === undefined) {
err.code = Status.INTERNAL;
}
if (this.channelzEnabled) {
this.callTracker.addCallFailed();
channelzSessionInfo?.streamTracker.addCallFailed()
channelzSessionInfo?.streamTracker.addCallFailed();
}
call.sendError(err);
}
private _channelzHandler(stream: http2.ServerHttp2Stream, headers: http2.IncomingHttpHeaders) {
const channelzSessionInfo = this.sessions.get(stream.session as http2.ServerHttp2Session);
private _channelzHandler(
stream: http2.ServerHttp2Stream,
headers: http2.IncomingHttpHeaders
) {
const channelzSessionInfo = this.sessions.get(
stream.session as http2.ServerHttp2Session
);
this.callTracker.addCallStarted();
channelzSessionInfo?.streamTracker.addCallStarted();
if (!this._verifyContentType(stream, headers)) {
this.callTracker.addCallFailed();
channelzSessionInfo?.streamTracker.addCallFailed();
return
return;
}
let handler: Handler<any, any>
let handler: Handler<any, any>;
try {
handler = this._retrieveHandler(headers)
handler = this._retrieveHandler(headers);
} catch (err) {
this._respondWithError({
details: getErrorMessage(err),
code: getErrorCode(err) ?? undefined
}, stream, channelzSessionInfo)
return
this._respondWithError(
{
details: getErrorMessage(err),
code: getErrorCode(err) ?? undefined,
},
stream,
channelzSessionInfo
);
return;
}
const call = new Http2ServerCallStream(stream, handler, this.options);
call.once('callEnd', (code: Status) => {
if (code === Status.OK) {
this.callTracker.addCallSucceeded();
@ -844,7 +936,7 @@ export class Server {
this.callTracker.addCallFailed();
}
});
if (channelzSessionInfo) {
call.once('streamEnd', (success: boolean) => {
if (success) {
@ -865,46 +957,58 @@ export class Server {
if (!this._runHandlerForCall(call, handler, headers)) {
this.callTracker.addCallFailed();
channelzSessionInfo?.streamTracker.addCallFailed()
channelzSessionInfo?.streamTracker.addCallFailed();
call.sendError({
code: Status.INTERNAL,
details: `Unknown handler type: ${handler.type}`
details: `Unknown handler type: ${handler.type}`,
});
}
}
private _streamHandler(stream: http2.ServerHttp2Stream, headers: http2.IncomingHttpHeaders) {
private _streamHandler(
stream: http2.ServerHttp2Stream,
headers: http2.IncomingHttpHeaders
) {
if (this._verifyContentType(stream, headers) !== true) {
return
return;
}
let handler: Handler<any, any>
let handler: Handler<any, any>;
try {
handler = this._retrieveHandler(headers)
handler = this._retrieveHandler(headers);
} catch (err) {
this._respondWithError({
details: getErrorMessage(err),
code: getErrorCode(err) ?? undefined
}, stream, null)
return
this._respondWithError(
{
details: getErrorMessage(err),
code: getErrorCode(err) ?? undefined,
},
stream,
null
);
return;
}
const call = new Http2ServerCallStream(stream, handler, this.options)
const call = new Http2ServerCallStream(stream, handler, this.options);
if (!this._runHandlerForCall(call, handler, headers)) {
call.sendError({
code: Status.INTERNAL,
details: `Unknown handler type: ${handler.type}`
details: `Unknown handler type: ${handler.type}`,
});
}
}
private _runHandlerForCall(call: Http2ServerCallStream<any, any>, handler: Handler<any, any>, headers: http2.IncomingHttpHeaders): boolean {
private _runHandlerForCall(
call: Http2ServerCallStream<any, any>,
handler: Handler<any, any>,
headers: http2.IncomingHttpHeaders
): boolean {
const metadata = call.receiveMetadata(headers);
const encoding = (metadata.get('grpc-encoding')[0] as string | undefined) ?? 'identity';
const encoding =
(metadata.get('grpc-encoding')[0] as string | undefined) ?? 'identity';
metadata.remove('grpc-encoding');
const { type } = handler
const { type } = handler;
if (type === 'unary') {
handleUnary(call, handler as UntypedUnaryHandler, metadata, encoding);
} else if (type === 'clientStream') {
@ -929,10 +1033,10 @@ export class Server {
encoding
);
} else {
return false
return false;
}
return true
return true;
}
private _setupHandlers(
@ -943,30 +1047,32 @@ export class Server {
}
const serverAddress = http2Server.address();
let serverAddressString = 'null'
let serverAddressString = 'null';
if (serverAddress) {
if (typeof serverAddress === 'string') {
serverAddressString = serverAddress
serverAddressString = serverAddress;
} else {
serverAddressString =
serverAddress.address + ':' + serverAddress.port
serverAddressString = serverAddress.address + ':' + serverAddress.port;
}
}
this.serverAddressString = serverAddressString
this.serverAddressString = serverAddressString;
const handler = this.channelzEnabled
? this._channelzHandler
: this._streamHandler
const handler = this.channelzEnabled
? this._channelzHandler
: this._streamHandler;
http2Server.on('stream', handler.bind(this))
http2Server.on('session', (session) => {
http2Server.on('stream', handler.bind(this));
http2Server.on('session', session => {
if (!this.started) {
session.destroy();
return;
}
let channelzRef: SocketRef;
channelzRef = registerChannelzSocket(session.socket.remoteAddress ?? 'unknown', this.getChannelzSessionInfoGetter(session), this.channelzEnabled);
const channelzRef = registerChannelzSocket(
session.socket.remoteAddress ?? 'unknown',
this.getChannelzSessionInfoGetter(session),
this.channelzEnabled
);
const channelzSessionInfo: ChannelzSessionInfo = {
ref: channelzRef,
@ -974,13 +1080,16 @@ export class Server {
messagesSent: 0,
messagesReceived: 0,
lastMessageSentTimestamp: null,
lastMessageReceivedTimestamp: null
lastMessageReceivedTimestamp: null,
};
this.sessions.set(session, channelzSessionInfo);
const clientAddress = session.socket.remoteAddress;
if (this.channelzEnabled) {
this.channelzTrace.addTrace('CT_INFO', 'Connection established by client ' + clientAddress);
this.channelzTrace.addTrace(
'CT_INFO',
'Connection established by client ' + clientAddress
);
this.sessionChildrenTracker.refChild(channelzRef);
}
let connectionAgeTimer: NodeJS.Timer | null = null;
@ -993,10 +1102,17 @@ export class Server {
connectionAgeTimer = setTimeout(() => {
sessionClosedByServer = true;
if (this.channelzEnabled) {
this.channelzTrace.addTrace('CT_INFO', 'Connection dropped by max connection age from ' + clientAddress);
this.channelzTrace.addTrace(
'CT_INFO',
'Connection dropped by max connection age from ' + clientAddress
);
}
try {
session.goaway(http2.constants.NGHTTP2_NO_ERROR, ~(1<<31), Buffer.from('max_age'));
session.goaway(
http2.constants.NGHTTP2_NO_ERROR,
~(1 << 31),
Buffer.from('max_age')
);
} catch (e) {
// The goaway can't be sent because the session is already closed
session.destroy();
@ -1016,14 +1132,19 @@ export class Server {
const timeoutTImer = setTimeout(() => {
sessionClosedByServer = true;
if (this.channelzEnabled) {
this.channelzTrace.addTrace('CT_INFO', 'Connection dropped by keepalive timeout from ' + clientAddress);
this.channelzTrace.addTrace(
'CT_INFO',
'Connection dropped by keepalive timeout from ' + clientAddress
);
}
session.close();
}, this.keepaliveTimeoutMs).unref?.();
try {
session.ping((err: Error | null, duration: number, payload: Buffer) => {
clearTimeout(timeoutTImer);
});
session.ping(
(err: Error | null, duration: number, payload: Buffer) => {
clearTimeout(timeoutTImer);
}
);
} catch (e) {
// The ping can't be sent because the session is already closed
session.destroy();
@ -1032,7 +1153,10 @@ export class Server {
session.on('close', () => {
if (this.channelzEnabled) {
if (!sessionClosedByServer) {
this.channelzTrace.addTrace('CT_INFO', 'Connection dropped by client ' + clientAddress);
this.channelzTrace.addTrace(
'CT_INFO',
'Connection dropped by client ' + clientAddress
);
}
this.sessionChildrenTracker.unrefChild(channelzRef);
unregisterChannelzRef(channelzRef);
@ -1060,8 +1184,8 @@ function handleUnary<RequestType, ResponseType>(
): void {
call.receiveUnaryMessage(encoding, (err, request) => {
if (err) {
call.sendError(err)
return
call.sendError(err);
return;
}
if (request === undefined || call.cancelled) {
@ -1127,8 +1251,8 @@ function handleServerStreaming<RequestType, ResponseType>(
): void {
call.receiveUnaryMessage(encoding, (err, request) => {
if (err) {
call.sendError(err)
return
call.sendError(err);
return;
}
if (request === undefined || call.cancelled) {

View File

@ -112,35 +112,71 @@ function validateName(obj: any): MethodConfigName {
}
function validateRetryPolicy(obj: any): RetryPolicy {
if (!('maxAttempts' in obj) || !Number.isInteger(obj.maxAttempts) || obj.maxAttempts < 2) {
throw new Error('Invalid method config retry policy: maxAttempts must be an integer at least 2');
if (
!('maxAttempts' in obj) ||
!Number.isInteger(obj.maxAttempts) ||
obj.maxAttempts < 2
) {
throw new Error(
'Invalid method config retry policy: maxAttempts must be an integer at least 2'
);
}
if (!('initialBackoff' in obj) || typeof obj.initialBackoff !== 'string' || !DURATION_REGEX.test(obj.initialBackoff)) {
throw new Error('Invalid method config retry policy: initialBackoff must be a string consisting of a positive integer followed by s');
if (
!('initialBackoff' in obj) ||
typeof obj.initialBackoff !== 'string' ||
!DURATION_REGEX.test(obj.initialBackoff)
) {
throw new Error(
'Invalid method config retry policy: initialBackoff must be a string consisting of a positive integer followed by s'
);
}
if (!('maxBackoff' in obj) || typeof obj.maxBackoff !== 'string' || !DURATION_REGEX.test(obj.maxBackoff)) {
throw new Error('Invalid method config retry policy: maxBackoff must be a string consisting of a positive integer followed by s');
if (
!('maxBackoff' in obj) ||
typeof obj.maxBackoff !== 'string' ||
!DURATION_REGEX.test(obj.maxBackoff)
) {
throw new Error(
'Invalid method config retry policy: maxBackoff must be a string consisting of a positive integer followed by s'
);
}
if (!('backoffMultiplier' in obj) || typeof obj.backoffMultiplier !== 'number' || obj.backoffMultiplier <= 0) {
throw new Error('Invalid method config retry policy: backoffMultiplier must be a number greater than 0');
if (
!('backoffMultiplier' in obj) ||
typeof obj.backoffMultiplier !== 'number' ||
obj.backoffMultiplier <= 0
) {
throw new Error(
'Invalid method config retry policy: backoffMultiplier must be a number greater than 0'
);
}
if (!(('retryableStatusCodes' in obj) && Array.isArray(obj.retryableStatusCodes))) {
throw new Error('Invalid method config retry policy: retryableStatusCodes is required');
if (
!('retryableStatusCodes' in obj && Array.isArray(obj.retryableStatusCodes))
) {
throw new Error(
'Invalid method config retry policy: retryableStatusCodes is required'
);
}
if (obj.retryableStatusCodes.length === 0) {
throw new Error('Invalid method config retry policy: retryableStatusCodes must be non-empty');
throw new Error(
'Invalid method config retry policy: retryableStatusCodes must be non-empty'
);
}
for (const value of obj.retryableStatusCodes) {
if (typeof value === 'number') {
if (!Object.values(Status).includes(value)) {
throw new Error('Invalid method config retry policy: retryableStatusCodes value not in status code range');
throw new Error(
'Invalid method config retry policy: retryableStatusCodes value not in status code range'
);
}
} else if (typeof value === 'string') {
if (!Object.values(Status).includes(value.toUpperCase())) {
throw new Error('Invalid method config retry policy: retryableStatusCodes value not a status code name');
throw new Error(
'Invalid method config retry policy: retryableStatusCodes value not a status code name'
);
}
} else {
throw new Error('Invalid method config retry policy: retryableStatusCodes value must be a string or number');
throw new Error(
'Invalid method config retry policy: retryableStatusCodes value must be a string or number'
);
}
}
return {
@ -148,35 +184,53 @@ function validateRetryPolicy(obj: any): RetryPolicy {
initialBackoff: obj.initialBackoff,
maxBackoff: obj.maxBackoff,
backoffMultiplier: obj.backoffMultiplier,
retryableStatusCodes: obj.retryableStatusCodes
retryableStatusCodes: obj.retryableStatusCodes,
};
}
function validateHedgingPolicy(obj: any): HedgingPolicy {
if (!('maxAttempts' in obj) || !Number.isInteger(obj.maxAttempts) || obj.maxAttempts < 2) {
throw new Error('Invalid method config hedging policy: maxAttempts must be an integer at least 2');
if (
!('maxAttempts' in obj) ||
!Number.isInteger(obj.maxAttempts) ||
obj.maxAttempts < 2
) {
throw new Error(
'Invalid method config hedging policy: maxAttempts must be an integer at least 2'
);
}
if (('hedgingDelay' in obj) && (typeof obj.hedgingDelay !== 'string' || !DURATION_REGEX.test(obj.hedgingDelay))) {
throw new Error('Invalid method config hedging policy: hedgingDelay must be a string consisting of a positive integer followed by s');
if (
'hedgingDelay' in obj &&
(typeof obj.hedgingDelay !== 'string' ||
!DURATION_REGEX.test(obj.hedgingDelay))
) {
throw new Error(
'Invalid method config hedging policy: hedgingDelay must be a string consisting of a positive integer followed by s'
);
}
if (('nonFatalStatusCodes' in obj) && Array.isArray(obj.nonFatalStatusCodes)) {
if ('nonFatalStatusCodes' in obj && Array.isArray(obj.nonFatalStatusCodes)) {
for (const value of obj.nonFatalStatusCodes) {
if (typeof value === 'number') {
if (!Object.values(Status).includes(value)) {
throw new Error('Invlid method config hedging policy: nonFatalStatusCodes value not in status code range');
throw new Error(
'Invlid method config hedging policy: nonFatalStatusCodes value not in status code range'
);
}
} else if (typeof value === 'string') {
if (!Object.values(Status).includes(value.toUpperCase())) {
throw new Error('Invlid method config hedging policy: nonFatalStatusCodes value not a status code name');
throw new Error(
'Invlid method config hedging policy: nonFatalStatusCodes value not a status code name'
);
}
} else {
throw new Error('Invlid method config hedging policy: nonFatalStatusCodes value must be a string or number');
throw new Error(
'Invlid method config hedging policy: nonFatalStatusCodes value must be a string or number'
);
}
}
}
const result: HedgingPolicy = {
maxAttempts: obj.maxAttempts
}
maxAttempts: obj.maxAttempts,
};
if (obj.hedgingDelay) {
result.hedgingDelay = obj.hedgingDelay;
}
@ -246,7 +300,9 @@ function validateMethodConfig(obj: any): MethodConfig {
}
if ('retryPolicy' in obj) {
if ('hedgingPolicy' in obj) {
throw new Error('Invalid method config: retryPolicy and hedgingPolicy cannot both be specified');
throw new Error(
'Invalid method config: retryPolicy and hedgingPolicy cannot both be specified'
);
} else {
result.retryPolicy = validateRetryPolicy(obj.retryPolicy);
}
@ -257,15 +313,28 @@ function validateMethodConfig(obj: any): MethodConfig {
}
export function validateRetryThrottling(obj: any): RetryThrottling {
if (!('maxTokens' in obj) || typeof obj.maxTokens !== 'number' || obj.maxTokens <=0 || obj.maxTokens > 1000) {
throw new Error('Invalid retryThrottling: maxTokens must be a number in (0, 1000]');
if (
!('maxTokens' in obj) ||
typeof obj.maxTokens !== 'number' ||
obj.maxTokens <= 0 ||
obj.maxTokens > 1000
) {
throw new Error(
'Invalid retryThrottling: maxTokens must be a number in (0, 1000]'
);
}
if (!('tokenRatio' in obj) || typeof obj.tokenRatio !== 'number' || obj.tokenRatio <= 0) {
throw new Error('Invalid retryThrottling: tokenRatio must be a number greater than 0');
if (
!('tokenRatio' in obj) ||
typeof obj.tokenRatio !== 'number' ||
obj.tokenRatio <= 0
) {
throw new Error(
'Invalid retryThrottling: tokenRatio must be a number greater than 0'
);
}
return {
maxTokens: +(obj.maxTokens as number).toFixed(3),
tokenRatio: +(obj.tokenRatio as number).toFixed(3)
tokenRatio: +(obj.tokenRatio as number).toFixed(3),
};
}

View File

@ -15,7 +15,7 @@
*
*/
import { isIP } from "net";
import { isIP } from 'net';
export interface TcpSubchannelAddress {
port: number;
@ -71,15 +71,18 @@ export function subchannelAddressToString(address: SubchannelAddress): string {
const DEFAULT_PORT = 443;
export function stringToSubchannelAddress(addressString: string, port?: number): SubchannelAddress {
export function stringToSubchannelAddress(
addressString: string,
port?: number
): SubchannelAddress {
if (isIP(addressString)) {
return {
host: addressString,
port: port ?? DEFAULT_PORT
port: port ?? DEFAULT_PORT,
};
} else {
return {
path: addressString
path: addressString,
};
}
}
}

View File

@ -25,16 +25,18 @@ import * as logging from './logging';
import { LogVerbosity } from './constants';
import { ServerSurfaceCall } from './server-call';
import { Deadline } from './deadline';
import { InterceptingListener, MessageContext, StatusObject, WriteCallback } from './call-interface';
import {
InterceptingListener,
MessageContext,
StatusObject,
WriteCallback,
} from './call-interface';
import { CallEventTracker, Transport } from './transport';
const TRACER_NAME = 'subchannel_call';
const {
HTTP2_HEADER_STATUS,
HTTP2_HEADER_CONTENT_TYPE,
NGHTTP2_CANCEL,
} = http2.constants;
const { HTTP2_HEADER_STATUS, HTTP2_HEADER_CONTENT_TYPE, NGHTTP2_CANCEL } =
http2.constants;
/**
* https://nodejs.org/api/errors.html#errors_class_systemerror
@ -79,7 +81,8 @@ export interface StatusObjectWithRstCode extends StatusObject {
rstCode?: number;
}
export interface SubchannelCallInterceptingListener extends InterceptingListener {
export interface SubchannelCallInterceptingListener
extends InterceptingListener {
onReceiveStatus(status: StatusObjectWithRstCode): void;
}
@ -235,7 +238,10 @@ export class Http2SubchannelCall implements SubchannelCall {
* "Internal server error" message. */
details = `Received RST_STREAM with code ${http2Stream.rstCode} (Internal server error)`;
} else {
if (this.internalError.code === 'ECONNRESET' || this.internalError.code === 'ETIMEDOUT') {
if (
this.internalError.code === 'ECONNRESET' ||
this.internalError.code === 'ETIMEDOUT'
) {
code = Status.UNAVAILABLE;
details = this.internalError.message;
} else {
@ -255,7 +261,12 @@ export class Http2SubchannelCall implements SubchannelCall {
// This is OK, because status codes emitted here correspond to more
// catastrophic issues that prevent us from receiving trailers in the
// first place.
this.endCall({ code, details, metadata: new Metadata(), rstCode: http2Stream.rstCode });
this.endCall({
code,
details,
metadata: new Metadata(),
rstCode: http2Stream.rstCode,
});
});
});
http2Stream.on('error', (err: SystemError) => {
@ -488,7 +499,7 @@ export class Http2SubchannelCall implements SubchannelCall {
return;
}
/* Only resume reading from the http2Stream if we don't have any pending
* messages to emit */
* messages to emit */
this.http2Stream.resume();
}
@ -496,7 +507,9 @@ export class Http2SubchannelCall implements SubchannelCall {
this.trace('write() called with message of length ' + message.length);
const cb: WriteCallback = (error?: Error | null) => {
let code: Status = Status.UNAVAILABLE;
if ((error as NodeJS.ErrnoException)?.code === 'ERR_STREAM_WRITE_AFTER_END') {
if (
(error as NodeJS.ErrnoException)?.code === 'ERR_STREAM_WRITE_AFTER_END'
) {
code = Status.INTERNAL;
}
if (error) {
@ -512,7 +525,7 @@ export class Http2SubchannelCall implements SubchannelCall {
this.endCall({
code: Status.UNAVAILABLE,
details: `Write failed with error ${(error as Error).message}`,
metadata: new Metadata()
metadata: new Metadata(),
});
}
}

View File

@ -15,9 +15,9 @@
*
*/
import { SubchannelRef } from "./channelz";
import { ConnectivityState } from "./connectivity-state";
import { Subchannel } from "./subchannel";
import { SubchannelRef } from './channelz';
import { ConnectivityState } from './connectivity-state';
import { Subchannel } from './subchannel';
export type ConnectivityStateListener = (
subchannel: SubchannelInterface,
@ -30,7 +30,7 @@ export type ConnectivityStateListener = (
* This is an interface for load balancing policies to use to interact with
* subchannels. This allows load balancing policies to wrap and unwrap
* subchannels.
*
*
* Any load balancing policy that wraps subchannels must unwrap the subchannel
* in the picker, so that other load balancing policies consistently have
* access to their own wrapper objects.
@ -84,4 +84,4 @@ export abstract class BaseSubchannelWrapper implements SubchannelInterface {
getRealSubchannel(): Subchannel {
return this.child.getRealSubchannel();
}
}
}

View File

@ -68,7 +68,7 @@ export class SubchannelPool {
const subchannelObjArray = this.pool[channelTarget];
const refedSubchannels = subchannelObjArray.filter(
(value) => !value.subchannel.unrefIfOneRef()
value => !value.subchannel.unrefIfOneRef()
);
if (refedSubchannels.length > 0) {

View File

@ -27,7 +27,15 @@ import {
SubchannelAddress,
subchannelAddressToString,
} from './subchannel-address';
import { SubchannelRef, ChannelzTrace, ChannelzChildrenTracker, SubchannelInfo, registerChannelzSubchannel, ChannelzCallTracker, unregisterChannelzRef } from './channelz';
import {
SubchannelRef,
ChannelzTrace,
ChannelzChildrenTracker,
SubchannelInfo,
registerChannelzSubchannel,
ChannelzCallTracker,
unregisterChannelzRef,
} from './channelz';
import { ConnectivityStateListener } from './subchannel-interface';
import { SubchannelCallInterceptingListener } from './subchannel-call';
import { SubchannelCall } from './subchannel-call';
@ -117,11 +125,18 @@ export class Subchannel {
this.channelzEnabled = false;
}
this.channelzTrace = new ChannelzTrace();
this.channelzRef = registerChannelzSubchannel(this.subchannelAddressString, () => this.getChannelzInfo(), this.channelzEnabled);
this.channelzRef = registerChannelzSubchannel(
this.subchannelAddressString,
() => this.getChannelzInfo(),
this.channelzEnabled
);
if (this.channelzEnabled) {
this.channelzTrace.addTrace('CT_INFO', 'Subchannel created');
}
this.trace('Subchannel constructed with options ' + JSON.stringify(options, undefined, 2));
this.trace(
'Subchannel constructed with options ' +
JSON.stringify(options, undefined, 2)
);
}
private getChannelzInfo(): SubchannelInfo {
@ -130,16 +145,34 @@ export class Subchannel {
trace: this.channelzTrace,
callTracker: this.callTracker,
children: this.childrenTracker.getChildLists(),
target: this.subchannelAddressString
target: this.subchannelAddressString,
};
}
private trace(text: string): void {
logging.trace(LogVerbosity.DEBUG, TRACER_NAME, '(' + this.channelzRef.id + ') ' + this.subchannelAddressString + ' ' + text);
logging.trace(
LogVerbosity.DEBUG,
TRACER_NAME,
'(' +
this.channelzRef.id +
') ' +
this.subchannelAddressString +
' ' +
text
);
}
private refTrace(text: string): void {
logging.trace(LogVerbosity.DEBUG, 'subchannel_refcount', '(' + this.channelzRef.id + ') ' + this.subchannelAddressString + ' ' + text);
logging.trace(
LogVerbosity.DEBUG,
'subchannel_refcount',
'(' +
this.channelzRef.id +
') ' +
this.subchannelAddressString +
' ' +
text
);
}
private handleBackoffTimer() {
@ -171,36 +204,52 @@ export class Subchannel {
private startConnectingInternal() {
let options = this.options;
if (options['grpc.keepalive_time_ms']) {
const adjustedKeepaliveTime = Math.min(this.keepaliveTime, KEEPALIVE_MAX_TIME_MS);
options = {...options, 'grpc.keepalive_time_ms': adjustedKeepaliveTime};
const adjustedKeepaliveTime = Math.min(
this.keepaliveTime,
KEEPALIVE_MAX_TIME_MS
);
options = { ...options, 'grpc.keepalive_time_ms': adjustedKeepaliveTime };
}
this.connector.connect(this.subchannelAddress, this.credentials, options).then(
transport => {
if (this.transitionToState([ConnectivityState.CONNECTING], ConnectivityState.READY)) {
this.transport = transport;
if (this.channelzEnabled) {
this.childrenTracker.refChild(transport.getChannelzRef());
}
transport.addDisconnectListener((tooManyPings) => {
this.transitionToState([ConnectivityState.READY], ConnectivityState.IDLE);
if (tooManyPings && this.keepaliveTime > 0) {
this.keepaliveTime *= 2;
logging.log(
LogVerbosity.ERROR,
`Connection to ${uriToString(this.channelTarget)} at ${
this.subchannelAddressString
} rejected by server because of excess pings. Increasing ping interval to ${
this.keepaliveTime
} ms`
);
this.connector
.connect(this.subchannelAddress, this.credentials, options)
.then(
transport => {
if (
this.transitionToState(
[ConnectivityState.CONNECTING],
ConnectivityState.READY
)
) {
this.transport = transport;
if (this.channelzEnabled) {
this.childrenTracker.refChild(transport.getChannelzRef());
}
});
transport.addDisconnectListener(tooManyPings => {
this.transitionToState(
[ConnectivityState.READY],
ConnectivityState.IDLE
);
if (tooManyPings && this.keepaliveTime > 0) {
this.keepaliveTime *= 2;
logging.log(
LogVerbosity.ERROR,
`Connection to ${uriToString(this.channelTarget)} at ${
this.subchannelAddressString
} rejected by server because of excess pings. Increasing ping interval to ${
this.keepaliveTime
} ms`
);
}
});
}
},
error => {
this.transitionToState(
[ConnectivityState.CONNECTING],
ConnectivityState.TRANSIENT_FAILURE
);
}
},
error => {
this.transitionToState([ConnectivityState.CONNECTING], ConnectivityState.TRANSIENT_FAILURE);
}
)
);
}
/**
@ -223,7 +272,12 @@ export class Subchannel {
ConnectivityState[newState]
);
if (this.channelzEnabled) {
this.channelzTrace.addTrace('CT_INFO', ConnectivityState[this.connectivityState] + ' -> ' + ConnectivityState[newState]);
this.channelzTrace.addTrace(
'CT_INFO',
ConnectivityState[this.connectivityState] +
' -> ' +
ConnectivityState[newState]
);
}
const previousState = this.connectivityState;
this.connectivityState = newState;
@ -268,22 +322,12 @@ export class Subchannel {
}
ref() {
this.refTrace(
'refcount ' +
this.refcount +
' -> ' +
(this.refcount + 1)
);
this.refTrace('refcount ' + this.refcount + ' -> ' + (this.refcount + 1));
this.refcount += 1;
}
unref() {
this.refTrace(
'refcount ' +
this.refcount +
' -> ' +
(this.refcount - 1)
);
this.refTrace('refcount ' + this.refcount + ' -> ' + (this.refcount - 1));
this.refcount -= 1;
if (this.refcount === 0) {
if (this.channelzEnabled) {
@ -309,7 +353,12 @@ export class Subchannel {
return false;
}
createCall(metadata: Metadata, host: string, method: string, listener: SubchannelCallInterceptingListener): SubchannelCall {
createCall(
metadata: Metadata,
host: string,
method: string,
listener: SubchannelCallInterceptingListener
): SubchannelCall {
if (!this.transport) {
throw new Error('Cannot create call, subchannel not READY');
}
@ -324,12 +373,18 @@ export class Subchannel {
} else {
this.callTracker.addCallFailed();
}
}
}
},
};
} else {
statsTracker = {};
}
return this.transport.createCall(metadata, host, method, listener, statsTracker);
return this.transport.createCall(
metadata,
host,
method,
listener,
statsTracker
);
}
/**
@ -341,9 +396,9 @@ export class Subchannel {
startConnecting() {
process.nextTick(() => {
/* First, try to transition from IDLE to connecting. If that doesn't happen
* because the state is not currently IDLE, check if it is
* TRANSIENT_FAILURE, and if so indicate that it should go back to
* connecting after the backoff timer ends. Otherwise do nothing */
* because the state is not currently IDLE, check if it is
* TRANSIENT_FAILURE, and if so indicate that it should go back to
* connecting after the backoff timer ends. Otherwise do nothing */
if (
!this.transitionToState(
[ConnectivityState.IDLE],

View File

@ -16,19 +16,40 @@
*/
import * as http2 from 'http2';
import { checkServerIdentity, CipherNameAndProtocol, ConnectionOptions, PeerCertificate, TLSSocket } from 'tls';
import {
checkServerIdentity,
CipherNameAndProtocol,
ConnectionOptions,
PeerCertificate,
TLSSocket,
} from 'tls';
import { StatusObject } from './call-interface';
import { ChannelCredentials } from './channel-credentials';
import { ChannelOptions } from './channel-options';
import { ChannelzCallTracker, registerChannelzSocket, SocketInfo, SocketRef, TlsInfo, unregisterChannelzRef } from './channelz';
import {
ChannelzCallTracker,
registerChannelzSocket,
SocketInfo,
SocketRef,
TlsInfo,
unregisterChannelzRef,
} from './channelz';
import { LogVerbosity } from './constants';
import { getProxiedConnection, ProxyConnectionResult } from './http_proxy';
import * as logging from './logging';
import { getDefaultAuthority } from './resolver';
import { stringToSubchannelAddress, SubchannelAddress, subchannelAddressToString } from './subchannel-address';
import {
stringToSubchannelAddress,
SubchannelAddress,
subchannelAddressToString,
} from './subchannel-address';
import { GrpcUri, parseUri, splitHostPort, uriToString } from './uri-parser';
import * as net from 'net';
import { Http2SubchannelCall, SubchannelCall, SubchannelCallInterceptingListener } from './subchannel-call';
import {
Http2SubchannelCall,
SubchannelCall,
SubchannelCallInterceptingListener,
} from './subchannel-call';
import { Metadata } from './metadata';
import { getNextCallNumber } from './call-number';
@ -66,7 +87,13 @@ export interface TransportDisconnectListener {
export interface Transport {
getChannelzRef(): SocketRef;
getPeerName(): string;
createCall(metadata: Metadata, host: string, method: string, listener: SubchannelCallInterceptingListener, subchannelCallStatsTracker: Partial<CallEventTracker>): SubchannelCall;
createCall(
metadata: Metadata,
host: string,
method: string,
listener: SubchannelCallInterceptingListener,
subchannelCallStatsTracker: Partial<CallEventTracker>
): SubchannelCall;
addDisconnectListener(listener: TransportDisconnectListener): void;
shutdown(): void;
}
@ -77,7 +104,7 @@ class Http2Transport implements Transport {
/**
* The amount of time in between sending pings
*/
private keepaliveTimeMs: number = -1;
private keepaliveTimeMs = -1;
/**
* The amount of time to wait for an acknowledgement after sending a ping
*/
@ -131,9 +158,9 @@ class Http2Transport implements Transport {
`grpc-node-js/${clientVersion}`,
options['grpc.secondary_user_agent'],
]
.filter((e) => e)
.filter(e => e)
.join(' '); // remove falsey values first
if ('grpc.keepalive_time_ms' in options) {
this.keepaliveTimeMs = options['grpc.keepalive_time_ms']!;
}
@ -157,36 +184,37 @@ class Http2Transport implements Transport {
if (options['grpc.enable_channelz'] === 0) {
this.channelzEnabled = false;
}
this.channelzRef = registerChannelzSocket(this.subchannelAddressString, () => this.getChannelzInfo(), this.channelzEnabled);
this.channelzRef = registerChannelzSocket(
this.subchannelAddressString,
() => this.getChannelzInfo(),
this.channelzEnabled
);
session.once('close', () => {
this.trace('session closed');
this.stopKeepalivePings();
this.handleDisconnect();
});
session.once('goaway', (errorCode: number, lastStreamID: number, opaqueData: Buffer) => {
let tooManyPings = false;
/* See the last paragraph of
* https://github.com/grpc/proposal/blob/master/A8-client-side-keepalive.md#basic-keepalive */
if (
errorCode === http2.constants.NGHTTP2_ENHANCE_YOUR_CALM &&
opaqueData.equals(tooManyPingsData)
) {
tooManyPings = true;
session.once(
'goaway',
(errorCode: number, lastStreamID: number, opaqueData: Buffer) => {
let tooManyPings = false;
/* See the last paragraph of
* https://github.com/grpc/proposal/blob/master/A8-client-side-keepalive.md#basic-keepalive */
if (
errorCode === http2.constants.NGHTTP2_ENHANCE_YOUR_CALM &&
opaqueData.equals(tooManyPingsData)
) {
tooManyPings = true;
}
this.trace('connection closed by GOAWAY with code ' + errorCode);
this.reportDisconnectToOwner(tooManyPings);
}
this.trace(
'connection closed by GOAWAY with code ' +
errorCode
);
this.reportDisconnectToOwner(tooManyPings);
});
);
session.once('error', error => {
/* Do nothing here. Any error should also trigger a close event, which is
* where we want to handle that. */
this.trace(
'connection closed with error ' +
(error as Error).message
);
this.trace('connection closed with error ' + (error as Error).message);
});
if (logging.isTracerEnabled(TRACER_NAME)) {
session.on('remoteSettings', (settings: http2.Settings) => {
@ -210,19 +238,34 @@ class Http2Transport implements Transport {
private getChannelzInfo(): SocketInfo {
const sessionSocket = this.session.socket;
const remoteAddress = sessionSocket.remoteAddress ? stringToSubchannelAddress(sessionSocket.remoteAddress, sessionSocket.remotePort) : null;
const localAddress = sessionSocket.localAddress ? stringToSubchannelAddress(sessionSocket.localAddress, sessionSocket.localPort) : null;
const remoteAddress = sessionSocket.remoteAddress
? stringToSubchannelAddress(
sessionSocket.remoteAddress,
sessionSocket.remotePort
)
: null;
const localAddress = sessionSocket.localAddress
? stringToSubchannelAddress(
sessionSocket.localAddress,
sessionSocket.localPort
)
: null;
let tlsInfo: TlsInfo | null;
if (this.session.encrypted) {
const tlsSocket: TLSSocket = sessionSocket as TLSSocket;
const cipherInfo: CipherNameAndProtocol & {standardName?: string} = tlsSocket.getCipher();
const cipherInfo: CipherNameAndProtocol & { standardName?: string } =
tlsSocket.getCipher();
const certificate = tlsSocket.getCertificate();
const peerCertificate = tlsSocket.getPeerCertificate();
tlsInfo = {
cipherSuiteStandardName: cipherInfo.standardName ?? null,
cipherSuiteOtherName: cipherInfo.standardName ? null : cipherInfo.name,
localCertificate: (certificate && 'raw' in certificate) ? certificate.raw : null,
remoteCertificate: (peerCertificate && 'raw' in peerCertificate) ? peerCertificate.raw : null
localCertificate:
certificate && 'raw' in certificate ? certificate.raw : null,
remoteCertificate:
peerCertificate && 'raw' in peerCertificate
? peerCertificate.raw
: null,
};
} else {
tlsInfo = null;
@ -238,30 +281,67 @@ class Http2Transport implements Transport {
messagesSent: this.messagesSent,
messagesReceived: this.messagesReceived,
keepAlivesSent: this.keepalivesSent,
lastLocalStreamCreatedTimestamp: this.streamTracker.lastCallStartedTimestamp,
lastLocalStreamCreatedTimestamp:
this.streamTracker.lastCallStartedTimestamp,
lastRemoteStreamCreatedTimestamp: null,
lastMessageSentTimestamp: this.lastMessageSentTimestamp,
lastMessageReceivedTimestamp: this.lastMessageReceivedTimestamp,
localFlowControlWindow: this.session.state.localWindowSize ?? null,
remoteFlowControlWindow: this.session.state.remoteWindowSize ?? null
remoteFlowControlWindow: this.session.state.remoteWindowSize ?? null,
};
return socketInfo;
}
private trace(text: string): void {
logging.trace(LogVerbosity.DEBUG, TRACER_NAME, '(' + this.channelzRef.id + ') ' + this.subchannelAddressString + ' ' + text);
logging.trace(
LogVerbosity.DEBUG,
TRACER_NAME,
'(' +
this.channelzRef.id +
') ' +
this.subchannelAddressString +
' ' +
text
);
}
private keepaliveTrace(text: string): void {
logging.trace(LogVerbosity.DEBUG, 'keepalive', '(' + this.channelzRef.id + ') ' + this.subchannelAddressString + ' ' + text);
logging.trace(
LogVerbosity.DEBUG,
'keepalive',
'(' +
this.channelzRef.id +
') ' +
this.subchannelAddressString +
' ' +
text
);
}
private flowControlTrace(text: string): void {
logging.trace(LogVerbosity.DEBUG, FLOW_CONTROL_TRACER_NAME, '(' + this.channelzRef.id + ') ' + this.subchannelAddressString + ' ' + text);
logging.trace(
LogVerbosity.DEBUG,
FLOW_CONTROL_TRACER_NAME,
'(' +
this.channelzRef.id +
') ' +
this.subchannelAddressString +
' ' +
text
);
}
private internalsTrace(text: string): void {
logging.trace(LogVerbosity.DEBUG, 'transport_internals', '(' + this.channelzRef.id + ') ' + this.subchannelAddressString + ' ' + text);
logging.trace(
LogVerbosity.DEBUG,
'transport_internals',
'(' +
this.channelzRef.id +
') ' +
this.subchannelAddressString +
' ' +
text
);
}
/**
@ -271,7 +351,7 @@ class Http2Transport implements Transport {
* @param tooManyPings If true, this was triggered by a GOAWAY with data
* indicating that the session was closed becaues the client sent too many
* pings.
* @returns
* @returns
*/
private reportDisconnectToOwner(tooManyPings: boolean) {
if (this.disconnectHandled) {
@ -311,7 +391,9 @@ class Http2Transport implements Transport {
if (this.channelzEnabled) {
this.keepalivesSent += 1;
}
this.keepaliveTrace('Sending ping with timeout ' + this.keepaliveTimeoutMs + 'ms');
this.keepaliveTrace(
'Sending ping with timeout ' + this.keepaliveTimeoutMs + 'ms'
);
if (!this.keepaliveTimeoutId) {
this.keepaliveTimeoutId = setTimeout(() => {
this.keepaliveTrace('Ping timeout passed without response');
@ -375,7 +457,13 @@ class Http2Transport implements Transport {
this.activeCalls.add(call);
}
createCall(metadata: Metadata, host: string, method: string, listener: SubchannelCallInterceptingListener, subchannelCallStatsTracker: Partial<CallEventTracker>): Http2SubchannelCall {
createCall(
metadata: Metadata,
host: string,
method: string,
listener: SubchannelCallInterceptingListener,
subchannelCallStatsTracker: Partial<CallEventTracker>
): Http2SubchannelCall {
const headers = metadata.toHttp2Headers();
headers[HTTP2_HEADER_AUTHORITY] = host;
headers[HTTP2_HEADER_USER_AGENT] = this.userAgent;
@ -405,13 +493,15 @@ class Http2Transport implements Transport {
this.session.state.remoteWindowSize
);
this.internalsTrace(
'session.closed=' +
this.session.closed +
' session.destroyed=' +
this.session.destroyed +
' session.socket.destroyed=' +
this.session.socket.destroyed);
'session.closed=' +
this.session.closed +
' session.destroyed=' +
this.session.destroyed +
' session.socket.destroyed=' +
this.session.socket.destroyed
);
let eventTracker: CallEventTracker;
// eslint-disable-next-line prefer-const
let call: Http2SubchannelCall;
if (this.channelzEnabled) {
this.streamTracker.addCallStarted();
@ -437,8 +527,8 @@ class Http2Transport implements Transport {
this.streamTracker.addCallFailed();
}
subchannelCallStatsTracker.onStreamEnd?.(success);
}
}
},
};
} else {
eventTracker = {
addMessageSent: () => {
@ -447,16 +537,22 @@ class Http2Transport implements Transport {
addMessageReceived: () => {
subchannelCallStatsTracker.addMessageReceived?.();
},
onCallEnd: (status) => {
onCallEnd: status => {
subchannelCallStatsTracker.onCallEnd?.(status);
this.removeActiveCall(call);
},
onStreamEnd: (success) => {
onStreamEnd: success => {
subchannelCallStatsTracker.onStreamEnd?.(success);
}
}
},
};
}
call = new Http2SubchannelCall(http2Stream, eventTracker, listener, this, getNextCallNumber());
call = new Http2SubchannelCall(
http2Stream,
eventTracker,
listener,
this,
getNextCallNumber()
);
this.addActiveCall(call);
return call;
}
@ -476,7 +572,11 @@ class Http2Transport implements Transport {
}
export interface SubchannelConnector {
connect(address: SubchannelAddress, credentials: ChannelCredentials, options: ChannelOptions): Promise<Transport>;
connect(
address: SubchannelAddress,
credentials: ChannelCredentials,
options: ChannelOptions
): Promise<Transport>;
shutdown(): void;
}
@ -484,10 +584,13 @@ export class Http2SubchannelConnector implements SubchannelConnector {
private session: http2.ClientHttp2Session | null = null;
private isShutdown = false;
constructor(private channelTarget: GrpcUri) {}
private trace(text: string) {
}
private createSession(address: SubchannelAddress, credentials: ChannelCredentials, options: ChannelOptions, proxyConnectionResult: ProxyConnectionResult): Promise<Http2Transport> {
private trace(text: string) {}
private createSession(
address: SubchannelAddress,
credentials: ChannelCredentials,
options: ChannelOptions,
proxyConnectionResult: ProxyConnectionResult
): Promise<Http2Transport> {
if (this.isShutdown) {
return Promise.reject();
}
@ -495,10 +598,15 @@ export class Http2SubchannelConnector implements SubchannelConnector {
let remoteName: string | null;
if (proxyConnectionResult.realTarget) {
remoteName = uriToString(proxyConnectionResult.realTarget);
this.trace('creating HTTP/2 session through proxy to ' + uriToString(proxyConnectionResult.realTarget));
this.trace(
'creating HTTP/2 session through proxy to ' +
uriToString(proxyConnectionResult.realTarget)
);
} else {
remoteName = null;
this.trace('creating HTTP/2 session to ' + subchannelAddressToString(address));
this.trace(
'creating HTTP/2 session to ' + subchannelAddressToString(address)
);
}
const targetAuthority = getDefaultAuthority(
proxyConnectionResult.realTarget ?? this.channelTarget
@ -507,9 +615,8 @@ export class Http2SubchannelConnector implements SubchannelConnector {
credentials._getConnectionOptions() || {};
connectionOptions.maxSendHeaderBlockLength = Number.MAX_SAFE_INTEGER;
if ('grpc-node.max_session_memory' in options) {
connectionOptions.maxSessionMemory = options[
'grpc-node.max_session_memory'
];
connectionOptions.maxSessionMemory =
options['grpc-node.max_session_memory'];
} else {
/* By default, set a very large max session memory limit, to effectively
* disable enforcement of the limit. Some testing indicates that Node's
@ -524,9 +631,8 @@ export class Http2SubchannelConnector implements SubchannelConnector {
// to override the target hostname when checking server identity.
// This option is used for testing only.
if (options['grpc.ssl_target_name_override']) {
const sslTargetNameOverride = options[
'grpc.ssl_target_name_override'
]!;
const sslTargetNameOverride =
options['grpc.ssl_target_name_override']!;
connectionOptions.checkServerIdentity = (
host: string,
cert: PeerCertificate
@ -565,12 +671,12 @@ export class Http2SubchannelConnector implements SubchannelConnector {
}
};
}
connectionOptions = {
...connectionOptions,
...address,
};
/* http2.connect uses the options here:
* https://github.com/nodejs/node/blob/70c32a6d190e2b5d7b9ff9d5b6a459d14e8b7d59/lib/internal/http2/core.js#L3028-L3036
* The spread operator overides earlier values with later ones, so any port
@ -604,11 +710,15 @@ export class Http2SubchannelConnector implements SubchannelConnector {
reject();
});
session.once('error', error => {
this.trace('connection failed with error ' + (error as Error).message)
this.trace('connection failed with error ' + (error as Error).message);
});
});
}
connect(address: SubchannelAddress, credentials: ChannelCredentials, options: ChannelOptions): Promise<Http2Transport> {
connect(
address: SubchannelAddress,
credentials: ChannelCredentials,
options: ChannelOptions
): Promise<Http2Transport> {
if (this.isShutdown) {
return Promise.reject();
}
@ -625,9 +735,7 @@ export class Http2SubchannelConnector implements SubchannelConnector {
// to override the target hostname when checking server identity.
// This option is used for testing only.
if (options['grpc.ssl_target_name_override']) {
const sslTargetNameOverride = options[
'grpc.ssl_target_name_override'
]!;
const sslTargetNameOverride = options['grpc.ssl_target_name_override']!;
connectionOptions.checkServerIdentity = (
host: string,
cert: PeerCertificate
@ -652,11 +760,7 @@ export class Http2SubchannelConnector implements SubchannelConnector {
}
}
return getProxiedConnection(
address,
options,
connectionOptions
).then(
return getProxiedConnection(address, options, connectionOptions).then(
result => this.createSession(address, credentials, options, result)
);
}
@ -666,4 +770,4 @@ export class Http2SubchannelConnector implements SubchannelConnector {
this.session?.close();
this.session = null;
}
}
}

View File

@ -0,0 +1,93 @@
/*
* Copyright 2019 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 * as assert from 'assert';
const toCall = new Map<() => void, number>();
const afterCallsQueue: Array<() => void> = [];
/**
* Assert that the given function doesn't throw an error, and then return
* its value.
* @param fn The function to evaluate.
*/
export function noThrowAndReturn<T>(fn: () => T): T {
try {
return fn();
} catch (e) {
assert.throws(() => {
throw e;
});
throw e; // for type safety only
}
}
/**
* Helper function that returns true when every function wrapped with
* mustCall has been called.
*/
function mustCallsSatisfied(): boolean {
let result = true;
toCall.forEach(value => {
result = result && value === 0;
});
return result;
}
export function clearMustCalls(): void {
afterCallsQueue.length = 0;
}
/**
* Wraps a function to keep track of whether it was called or not.
* @param fn The function to wrap.
*/
// tslint:disable:no-any
export function mustCall<T>(fn: (...args: any[]) => T): (...args: any[]) => T {
const existingValue = toCall.get(fn);
if (existingValue !== undefined) {
toCall.set(fn, existingValue + 1);
} else {
toCall.set(fn, 1);
}
return (...args: any[]) => {
const result = fn(...args);
const existingValue = toCall.get(fn);
if (existingValue !== undefined) {
toCall.set(fn, existingValue - 1);
}
if (mustCallsSatisfied()) {
afterCallsQueue.forEach(fn => fn());
afterCallsQueue.length = 0;
}
return result;
};
}
/**
* Calls the given function when every function that was wrapped with
* mustCall has been called.
* @param fn The function to call once all mustCall-wrapped functions have
* been called.
*/
export function afterMustCallsSatisfied(fn: () => void): void {
if (!mustCallsSatisfied()) {
afterCallsQueue.push(fn);
} else {
fn();
}
}

View File

@ -16,7 +16,7 @@
*/
import * as loader from '@grpc/proto-loader';
import * as assert from 'assert';
import * as assert2 from './assert2';
import { GrpcObject, loadPackageDefinition } from '../src/make-client';
@ -32,86 +32,9 @@ export function mockFunction(): never {
throw new Error('Not implemented');
}
export namespace assert2 {
const toCall = new Map<() => void, number>();
const afterCallsQueue: Array<() => void> = [];
/**
* Assert that the given function doesn't throw an error, and then return
* its value.
* @param fn The function to evaluate.
*/
export function noThrowAndReturn<T>(fn: () => T): T {
try {
return fn();
} catch (e) {
assert.throws(() => {
throw e;
});
throw e; // for type safety only
}
}
/**
* Helper function that returns true when every function wrapped with
* mustCall has been called.
*/
function mustCallsSatisfied(): boolean {
let result = true;
toCall.forEach(value => {
result = result && value === 0;
});
return result;
}
export function clearMustCalls(): void {
afterCallsQueue.length = 0;
}
/**
* Wraps a function to keep track of whether it was called or not.
* @param fn The function to wrap.
*/
// tslint:disable:no-any
export function mustCall<T>(
fn: (...args: any[]) => T
): (...args: any[]) => T {
const existingValue = toCall.get(fn);
if (existingValue !== undefined) {
toCall.set(fn, existingValue + 1);
} else {
toCall.set(fn, 1);
}
return (...args: any[]) => {
const result = fn(...args);
const existingValue = toCall.get(fn);
if (existingValue !== undefined) {
toCall.set(fn, existingValue - 1);
}
if (mustCallsSatisfied()) {
afterCallsQueue.forEach(fn => fn());
afterCallsQueue.length = 0;
}
return result;
};
}
/**
* Calls the given function when every function that was wrapped with
* mustCall has been called.
* @param fn The function to call once all mustCall-wrapped functions have
* been called.
*/
export function afterMustCallsSatisfied(fn: () => void): void {
if (!mustCallsSatisfied()) {
afterCallsQueue.push(fn);
} else {
fn();
}
}
}
export function loadProtoFile(file: string): GrpcObject {
const packageDefinition = loader.loadSync(file, protoLoaderOptions);
return loadPackageDefinition(packageDefinition);
}
export { assert2 };

View File

@ -86,21 +86,16 @@ describe('CallCredentials', () => {
const callCredentials = CallCredentials.createFromMetadataGenerator(
generateFromServiceURL
);
let metadata: Metadata;
try {
metadata = await callCredentials.generateMetadata({
service_url: 'foo',
});
} catch (err) {
throw err;
}
const metadata: Metadata = await callCredentials.generateMetadata({
service_url: 'foo',
});
assert.deepStrictEqual(metadata.get('service_url'), ['foo']);
});
it('should emit an error if the associated metadataGenerator does', async () => {
const callCredentials = CallCredentials.createFromMetadataGenerator(
generateWithError
);
const callCredentials =
CallCredentials.createFromMetadataGenerator(generateWithError);
let metadata: Metadata | null = null;
try {
metadata = await callCredentials.generateMetadata({ service_url: '' });
@ -112,14 +107,10 @@ describe('CallCredentials', () => {
it('should combine metadata from multiple generators', async () => {
const [callCreds1, callCreds2, callCreds3, callCreds4] = [
50,
100,
150,
200,
50, 100, 150, 200,
].map(ms => {
const generator: CallMetadataGenerator = makeAfterMsElapsedGenerator(
ms
);
const generator: CallMetadataGenerator =
makeAfterMsElapsedGenerator(ms);
return CallCredentials.createFromMetadataGenerator(generator);
});
const testCases = [
@ -147,12 +138,10 @@ describe('CallCredentials', () => {
await Promise.all(
testCases.map(async testCase => {
const { credentials, expected } = testCase;
let metadata: Metadata;
try {
metadata = await credentials.generateMetadata({ service_url: '' });
} catch (err) {
throw err;
}
const metadata: Metadata = await credentials.generateMetadata({
service_url: '',
});
assert.deepStrictEqual(metadata.get('msElapsed'), expected);
})
);

View File

@ -29,7 +29,7 @@ function multiDone(done: () => void, target: number) {
if (count >= target) {
done();
}
}
};
}
describe('Call propagation', () => {
@ -39,33 +39,48 @@ describe('Call propagation', () => {
let proxyServer: grpc.Server;
let proxyClient: ServiceClient;
before((done) => {
Client = loadProtoFile(__dirname + '/fixtures/test_service.proto').TestService as ServiceClientConstructor;
before(done => {
Client = loadProtoFile(__dirname + '/fixtures/test_service.proto')
.TestService as ServiceClientConstructor;
server = new grpc.Server();
server.addService(Client.service, {
unary: () => {},
clientStream: () => {},
serverStream: () => {},
bidiStream: () => {}
bidiStream: () => {},
});
proxyServer = new grpc.Server();
server.bindAsync('localhost:0', grpc.ServerCredentials.createInsecure(), (error, port) => {
if (error) {
done(error);
return;
}
server.start();
client = new Client(`localhost:${port}`, grpc.credentials.createInsecure());
proxyServer.bindAsync('localhost:0', grpc.ServerCredentials.createInsecure(), (error, proxyPort) => {
server.bindAsync(
'localhost:0',
grpc.ServerCredentials.createInsecure(),
(error, port) => {
if (error) {
done(error);
return;
}
proxyServer.start();
proxyClient = new Client(`localhost:${proxyPort}`, grpc.credentials.createInsecure());
done();
});
});
server.start();
client = new Client(
`localhost:${port}`,
grpc.credentials.createInsecure()
);
proxyServer.bindAsync(
'localhost:0',
grpc.ServerCredentials.createInsecure(),
(error, proxyPort) => {
if (error) {
done(error);
return;
}
proxyServer.start();
proxyClient = new Client(
`localhost:${proxyPort}`,
grpc.credentials.createInsecure()
);
done();
}
);
}
);
});
afterEach(() => {
proxyServer.removeService(Client.service);
@ -75,63 +90,84 @@ describe('Call propagation', () => {
proxyServer.forceShutdown();
});
describe('Cancellation', () => {
it('should work with unary requests', (done) => {
it('should work with unary requests', done => {
done = multiDone(done, 2);
// eslint-disable-next-line prefer-const
let call: grpc.ClientUnaryCall;
proxyServer.addService(Client.service, {
unary: (parent: grpc.ServerUnaryCall<any, any>, callback: grpc.sendUnaryData<any>) => {
client.unary(parent.request, {parent: parent}, (error: grpc.ServiceError, value: unknown) => {
callback(error, value);
assert(error);
assert.strictEqual(error.code, grpc.status.CANCELLED);
done();
});
unary: (
parent: grpc.ServerUnaryCall<any, any>,
callback: grpc.sendUnaryData<any>
) => {
client.unary(
parent.request,
{ parent: parent },
(error: grpc.ServiceError, value: unknown) => {
callback(error, value);
assert(error);
assert.strictEqual(error.code, grpc.status.CANCELLED);
done();
}
);
/* Cancel the original call after the server starts processing it to
* ensure that it does reach the server. */
call.cancel();
},
});
call = proxyClient.unary(
{},
(error: grpc.ServiceError, value: unknown) => {
assert(error);
assert.strictEqual(error.code, grpc.status.CANCELLED);
done();
}
});
call = proxyClient.unary({}, (error: grpc.ServiceError, value: unknown) => {
assert(error);
assert.strictEqual(error.code, grpc.status.CANCELLED);
done();
});
);
});
it('Should work with client streaming requests', (done) => {
it('Should work with client streaming requests', done => {
done = multiDone(done, 2);
// eslint-disable-next-line prefer-const
let call: grpc.ClientWritableStream<unknown>;
proxyServer.addService(Client.service, {
clientStream: (parent: grpc.ServerReadableStream<any, any>, callback: grpc.sendUnaryData<any>) => {
client.clientStream({parent: parent}, (error: grpc.ServiceError, value: unknown) => {
callback(error, value);
assert(error);
assert.strictEqual(error.code, grpc.status.CANCELLED);
done();
});
clientStream: (
parent: grpc.ServerReadableStream<any, any>,
callback: grpc.sendUnaryData<any>
) => {
client.clientStream(
{ parent: parent },
(error: grpc.ServiceError, value: unknown) => {
callback(error, value);
assert(error);
assert.strictEqual(error.code, grpc.status.CANCELLED);
done();
}
);
/* Cancel the original call after the server starts processing it to
* ensure that it does reach the server. */
call.cancel();
},
});
call = proxyClient.clientStream(
(error: grpc.ServiceError, value: unknown) => {
assert(error);
assert.strictEqual(error.code, grpc.status.CANCELLED);
done();
}
});
call = proxyClient.clientStream((error: grpc.ServiceError, value: unknown) => {
assert(error);
assert.strictEqual(error.code, grpc.status.CANCELLED);
done();
});
);
});
it('Should work with server streaming requests', (done) => {
it('Should work with server streaming requests', done => {
done = multiDone(done, 2);
// eslint-disable-next-line prefer-const
let call: grpc.ClientReadableStream<unknown>;
proxyServer.addService(Client.service, {
serverStream: (parent: grpc.ServerWritableStream<any, any>) => {
const child = client.serverStream(parent.request, {parent: parent});
const child = client.serverStream(parent.request, { parent: parent });
child.on('error', () => {});
child.on('status', (status: grpc.StatusObject) => {
assert.strictEqual(status.code, grpc.status.CANCELLED);
done();
});
call.cancel();
}
},
});
call = proxyClient.serverStream({});
call.on('error', () => {});
@ -140,19 +176,20 @@ describe('Call propagation', () => {
done();
});
});
it('Should work with bidi streaming requests', (done) => {
it('Should work with bidi streaming requests', done => {
done = multiDone(done, 2);
// eslint-disable-next-line prefer-const
let call: grpc.ClientDuplexStream<unknown, unknown>;
proxyServer.addService(Client.service, {
bidiStream: (parent: grpc.ServerDuplexStream<any, any>) => {
const child = client.bidiStream({parent: parent});
const child = client.bidiStream({ parent: parent });
child.on('error', () => {});
child.on('status', (status: grpc.StatusObject) => {
assert.strictEqual(status.code, grpc.status.CANCELLED);
done();
});
call.cancel();
}
},
});
call = proxyClient.bidiStream();
call.on('error', () => {});
@ -163,86 +200,113 @@ describe('Call propagation', () => {
});
});
describe('Deadlines', () => {
it('should work with unary requests', (done) => {
it('should work with unary requests', done => {
done = multiDone(done, 2);
let call: grpc.ClientUnaryCall;
proxyServer.addService(Client.service, {
unary: (parent: grpc.ServerUnaryCall<any, any>, callback: grpc.sendUnaryData<any>) => {
client.unary(parent.request, {parent: parent, propagate_flags: grpc.propagate.DEADLINE}, (error: grpc.ServiceError, value: unknown) => {
callback(error, value);
assert(error);
assert.strictEqual(error.code, grpc.status.DEADLINE_EXCEEDED);
done();
});
}
unary: (
parent: grpc.ServerUnaryCall<any, any>,
callback: grpc.sendUnaryData<any>
) => {
client.unary(
parent.request,
{ parent: parent, propagate_flags: grpc.propagate.DEADLINE },
(error: grpc.ServiceError, value: unknown) => {
callback(error, value);
assert(error);
assert.strictEqual(error.code, grpc.status.DEADLINE_EXCEEDED);
done();
}
);
},
});
const deadline = new Date();
deadline.setMilliseconds(deadline.getMilliseconds() + 100);
call = proxyClient.unary({}, {deadline}, (error: grpc.ServiceError, value: unknown) => {
assert(error);
assert.strictEqual(error.code, grpc.status.DEADLINE_EXCEEDED);
done();
});
});
it('Should work with client streaming requests', (done) => {
done = multiDone(done, 2);
let call: grpc.ClientWritableStream<unknown>;
proxyServer.addService(Client.service, {
clientStream: (parent: grpc.ServerReadableStream<any, any>, callback: grpc.sendUnaryData<any>) => {
client.clientStream({parent: parent, propagate_flags: grpc.propagate.DEADLINE}, (error: grpc.ServiceError, value: unknown) => {
callback(error, value);
assert(error);
assert.strictEqual(error.code, grpc.status.DEADLINE_EXCEEDED);
done();
});
proxyClient.unary(
{},
{ deadline },
(error: grpc.ServiceError, value: unknown) => {
assert(error);
assert.strictEqual(error.code, grpc.status.DEADLINE_EXCEEDED);
done();
}
);
});
it('Should work with client streaming requests', done => {
done = multiDone(done, 2);
proxyServer.addService(Client.service, {
clientStream: (
parent: grpc.ServerReadableStream<any, any>,
callback: grpc.sendUnaryData<any>
) => {
client.clientStream(
{ parent: parent, propagate_flags: grpc.propagate.DEADLINE },
(error: grpc.ServiceError, value: unknown) => {
callback(error, value);
assert(error);
assert.strictEqual(error.code, grpc.status.DEADLINE_EXCEEDED);
done();
}
);
},
});
const deadline = new Date();
deadline.setMilliseconds(deadline.getMilliseconds() + 100);
call = proxyClient.clientStream({deadline, propagate_flags: grpc.propagate.DEADLINE}, (error: grpc.ServiceError, value: unknown) => {
assert(error);
assert.strictEqual(error.code, grpc.status.DEADLINE_EXCEEDED);
done();
});
proxyClient.clientStream(
{ deadline, propagate_flags: grpc.propagate.DEADLINE },
(error: grpc.ServiceError, value: unknown) => {
assert(error);
assert.strictEqual(error.code, grpc.status.DEADLINE_EXCEEDED);
done();
}
);
});
it('Should work with server streaming requests', (done) => {
it('Should work with server streaming requests', done => {
done = multiDone(done, 2);
let call: grpc.ClientReadableStream<unknown>;
proxyServer.addService(Client.service, {
serverStream: (parent: grpc.ServerWritableStream<any, any>) => {
const child = client.serverStream(parent.request, {parent: parent, propagate_flags: grpc.propagate.DEADLINE});
const child = client.serverStream(parent.request, {
parent: parent,
propagate_flags: grpc.propagate.DEADLINE,
});
child.on('error', () => {});
child.on('status', (status: grpc.StatusObject) => {
assert.strictEqual(status.code, grpc.status.DEADLINE_EXCEEDED);
done();
});
}
},
});
const deadline = new Date();
deadline.setMilliseconds(deadline.getMilliseconds() + 100);
call = proxyClient.serverStream({}, {deadline});
// eslint-disable-next-line prefer-const
call = proxyClient.serverStream({}, { deadline });
call.on('error', () => {});
call.on('status', (status: grpc.StatusObject) => {
assert.strictEqual(status.code, grpc.status.DEADLINE_EXCEEDED);
done();
});
});
it('Should work with bidi streaming requests', (done) => {
it('Should work with bidi streaming requests', done => {
done = multiDone(done, 2);
let call: grpc.ClientDuplexStream<unknown, unknown>;
proxyServer.addService(Client.service, {
bidiStream: (parent: grpc.ServerDuplexStream<any, any>) => {
const child = client.bidiStream({parent: parent, propagate_flags: grpc.propagate.DEADLINE});
const child = client.bidiStream({
parent: parent,
propagate_flags: grpc.propagate.DEADLINE,
});
child.on('error', () => {});
child.on('status', (status: grpc.StatusObject) => {
assert.strictEqual(status.code, grpc.status.DEADLINE_EXCEEDED);
done();
});
}
},
});
const deadline = new Date();
deadline.setMilliseconds(deadline.getMilliseconds() + 100);
call = proxyClient.bidiStream({deadline});
// eslint-disable-next-line prefer-const
call = proxyClient.bidiStream({ deadline });
call.on('error', () => {});
call.on('status', (status: grpc.StatusObject) => {
assert.strictEqual(status.code, grpc.status.DEADLINE_EXCEEDED);
@ -250,4 +314,4 @@ describe('Call propagation', () => {
});
});
});
});
});

View File

@ -25,14 +25,18 @@ import { CallCredentials } from '../src/call-credentials';
import { ChannelCredentials } from '../src/channel-credentials';
import * as grpc from '../src';
import { ServiceClient, ServiceClientConstructor } from '../src/make-client';
import { TestServiceClient, TestServiceHandlers } from './generated/TestService';
import {
TestServiceClient,
TestServiceHandlers,
} from './generated/TestService';
import { ProtoGrpcType as TestServiceGrpcType } from './generated/test_service';
import { assert2, loadProtoFile, mockFunction } from './common';
import { sendUnaryData, ServerUnaryCall, ServiceError } from '../src';
const protoFile = path.join(__dirname, 'fixtures', 'echo_service.proto');
const echoService = loadProtoFile(protoFile).EchoService as ServiceClientConstructor;
const echoService = loadProtoFile(protoFile)
.EchoService as ServiceClientConstructor;
class CallCredentialsMock implements CallCredentials {
child: CallCredentialsMock | null = null;
@ -153,17 +157,20 @@ describe('ChannelCredentials usage', () => {
let client: ServiceClient;
let server: grpc.Server;
before(async () => {
const {ca, key, cert} = await pFixtures;
const serverCreds = grpc.ServerCredentials.createSsl(null, [{private_key: key, cert_chain: cert}]);
const { ca, key, cert } = await pFixtures;
const serverCreds = grpc.ServerCredentials.createSsl(null, [
{ private_key: key, cert_chain: cert },
]);
const channelCreds = ChannelCredentials.createSsl(ca);
const callCreds = CallCredentials.createFromMetadataGenerator((options, cb) => {
const metadata = new grpc.Metadata();
metadata.set('test-key', 'test-value');
cb(null, metadata);
});
const callCreds = CallCredentials.createFromMetadataGenerator(
(options, cb) => {
const metadata = new grpc.Metadata();
metadata.set('test-key', 'test-value');
cb(null, metadata);
}
);
const combinedCreds = channelCreds.compose(callCreds);
return new Promise<void>((resolve, reject) => {
server = new grpc.Server();
server.addService(echoService.service, {
echo(call: ServerUnaryCall<any, any>, callback: sendUnaryData<any>) {
@ -171,31 +178,26 @@ describe('ChannelCredentials usage', () => {
callback(null, call.request);
},
});
server.bindAsync(
'localhost:0',
serverCreds,
(err, port) => {
if (err) {
reject(err);
return;
}
client = new echoService(
`localhost:${port}`,
combinedCreds,
{'grpc.ssl_target_name_override': 'foo.test.google.fr', 'grpc.default_authority': 'foo.test.google.fr'}
);
server.start();
resolve();
server.bindAsync('localhost:0', serverCreds, (err, port) => {
if (err) {
reject(err);
return;
}
);
client = new echoService(`localhost:${port}`, combinedCreds, {
'grpc.ssl_target_name_override': 'foo.test.google.fr',
'grpc.default_authority': 'foo.test.google.fr',
});
server.start();
resolve();
});
});
});
after(() => {
server.forceShutdown();
});
it('Should send the metadata from call credentials attached to channel credentials', (done) => {
it('Should send the metadata from call credentials attached to channel credentials', done => {
const call = client.echo(
{ value: 'test value', value2: 3 },
assert2.mustCall((error: ServiceError, response: any) => {
@ -203,10 +205,12 @@ describe('ChannelCredentials usage', () => {
assert.deepStrictEqual(response, { value: 'test value', value2: 3 });
})
);
call.on('metadata', assert2.mustCall((metadata: grpc.Metadata) => {
assert.deepStrictEqual(metadata.get('test-key'), ['test-value']);
}));
call.on(
'metadata',
assert2.mustCall((metadata: grpc.Metadata) => {
assert.deepStrictEqual(metadata.get('test-key'), ['test-value']);
})
);
assert2.afterMustCallsSatisfied(done);
});
});
});

View File

@ -19,7 +19,7 @@ import * as assert from 'assert';
import * as protoLoader from '@grpc/proto-loader';
import * as grpc from '../src';
import { ProtoGrpcType } from '../src/generated/channelz'
import { ProtoGrpcType } from '../src/generated/channelz';
import { ChannelzClient } from '../src/generated/grpc/channelz/v1/Channelz';
import { Channel__Output } from '../src/generated/grpc/channelz/v1/Channel';
import { Server__Output } from '../src/generated/grpc/channelz/v1/Server';
@ -32,28 +32,33 @@ const loadedChannelzProto = protoLoader.loadSync('channelz.proto', {
enums: String,
defaults: true,
oneofs: true,
includeDirs: [
`${__dirname}/../../proto`
]
includeDirs: [`${__dirname}/../../proto`],
});
const channelzGrpcObject = grpc.loadPackageDefinition(loadedChannelzProto) as unknown as ProtoGrpcType;
const channelzGrpcObject = grpc.loadPackageDefinition(
loadedChannelzProto
) as unknown as ProtoGrpcType;
const TestServiceClient = loadProtoFile(`${__dirname}/fixtures/test_service.proto`).TestService as ServiceClientConstructor;
const TestServiceClient = loadProtoFile(
`${__dirname}/fixtures/test_service.proto`
).TestService as ServiceClientConstructor;
const testServiceImpl: grpc.UntypedServiceImplementation = {
unary(call: grpc.ServerUnaryCall<any, any>, callback: grpc.sendUnaryData<any>) {
unary(
call: grpc.ServerUnaryCall<any, any>,
callback: grpc.sendUnaryData<any>
) {
if (call.request.error) {
setTimeout(() => {
callback({
code: grpc.status.INVALID_ARGUMENT,
details: call.request.message
details: call.request.message,
});
}, call.request.errorAfter)
}, call.request.errorAfter);
} else {
callback(null, {count: 1});
callback(null, { count: 1 });
}
}
}
},
};
describe('Channelz', () => {
let channelzServer: grpc.Server;
@ -61,18 +66,28 @@ describe('Channelz', () => {
let testServer: grpc.Server;
let testClient: ServiceClient;
before((done) => {
before(done => {
channelzServer = new grpc.Server();
channelzServer.addService(grpc.getChannelzServiceDefinition(), grpc.getChannelzHandlers());
channelzServer.bindAsync('localhost:0', grpc.ServerCredentials.createInsecure(), (error, port) => {
if (error) {
done(error);
return;
channelzServer.addService(
grpc.getChannelzServiceDefinition(),
grpc.getChannelzHandlers()
);
channelzServer.bindAsync(
'localhost:0',
grpc.ServerCredentials.createInsecure(),
(error, port) => {
if (error) {
done(error);
return;
}
channelzServer.start();
channelzClient = new channelzGrpcObject.grpc.channelz.v1.Channelz(
`localhost:${port}`,
grpc.credentials.createInsecure()
);
done();
}
channelzServer.start();
channelzClient = new channelzGrpcObject.grpc.channelz.v1.Channelz(`localhost:${port}`, grpc.credentials.createInsecure());
done();
});
);
});
after(() => {
@ -80,18 +95,25 @@ describe('Channelz', () => {
channelzServer.forceShutdown();
});
beforeEach((done) => {
beforeEach(done => {
testServer = new grpc.Server();
testServer.addService(TestServiceClient.service, testServiceImpl);
testServer.bindAsync('localhost:0', grpc.ServerCredentials.createInsecure(), (error, port) => {
if (error) {
done(error);
return;
testServer.bindAsync(
'localhost:0',
grpc.ServerCredentials.createInsecure(),
(error, port) => {
if (error) {
done(error);
return;
}
testServer.start();
testClient = new TestServiceClient(
`localhost:${port}`,
grpc.credentials.createInsecure()
);
done();
}
testServer.start();
testClient = new TestServiceClient(`localhost:${port}`, grpc.credentials.createInsecure());
done();
});
);
});
afterEach(() => {
@ -99,210 +121,439 @@ describe('Channelz', () => {
testServer.forceShutdown();
});
it('should see a newly created channel', (done) => {
it('should see a newly created channel', done => {
// Test that the specific test client channel info can be retrieved
channelzClient.GetChannel({channel_id: testClient.getChannel().getChannelzRef().id}, (error, result) => {
assert.ifError(error);
assert(result);
assert(result.channel);
assert(result.channel.ref);
assert.strictEqual(+result.channel.ref.channel_id, testClient.getChannel().getChannelzRef().id);
// Test that the channel is in the list of top channels
channelzClient.getTopChannels({start_channel_id: testClient.getChannel().getChannelzRef().id, max_results:1}, (error, result) => {
channelzClient.GetChannel(
{ channel_id: testClient.getChannel().getChannelzRef().id },
(error, result) => {
assert.ifError(error);
assert(result);
assert.strictEqual(result.channel.length, 1);
assert(result.channel[0].ref);
assert.strictEqual(+result.channel[0].ref.channel_id, testClient.getChannel().getChannelzRef().id);
done();
});
});
assert(result.channel);
assert(result.channel.ref);
assert.strictEqual(
+result.channel.ref.channel_id,
testClient.getChannel().getChannelzRef().id
);
// Test that the channel is in the list of top channels
channelzClient.getTopChannels(
{
start_channel_id: testClient.getChannel().getChannelzRef().id,
max_results: 1,
},
(error, result) => {
assert.ifError(error);
assert(result);
assert.strictEqual(result.channel.length, 1);
assert(result.channel[0].ref);
assert.strictEqual(
+result.channel[0].ref.channel_id,
testClient.getChannel().getChannelzRef().id
);
done();
}
);
}
);
});
it('should see a newly created server', (done) => {
it('should see a newly created server', done => {
// Test that the specific test server info can be retrieved
channelzClient.getServer({server_id: testServer.getChannelzRef().id}, (error, result) => {
assert.ifError(error);
assert(result);
assert(result.server);
assert(result.server.ref);
assert.strictEqual(+result.server.ref.server_id, testServer.getChannelzRef().id);
// Test that the server is in the list of servers
channelzClient.getServers({start_server_id: testServer.getChannelzRef().id, max_results: 1}, (error, result) => {
channelzClient.getServer(
{ server_id: testServer.getChannelzRef().id },
(error, result) => {
assert.ifError(error);
assert(result);
assert.strictEqual(result.server.length, 1);
assert(result.server[0].ref);
assert.strictEqual(+result.server[0].ref.server_id, testServer.getChannelzRef().id);
done();
});
});
assert(result.server);
assert(result.server.ref);
assert.strictEqual(
+result.server.ref.server_id,
testServer.getChannelzRef().id
);
// Test that the server is in the list of servers
channelzClient.getServers(
{ start_server_id: testServer.getChannelzRef().id, max_results: 1 },
(error, result) => {
assert.ifError(error);
assert(result);
assert.strictEqual(result.server.length, 1);
assert(result.server[0].ref);
assert.strictEqual(
+result.server[0].ref.server_id,
testServer.getChannelzRef().id
);
done();
}
);
}
);
});
it('should count successful calls', (done) => {
it('should count successful calls', done => {
testClient.unary({}, (error: grpc.ServiceError, value: unknown) => {
assert.ifError(error);
// Channel data tests
channelzClient.GetChannel({channel_id: testClient.getChannel().getChannelzRef().id}, (error, channelResult) => {
assert.ifError(error);
assert(channelResult);
assert(channelResult.channel);
assert(channelResult.channel.ref);
assert(channelResult.channel.data);
assert.strictEqual(+channelResult.channel.data.calls_started, 1);
assert.strictEqual(+channelResult.channel.data.calls_succeeded, 1);
assert.strictEqual(+channelResult.channel.data.calls_failed, 0);
assert.strictEqual(channelResult.channel.subchannel_ref.length, 1);
channelzClient.getSubchannel({subchannel_id: channelResult.channel.subchannel_ref[0].subchannel_id}, (error, subchannelResult) => {
channelzClient.GetChannel(
{ channel_id: testClient.getChannel().getChannelzRef().id },
(error, channelResult) => {
assert.ifError(error);
assert(subchannelResult);
assert(subchannelResult.subchannel);
assert(subchannelResult.subchannel.ref);
assert(subchannelResult.subchannel.data);
assert.strictEqual(subchannelResult.subchannel.ref.subchannel_id, channelResult.channel!.subchannel_ref[0].subchannel_id);
assert.strictEqual(+subchannelResult.subchannel.data.calls_started, 1);
assert.strictEqual(+subchannelResult.subchannel.data.calls_succeeded, 1);
assert.strictEqual(+subchannelResult.subchannel.data.calls_failed, 0);
assert.strictEqual(subchannelResult.subchannel.socket_ref.length, 1);
channelzClient.getSocket({socket_id: subchannelResult.subchannel.socket_ref[0].socket_id}, (error, socketResult) => {
assert.ifError(error);
assert(socketResult);
assert(socketResult.socket);
assert(socketResult.socket.ref);
assert(socketResult.socket.data);
assert.strictEqual(socketResult.socket.ref.socket_id, subchannelResult.subchannel!.socket_ref[0].socket_id);
assert.strictEqual(+socketResult.socket.data.streams_started, 1);
assert.strictEqual(+socketResult.socket.data.streams_succeeded, 1);
assert.strictEqual(+socketResult.socket.data.streams_failed, 0);
assert.strictEqual(+socketResult.socket.data.messages_received, 1);
assert.strictEqual(+socketResult.socket.data.messages_sent, 1);
// Server data tests
channelzClient.getServer({server_id: testServer.getChannelzRef().id}, (error, serverResult) => {
assert(channelResult);
assert(channelResult.channel);
assert(channelResult.channel.ref);
assert(channelResult.channel.data);
assert.strictEqual(+channelResult.channel.data.calls_started, 1);
assert.strictEqual(+channelResult.channel.data.calls_succeeded, 1);
assert.strictEqual(+channelResult.channel.data.calls_failed, 0);
assert.strictEqual(channelResult.channel.subchannel_ref.length, 1);
channelzClient.getSubchannel(
{
subchannel_id:
channelResult.channel.subchannel_ref[0].subchannel_id,
},
(error, subchannelResult) => {
assert.ifError(error);
assert(serverResult);
assert(serverResult.server);
assert(serverResult.server.ref);
assert(serverResult.server.data);
assert.strictEqual(+serverResult.server.ref.server_id, testServer.getChannelzRef().id);
assert.strictEqual(+serverResult.server.data.calls_started, 1);
assert.strictEqual(+serverResult.server.data.calls_succeeded, 1);
assert.strictEqual(+serverResult.server.data.calls_failed, 0);
channelzClient.getServerSockets({server_id: testServer.getChannelzRef().id}, (error, socketsResult) => {
assert.ifError(error);
assert(socketsResult);
assert.strictEqual(socketsResult.socket_ref.length, 1);
channelzClient.getSocket({socket_id: socketsResult.socket_ref[0].socket_id}, (error, serverSocketResult) => {
assert(subchannelResult);
assert(subchannelResult.subchannel);
assert(subchannelResult.subchannel.ref);
assert(subchannelResult.subchannel.data);
assert.strictEqual(
subchannelResult.subchannel.ref.subchannel_id,
channelResult.channel!.subchannel_ref[0].subchannel_id
);
assert.strictEqual(
+subchannelResult.subchannel.data.calls_started,
1
);
assert.strictEqual(
+subchannelResult.subchannel.data.calls_succeeded,
1
);
assert.strictEqual(
+subchannelResult.subchannel.data.calls_failed,
0
);
assert.strictEqual(
subchannelResult.subchannel.socket_ref.length,
1
);
channelzClient.getSocket(
{
socket_id:
subchannelResult.subchannel.socket_ref[0].socket_id,
},
(error, socketResult) => {
assert.ifError(error);
assert(serverSocketResult);
assert(serverSocketResult.socket);
assert(serverSocketResult.socket.ref);
assert(serverSocketResult.socket.data);
assert.strictEqual(serverSocketResult.socket.ref.socket_id, socketsResult.socket_ref[0].socket_id);
assert.strictEqual(+serverSocketResult.socket.data.streams_started, 1);
assert.strictEqual(+serverSocketResult.socket.data.streams_succeeded, 1);
assert.strictEqual(+serverSocketResult.socket.data.streams_failed, 0);
assert.strictEqual(+serverSocketResult.socket.data.messages_received, 1);
assert.strictEqual(+serverSocketResult.socket.data.messages_sent, 1);
done();
});
});
});
});
});
});
assert(socketResult);
assert(socketResult.socket);
assert(socketResult.socket.ref);
assert(socketResult.socket.data);
assert.strictEqual(
socketResult.socket.ref.socket_id,
subchannelResult.subchannel!.socket_ref[0].socket_id
);
assert.strictEqual(
+socketResult.socket.data.streams_started,
1
);
assert.strictEqual(
+socketResult.socket.data.streams_succeeded,
1
);
assert.strictEqual(
+socketResult.socket.data.streams_failed,
0
);
assert.strictEqual(
+socketResult.socket.data.messages_received,
1
);
assert.strictEqual(
+socketResult.socket.data.messages_sent,
1
);
// Server data tests
channelzClient.getServer(
{ server_id: testServer.getChannelzRef().id },
(error, serverResult) => {
assert.ifError(error);
assert(serverResult);
assert(serverResult.server);
assert(serverResult.server.ref);
assert(serverResult.server.data);
assert.strictEqual(
+serverResult.server.ref.server_id,
testServer.getChannelzRef().id
);
assert.strictEqual(
+serverResult.server.data.calls_started,
1
);
assert.strictEqual(
+serverResult.server.data.calls_succeeded,
1
);
assert.strictEqual(
+serverResult.server.data.calls_failed,
0
);
channelzClient.getServerSockets(
{ server_id: testServer.getChannelzRef().id },
(error, socketsResult) => {
assert.ifError(error);
assert(socketsResult);
assert.strictEqual(
socketsResult.socket_ref.length,
1
);
channelzClient.getSocket(
{
socket_id: socketsResult.socket_ref[0].socket_id,
},
(error, serverSocketResult) => {
assert.ifError(error);
assert(serverSocketResult);
assert(serverSocketResult.socket);
assert(serverSocketResult.socket.ref);
assert(serverSocketResult.socket.data);
assert.strictEqual(
serverSocketResult.socket.ref.socket_id,
socketsResult.socket_ref[0].socket_id
);
assert.strictEqual(
+serverSocketResult.socket.data.streams_started,
1
);
assert.strictEqual(
+serverSocketResult.socket.data
.streams_succeeded,
1
);
assert.strictEqual(
+serverSocketResult.socket.data.streams_failed,
0
);
assert.strictEqual(
+serverSocketResult.socket.data
.messages_received,
1
);
assert.strictEqual(
+serverSocketResult.socket.data.messages_sent,
1
);
done();
}
);
}
);
}
);
}
);
}
);
}
);
});
});
it('should count failed calls', (done) => {
testClient.unary({error: true}, (error: grpc.ServiceError, value: unknown) => {
assert(error);
// Channel data tests
channelzClient.GetChannel({channel_id: testClient.getChannel().getChannelzRef().id}, (error, channelResult) => {
assert.ifError(error);
assert(channelResult);
assert(channelResult.channel);
assert(channelResult.channel.ref);
assert(channelResult.channel.data);
assert.strictEqual(+channelResult.channel.data.calls_started, 1);
assert.strictEqual(+channelResult.channel.data.calls_succeeded, 0);
assert.strictEqual(+channelResult.channel.data.calls_failed, 1);
assert.strictEqual(channelResult.channel.subchannel_ref.length, 1);
channelzClient.getSubchannel({subchannel_id: channelResult.channel.subchannel_ref[0].subchannel_id}, (error, subchannelResult) => {
assert.ifError(error);
assert(subchannelResult);
assert(subchannelResult.subchannel);
assert(subchannelResult.subchannel.ref);
assert(subchannelResult.subchannel.data);
assert.strictEqual(subchannelResult.subchannel.ref.subchannel_id, channelResult.channel!.subchannel_ref[0].subchannel_id);
assert.strictEqual(+subchannelResult.subchannel.data.calls_started, 1);
assert.strictEqual(+subchannelResult.subchannel.data.calls_succeeded, 0);
assert.strictEqual(+subchannelResult.subchannel.data.calls_failed, 1);
assert.strictEqual(subchannelResult.subchannel.socket_ref.length, 1);
channelzClient.getSocket({socket_id: subchannelResult.subchannel.socket_ref[0].socket_id}, (error, socketResult) => {
it('should count failed calls', done => {
testClient.unary(
{ error: true },
(error: grpc.ServiceError, value: unknown) => {
assert(error);
// Channel data tests
channelzClient.GetChannel(
{ channel_id: testClient.getChannel().getChannelzRef().id },
(error, channelResult) => {
assert.ifError(error);
assert(socketResult);
assert(socketResult.socket);
assert(socketResult.socket.ref);
assert(socketResult.socket.data);
assert.strictEqual(socketResult.socket.ref.socket_id, subchannelResult.subchannel!.socket_ref[0].socket_id);
assert.strictEqual(+socketResult.socket.data.streams_started, 1);
assert.strictEqual(+socketResult.socket.data.streams_succeeded, 1);
assert.strictEqual(+socketResult.socket.data.streams_failed, 0);
assert.strictEqual(+socketResult.socket.data.messages_received, 0);
assert.strictEqual(+socketResult.socket.data.messages_sent, 1);
// Server data tests
channelzClient.getServer({server_id: testServer.getChannelzRef().id}, (error, serverResult) => {
assert.ifError(error);
assert(serverResult);
assert(serverResult.server);
assert(serverResult.server.ref);
assert(serverResult.server.data);
assert.strictEqual(+serverResult.server.ref.server_id, testServer.getChannelzRef().id);
assert.strictEqual(+serverResult.server.data.calls_started, 1);
assert.strictEqual(+serverResult.server.data.calls_succeeded, 0);
assert.strictEqual(+serverResult.server.data.calls_failed, 1);
channelzClient.getServerSockets({server_id: testServer.getChannelzRef().id}, (error, socketsResult) => {
assert(channelResult);
assert(channelResult.channel);
assert(channelResult.channel.ref);
assert(channelResult.channel.data);
assert.strictEqual(+channelResult.channel.data.calls_started, 1);
assert.strictEqual(+channelResult.channel.data.calls_succeeded, 0);
assert.strictEqual(+channelResult.channel.data.calls_failed, 1);
assert.strictEqual(channelResult.channel.subchannel_ref.length, 1);
channelzClient.getSubchannel(
{
subchannel_id:
channelResult.channel.subchannel_ref[0].subchannel_id,
},
(error, subchannelResult) => {
assert.ifError(error);
assert(socketsResult);
assert.strictEqual(socketsResult.socket_ref.length, 1);
channelzClient.getSocket({socket_id: socketsResult.socket_ref[0].socket_id}, (error, serverSocketResult) => {
assert.ifError(error);
assert(serverSocketResult);
assert(serverSocketResult.socket);
assert(serverSocketResult.socket.ref);
assert(serverSocketResult.socket.data);
assert.strictEqual(serverSocketResult.socket.ref.socket_id, socketsResult.socket_ref[0].socket_id);
assert.strictEqual(+serverSocketResult.socket.data.streams_started, 1);
assert.strictEqual(+serverSocketResult.socket.data.streams_succeeded, 0);
assert.strictEqual(+serverSocketResult.socket.data.streams_failed, 1);
assert.strictEqual(+serverSocketResult.socket.data.messages_received, 1);
assert.strictEqual(+serverSocketResult.socket.data.messages_sent, 0);
done();
});
});
});
});
});
});
});
assert(subchannelResult);
assert(subchannelResult.subchannel);
assert(subchannelResult.subchannel.ref);
assert(subchannelResult.subchannel.data);
assert.strictEqual(
subchannelResult.subchannel.ref.subchannel_id,
channelResult.channel!.subchannel_ref[0].subchannel_id
);
assert.strictEqual(
+subchannelResult.subchannel.data.calls_started,
1
);
assert.strictEqual(
+subchannelResult.subchannel.data.calls_succeeded,
0
);
assert.strictEqual(
+subchannelResult.subchannel.data.calls_failed,
1
);
assert.strictEqual(
subchannelResult.subchannel.socket_ref.length,
1
);
channelzClient.getSocket(
{
socket_id:
subchannelResult.subchannel.socket_ref[0].socket_id,
},
(error, socketResult) => {
assert.ifError(error);
assert(socketResult);
assert(socketResult.socket);
assert(socketResult.socket.ref);
assert(socketResult.socket.data);
assert.strictEqual(
socketResult.socket.ref.socket_id,
subchannelResult.subchannel!.socket_ref[0].socket_id
);
assert.strictEqual(
+socketResult.socket.data.streams_started,
1
);
assert.strictEqual(
+socketResult.socket.data.streams_succeeded,
1
);
assert.strictEqual(
+socketResult.socket.data.streams_failed,
0
);
assert.strictEqual(
+socketResult.socket.data.messages_received,
0
);
assert.strictEqual(
+socketResult.socket.data.messages_sent,
1
);
// Server data tests
channelzClient.getServer(
{ server_id: testServer.getChannelzRef().id },
(error, serverResult) => {
assert.ifError(error);
assert(serverResult);
assert(serverResult.server);
assert(serverResult.server.ref);
assert(serverResult.server.data);
assert.strictEqual(
+serverResult.server.ref.server_id,
testServer.getChannelzRef().id
);
assert.strictEqual(
+serverResult.server.data.calls_started,
1
);
assert.strictEqual(
+serverResult.server.data.calls_succeeded,
0
);
assert.strictEqual(
+serverResult.server.data.calls_failed,
1
);
channelzClient.getServerSockets(
{ server_id: testServer.getChannelzRef().id },
(error, socketsResult) => {
assert.ifError(error);
assert(socketsResult);
assert.strictEqual(
socketsResult.socket_ref.length,
1
);
channelzClient.getSocket(
{
socket_id:
socketsResult.socket_ref[0].socket_id,
},
(error, serverSocketResult) => {
assert.ifError(error);
assert(serverSocketResult);
assert(serverSocketResult.socket);
assert(serverSocketResult.socket.ref);
assert(serverSocketResult.socket.data);
assert.strictEqual(
serverSocketResult.socket.ref.socket_id,
socketsResult.socket_ref[0].socket_id
);
assert.strictEqual(
+serverSocketResult.socket.data
.streams_started,
1
);
assert.strictEqual(
+serverSocketResult.socket.data
.streams_succeeded,
0
);
assert.strictEqual(
+serverSocketResult.socket.data
.streams_failed,
1
);
assert.strictEqual(
+serverSocketResult.socket.data
.messages_received,
1
);
assert.strictEqual(
+serverSocketResult.socket.data.messages_sent,
0
);
done();
}
);
}
);
}
);
}
);
}
);
}
);
}
);
});
});
describe('Disabling channelz', () => {
let testServer: grpc.Server;
let testClient: ServiceClient;
beforeEach((done) => {
testServer = new grpc.Server({'grpc.enable_channelz': 0});
beforeEach(done => {
testServer = new grpc.Server({ 'grpc.enable_channelz': 0 });
testServer.addService(TestServiceClient.service, testServiceImpl);
testServer.bindAsync('localhost:0', grpc.ServerCredentials.createInsecure(), (error, port) => {
if (error) {
done(error);
return;
testServer.bindAsync(
'localhost:0',
grpc.ServerCredentials.createInsecure(),
(error, port) => {
if (error) {
done(error);
return;
}
testServer.start();
testClient = new TestServiceClient(
`localhost:${port}`,
grpc.credentials.createInsecure(),
{ 'grpc.enable_channelz': 0 }
);
done();
}
testServer.start();
testClient = new TestServiceClient(`localhost:${port}`, grpc.credentials.createInsecure(), {'grpc.enable_channelz': 0});
done();
});
);
});
afterEach(() => {
@ -310,12 +561,16 @@ describe('Disabling channelz', () => {
testServer.forceShutdown();
});
it('Should still work', (done) => {
it('Should still work', done => {
const deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 1);
testClient.unary({}, {deadline}, (error: grpc.ServiceError, value: unknown) => {
assert.ifError(error);
done();
});
testClient.unary(
{},
{ deadline },
(error: grpc.ServiceError, value: unknown) => {
assert.ifError(error);
done();
}
);
});
});
});

View File

@ -20,7 +20,7 @@ import * as assert from 'assert';
import * as grpc from '../src';
import { Server, ServerCredentials } from '../src';
import { Client } from '../src';
import { ConnectivityState } from "../src/connectivity-state";
import { ConnectivityState } from '../src/connectivity-state';
const clientInsecureCreds = grpc.credentials.createInsecure();
const serverInsecureCreds = ServerCredentials.createInsecure();
@ -32,19 +32,12 @@ describe('Client', () => {
before(done => {
server = new Server();
server.bindAsync(
'localhost:0',
serverInsecureCreds,
(err, port) => {
assert.ifError(err);
client = new Client(
`localhost:${port}`,
clientInsecureCreds
);
server.start();
done();
}
);
server.bindAsync('localhost:0', serverInsecureCreds, (err, port) => {
assert.ifError(err);
client = new Client(`localhost:${port}`, clientInsecureCreds);
server.start();
done();
});
});
after(done => {
@ -79,18 +72,30 @@ describe('Client without a server', () => {
after(() => {
client.close();
});
it('should fail multiple calls to the nonexistent server', function(done) {
it('should fail multiple calls to the nonexistent server', function (done) {
this.timeout(5000);
// Regression test for https://github.com/grpc/grpc-node/issues/1411
client.makeUnaryRequest('/service/method', x => x, x => x, Buffer.from([]), (error, value) => {
assert(error);
assert.strictEqual(error?.code, grpc.status.UNAVAILABLE);
client.makeUnaryRequest('/service/method', x => x, x => x, Buffer.from([]), (error, value) => {
client.makeUnaryRequest(
'/service/method',
x => x,
x => x,
Buffer.from([]),
(error, value) => {
assert(error);
assert.strictEqual(error?.code, grpc.status.UNAVAILABLE);
done();
});
});
client.makeUnaryRequest(
'/service/method',
x => x,
x => x,
Buffer.from([]),
(error, value) => {
assert(error);
assert.strictEqual(error?.code, grpc.status.UNAVAILABLE);
done();
}
);
}
);
});
});
@ -103,17 +108,29 @@ describe('Client with a nonexistent target domain', () => {
after(() => {
client.close();
});
it('should fail multiple calls', function(done) {
it('should fail multiple calls', function (done) {
this.timeout(5000);
// Regression test for https://github.com/grpc/grpc-node/issues/1411
client.makeUnaryRequest('/service/method', x => x, x => x, Buffer.from([]), (error, value) => {
assert(error);
assert.strictEqual(error?.code, grpc.status.UNAVAILABLE);
client.makeUnaryRequest('/service/method', x => x, x => x, Buffer.from([]), (error, value) => {
client.makeUnaryRequest(
'/service/method',
x => x,
x => x,
Buffer.from([]),
(error, value) => {
assert(error);
assert.strictEqual(error?.code, grpc.status.UNAVAILABLE);
done();
});
});
client.makeUnaryRequest(
'/service/method',
x => x,
x => x,
Buffer.from([]),
(error, value) => {
assert(error);
assert.strictEqual(error?.code, grpc.status.UNAVAILABLE);
done();
}
);
}
);
});
});
});

View File

@ -29,40 +29,49 @@ const serverInsecureCreds = ServerCredentials.createInsecure();
const TIMEOUT_SERVICE_CONFIG: ServiceConfig = {
loadBalancingConfig: [],
methodConfig: [{
name: [
{service: 'TestService'}
],
timeout: {
seconds: 1,
nanos: 0
}
}]
methodConfig: [
{
name: [{ service: 'TestService' }],
timeout: {
seconds: 1,
nanos: 0,
},
},
],
};
describe('Client with configured timeout', () => {
let server: grpc.Server;
let Client: ServiceClientConstructor;
let client: ServiceClient;
before(done => {
Client = loadProtoFile(__dirname + '/fixtures/test_service.proto').TestService as ServiceClientConstructor;
Client = loadProtoFile(__dirname + '/fixtures/test_service.proto')
.TestService as ServiceClientConstructor;
server = new grpc.Server();
server.addService(Client.service, {
unary: () => {},
clientStream: () => {},
serverStream: () => {},
bidiStream: () => {}
bidiStream: () => {},
});
server.bindAsync('localhost:0', grpc.ServerCredentials.createInsecure(), (error, port) => {
if (error) {
done(error);
return;
server.bindAsync(
'localhost:0',
grpc.ServerCredentials.createInsecure(),
(error, port) => {
if (error) {
done(error);
return;
}
server.start();
client = new Client(
`localhost:${port}`,
grpc.credentials.createInsecure(),
{ 'grpc.service_config': JSON.stringify(TIMEOUT_SERVICE_CONFIG) }
);
done();
}
server.start();
client = new Client(`localhost:${port}`, grpc.credentials.createInsecure(), {'grpc.service_config': JSON.stringify(TIMEOUT_SERVICE_CONFIG)});
done();
});
);
});
after(done => {
@ -71,7 +80,7 @@ describe('Client with configured timeout', () => {
});
it('Should end calls without explicit deadline with DEADLINE_EXCEEDED', done => {
client.unary({}, (error: grpc.ServiceError, value: unknown) =>{
client.unary({}, (error: grpc.ServiceError, value: unknown) => {
assert(error);
assert.strictEqual(error.code, grpc.status.DEADLINE_EXCEEDED);
done();
@ -81,10 +90,10 @@ describe('Client with configured timeout', () => {
it('Should end calls with a long explicit deadline with DEADLINE_EXCEEDED', done => {
const deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 20);
client.unary({}, (error: grpc.ServiceError, value: unknown) =>{
client.unary({}, (error: grpc.ServiceError, value: unknown) => {
assert(error);
assert.strictEqual(error.code, grpc.status.DEADLINE_EXCEEDED);
done();
});
});
});
});

View File

@ -19,13 +19,20 @@ import * as assert from 'assert';
import * as path from 'path';
import * as grpc from '../src';
import {sendUnaryData, Server, ServerCredentials, ServerUnaryCall, ServiceClientConstructor, ServiceError} from '../src';
import {
sendUnaryData,
Server,
ServerCredentials,
ServerUnaryCall,
ServiceClientConstructor,
ServiceError,
} from '../src';
import {loadProtoFile} from './common';
import { loadProtoFile } from './common';
const protoFile = path.join(__dirname, 'fixtures', 'echo_service.proto');
const echoService =
loadProtoFile(protoFile).EchoService as ServiceClientConstructor;
const echoService = loadProtoFile(protoFile)
.EchoService as ServiceClientConstructor;
describe('Global subchannel pool', () => {
let server: Server;
@ -45,72 +52,84 @@ describe('Global subchannel pool', () => {
});
server.bindAsync(
'localhost:0', ServerCredentials.createInsecure(), (err, port) => {
assert.ifError(err);
serverPort = port;
server.start();
done();
});
'localhost:0',
ServerCredentials.createInsecure(),
(err, port) => {
assert.ifError(err);
serverPort = port;
server.start();
done();
}
);
});
beforeEach(() => {
promises = [];
})
});
after(done => {
server.tryShutdown(done);
});
function callService(client: InstanceType<grpc.ServiceClientConstructor>) {
return new Promise<void>((resolve) => {
const request = {value: 'test value', value2: 3};
return new Promise<void>(resolve => {
const request = { value: 'test value', value2: 3 };
client.echo(request, (error: ServiceError, response: any) => {
assert.ifError(error);
assert.deepStrictEqual(response, request);
resolve();
});
})
});
}
function connect() {
const grpcOptions = {
'grpc.use_local_subchannel_pool': 0,
}
};
client1 = new echoService(
`127.0.0.1:${serverPort}`, grpc.credentials.createInsecure(),
grpcOptions);
`127.0.0.1:${serverPort}`,
grpc.credentials.createInsecure(),
grpcOptions
);
client2 = new echoService(
`127.0.0.1:${serverPort}`, grpc.credentials.createInsecure(),
grpcOptions);
`127.0.0.1:${serverPort}`,
grpc.credentials.createInsecure(),
grpcOptions
);
}
/* This is a regression test for a bug where client1.close in the
* waitForReady callback would cause the subchannel to transition to IDLE
* even though client2 is also using it. */
it('Should handle client.close calls in waitForReady',
done => {
connect();
it('Should handle client.close calls in waitForReady', done => {
connect();
promises.push(new Promise<void>((resolve) => {
client1.waitForReady(Date.now() + 50, (error) => {
assert.ifError(error);
client1.close();
resolve();
});
}))
promises.push(
new Promise<void>(resolve => {
client1.waitForReady(Date.now() + 50, error => {
assert.ifError(error);
client1.close();
resolve();
});
})
);
promises.push(new Promise<void>((resolve) => {
client2.waitForReady(Date.now() + 50, (error) => {
promises.push(
new Promise<void>(resolve => {
client2.waitForReady(Date.now() + 50, error => {
assert.ifError(error);
resolve();
});
}))
});
})
);
Promise.all(promises).then(() => {done()});
})
Promise.all(promises).then(() => {
done();
});
});
it('Call the service', done => {
promises.push(callService(client2));
@ -118,13 +137,13 @@ describe('Global subchannel pool', () => {
Promise.all(promises).then(() => {
done();
});
})
});
it('Should complete the client lifecycle without error', done => {
setTimeout(() => {
client1.close();
client2.close();
done()
done();
}, 500);
});
});

View File

@ -18,7 +18,14 @@
import * as assert from 'assert';
import * as path from 'path';
import * as grpc from '../src';
import { sendUnaryData, Server, ServerCredentials, ServerUnaryCall, ServiceClientConstructor, ServiceError } from "../src";
import {
sendUnaryData,
Server,
ServerCredentials,
ServerUnaryCall,
ServiceClientConstructor,
ServiceError,
} from '../src';
import { loadProtoFile } from './common';
const protoFile = path.join(__dirname, 'fixtures', 'echo_service.proto');
@ -30,7 +37,6 @@ describe('Local subchannel pool', () => {
let serverPort: number;
before(done => {
server = new Server();
server.addService(echoService.service, {
echo(call: ServerUnaryCall<any, any>, callback: sendUnaryData<any>) {
@ -58,7 +64,7 @@ describe('Local subchannel pool', () => {
const client = new echoService(
`localhost:${serverPort}`,
grpc.credentials.createInsecure(),
{'grpc.use_local_subchannel_pool': 1}
{ 'grpc.use_local_subchannel_pool': 1 }
);
client.echo(
{ value: 'test value', value2: 3 },
@ -70,4 +76,4 @@ describe('Local subchannel pool', () => {
}
);
});
});
});

View File

@ -19,7 +19,7 @@ import * as assert from 'assert';
import * as path from 'path';
import * as grpc from '../src';
import { loadProtoFile } from './common';
import { OutlierDetectionLoadBalancingConfig } from '../src/load-balancer-outlier-detection'
import { OutlierDetectionLoadBalancingConfig } from '../src/load-balancer-outlier-detection';
import { ServiceClient } from '../src/make-client';
function multiDone(done: Mocha.Done, target: number) {
@ -32,7 +32,7 @@ function multiDone(done: Mocha.Done, target: number) {
if (count >= target) {
done();
}
}
};
}
const defaultOutlierDetectionServiceConfig = {
@ -42,13 +42,15 @@ const defaultOutlierDetectionServiceConfig = {
outlier_detection: {
success_rate_ejection: {},
failure_percentage_ejection: {},
child_policy: [{round_robin: {}}]
}
}
]
child_policy: [{ round_robin: {} }],
},
},
],
};
const defaultOutlierDetectionServiceConfigString = JSON.stringify(defaultOutlierDetectionServiceConfig);
const defaultOutlierDetectionServiceConfigString = JSON.stringify(
defaultOutlierDetectionServiceConfig
);
const successRateOutlierDetectionServiceConfig = {
methodConfig: [],
@ -57,22 +59,24 @@ const successRateOutlierDetectionServiceConfig = {
outlier_detection: {
interval: {
seconds: 1,
nanos: 0
nanos: 0,
},
base_ejection_time: {
seconds: 3,
nanos: 0
nanos: 0,
},
success_rate_ejection: {
request_volume: 5
request_volume: 5,
},
child_policy: [{round_robin: {}}]
}
}
]
child_policy: [{ round_robin: {} }],
},
},
],
};
const successRateOutlierDetectionServiceConfigString = JSON.stringify(successRateOutlierDetectionServiceConfig);
const successRateOutlierDetectionServiceConfigString = JSON.stringify(
successRateOutlierDetectionServiceConfig
);
const failurePercentageOutlierDetectionServiceConfig = {
methodConfig: [],
@ -81,37 +85,45 @@ const failurePercentageOutlierDetectionServiceConfig = {
outlier_detection: {
interval: {
seconds: 1,
nanos: 0
nanos: 0,
},
base_ejection_time: {
seconds: 3,
nanos: 0
nanos: 0,
},
failure_percentage_ejection: {
request_volume: 5
request_volume: 5,
},
child_policy: [{round_robin: {}}]
}
}
]
child_policy: [{ round_robin: {} }],
},
},
],
};
const falurePercentageOutlierDetectionServiceConfigString = JSON.stringify(failurePercentageOutlierDetectionServiceConfig);
const falurePercentageOutlierDetectionServiceConfigString = JSON.stringify(
failurePercentageOutlierDetectionServiceConfig
);
const goodService = {
echo: (call: grpc.ServerUnaryCall<any, any>, callback: grpc.sendUnaryData<any>) => {
callback(null, call.request)
}
echo: (
call: grpc.ServerUnaryCall<any, any>,
callback: grpc.sendUnaryData<any>
) => {
callback(null, call.request);
},
};
const badService = {
echo: (call: grpc.ServerUnaryCall<any, any>, callback: grpc.sendUnaryData<any>) => {
echo: (
call: grpc.ServerUnaryCall<any, any>,
callback: grpc.sendUnaryData<any>
) => {
callback({
code: grpc.status.PERMISSION_DENIED,
details: 'Permission denied'
})
}
}
details: 'Permission denied',
});
},
};
const protoFile = path.join(__dirname, 'fixtures', 'echo_service.proto');
const EchoService = loadProtoFile(protoFile)
@ -123,9 +135,9 @@ describe('Outlier detection config validation', () => {
const loadBalancingConfig = {
interval: {
seconds: -1,
nanos: 0
nanos: 0,
},
child_policy: [{round_robin: {}}]
child_policy: [{ round_robin: {} }],
};
assert.throws(() => {
OutlierDetectionLoadBalancingConfig.createFromJson(loadBalancingConfig);
@ -135,9 +147,9 @@ describe('Outlier detection config validation', () => {
const loadBalancingConfig = {
interval: {
seconds: 1e12,
nanos: 0
nanos: 0,
},
child_policy: [{round_robin: {}}]
child_policy: [{ round_robin: {} }],
};
assert.throws(() => {
OutlierDetectionLoadBalancingConfig.createFromJson(loadBalancingConfig);
@ -147,9 +159,9 @@ describe('Outlier detection config validation', () => {
const loadBalancingConfig = {
interval: {
seconds: 0,
nanos: -1
nanos: -1,
},
child_policy: [{round_robin: {}}]
child_policy: [{ round_robin: {} }],
};
assert.throws(() => {
OutlierDetectionLoadBalancingConfig.createFromJson(loadBalancingConfig);
@ -159,9 +171,9 @@ describe('Outlier detection config validation', () => {
const loadBalancingConfig = {
interval: {
seconds: 0,
nanos: 1e12
nanos: 1e12,
},
child_policy: [{round_robin: {}}]
child_policy: [{ round_robin: {} }],
};
assert.throws(() => {
OutlierDetectionLoadBalancingConfig.createFromJson(loadBalancingConfig);
@ -173,9 +185,9 @@ describe('Outlier detection config validation', () => {
const loadBalancingConfig = {
base_ejection_time: {
seconds: -1,
nanos: 0
nanos: 0,
},
child_policy: [{round_robin: {}}]
child_policy: [{ round_robin: {} }],
};
assert.throws(() => {
OutlierDetectionLoadBalancingConfig.createFromJson(loadBalancingConfig);
@ -185,9 +197,9 @@ describe('Outlier detection config validation', () => {
const loadBalancingConfig = {
base_ejection_time: {
seconds: 1e12,
nanos: 0
nanos: 0,
},
child_policy: [{round_robin: {}}]
child_policy: [{ round_robin: {} }],
};
assert.throws(() => {
OutlierDetectionLoadBalancingConfig.createFromJson(loadBalancingConfig);
@ -197,9 +209,9 @@ describe('Outlier detection config validation', () => {
const loadBalancingConfig = {
base_ejection_time: {
seconds: 0,
nanos: -1
nanos: -1,
},
child_policy: [{round_robin: {}}]
child_policy: [{ round_robin: {} }],
};
assert.throws(() => {
OutlierDetectionLoadBalancingConfig.createFromJson(loadBalancingConfig);
@ -209,9 +221,9 @@ describe('Outlier detection config validation', () => {
const loadBalancingConfig = {
base_ejection_time: {
seconds: 0,
nanos: 1e12
nanos: 1e12,
},
child_policy: [{round_robin: {}}]
child_policy: [{ round_robin: {} }],
};
assert.throws(() => {
OutlierDetectionLoadBalancingConfig.createFromJson(loadBalancingConfig);
@ -223,9 +235,9 @@ describe('Outlier detection config validation', () => {
const loadBalancingConfig = {
max_ejection_time: {
seconds: -1,
nanos: 0
nanos: 0,
},
child_policy: [{round_robin: {}}]
child_policy: [{ round_robin: {} }],
};
assert.throws(() => {
OutlierDetectionLoadBalancingConfig.createFromJson(loadBalancingConfig);
@ -235,9 +247,9 @@ describe('Outlier detection config validation', () => {
const loadBalancingConfig = {
max_ejection_time: {
seconds: 1e12,
nanos: 0
nanos: 0,
},
child_policy: [{round_robin: {}}]
child_policy: [{ round_robin: {} }],
};
assert.throws(() => {
OutlierDetectionLoadBalancingConfig.createFromJson(loadBalancingConfig);
@ -247,9 +259,9 @@ describe('Outlier detection config validation', () => {
const loadBalancingConfig = {
max_ejection_time: {
seconds: 0,
nanos: -1
nanos: -1,
},
child_policy: [{round_robin: {}}]
child_policy: [{ round_robin: {} }],
};
assert.throws(() => {
OutlierDetectionLoadBalancingConfig.createFromJson(loadBalancingConfig);
@ -259,9 +271,9 @@ describe('Outlier detection config validation', () => {
const loadBalancingConfig = {
max_ejection_time: {
seconds: 0,
nanos: 1e12
nanos: 1e12,
},
child_policy: [{round_robin: {}}]
child_policy: [{ round_robin: {} }],
};
assert.throws(() => {
OutlierDetectionLoadBalancingConfig.createFromJson(loadBalancingConfig);
@ -272,7 +284,7 @@ describe('Outlier detection config validation', () => {
it('Should reject a value above 100', () => {
const loadBalancingConfig = {
max_ejection_percent: 101,
child_policy: [{round_robin: {}}]
child_policy: [{ round_robin: {} }],
};
assert.throws(() => {
OutlierDetectionLoadBalancingConfig.createFromJson(loadBalancingConfig);
@ -281,7 +293,7 @@ describe('Outlier detection config validation', () => {
it('Should reject a negative value', () => {
const loadBalancingConfig = {
max_ejection_percent: -1,
child_policy: [{round_robin: {}}]
child_policy: [{ round_robin: {} }],
};
assert.throws(() => {
OutlierDetectionLoadBalancingConfig.createFromJson(loadBalancingConfig);
@ -292,9 +304,9 @@ describe('Outlier detection config validation', () => {
it('Should reject a value above 100', () => {
const loadBalancingConfig = {
success_rate_ejection: {
enforcement_percentage: 101
enforcement_percentage: 101,
},
child_policy: [{round_robin: {}}]
child_policy: [{ round_robin: {} }],
};
assert.throws(() => {
OutlierDetectionLoadBalancingConfig.createFromJson(loadBalancingConfig);
@ -303,9 +315,9 @@ describe('Outlier detection config validation', () => {
it('Should reject a negative value', () => {
const loadBalancingConfig = {
success_rate_ejection: {
enforcement_percentage: -1
enforcement_percentage: -1,
},
child_policy: [{round_robin: {}}]
child_policy: [{ round_robin: {} }],
};
assert.throws(() => {
OutlierDetectionLoadBalancingConfig.createFromJson(loadBalancingConfig);
@ -316,9 +328,9 @@ describe('Outlier detection config validation', () => {
it('Should reject a value above 100', () => {
const loadBalancingConfig = {
failure_percentage_ejection: {
threshold: 101
threshold: 101,
},
child_policy: [{round_robin: {}}]
child_policy: [{ round_robin: {} }],
};
assert.throws(() => {
OutlierDetectionLoadBalancingConfig.createFromJson(loadBalancingConfig);
@ -327,9 +339,9 @@ describe('Outlier detection config validation', () => {
it('Should reject a negative value', () => {
const loadBalancingConfig = {
failure_percentage_ejection: {
threshold: -1
threshold: -1,
},
child_policy: [{round_robin: {}}]
child_policy: [{ round_robin: {} }],
};
assert.throws(() => {
OutlierDetectionLoadBalancingConfig.createFromJson(loadBalancingConfig);
@ -340,9 +352,9 @@ describe('Outlier detection config validation', () => {
it('Should reject a value above 100', () => {
const loadBalancingConfig = {
failure_percentage_ejection: {
enforcement_percentage: 101
enforcement_percentage: 101,
},
child_policy: [{round_robin: {}}]
child_policy: [{ round_robin: {} }],
};
assert.throws(() => {
OutlierDetectionLoadBalancingConfig.createFromJson(loadBalancingConfig);
@ -351,9 +363,9 @@ describe('Outlier detection config validation', () => {
it('Should reject a negative value', () => {
const loadBalancingConfig = {
failure_percentage_ejection: {
enforcement_percentage: -1
enforcement_percentage: -1,
},
child_policy: [{round_robin: {}}]
child_policy: [{ round_robin: {} }],
};
assert.throws(() => {
OutlierDetectionLoadBalancingConfig.createFromJson(loadBalancingConfig);
@ -377,32 +389,44 @@ describe('Outlier detection', () => {
goodServer = new grpc.Server();
goodServer.addService(EchoService.service, goodService);
for (let i = 0; i < GOOD_PORTS; i++) {
goodServer.bindAsync('localhost:0', grpc.ServerCredentials.createInsecure(), (error, port) => {
goodServer.bindAsync(
'localhost:0',
grpc.ServerCredentials.createInsecure(),
(error, port) => {
if (error) {
eachDone(error);
return;
}
goodPorts.push(port);
eachDone();
}
);
}
badServer = new grpc.Server();
badServer.addService(EchoService.service, badService);
badServer.bindAsync(
'localhost:0',
grpc.ServerCredentials.createInsecure(),
(error, port) => {
if (error) {
eachDone(error);
return;
}
goodPorts.push(port);
badPort = port;
eachDone();
});
}
badServer = new grpc.Server();
badServer.addService(EchoService.service, badService);
badServer.bindAsync('localhost:0', grpc.ServerCredentials.createInsecure(), (error, port) => {
if (error) {
eachDone(error);
return;
}
badPort = port;
eachDone();
});
);
});
after(() => {
goodServer.forceShutdown();
badServer.forceShutdown();
});
function makeManyRequests(makeOneRequest: (callback: (error?: Error) => void) => void, total: number, callback: (error?: Error) => void) {
function makeManyRequests(
makeOneRequest: (callback: (error?: Error) => void) => void,
total: number,
callback: (error?: Error) => void
) {
if (total === 0) {
callback();
return;
@ -417,7 +441,11 @@ describe('Outlier detection', () => {
}
it('Should allow normal operation with one server', done => {
const client = new EchoService(`localhost:${goodPorts[0]}`, grpc.credentials.createInsecure(), {'grpc.service_config': defaultOutlierDetectionServiceConfigString});
const client = new EchoService(
`localhost:${goodPorts[0]}`,
grpc.credentials.createInsecure(),
{ 'grpc.service_config': defaultOutlierDetectionServiceConfigString }
);
client.echo(
{ value: 'test value', value2: 3 },
(error: grpc.ServiceError, response: any) => {
@ -429,10 +457,19 @@ describe('Outlier detection', () => {
});
describe('Success rate', () => {
let makeCheckedRequest: (callback: () => void) => void;
let makeUncheckedRequest:(callback: (error?: Error) => void) => void;
let makeUncheckedRequest: (callback: (error?: Error) => void) => void;
before(() => {
const target = 'ipv4:///' + goodPorts.map(port => `127.0.0.1:${port}`).join(',') + `,127.0.0.1:${badPort}`;
const client = new EchoService(target, grpc.credentials.createInsecure(), {'grpc.service_config': successRateOutlierDetectionServiceConfigString});
const target =
'ipv4:///' +
goodPorts.map(port => `127.0.0.1:${port}`).join(',') +
`,127.0.0.1:${badPort}`;
const client = new EchoService(
target,
grpc.credentials.createInsecure(),
{
'grpc.service_config': successRateOutlierDetectionServiceConfigString,
}
);
makeUncheckedRequest = (callback: () => void) => {
client.echo(
{ value: 'test value', value2: 3 },
@ -460,7 +497,7 @@ describe('Outlier detection', () => {
}, 1000);
});
});
it('Should uneject a server after the ejection period', function(done) {
it('Should uneject a server after the ejection period', function (done) {
this.timeout(5000);
makeManyRequests(makeUncheckedRequest, 50, () => {
setTimeout(() => {
@ -477,15 +514,25 @@ describe('Outlier detection', () => {
}, 3000);
});
}, 1000);
})
});
});
});
describe('Failure percentage', () => {
let makeCheckedRequest: (callback: () => void) => void;
let makeUncheckedRequest:(callback: (error?: Error) => void) => void;
let makeUncheckedRequest: (callback: (error?: Error) => void) => void;
before(() => {
const target = 'ipv4:///' + goodPorts.map(port => `127.0.0.1:${port}`).join(',') + `,127.0.0.1:${badPort}`;
const client = new EchoService(target, grpc.credentials.createInsecure(), {'grpc.service_config': falurePercentageOutlierDetectionServiceConfigString});
const target =
'ipv4:///' +
goodPorts.map(port => `127.0.0.1:${port}`).join(',') +
`,127.0.0.1:${badPort}`;
const client = new EchoService(
target,
grpc.credentials.createInsecure(),
{
'grpc.service_config':
falurePercentageOutlierDetectionServiceConfigString,
}
);
makeUncheckedRequest = (callback: () => void) => {
client.echo(
{ value: 'test value', value2: 3 },
@ -513,7 +560,7 @@ describe('Outlier detection', () => {
}, 1000);
});
});
it('Should uneject a server after the ejection period', function(done) {
it('Should uneject a server after the ejection period', function (done) {
this.timeout(5000);
makeManyRequests(makeUncheckedRequest, 50, () => {
setTimeout(() => {
@ -530,7 +577,7 @@ describe('Outlier detection', () => {
}, 3000);
});
}, 1000);
})
});
});
});
});
});

View File

@ -21,11 +21,11 @@ import { loadPackageDefinition } from '../src';
describe('loadPackageDefinition', () => {
it('Should not allow prototype pollution', () => {
loadPackageDefinition({'__proto__.polluted': true} as any);
assert.notStrictEqual(({} as any).polluted, true);
loadPackageDefinition({ '__proto__.polluted': true } as any);
assert.notStrictEqual(({} as any).polluted, true);
});
it('Should not allow prototype pollution #2', () => {
loadPackageDefinition({'constructor.prototype.polluted': true} as any);
assert.notStrictEqual(({} as any).polluted, true);
loadPackageDefinition({ 'constructor.prototype.polluted': true } as any);
assert.notStrictEqual(({} as any).polluted, true);
});
});

View File

@ -24,7 +24,11 @@ import * as resolver_uds from '../src/resolver-uds';
import * as resolver_ip from '../src/resolver-ip';
import { ServiceConfig } from '../src/service-config';
import { StatusObject } from '../src/call-interface';
import { SubchannelAddress, isTcpSubchannelAddress, subchannelAddressToString } from "../src/subchannel-address";
import {
SubchannelAddress,
isTcpSubchannelAddress,
subchannelAddressToString,
} from '../src/subchannel-address';
import { parseUri, GrpcUri } from '../src/uri-parser';
describe('Name Resolver', () => {
@ -33,11 +37,13 @@ describe('Name Resolver', () => {
resolver_uds.setup();
resolver_ip.setup();
});
describe('DNS Names', function() {
describe('DNS Names', function () {
// For some reason DNS queries sometimes take a long time on Windows
this.timeout(4000);
it('Should resolve localhost properly', done => {
const target = resolverManager.mapUriDefaultScheme(parseUri('localhost:50051')!)!;
const target = resolverManager.mapUriDefaultScheme(
parseUri('localhost:50051')!
)!;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
@ -72,7 +78,9 @@ describe('Name Resolver', () => {
resolver.updateResolution();
});
it('Should default to port 443', done => {
const target = resolverManager.mapUriDefaultScheme(parseUri('localhost')!)!;
const target = resolverManager.mapUriDefaultScheme(
parseUri('localhost')!
)!;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
@ -161,7 +169,9 @@ describe('Name Resolver', () => {
resolver.updateResolution();
});
it('Should correctly represent a bracketed ipv6 address', done => {
const target = resolverManager.mapUriDefaultScheme(parseUri('[::1]:50051')!)!;
const target = resolverManager.mapUriDefaultScheme(
parseUri('[::1]:50051')!
)!;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
@ -188,7 +198,9 @@ describe('Name Resolver', () => {
resolver.updateResolution();
});
it('Should resolve a public address', done => {
const target = resolverManager.mapUriDefaultScheme(parseUri('example.com')!)!;
const target = resolverManager.mapUriDefaultScheme(
parseUri('example.com')!
)!;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
@ -210,7 +222,9 @@ describe('Name Resolver', () => {
// Created DNS TXT record using TXT sample from https://github.com/grpc/proposal/blob/master/A2-service-configs-in-dns.md
// "grpc_config=[{\"serviceConfig\":{\"loadBalancingPolicy\":\"round_robin\",\"methodConfig\":[{\"name\":[{\"service\":\"MyService\",\"method\":\"Foo\"}],\"waitForReady\":true}]}}]"
it.skip('Should resolve a name with TXT service config', done => {
const target = resolverManager.mapUriDefaultScheme(parseUri('grpctest.kleinsch.com')!)!;
const target = resolverManager.mapUriDefaultScheme(
parseUri('grpctest.kleinsch.com')!
)!;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
@ -232,39 +246,36 @@ describe('Name Resolver', () => {
const resolver = resolverManager.createResolver(target, listener, {});
resolver.updateResolution();
});
it.skip(
'Should not resolve TXT service config if we disabled service config',
(done) => {
const target = resolverManager.mapUriDefaultScheme(
parseUri('grpctest.kleinsch.com')!
)!;
let count = 0;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
serviceConfig: ServiceConfig | null,
serviceConfigError: StatusObject | null
) => {
assert(
serviceConfig === null,
'Should not have found service config'
);
count++;
},
onError: (error: StatusObject) => {
done(new Error(`Failed with status ${error.details}`));
},
};
const resolver = resolverManager.createResolver(target, listener, {
'grpc.service_config_disable_resolution': 1,
});
resolver.updateResolution();
setTimeout(() => {
assert(count === 1, 'Should have only resolved once');
done();
}, 2_000);
}
);
it.skip('Should not resolve TXT service config if we disabled service config', done => {
const target = resolverManager.mapUriDefaultScheme(
parseUri('grpctest.kleinsch.com')!
)!;
let count = 0;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
serviceConfig: ServiceConfig | null,
serviceConfigError: StatusObject | null
) => {
assert(
serviceConfig === null,
'Should not have found service config'
);
count++;
},
onError: (error: StatusObject) => {
done(new Error(`Failed with status ${error.details}`));
},
};
const resolver = resolverManager.createResolver(target, listener, {
'grpc.service_config_disable_resolution': 1,
});
resolver.updateResolution();
setTimeout(() => {
assert(count === 1, 'Should have only resolved once');
done();
}, 2_000);
});
/* The DNS entry for loopback4.unittest.grpc.io only has a single A record
* with the address 127.0.0.1, but the Mac DNS resolver appears to use
* NAT64 to create an IPv6 address in that case, so it instead returns
@ -274,7 +285,9 @@ describe('Name Resolver', () => {
* and the test 'Should resolve gRPC interop servers' tests the same thing.
*/
it.skip('Should resolve a name with multiple dots', done => {
const target = resolverManager.mapUriDefaultScheme(parseUri('loopback4.unittest.grpc.io')!)!;
const target = resolverManager.mapUriDefaultScheme(
parseUri('loopback4.unittest.grpc.io')!
)!;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
@ -289,7 +302,10 @@ describe('Name Resolver', () => {
isTcpSubchannelAddress(addr) &&
addr.host === '127.0.0.1' &&
addr.port === 443
), `None of [${addressList.map(addr => subchannelAddressToString(addr))}] matched '127.0.0.1:443'`
),
`None of [${addressList.map(addr =>
subchannelAddressToString(addr)
)}] matched '127.0.0.1:443'`
);
done();
},
@ -303,7 +319,9 @@ describe('Name Resolver', () => {
/* TODO(murgatroid99): re-enable this test, once we can get the IPv6 result
* consistently */
it.skip('Should resolve a DNS name to an IPv6 address', done => {
const target = resolverManager.mapUriDefaultScheme(parseUri('loopback6.unittest.grpc.io')!)!;
const target = resolverManager.mapUriDefaultScheme(
parseUri('loopback6.unittest.grpc.io')!
)!;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
@ -333,7 +351,9 @@ describe('Name Resolver', () => {
* IPv6 address on Mac. There is no result that we can consistently test
* for here. */
it.skip('Should resolve a DNS name to IPv4 and IPv6 addresses', done => {
const target = resolverManager.mapUriDefaultScheme(parseUri('loopback46.unittest.grpc.io')!)!;
const target = resolverManager.mapUriDefaultScheme(
parseUri('loopback46.unittest.grpc.io')!
)!;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
@ -348,7 +368,10 @@ describe('Name Resolver', () => {
isTcpSubchannelAddress(addr) &&
addr.host === '127.0.0.1' &&
addr.port === 443
), `None of [${addressList.map(addr => subchannelAddressToString(addr))}] matched '127.0.0.1:443'`
),
`None of [${addressList.map(addr =>
subchannelAddressToString(addr)
)}] matched '127.0.0.1:443'`
);
/* TODO(murgatroid99): check for IPv6 result, once we can get that
* consistently */
@ -364,7 +387,9 @@ describe('Name Resolver', () => {
it('Should resolve a name with a hyphen', done => {
/* TODO(murgatroid99): Find or create a better domain name to test this with.
* This is just the first one I found with a hyphen. */
const target = resolverManager.mapUriDefaultScheme(parseUri('network-tools.com')!)!;
const target = resolverManager.mapUriDefaultScheme(
parseUri('network-tools.com')!
)!;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
@ -389,8 +414,12 @@ describe('Name Resolver', () => {
* unless there is another test for the same issue. */
it('Should resolve gRPC interop servers', done => {
let completeCount = 0;
const target1 = resolverManager.mapUriDefaultScheme(parseUri('grpc-test.sandbox.googleapis.com')!)!;
const target2 = resolverManager.mapUriDefaultScheme(parseUri('grpc-test4.sandbox.googleapis.com')!)!;
const target1 = resolverManager.mapUriDefaultScheme(
parseUri('grpc-test.sandbox.googleapis.com')!
)!;
const target2 = resolverManager.mapUriDefaultScheme(
parseUri('grpc-test4.sandbox.googleapis.com')!
)!;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
@ -415,39 +444,45 @@ describe('Name Resolver', () => {
resolver2.updateResolution();
});
it('should not keep repeating successful resolutions', done => {
const target = resolverManager.mapUriDefaultScheme(parseUri('localhost')!)!;
const target = resolverManager.mapUriDefaultScheme(
parseUri('localhost')!
)!;
let resultCount = 0;
const resolver = resolverManager.createResolver(target, {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
serviceConfig: ServiceConfig | null,
serviceConfigError: StatusObject | null
) => {
assert(
addressList.some(
addr =>
isTcpSubchannelAddress(addr) &&
addr.host === '127.0.0.1' &&
addr.port === 443
)
);
assert(
addressList.some(
addr =>
isTcpSubchannelAddress(addr) &&
addr.host === '::1' &&
addr.port === 443
)
);
resultCount += 1;
if (resultCount === 1) {
process.nextTick(() => resolver.updateResolution());
}
const resolver = resolverManager.createResolver(
target,
{
onSuccessfulResolution: (
addressList: SubchannelAddress[],
serviceConfig: ServiceConfig | null,
serviceConfigError: StatusObject | null
) => {
assert(
addressList.some(
addr =>
isTcpSubchannelAddress(addr) &&
addr.host === '127.0.0.1' &&
addr.port === 443
)
);
assert(
addressList.some(
addr =>
isTcpSubchannelAddress(addr) &&
addr.host === '::1' &&
addr.port === 443
)
);
resultCount += 1;
if (resultCount === 1) {
process.nextTick(() => resolver.updateResolution());
}
},
onError: (error: StatusObject) => {
assert.ifError(error);
},
},
onError: (error: StatusObject) => {
assert.ifError(error);
},
}, {'grpc.dns_min_time_between_resolutions_ms': 2000});
{ 'grpc.dns_min_time_between_resolutions_ms': 2000 }
);
resolver.updateResolution();
setTimeout(() => {
assert.strictEqual(resultCount, 2, `resultCount ${resultCount} !== 2`);
@ -455,23 +490,29 @@ describe('Name Resolver', () => {
}, 10_000);
}).timeout(15_000);
it('should not keep repeating failed resolutions', done => {
const target = resolverManager.mapUriDefaultScheme(parseUri('host.invalid')!)!;
const target = resolverManager.mapUriDefaultScheme(
parseUri('host.invalid')!
)!;
let resultCount = 0;
const resolver = resolverManager.createResolver(target, {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
serviceConfig: ServiceConfig | null,
serviceConfigError: StatusObject | null
) => {
assert.fail('Resolution succeeded unexpectedly');
const resolver = resolverManager.createResolver(
target,
{
onSuccessfulResolution: (
addressList: SubchannelAddress[],
serviceConfig: ServiceConfig | null,
serviceConfigError: StatusObject | null
) => {
assert.fail('Resolution succeeded unexpectedly');
},
onError: (error: StatusObject) => {
resultCount += 1;
if (resultCount === 1) {
process.nextTick(() => resolver.updateResolution());
}
},
},
onError: (error: StatusObject) => {
resultCount += 1;
if (resultCount === 1) {
process.nextTick(() => resolver.updateResolution());
}
},
}, {});
{}
);
resolver.updateResolution();
setTimeout(() => {
assert.strictEqual(resultCount, 2, `resultCount ${resultCount} !== 2`);
@ -481,7 +522,9 @@ describe('Name Resolver', () => {
});
describe('UDS Names', () => {
it('Should handle a relative Unix Domain Socket name', done => {
const target = resolverManager.mapUriDefaultScheme(parseUri('unix:socket')!)!;
const target = resolverManager.mapUriDefaultScheme(
parseUri('unix:socket')!
)!;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
@ -505,7 +548,9 @@ describe('Name Resolver', () => {
resolver.updateResolution();
});
it('Should handle an absolute Unix Domain Socket name', done => {
const target = resolverManager.mapUriDefaultScheme(parseUri('unix:///tmp/socket')!)!;
const target = resolverManager.mapUriDefaultScheme(
parseUri('unix:///tmp/socket')!
)!;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
@ -532,7 +577,9 @@ describe('Name Resolver', () => {
});
describe('IP Addresses', () => {
it('should handle one IPv4 address with no port', done => {
const target = resolverManager.mapUriDefaultScheme(parseUri('ipv4:127.0.0.1')!)!;
const target = resolverManager.mapUriDefaultScheme(
parseUri('ipv4:127.0.0.1')!
)!;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
@ -559,7 +606,9 @@ describe('Name Resolver', () => {
resolver.updateResolution();
});
it('should handle one IPv4 address with a port', done => {
const target = resolverManager.mapUriDefaultScheme(parseUri('ipv4:127.0.0.1:50051')!)!;
const target = resolverManager.mapUriDefaultScheme(
parseUri('ipv4:127.0.0.1:50051')!
)!;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
@ -586,7 +635,9 @@ describe('Name Resolver', () => {
resolver.updateResolution();
});
it('should handle multiple IPv4 addresses with different ports', done => {
const target = resolverManager.mapUriDefaultScheme(parseUri('ipv4:127.0.0.1:50051,127.0.0.1:50052')!)!;
const target = resolverManager.mapUriDefaultScheme(
parseUri('ipv4:127.0.0.1:50051,127.0.0.1:50052')!
)!;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
@ -621,7 +672,9 @@ describe('Name Resolver', () => {
resolver.updateResolution();
});
it('should handle one IPv6 address with no port', done => {
const target = resolverManager.mapUriDefaultScheme(parseUri('ipv6:::1')!)!;
const target = resolverManager.mapUriDefaultScheme(
parseUri('ipv6:::1')!
)!;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
@ -648,7 +701,9 @@ describe('Name Resolver', () => {
resolver.updateResolution();
});
it('should handle one IPv6 address with a port', done => {
const target = resolverManager.mapUriDefaultScheme(parseUri('ipv6:[::1]:50051')!)!;
const target = resolverManager.mapUriDefaultScheme(
parseUri('ipv6:[::1]:50051')!
)!;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
@ -675,7 +730,9 @@ describe('Name Resolver', () => {
resolver.updateResolution();
});
it('should handle multiple IPv6 addresses with different ports', done => {
const target = resolverManager.mapUriDefaultScheme(parseUri('ipv6:[::1]:50051,[::1]:50052')!)!;
const target = resolverManager.mapUriDefaultScheme(
parseUri('ipv6:[::1]:50051,[::1]:50052')!
)!;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
@ -716,8 +773,7 @@ describe('Name Resolver', () => {
return [];
}
destroy() {
}
destroy() {}
static getDefaultAuthority(target: GrpcUri): string {
return 'other';
@ -726,7 +782,9 @@ describe('Name Resolver', () => {
it('Should return the correct authority if a different resolver has been registered', () => {
resolverManager.registerResolver('other', OtherResolver);
const target = resolverManager.mapUriDefaultScheme(parseUri('other:name')!)!;
const target = resolverManager.mapUriDefaultScheme(
parseUri('other:name')!
)!;
console.log(target);
const authority = resolverManager.getDefaultAuthority(target);

View File

@ -15,22 +15,24 @@
*
*/
import assert = require("assert");
import { validateServiceConfig } from "../src/service-config";
import assert = require('assert');
import { validateServiceConfig } from '../src/service-config';
function createRetryServiceConfig(retryConfig: object): object {
return {
loadBalancingConfig: [],
methodConfig: [
{
name: [{
service: 'A',
method: 'B'
}],
name: [
{
service: 'A',
method: 'B',
},
],
retryPolicy: retryConfig
}
]
retryPolicy: retryConfig,
},
],
};
}
@ -39,14 +41,16 @@ function createHedgingServiceConfig(hedgingConfig: object): object {
loadBalancingConfig: [],
methodConfig: [
{
name: [{
service: 'A',
method: 'B'
}],
name: [
{
service: 'A',
method: 'B',
},
],
hedgingPolicy: hedgingConfig
}
]
hedgingPolicy: hedgingConfig,
},
],
};
}
@ -54,7 +58,7 @@ function createThrottlingServiceConfig(retryThrottling: object): object {
return {
loadBalancingConfig: [],
methodConfig: [],
retryThrottling: retryThrottling
retryThrottling: retryThrottling,
};
}
@ -69,7 +73,7 @@ const validRetryConfig = {
initialBackoff: '1s',
maxBackoff: '1s',
backoffMultiplier: 1,
retryableStatusCodes: [14, 'RESOURCE_EXHAUSTED']
retryableStatusCodes: [14, 'RESOURCE_EXHAUSTED'],
};
const RETRY_TEST_CASES: TestCase[] = [
@ -79,14 +83,14 @@ const RETRY_TEST_CASES: TestCase[] = [
initialBackoff: '1s',
maxBackoff: '1s',
backoffMultiplier: 1,
retryableStatusCodes: [14]
retryableStatusCodes: [14],
},
error: /retry policy: maxAttempts must be an integer at least 2/
error: /retry policy: maxAttempts must be an integer at least 2/,
},
{
description: 'a low maxAttempts',
config: {...validRetryConfig, maxAttempts: 1},
error: /retry policy: maxAttempts must be an integer at least 2/
config: { ...validRetryConfig, maxAttempts: 1 },
error: /retry policy: maxAttempts must be an integer at least 2/,
},
{
description: 'omitted initialBackoff',
@ -94,19 +98,22 @@ const RETRY_TEST_CASES: TestCase[] = [
maxAttempts: 2,
maxBackoff: '1s',
backoffMultiplier: 1,
retryableStatusCodes: [14]
retryableStatusCodes: [14],
},
error: /retry policy: initialBackoff must be a string consisting of a positive integer followed by s/
error:
/retry policy: initialBackoff must be a string consisting of a positive integer followed by s/,
},
{
description: 'a non-numeric initialBackoff',
config: {...validRetryConfig, initialBackoff: 'abcs'},
error: /retry policy: initialBackoff must be a string consisting of a positive integer followed by s/
config: { ...validRetryConfig, initialBackoff: 'abcs' },
error:
/retry policy: initialBackoff must be a string consisting of a positive integer followed by s/,
},
{
description: 'an initialBackoff without an s',
config: {...validRetryConfig, initialBackoff: '123'},
error: /retry policy: initialBackoff must be a string consisting of a positive integer followed by s/
config: { ...validRetryConfig, initialBackoff: '123' },
error:
/retry policy: initialBackoff must be a string consisting of a positive integer followed by s/,
},
{
description: 'omitted maxBackoff',
@ -114,19 +121,22 @@ const RETRY_TEST_CASES: TestCase[] = [
maxAttempts: 2,
initialBackoff: '1s',
backoffMultiplier: 1,
retryableStatusCodes: [14]
retryableStatusCodes: [14],
},
error: /retry policy: maxBackoff must be a string consisting of a positive integer followed by s/
error:
/retry policy: maxBackoff must be a string consisting of a positive integer followed by s/,
},
{
description: 'a non-numeric maxBackoff',
config: {...validRetryConfig, maxBackoff: 'abcs'},
error: /retry policy: maxBackoff must be a string consisting of a positive integer followed by s/
config: { ...validRetryConfig, maxBackoff: 'abcs' },
error:
/retry policy: maxBackoff must be a string consisting of a positive integer followed by s/,
},
{
description: 'an maxBackoff without an s',
config: {...validRetryConfig, maxBackoff: '123'},
error: /retry policy: maxBackoff must be a string consisting of a positive integer followed by s/
config: { ...validRetryConfig, maxBackoff: '123' },
error:
/retry policy: maxBackoff must be a string consisting of a positive integer followed by s/,
},
{
description: 'omitted backoffMultiplier',
@ -134,14 +144,14 @@ const RETRY_TEST_CASES: TestCase[] = [
maxAttempts: 2,
initialBackoff: '1s',
maxBackoff: '1s',
retryableStatusCodes: [14]
retryableStatusCodes: [14],
},
error: /retry policy: backoffMultiplier must be a number greater than 0/
error: /retry policy: backoffMultiplier must be a number greater than 0/,
},
{
description: 'a negative backoffMultiplier',
config: {...validRetryConfig, backoffMultiplier: -1},
error: /retry policy: backoffMultiplier must be a number greater than 0/
config: { ...validRetryConfig, backoffMultiplier: -1 },
error: /retry policy: backoffMultiplier must be a number greater than 0/,
},
{
description: 'omitted retryableStatusCodes',
@ -149,95 +159,97 @@ const RETRY_TEST_CASES: TestCase[] = [
maxAttempts: 2,
initialBackoff: '1s',
maxBackoff: '1s',
backoffMultiplier: 1
backoffMultiplier: 1,
},
error: /retry policy: retryableStatusCodes is required/
error: /retry policy: retryableStatusCodes is required/,
},
{
description: 'empty retryableStatusCodes',
config: {...validRetryConfig, retryableStatusCodes: []},
error: /retry policy: retryableStatusCodes must be non-empty/
config: { ...validRetryConfig, retryableStatusCodes: [] },
error: /retry policy: retryableStatusCodes must be non-empty/,
},
{
description: 'unknown status code name',
config: {...validRetryConfig, retryableStatusCodes: ['abcd']},
error: /retry policy: retryableStatusCodes value not a status code name/
config: { ...validRetryConfig, retryableStatusCodes: ['abcd'] },
error: /retry policy: retryableStatusCodes value not a status code name/,
},
{
description: 'out of range status code number',
config: {...validRetryConfig, retryableStatusCodes: [12345]},
error: /retry policy: retryableStatusCodes value not in status code range/
}
config: { ...validRetryConfig, retryableStatusCodes: [12345] },
error: /retry policy: retryableStatusCodes value not in status code range/,
},
];
const validHedgingConfig = {
maxAttempts: 2
maxAttempts: 2,
};
const HEDGING_TEST_CASES: TestCase[] = [
{
description: 'omitted maxAttempts',
config: {},
error: /hedging policy: maxAttempts must be an integer at least 2/
error: /hedging policy: maxAttempts must be an integer at least 2/,
},
{
description: 'a low maxAttempts',
config: {...validHedgingConfig, maxAttempts: 1},
error: /hedging policy: maxAttempts must be an integer at least 2/
config: { ...validHedgingConfig, maxAttempts: 1 },
error: /hedging policy: maxAttempts must be an integer at least 2/,
},
{
description: 'a non-numeric hedgingDelay',
config: {...validHedgingConfig, hedgingDelay: 'abcs'},
error: /hedging policy: hedgingDelay must be a string consisting of a positive integer followed by s/
config: { ...validHedgingConfig, hedgingDelay: 'abcs' },
error:
/hedging policy: hedgingDelay must be a string consisting of a positive integer followed by s/,
},
{
description: 'a hedgingDelay without an s',
config: {...validHedgingConfig, hedgingDelay: '123'},
error: /hedging policy: hedgingDelay must be a string consisting of a positive integer followed by s/
config: { ...validHedgingConfig, hedgingDelay: '123' },
error:
/hedging policy: hedgingDelay must be a string consisting of a positive integer followed by s/,
},
{
description: 'unknown status code name',
config: {...validHedgingConfig, nonFatalStatusCodes: ['abcd']},
error: /hedging policy: nonFatalStatusCodes value not a status code name/
config: { ...validHedgingConfig, nonFatalStatusCodes: ['abcd'] },
error: /hedging policy: nonFatalStatusCodes value not a status code name/,
},
{
description: 'out of range status code number',
config: {...validHedgingConfig, nonFatalStatusCodes: [12345]},
error: /hedging policy: nonFatalStatusCodes value not in status code range/
}
config: { ...validHedgingConfig, nonFatalStatusCodes: [12345] },
error: /hedging policy: nonFatalStatusCodes value not in status code range/,
},
];
const validThrottlingConfig = {
maxTokens: 100,
tokenRatio: 0.1
tokenRatio: 0.1,
};
const THROTTLING_TEST_CASES: TestCase[] = [
{
description: 'omitted maxTokens',
config: {tokenRatio: 0.1},
error: /retryThrottling: maxTokens must be a number in \(0, 1000\]/
config: { tokenRatio: 0.1 },
error: /retryThrottling: maxTokens must be a number in \(0, 1000\]/,
},
{
description: 'a large maxTokens',
config: {...validThrottlingConfig, maxTokens: 1001},
error: /retryThrottling: maxTokens must be a number in \(0, 1000\]/
config: { ...validThrottlingConfig, maxTokens: 1001 },
error: /retryThrottling: maxTokens must be a number in \(0, 1000\]/,
},
{
description: 'zero maxTokens',
config: {...validThrottlingConfig, maxTokens: 0},
error: /retryThrottling: maxTokens must be a number in \(0, 1000\]/
config: { ...validThrottlingConfig, maxTokens: 0 },
error: /retryThrottling: maxTokens must be a number in \(0, 1000\]/,
},
{
description: 'omitted tokenRatio',
config: {maxTokens: 100},
error: /retryThrottling: tokenRatio must be a number greater than 0/
config: { maxTokens: 100 },
error: /retryThrottling: tokenRatio must be a number greater than 0/,
},
{
description: 'zero tokenRatio',
config: {...validThrottlingConfig, tokenRatio: 0},
error: /retryThrottling: tokenRatio must be a number greater than 0/
}
config: { ...validThrottlingConfig, tokenRatio: 0 },
error: /retryThrottling: tokenRatio must be a number greater than 0/,
},
];
describe('Retry configs', () => {
@ -261,10 +273,20 @@ describe('Retry configs', () => {
validateServiceConfig(createHedgingServiceConfig(validHedgingConfig));
});
assert.doesNotThrow(() => {
validateServiceConfig(createHedgingServiceConfig({...validHedgingConfig, hedgingDelay: '1s'}));
validateServiceConfig(
createHedgingServiceConfig({
...validHedgingConfig,
hedgingDelay: '1s',
})
);
});
assert.doesNotThrow(() => {
validateServiceConfig(createHedgingServiceConfig({...validHedgingConfig, nonFatalStatusCodes: [14, 'RESOURCE_EXHAUSTED']}));
validateServiceConfig(
createHedgingServiceConfig({
...validHedgingConfig,
nonFatalStatusCodes: [14, 'RESOURCE_EXHAUSTED'],
})
);
});
});
for (const testCase of HEDGING_TEST_CASES) {
@ -278,7 +300,9 @@ describe('Retry configs', () => {
describe('Throttling', () => {
it('Should accept a valid config', () => {
assert.doesNotThrow(() => {
validateServiceConfig(createThrottlingServiceConfig(validThrottlingConfig));
validateServiceConfig(
createThrottlingServiceConfig(validThrottlingConfig)
);
});
});
for (const testCase of THROTTLING_TEST_CASES) {
@ -289,4 +313,4 @@ describe('Retry configs', () => {
});
}
});
});
});

View File

@ -25,37 +25,50 @@ const EchoService = loadProtoFile(protoFile)
.EchoService as grpc.ServiceClientConstructor;
const serviceImpl = {
echo: (call: grpc.ServerUnaryCall<any, any>, callback: grpc.sendUnaryData<any>) => {
echo: (
call: grpc.ServerUnaryCall<any, any>,
callback: grpc.sendUnaryData<any>
) => {
const succeedOnRetryAttempt = call.metadata.get('succeed-on-retry-attempt');
const previousAttempts = call.metadata.get('grpc-previous-rpc-attempts');
if (succeedOnRetryAttempt.length === 0 || (previousAttempts.length > 0 && previousAttempts[0] === succeedOnRetryAttempt[0])) {
if (
succeedOnRetryAttempt.length === 0 ||
(previousAttempts.length > 0 &&
previousAttempts[0] === succeedOnRetryAttempt[0])
) {
callback(null, call.request);
} else {
const statusCode = call.metadata.get('respond-with-status');
const code = statusCode[0] ? Number.parseInt(statusCode[0] as string) : grpc.status.UNKNOWN;
const code = statusCode[0]
? Number.parseInt(statusCode[0] as string)
: grpc.status.UNKNOWN;
callback({
code: code,
details: `Failed on retry ${previousAttempts[0] ?? 0}`
details: `Failed on retry ${previousAttempts[0] ?? 0}`,
});
}
}
}
},
};
describe('Retries', () => {
let server: grpc.Server;
let port: number;
before((done) => {
before(done => {
server = new grpc.Server();
server.addService(EchoService.service, serviceImpl);
server.bindAsync('localhost:0', grpc.ServerCredentials.createInsecure(), (error, portNumber) => {
if (error) {
done(error);
return;
server.bindAsync(
'localhost:0',
grpc.ServerCredentials.createInsecure(),
(error, portNumber) => {
if (error) {
done(error);
return;
}
port = portNumber;
server.start();
done();
}
port = portNumber;
server.start();
done();
});
);
});
after(() => {
@ -65,14 +78,18 @@ describe('Retries', () => {
describe('Client with retries disabled', () => {
let client: InstanceType<grpc.ServiceClientConstructor>;
before(() => {
client = new EchoService(`localhost:${port}`, grpc.credentials.createInsecure(), {'grpc.enable_retries': 0});
client = new EchoService(
`localhost:${port}`,
grpc.credentials.createInsecure(),
{ 'grpc.enable_retries': 0 }
);
});
after(() =>{
after(() => {
client.close();
});
it('Should be able to make a basic request', (done) => {
it('Should be able to make a basic request', done => {
client.echo(
{ value: 'test value', value2: 3 },
(error: grpc.ServiceError, response: any) => {
@ -83,7 +100,7 @@ describe('Retries', () => {
);
});
it('Should fail if the server fails the first request', (done) =>{
it('Should fail if the server fails the first request', done => {
const metadata = new grpc.Metadata();
metadata.set('succeed-on-retry-attempt', '1');
client.echo(
@ -101,14 +118,17 @@ describe('Retries', () => {
describe('Client with retries enabled but not configured', () => {
let client: InstanceType<grpc.ServiceClientConstructor>;
before(() => {
client = new EchoService(`localhost:${port}`, grpc.credentials.createInsecure());
client = new EchoService(
`localhost:${port}`,
grpc.credentials.createInsecure()
);
});
after(() =>{
after(() => {
client.close();
});
it('Should be able to make a basic request', (done) => {
it('Should be able to make a basic request', done => {
client.echo(
{ value: 'test value', value2: 3 },
(error: grpc.ServiceError, response: any) => {
@ -119,7 +139,7 @@ describe('Retries', () => {
);
});
it('Should fail if the server fails the first request', (done) =>{
it('Should fail if the server fails the first request', done => {
const metadata = new grpc.Metadata();
metadata.set('succeed-on-retry-attempt', '1');
client.echo(
@ -141,27 +161,33 @@ describe('Retries', () => {
loadBalancingConfig: [],
methodConfig: [
{
name: [{
service: 'EchoService'
}],
name: [
{
service: 'EchoService',
},
],
retryPolicy: {
maxAttempts: 3,
initialBackoff: '0.1s',
maxBackoff: '10s',
backoffMultiplier: 1.2,
retryableStatusCodes: [14, 'RESOURCE_EXHAUSTED']
}
}
]
}
client = new EchoService(`localhost:${port}`, grpc.credentials.createInsecure(), {'grpc.service_config': JSON.stringify(serviceConfig)});
retryableStatusCodes: [14, 'RESOURCE_EXHAUSTED'],
},
},
],
};
client = new EchoService(
`localhost:${port}`,
grpc.credentials.createInsecure(),
{ 'grpc.service_config': JSON.stringify(serviceConfig) }
);
});
after(() =>{
after(() => {
client.close();
});
it('Should be able to make a basic request', (done) => {
it('Should be able to make a basic request', done => {
client.echo(
{ value: 'test value', value2: 3 },
(error: grpc.ServiceError, response: any) => {
@ -172,7 +198,7 @@ describe('Retries', () => {
);
});
it('Should succeed with few required attempts', (done) => {
it('Should succeed with few required attempts', done => {
const metadata = new grpc.Metadata();
metadata.set('succeed-on-retry-attempt', '2');
metadata.set('respond-with-status', `${grpc.status.RESOURCE_EXHAUSTED}`);
@ -187,7 +213,7 @@ describe('Retries', () => {
);
});
it('Should fail with many required attempts', (done) => {
it('Should fail with many required attempts', done => {
const metadata = new grpc.Metadata();
metadata.set('succeed-on-retry-attempt', '4');
metadata.set('respond-with-status', `${grpc.status.RESOURCE_EXHAUSTED}`);
@ -202,7 +228,7 @@ describe('Retries', () => {
);
});
it('Should fail with a fatal status code', (done) => {
it('Should fail with a fatal status code', done => {
const metadata = new grpc.Metadata();
metadata.set('succeed-on-retry-attempt', '2');
metadata.set('respond-with-status', `${grpc.status.NOT_FOUND}`);
@ -217,25 +243,31 @@ describe('Retries', () => {
);
});
it('Should not be able to make more than 5 attempts', (done) => {
it('Should not be able to make more than 5 attempts', done => {
const serviceConfig = {
loadBalancingConfig: [],
methodConfig: [
{
name: [{
service: 'EchoService'
}],
name: [
{
service: 'EchoService',
},
],
retryPolicy: {
maxAttempts: 10,
initialBackoff: '0.1s',
maxBackoff: '10s',
backoffMultiplier: 1.2,
retryableStatusCodes: [14, 'RESOURCE_EXHAUSTED']
}
}
]
}
const client2 = new EchoService(`localhost:${port}`, grpc.credentials.createInsecure(), {'grpc.service_config': JSON.stringify(serviceConfig)});
retryableStatusCodes: [14, 'RESOURCE_EXHAUSTED'],
},
},
],
};
const client2 = new EchoService(
`localhost:${port}`,
grpc.credentials.createInsecure(),
{ 'grpc.service_config': JSON.stringify(serviceConfig) }
);
const metadata = new grpc.Metadata();
metadata.set('succeed-on-retry-attempt', '6');
metadata.set('respond-with-status', `${grpc.status.RESOURCE_EXHAUSTED}`);
@ -248,7 +280,7 @@ describe('Retries', () => {
done();
}
);
})
});
});
describe('Client with hedging configured', () => {
@ -258,24 +290,30 @@ describe('Retries', () => {
loadBalancingConfig: [],
methodConfig: [
{
name: [{
service: 'EchoService'
}],
name: [
{
service: 'EchoService',
},
],
hedgingPolicy: {
maxAttempts: 3,
nonFatalStatusCodes: [14, 'RESOURCE_EXHAUSTED']
}
}
]
}
client = new EchoService(`localhost:${port}`, grpc.credentials.createInsecure(), {'grpc.service_config': JSON.stringify(serviceConfig)});
nonFatalStatusCodes: [14, 'RESOURCE_EXHAUSTED'],
},
},
],
};
client = new EchoService(
`localhost:${port}`,
grpc.credentials.createInsecure(),
{ 'grpc.service_config': JSON.stringify(serviceConfig) }
);
});
after(() =>{
after(() => {
client.close();
});
it('Should be able to make a basic request', (done) => {
it('Should be able to make a basic request', done => {
client.echo(
{ value: 'test value', value2: 3 },
(error: grpc.ServiceError, response: any) => {
@ -286,7 +324,7 @@ describe('Retries', () => {
);
});
it('Should succeed with few required attempts', (done) => {
it('Should succeed with few required attempts', done => {
const metadata = new grpc.Metadata();
metadata.set('succeed-on-retry-attempt', '2');
metadata.set('respond-with-status', `${grpc.status.RESOURCE_EXHAUSTED}`);
@ -301,7 +339,7 @@ describe('Retries', () => {
);
});
it('Should fail with many required attempts', (done) => {
it('Should fail with many required attempts', done => {
const metadata = new grpc.Metadata();
metadata.set('succeed-on-retry-attempt', '4');
metadata.set('respond-with-status', `${grpc.status.RESOURCE_EXHAUSTED}`);
@ -316,7 +354,7 @@ describe('Retries', () => {
);
});
it('Should fail with a fatal status code', (done) => {
it('Should fail with a fatal status code', done => {
const metadata = new grpc.Metadata();
metadata.set('succeed-on-retry-attempt', '2');
metadata.set('respond-with-status', `${grpc.status.NOT_FOUND}`);
@ -331,22 +369,28 @@ describe('Retries', () => {
);
});
it('Should not be able to make more than 5 attempts', (done) => {
it('Should not be able to make more than 5 attempts', done => {
const serviceConfig = {
loadBalancingConfig: [],
methodConfig: [
{
name: [{
service: 'EchoService'
}],
name: [
{
service: 'EchoService',
},
],
hedgingPolicy: {
maxAttempts: 10,
nonFatalStatusCodes: [14, 'RESOURCE_EXHAUSTED']
}
}
]
}
const client2 = new EchoService(`localhost:${port}`, grpc.credentials.createInsecure(), {'grpc.service_config': JSON.stringify(serviceConfig)});
nonFatalStatusCodes: [14, 'RESOURCE_EXHAUSTED'],
},
},
],
};
const client2 = new EchoService(
`localhost:${port}`,
grpc.credentials.createInsecure(),
{ 'grpc.service_config': JSON.stringify(serviceConfig) }
);
const metadata = new grpc.Metadata();
metadata.set('succeed-on-retry-attempt', '6');
metadata.set('respond-with-status', `${grpc.status.RESOURCE_EXHAUSTED}`);
@ -359,6 +403,6 @@ describe('Retries', () => {
done();
}
);
})
});
});
});
});

View File

@ -42,7 +42,8 @@ describe('Server deadlines', () => {
before(done => {
const protoFile = path.join(__dirname, 'fixtures', 'test_service.proto');
const testServiceDef = loadProtoFile(protoFile);
const testServiceClient = testServiceDef.TestService as ServiceClientConstructor;
const testServiceClient =
testServiceDef.TestService as ServiceClientConstructor;
server = new Server();
server.addService(testServiceClient.service, {
@ -126,7 +127,8 @@ describe('Cancellation', () => {
before(done => {
const protoFile = path.join(__dirname, 'fixtures', 'test_service.proto');
const testServiceDef = loadProtoFile(protoFile);
const testServiceClient = testServiceDef.TestService as ServiceClientConstructor;
const testServiceClient =
testServiceDef.TestService as ServiceClientConstructor;
server = new Server();
server.addService(testServiceClient.service, {

View File

@ -36,7 +36,8 @@ import { loadProtoFile } from './common';
const protoFile = join(__dirname, 'fixtures', 'test_service.proto');
const testServiceDef = loadProtoFile(protoFile);
const testServiceClient = testServiceDef.TestService as ServiceClientConstructor;
const testServiceClient =
testServiceDef.TestService as ServiceClientConstructor;
const clientInsecureCreds = grpc.credentials.createInsecure();
const serverInsecureCreds = grpc.ServerCredentials.createInsecure();
@ -723,7 +724,7 @@ describe('Other conditions', () => {
});
describe('should handle server stream errors correctly', () => {
it('should emit data for all messages before error', (done) => {
it('should emit data for all messages before error', done => {
const expectedDataCount = 2;
const call = client.serverStream({ errorAfter: expectedDataCount });

View File

@ -27,23 +27,35 @@ import * as grpc from '../src';
import { Server, ServerCredentials } from '../src';
import { ServiceError } from '../src/call';
import { ServiceClient, ServiceClientConstructor } from '../src/make-client';
import { sendUnaryData, ServerUnaryCall, ServerDuplexStream } from '../src/server-call';
import {
sendUnaryData,
ServerUnaryCall,
ServerDuplexStream,
} from '../src/server-call';
import { assert2, loadProtoFile } from './common';
import { TestServiceClient, TestServiceHandlers } from './generated/TestService';
import {
TestServiceClient,
TestServiceHandlers,
} from './generated/TestService';
import { ProtoGrpcType as TestServiceGrpcType } from './generated/test_service';
import { Request__Output } from './generated/Request';
import { CompressionAlgorithms } from '../src/compression-algorithms';
const loadedTestServiceProto = protoLoader.loadSync(path.join(__dirname, 'fixtures/test_service.proto'), {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
const loadedTestServiceProto = protoLoader.loadSync(
path.join(__dirname, 'fixtures/test_service.proto'),
{
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
}
);
const testServiceGrpcObject = grpc.loadPackageDefinition(loadedTestServiceProto) as unknown as TestServiceGrpcType;
const testServiceGrpcObject = grpc.loadPackageDefinition(
loadedTestServiceProto
) as unknown as TestServiceGrpcType;
const ca = fs.readFileSync(path.join(__dirname, 'fixtures', 'ca.pem'));
const key = fs.readFileSync(path.join(__dirname, 'fixtures', 'server1.key'));
@ -134,7 +146,11 @@ describe('Server', () => {
}, /creds must be a ServerCredentials object/);
assert.throws(() => {
server.bindAsync('localhost:0', grpc.credentials.createInsecure() as any, noop);
server.bindAsync(
'localhost:0',
grpc.credentials.createInsecure() as any,
noop
);
}, /creds must be a ServerCredentials object/);
assert.throws(() => {
@ -286,7 +302,7 @@ describe('Server', () => {
}
};
methodsToVerify.forEach((method) => {
methodsToVerify.forEach(method => {
const call = client[method]({}, assertFailsWithUnimplementedError); // for unary
call.on('error', assertFailsWithUnimplementedError); // for streamed
});
@ -294,14 +310,12 @@ describe('Server', () => {
it('fails for non-object service definition argument', () => {
assert.throws(() => {
server.removeService('upsie' as any)
}, /removeService.*requires object as argument/
);
server.removeService('upsie' as any);
}, /removeService.*requires object as argument/);
});
});
describe('unregister', () => {
let server: Server;
let client: ServiceClient;
@ -313,7 +327,7 @@ describe('Server', () => {
server = new Server();
server.addService(mathServiceAttrs, {
div(call: ServerUnaryCall<any, any>, callback: sendUnaryData<any>) {
callback(null, {quotient: '42'});
callback(null, { quotient: '42' });
},
});
server.bindAsync(
@ -338,7 +352,11 @@ describe('Server', () => {
it('removes handler by name and returns true', done => {
const name = mathServiceAttrs['Div'].path;
assert.strictEqual(server.unregister(name), true, 'Server#unregister should return true on success');
assert.strictEqual(
server.unregister(name),
true,
'Server#unregister should return true on success'
);
client.div(
{ divisor: 4, dividend: 3 },
@ -351,7 +369,11 @@ describe('Server', () => {
});
it('returns false for unknown handler', () => {
assert.strictEqual(server.unregister('noOneHere'), false, 'Server#unregister should return false on failure');
assert.strictEqual(
server.unregister('noOneHere'),
false,
'Server#unregister should return false on failure'
);
});
});
@ -473,11 +495,10 @@ describe('Echo service', () => {
call.on('end', () => {
call.end();
});
}
},
};
before(done => {
server = new Server();
server.addService(echoService.service, serviceImplementation);
@ -517,36 +538,46 @@ describe('Echo service', () => {
it.skip('should continue a stream after server shutdown', done => {
const server2 = new Server();
server2.addService(echoService.service, serviceImplementation);
server2.bindAsync('localhost:0', ServerCredentials.createInsecure(), (err, port) => {
if (err) {
done(err);
return;
server2.bindAsync(
'localhost:0',
ServerCredentials.createInsecure(),
(err, port) => {
if (err) {
done(err);
return;
}
const client2 = new echoService(
`localhost:${port}`,
grpc.credentials.createInsecure()
);
server2.start();
const stream = client2.echoBidiStream();
const totalMessages = 5;
let messagesSent = 0;
stream.write({ value: 'test value', value2: messagesSent });
messagesSent += 1;
stream.on('data', () => {
if (messagesSent === 1) {
server2.tryShutdown(assert2.mustCall(() => {}));
}
if (messagesSent >= totalMessages) {
stream.end();
} else {
stream.write({ value: 'test value', value2: messagesSent });
messagesSent += 1;
}
});
stream.on(
'status',
assert2.mustCall((status: grpc.StatusObject) => {
assert.strictEqual(status.code, grpc.status.OK);
assert.strictEqual(messagesSent, totalMessages);
})
);
stream.on('error', () => {});
assert2.afterMustCallsSatisfied(done);
}
const client2 = new echoService(`localhost:${port}`, grpc.credentials.createInsecure());
server2.start();
const stream = client2.echoBidiStream();
const totalMessages = 5;
let messagesSent = 0;
stream.write({ value: 'test value', value2: messagesSent});
messagesSent += 1;
stream.on('data', () => {
if (messagesSent === 1) {
server2.tryShutdown(assert2.mustCall(() => {}));
}
if (messagesSent >= totalMessages) {
stream.end();
} else {
stream.write({ value: 'test value', value2: messagesSent});
messagesSent += 1;
}
});
stream.on('status', assert2.mustCall((status: grpc.StatusObject) => {
assert.strictEqual(status.code, grpc.status.OK);
assert.strictEqual(messagesSent, totalMessages);
}));
stream.on('error', () => {});
assert2.afterMustCallsSatisfied(done);
});
);
});
});
@ -703,7 +734,7 @@ describe('Compressed requests', () => {
call.on('end', () => {
call.end();
});
}
},
};
describe('Test service client and server with deflate', () => {
@ -728,7 +759,8 @@ describe('Compressed requests', () => {
`localhost:${assignedPort}`,
grpc.credentials.createInsecure(),
{
'grpc.default_compression_algorithm': CompressionAlgorithms.deflate
'grpc.default_compression_algorithm':
CompressionAlgorithms.deflate,
}
);
done();
@ -772,7 +804,7 @@ describe('Compressed requests', () => {
timesResponded += 1;
});
serverStream.on('error', (err) => {
serverStream.on('error', err => {
assert.ifError(err);
done();
});
@ -792,7 +824,7 @@ describe('Compressed requests', () => {
timesResponded += 1;
});
bidiStream.on('error', (err) => {
bidiStream.on('error', err => {
assert.ifError(err);
done();
});
@ -809,8 +841,8 @@ describe('Compressed requests', () => {
bidiStream.write({ message: 'baz' }, () => {
timesRequested += 1;
setTimeout(() => bidiStream.end(), 10);
})
})
});
});
});
});
@ -819,7 +851,7 @@ describe('Compressed requests', () => {
`localhost:${assignedPort}`,
grpc.credentials.createInsecure(),
{
'grpc.default_compression_algorithm': CompressionAlgorithms.gzip
'grpc.default_compression_algorithm': CompressionAlgorithms.gzip,
}
);
@ -853,7 +885,7 @@ describe('Compressed requests', () => {
timesResponded += 1;
});
serverStream.on('error', (err) => {
serverStream.on('error', err => {
assert.ifError(err);
done();
});
@ -873,7 +905,7 @@ describe('Compressed requests', () => {
timesResponded += 1;
});
bidiStream.on('error', (err) => {
bidiStream.on('error', err => {
assert.ifError(err);
done();
});
@ -890,25 +922,27 @@ describe('Compressed requests', () => {
bidiStream.write({ message: 'baz' }, () => {
timesRequested += 1;
setTimeout(() => bidiStream.end(), 10);
})
})
});
});
});
});
it('Should handle large messages', done => {
let longMessage = '';
for (let i = 0; i < 400000; i++) {
const letter = 'abcdefghijklmnopqrstuvwxyz'[Math.floor(Math.random() * 26)];
const letter = 'abcdefghijklmnopqrstuvwxyz'[
Math.floor(Math.random() * 26)
];
longMessage = longMessage + letter.repeat(10);
}
client.unary({message: longMessage}, (err, response) => {
client.unary({ message: longMessage }, (err, response) => {
assert.ifError(err);
assert.strictEqual(response?.message, longMessage);
done();
})
})
});
});
/* As of Node 16, Writable and Duplex streams validate the encoding
* argument to write, and the flags values we are passing there are not
* valid. We don't currently have an alternative way to pass that flag
@ -922,7 +956,7 @@ describe('Compressed requests', () => {
timesResponded += 1;
});
bidiStream.on('error', (err) => {
bidiStream.on('error', err => {
assert.ifError(err);
done();
});

View File

@ -19,59 +19,129 @@ import * as assert from 'assert';
import * as uriParser from '../src/uri-parser';
import * as resolver from '../src/resolver';
describe('URI Parser', function(){
describe('parseUri', function() {
const expectationList: {target: string, result: uriParser.GrpcUri | null}[] = [
{target: 'localhost', result: {scheme: undefined, authority: undefined, path: 'localhost'}},
describe('URI Parser', function () {
describe('parseUri', function () {
const expectationList: {
target: string;
result: uriParser.GrpcUri | null;
}[] = [
{
target: 'localhost',
result: { scheme: undefined, authority: undefined, path: 'localhost' },
},
/* This looks weird, but it's OK because the resolver selection code will handle it */
{target: 'localhost:80', result: {scheme: 'localhost', authority: undefined, path: '80'}},
{target: 'dns:localhost', result: {scheme: 'dns', authority: undefined, path: 'localhost'}},
{target: 'dns:///localhost', result: {scheme: 'dns', authority: '', path: 'localhost'}},
{target: 'dns://authority/localhost', result: {scheme: 'dns', authority: 'authority', path: 'localhost'}},
{target: '//authority/localhost', result: {scheme: undefined, authority: 'authority', path: 'localhost'}},
{
target: 'localhost:80',
result: { scheme: 'localhost', authority: undefined, path: '80' },
},
{
target: 'dns:localhost',
result: { scheme: 'dns', authority: undefined, path: 'localhost' },
},
{
target: 'dns:///localhost',
result: { scheme: 'dns', authority: '', path: 'localhost' },
},
{
target: 'dns://authority/localhost',
result: { scheme: 'dns', authority: 'authority', path: 'localhost' },
},
{
target: '//authority/localhost',
result: {
scheme: undefined,
authority: 'authority',
path: 'localhost',
},
},
// Regression test for https://github.com/grpc/grpc-node/issues/1359
{target: 'dns:foo-internal.aws-us-east-2.tracing.staging-edge.foo-data.net:443:443', result: {scheme: 'dns', authority: undefined, path: 'foo-internal.aws-us-east-2.tracing.staging-edge.foo-data.net:443:443'}}
{
target:
'dns:foo-internal.aws-us-east-2.tracing.staging-edge.foo-data.net:443:443',
result: {
scheme: 'dns',
authority: undefined,
path: 'foo-internal.aws-us-east-2.tracing.staging-edge.foo-data.net:443:443',
},
},
];
for (const {target, result} of expectationList) {
it (target, function() {
for (const { target, result } of expectationList) {
it(target, function () {
assert.deepStrictEqual(uriParser.parseUri(target), result);
});
}
});
describe('parseUri + mapUriDefaultScheme', function() {
const expectationList: {target: string, result: uriParser.GrpcUri | null}[] = [
{target: 'localhost', result: {scheme: 'dns', authority: undefined, path: 'localhost'}},
{target: 'localhost:80', result: {scheme: 'dns', authority: undefined, path: 'localhost:80'}},
{target: 'dns:localhost', result: {scheme: 'dns', authority: undefined, path: 'localhost'}},
{target: 'dns:///localhost', result: {scheme: 'dns', authority: '', path: 'localhost'}},
{target: 'dns://authority/localhost', result: {scheme: 'dns', authority: 'authority', path: 'localhost'}},
{target: 'unix:socket', result: {scheme: 'unix', authority: undefined, path: 'socket'}},
{target: 'bad:path', result: {scheme: 'dns', authority: undefined, path: 'bad:path'}}
describe('parseUri + mapUriDefaultScheme', function () {
const expectationList: {
target: string;
result: uriParser.GrpcUri | null;
}[] = [
{
target: 'localhost',
result: { scheme: 'dns', authority: undefined, path: 'localhost' },
},
{
target: 'localhost:80',
result: { scheme: 'dns', authority: undefined, path: 'localhost:80' },
},
{
target: 'dns:localhost',
result: { scheme: 'dns', authority: undefined, path: 'localhost' },
},
{
target: 'dns:///localhost',
result: { scheme: 'dns', authority: '', path: 'localhost' },
},
{
target: 'dns://authority/localhost',
result: { scheme: 'dns', authority: 'authority', path: 'localhost' },
},
{
target: 'unix:socket',
result: { scheme: 'unix', authority: undefined, path: 'socket' },
},
{
target: 'bad:path',
result: { scheme: 'dns', authority: undefined, path: 'bad:path' },
},
];
for (const {target, result} of expectationList) {
it(target, function() {
assert.deepStrictEqual(resolver.mapUriDefaultScheme(uriParser.parseUri(target) ?? {path: 'null'}), result);
})
for (const { target, result } of expectationList) {
it(target, function () {
assert.deepStrictEqual(
resolver.mapUriDefaultScheme(
uriParser.parseUri(target) ?? { path: 'null' }
),
result
);
});
}
});
describe('splitHostPort', function() {
const expectationList: {path: string, result: uriParser.HostPort | null}[] = [
{path: 'localhost', result: {host: 'localhost'}},
{path: 'localhost:123', result: {host: 'localhost', port: 123}},
{path: '12345:6789', result: {host: '12345', port: 6789}},
{path: '[::1]:123', result: {host: '::1', port: 123}},
{path: '[::1]', result: {host: '::1'}},
{path: '[', result: null},
{path: '[123]', result: null},
describe('splitHostPort', function () {
const expectationList: {
path: string;
result: uriParser.HostPort | null;
}[] = [
{ path: 'localhost', result: { host: 'localhost' } },
{ path: 'localhost:123', result: { host: 'localhost', port: 123 } },
{ path: '12345:6789', result: { host: '12345', port: 6789 } },
{ path: '[::1]:123', result: { host: '::1', port: 123 } },
{ path: '[::1]', result: { host: '::1' } },
{ path: '[', result: null },
{ path: '[123]', result: null },
// Regression test for https://github.com/grpc/grpc-node/issues/1359
{path: 'foo-internal.aws-us-east-2.tracing.staging-edge.foo-data.net:443:443', result: {host: 'foo-internal.aws-us-east-2.tracing.staging-edge.foo-data.net:443:443'}}
{
path: 'foo-internal.aws-us-east-2.tracing.staging-edge.foo-data.net:443:443',
result: {
host: 'foo-internal.aws-us-east-2.tracing.staging-edge.foo-data.net:443:443',
},
},
];
for (const {path, result} of expectationList) {
it(path, function() {
for (const { path, result } of expectationList) {
it(path, function () {
assert.deepStrictEqual(uriParser.splitHostPort(path), result);
});
}
});
});
});

View File

@ -1,6 +1,15 @@
{
"extends": "./node_modules/gts/tsconfig-google.json",
"compilerOptions": {
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"declaration": true,
"forceConsistentCasingInFileNames": true,
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"pretty": true,
"sourceMap": true,
"strict": true,
"lib": ["es2017"],
"outDir": "build",
"target": "es2017",
@ -12,5 +21,8 @@
"include": [
"src/**/*.ts",
"test/**/*.ts"
],
"exclude": [
"node_modules"
]
}