mirror of
https://github.com/grpc/grpc-node.git
synced 2025-12-08 18:23:54 +00:00
Merge pull request #2465 from dancrumb/chore/#2464
Convert to eslint/prettier
This commit is contained in:
commit
dbaaa89a08
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
4
packages/grpc-js/apache-notice.md
Normal file
4
packages/grpc-js/apache-notice.md
Normal 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
|
||||
@ -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
|
||||
@ -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",
|
||||
|
||||
@ -2,4 +2,6 @@ module.exports = {
|
||||
proseWrap: 'always',
|
||||
singleQuote: true,
|
||||
trailingComma: 'es5',
|
||||
bracketSpacing: true,
|
||||
arrowParens: 'avoid',
|
||||
};
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,5 +18,5 @@
|
||||
let nextCallNumber = 0;
|
||||
|
||||
export function getNextCallNumber() {
|
||||
return nextCallNumber++;
|
||||
}
|
||||
return nextCallNumber++;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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],
|
||||
|
||||
@ -18,5 +18,5 @@
|
||||
export enum CompressionAlgorithms {
|
||||
identity = 0,
|
||||
deflate = 1,
|
||||
gzip = 2
|
||||
};
|
||||
gzip = 2,
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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';
|
||||
}
|
||||
|
||||
@ -34,4 +34,4 @@ export function getErrorCode(error: unknown): number | null {
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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))
|
||||
);
|
||||
}
|
||||
|
||||
@ -132,7 +132,7 @@ export function makeClientConstructor(
|
||||
[methodName: string]: Function;
|
||||
}
|
||||
|
||||
Object.keys(methods).forEach((name) => {
|
||||
Object.keys(methods).forEach(name => {
|
||||
if (isPrototypePolluted(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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],
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
93
packages/grpc-js/test/assert2.ts
Normal file
93
packages/grpc-js/test/assert2.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
@ -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 };
|
||||
|
||||
@ -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);
|
||||
})
|
||||
);
|
||||
|
||||
@ -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', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -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();
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -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();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@ -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', () => {
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -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);
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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', () => {
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -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();
|
||||
}
|
||||
);
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -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, {
|
||||
|
||||
@ -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 });
|
||||
|
||||
|
||||
@ -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();
|
||||
});
|
||||
|
||||
@ -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);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user