From c034ac1b01a78ef654b24fbe2a9a9bd5aa5415b5 Mon Sep 17 00:00:00 2001 From: Richard Bair Date: Thu, 27 Jun 2024 17:50:13 -0700 Subject: [PATCH] Apply fixes based on PR comments --- .../checkstyle/checkstyle-suppressions.xml | 39 - .../checkstyle-xpath-suppressions.xml | 32 - pbj-core/config/checkstyle/checkstyle.xml | 445 ---------- pbj-core/config/detekt/detekt.yml | 807 ------------------ .../config/markdownlint/.markdownlint.json | 8 - pbj-core/config/pmd/ruleset.xml | 15 - pbj-core/gradle.properties | 2 +- pbj-core/gradle/scripts/clear-gradle-cache.sh | 16 - .../pbj/grpc/helidon/DeadlineDetector.java | 4 +- .../pbj/grpc/helidon/PbjMethodRoute.java | 2 +- .../pbj/grpc/helidon/PbjProtocolHandler.java | 36 - .../pbj/grpc/helidon/PbjProtocolSelector.java | 14 - .../src/test/java/pbj/PbjTest.java | 531 ------------ .../hedera/pbj/runtime/grpc/Pipelines.java | 24 - 14 files changed, 4 insertions(+), 1971 deletions(-) delete mode 100644 pbj-core/config/checkstyle/checkstyle-suppressions.xml delete mode 100644 pbj-core/config/checkstyle/checkstyle-xpath-suppressions.xml delete mode 100644 pbj-core/config/checkstyle/checkstyle.xml delete mode 100644 pbj-core/config/detekt/detekt.yml delete mode 100644 pbj-core/config/markdownlint/.markdownlint.json delete mode 100644 pbj-core/config/pmd/ruleset.xml delete mode 100755 pbj-core/gradle/scripts/clear-gradle-cache.sh delete mode 100644 pbj-core/pbj-grpc-helidon/src/test/java/pbj/PbjTest.java diff --git a/pbj-core/config/checkstyle/checkstyle-suppressions.xml b/pbj-core/config/checkstyle/checkstyle-suppressions.xml deleted file mode 100644 index 073f001b..00000000 --- a/pbj-core/config/checkstyle/checkstyle-suppressions.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pbj-core/config/checkstyle/checkstyle-xpath-suppressions.xml b/pbj-core/config/checkstyle/checkstyle-xpath-suppressions.xml deleted file mode 100644 index 04b61512..00000000 --- a/pbj-core/config/checkstyle/checkstyle-xpath-suppressions.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pbj-core/config/checkstyle/checkstyle.xml b/pbj-core/config/checkstyle/checkstyle.xml deleted file mode 100644 index 2e0ebeaf..00000000 --- a/pbj-core/config/checkstyle/checkstyle.xml +++ /dev/null @@ -1,445 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pbj-core/config/detekt/detekt.yml b/pbj-core/config/detekt/detekt.yml deleted file mode 100644 index 00108581..00000000 --- a/pbj-core/config/detekt/detekt.yml +++ /dev/null @@ -1,807 +0,0 @@ -build: - excludeCorrectable: false - -config: - validation: true - warningsAsErrors: false - checkExhaustiveness: false - # when writing own rules with new properties, exclude the property path e.g.: ['my_rule_set', '.*>.*>[my_property]'] - excludes: [] - -processors: - active: true - exclude: - - 'DetektProgressListener' - # - 'KtFileCountProcessor' - # - 'PackageCountProcessor' - # - 'ClassCountProcessor' - # - 'FunctionCountProcessor' - # - 'PropertyCountProcessor' - # - 'ProjectComplexityProcessor' - # - 'ProjectCognitiveComplexityProcessor' - # - 'ProjectLLOCProcessor' - # - 'ProjectCLOCProcessor' - # - 'ProjectLOCProcessor' - # - 'ProjectSLOCProcessor' - # - 'LicenseHeaderLoaderExtension' - -console-reports: - active: true - exclude: - - 'ProjectStatisticsReport' - - 'ComplexityReport' - - 'NotificationReport' - - 'FindingsReport' - - 'FileBasedFindingsReport' - # - 'LiteFindingsReport' - -output-reports: - active: true - exclude: - # - 'TxtOutputReport' - # - 'XmlOutputReport' - # - 'HtmlOutputReport' - # - 'MdOutputReport' - # - 'sarif' - -comments: - active: true - AbsentOrWrongFileLicense: - active: false - licenseTemplateFile: 'license.template' - licenseTemplateIsRegex: false - CommentOverPrivateFunction: - active: false - CommentOverPrivateProperty: - active: false - DeprecatedBlockTag: - active: false - EndOfSentenceFormat: - active: false - endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' - KDocReferencesNonPublicProperty: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] - OutdatedDocumentation: - active: false - matchTypeParameters: true - matchDeclarationsOrder: true - allowParamOnConstructorProperties: false - UndocumentedPublicClass: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] - searchInNestedClass: true - searchInInnerClass: true - searchInInnerObject: true - searchInInnerInterface: true - searchInProtectedClass: false - UndocumentedPublicFunction: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] - searchProtectedFunction: false - UndocumentedPublicProperty: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] - searchProtectedProperty: false - -complexity: - active: true - CognitiveComplexMethod: - active: false - allowedComplexity: 15 - ComplexCondition: - active: true - allowedConditions: 3 - ComplexInterface: - active: false - allowedDefinitions: 10 - includeStaticDeclarations: false - includePrivateDeclarations: false - ignoreOverloaded: false - CyclomaticComplexMethod: - active: true - allowedComplexity: 14 - ignoreSingleWhenExpression: false - ignoreSimpleWhenEntries: false - ignoreNestingFunctions: false - ignoreLocalFunctions: false - nestingFunctions: - - 'also' - - 'apply' - - 'forEach' - - 'isNotNull' - - 'ifNull' - - 'let' - - 'run' - - 'use' - - 'with' - LabeledExpression: - active: false - ignoredLabels: [] - LargeClass: - active: true - allowedLines: 600 - LongMethod: - active: true - allowedLines: 60 - LongParameterList: - active: true - allowedFunctionParameters: 5 - allowedConstructorParameters: 6 - ignoreDefaultParameters: false - ignoreDataClasses: true - ignoreAnnotatedParameter: [] - MethodOverloading: - active: false - allowedOverloads: 6 - NamedArguments: - active: false - allowedArguments: 3 - ignoreArgumentsMatchingNames: false - NestedBlockDepth: - active: true - allowedDepth: 4 - NestedScopeFunctions: - active: false - allowedDepth: 1 - functions: - - 'kotlin.apply' - - 'kotlin.run' - - 'kotlin.with' - - 'kotlin.let' - - 'kotlin.also' - ReplaceSafeCallChainWithRun: - active: false - StringLiteralDuplication: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] - allowedDuplications: 2 - ignoreAnnotation: true - allowedWithLengthLessThan: 5 - ignoreStringsRegex: '$^' - TooManyFunctions: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] - allowedFunctionsPerFile: 11 - allowedFunctionsPerClass: 11 - allowedFunctionsPerInterface: 11 - allowedFunctionsPerObject: 11 - allowedFunctionsPerEnum: 11 - ignoreDeprecated: false - ignorePrivate: false - ignoreOverridden: false - -coroutines: - active: true - GlobalCoroutineUsage: - active: false - InjectDispatcher: - active: true - dispatcherNames: - - 'IO' - - 'Default' - - 'Unconfined' - RedundantSuspendModifier: - active: true - SleepInsteadOfDelay: - active: true - SuspendFunSwallowedCancellation: - active: false - SuspendFunWithCoroutineScopeReceiver: - active: false - SuspendFunWithFlowReturnType: - active: true - -empty-blocks: - active: true - EmptyCatchBlock: - active: true - allowedExceptionNameRegex: '_|(ignore|expected).*' - EmptyClassBlock: - active: true - EmptyDefaultConstructor: - active: true - EmptyDoWhileBlock: - active: true - EmptyElseBlock: - active: true - EmptyFinallyBlock: - active: true - EmptyForBlock: - active: true - EmptyFunctionBlock: - active: true - ignoreOverridden: false - EmptyIfBlock: - active: true - EmptyInitBlock: - active: true - EmptyKotlinFile: - active: true - EmptySecondaryConstructor: - active: true - EmptyTryBlock: - active: true - EmptyWhenBlock: - active: true - EmptyWhileBlock: - active: true - -exceptions: - active: true - ExceptionRaisedInUnexpectedLocation: - active: true - methodNames: - - 'equals' - - 'finalize' - - 'hashCode' - - 'toString' - InstanceOfCheckForException: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] - NotImplementedDeclaration: - active: false - ObjectExtendsThrowable: - active: false - PrintStackTrace: - active: true - RethrowCaughtException: - active: true - ReturnFromFinally: - active: true - ignoreLabeled: false - SwallowedException: - active: true - ignoredExceptionTypes: - - 'InterruptedException' - - 'MalformedURLException' - - 'NumberFormatException' - - 'ParseException' - allowedExceptionNameRegex: '_|(ignore|expected).*' - ThrowingExceptionFromFinally: - active: true - ThrowingExceptionInMain: - active: false - ThrowingExceptionsWithoutMessageOrCause: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] - exceptions: - - 'ArrayIndexOutOfBoundsException' - - 'Exception' - - 'IllegalArgumentException' - - 'IllegalMonitorStateException' - - 'IllegalStateException' - - 'IndexOutOfBoundsException' - - 'NullPointerException' - - 'RuntimeException' - - 'Throwable' - ThrowingNewInstanceOfSameException: - active: true - TooGenericExceptionCaught: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] - exceptionNames: - - 'ArrayIndexOutOfBoundsException' - - 'Error' - - 'Exception' - - 'IllegalMonitorStateException' - - 'IndexOutOfBoundsException' - - 'NullPointerException' - - 'RuntimeException' - - 'Throwable' - allowedExceptionNameRegex: '_|(ignore|expected).*' - TooGenericExceptionThrown: - active: true - exceptionNames: - - 'Error' - - 'Exception' - - 'RuntimeException' - - 'Throwable' - -naming: - active: true - BooleanPropertyNaming: - active: false - allowedPattern: '^(is|has|are)' - ClassNaming: - active: true - classPattern: '[A-Z][a-zA-Z0-9]*' - ConstructorParameterNaming: - active: true - parameterPattern: '[a-z][A-Za-z0-9]*' - privateParameterPattern: '[a-z][A-Za-z0-9]*' - excludeClassPattern: '$^' - EnumNaming: - active: true - enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' - ForbiddenClassName: - active: false - forbiddenName: [] - FunctionNameMaxLength: - active: false - maximumFunctionNameLength: 30 - FunctionNameMinLength: - active: false - minimumFunctionNameLength: 3 - FunctionNaming: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] - functionPattern: '[a-z][a-zA-Z0-9]*' - excludeClassPattern: '$^' - FunctionParameterNaming: - active: true - parameterPattern: '[a-z][A-Za-z0-9]*' - excludeClassPattern: '$^' - InvalidPackageDeclaration: - active: true - rootPackage: '' - requireRootInDeclaration: false - LambdaParameterNaming: - active: false - parameterPattern: '[a-z][A-Za-z0-9]*|_' - MatchingDeclarationName: - active: true - mustBeFirst: true - multiplatformTargets: - - 'ios' - - 'android' - - 'js' - - 'jvm' - - 'native' - - 'iosArm64' - - 'iosX64' - - 'macosX64' - - 'mingwX64' - - 'linuxX64' - MemberNameEqualsClassName: - active: true - ignoreOverridden: true - NoNameShadowing: - active: true - NonBooleanPropertyPrefixedWithIs: - active: false - ObjectPropertyNaming: - active: true - constantPattern: '[A-Za-z][_A-Za-z0-9]*' - propertyPattern: '[A-Za-z][_A-Za-z0-9]*' - privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' - PackageNaming: - active: true - packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*' - TopLevelPropertyNaming: - active: true - constantPattern: '[A-Z][_A-Z0-9]*' - propertyPattern: '[A-Za-z][_A-Za-z0-9]*' - privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' - VariableMaxLength: - active: false - maximumVariableNameLength: 64 - VariableMinLength: - active: false - minimumVariableNameLength: 1 - VariableNaming: - active: true - variablePattern: '[a-z][A-Za-z0-9]*' - privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' - excludeClassPattern: '$^' - -performance: - active: true - ArrayPrimitive: - active: true - CouldBeSequence: - active: false - allowedOperations: 2 - ForEachOnRange: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] - SpreadOperator: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] - UnnecessaryPartOfBinaryExpression: - active: false - UnnecessaryTemporaryInstantiation: - active: true - -potential-bugs: - active: true - AvoidReferentialEquality: - active: true - forbiddenTypePatterns: - - 'kotlin.String' - CastNullableToNonNullableType: - active: false - ignorePlatformTypes: true - CastToNullableType: - active: false - CharArrayToStringCall: - active: false - Deprecation: - active: false - DontDowncastCollectionTypes: - active: false - DoubleMutabilityForCollection: - active: true - mutableTypes: - - 'kotlin.collections.MutableList' - - 'kotlin.collections.MutableMap' - - 'kotlin.collections.MutableSet' - - 'java.util.ArrayList' - - 'java.util.LinkedHashSet' - - 'java.util.HashSet' - - 'java.util.LinkedHashMap' - - 'java.util.HashMap' - ElseCaseInsteadOfExhaustiveWhen: - active: false - ignoredSubjectTypes: [] - EqualsAlwaysReturnsTrueOrFalse: - active: true - EqualsWithHashCodeExist: - active: true - ExitOutsideMain: - active: false - ExplicitGarbageCollectionCall: - active: true - HasPlatformType: - active: true - IgnoredReturnValue: - active: true - restrictToConfig: true - returnValueAnnotations: - - 'CheckResult' - - '*.CheckResult' - - 'CheckReturnValue' - - '*.CheckReturnValue' - ignoreReturnValueAnnotations: - - 'CanIgnoreReturnValue' - - '*.CanIgnoreReturnValue' - returnValueTypes: - - 'kotlin.sequences.Sequence' - - 'kotlinx.coroutines.flow.*Flow' - - 'java.util.stream.*Stream' - ignoreFunctionCall: [] - ImplicitDefaultLocale: - active: true - ImplicitUnitReturnType: - active: false - allowExplicitReturnType: true - InvalidRange: - active: true - IteratorHasNextCallsNextMethod: - active: true - IteratorNotThrowingNoSuchElementException: - active: true - LateinitUsage: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] - ignoreOnClassesPattern: '' - MapGetWithNotNullAssertionOperator: - active: true - MissingPackageDeclaration: - active: false - excludes: ['**/*.kts'] - MissingUseCall: - active: false - NullCheckOnMutableProperty: - active: false - NullableToStringCall: - active: false - PropertyUsedBeforeDeclaration: - active: false - UnconditionalJumpStatementInLoop: - active: false - UnnamedParameterUse: - active: false - allowAdjacentDifferentTypeParams: true - allowSingleParamUse: true - UnnecessaryNotNullCheck: - active: false - UnnecessaryNotNullOperator: - active: true - UnnecessarySafeCall: - active: true - UnreachableCatchBlock: - active: true - UnreachableCode: - active: true - UnsafeCallOnNullableType: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] - UnsafeCast: - active: true - UnusedUnaryOperator: - active: true - UselessPostfixExpression: - active: true - WrongEqualsTypeParameter: - active: true - -style: - active: true - AbstractClassCanBeConcreteClass: - active: true - AbstractClassCanBeInterface: - active: true - AlsoCouldBeApply: - active: false - BracesOnIfStatements: - active: false - singleLine: 'never' - multiLine: 'always' - BracesOnWhenStatements: - active: false - singleLine: 'necessary' - multiLine: 'consistent' - CanBeNonNullable: - active: false - CascadingCallWrapping: - active: false - includeElvis: true - ClassOrdering: - active: false - CollapsibleIfStatements: - active: false - DataClassContainsFunctions: - active: false - conversionFunctionPrefix: - - 'to' - allowOperators: false - DataClassShouldBeImmutable: - active: false - DestructuringDeclarationWithTooManyEntries: - active: true - maxDestructuringEntries: 3 - DoubleNegativeLambda: - active: false - negativeFunctions: - - reason: 'Use `takeIf` instead.' - value: 'takeUnless' - - reason: 'Use `all` instead.' - value: 'none' - negativeFunctionNameParts: - - 'not' - - 'non' - EqualsNullCall: - active: true - EqualsOnSignatureLine: - active: false - ExplicitCollectionElementAccessMethod: - active: false - ExplicitItLambdaParameter: - active: true - ExpressionBodySyntax: - active: false - includeLineWrapping: false - ForbiddenAnnotation: - active: false - annotations: - - reason: 'it is a java annotation. Use `Suppress` instead.' - value: 'java.lang.SuppressWarnings' - - reason: 'it is a java annotation. Use `kotlin.Deprecated` instead.' - value: 'java.lang.Deprecated' - - reason: 'it is a java annotation. Use `kotlin.annotation.MustBeDocumented` instead.' - value: 'java.lang.annotation.Documented' - - reason: 'it is a java annotation. Use `kotlin.annotation.Target` instead.' - value: 'java.lang.annotation.Target' - - reason: 'it is a java annotation. Use `kotlin.annotation.Retention` instead.' - value: 'java.lang.annotation.Retention' - - reason: 'it is a java annotation. Use `kotlin.annotation.Repeatable` instead.' - value: 'java.lang.annotation.Repeatable' - - reason: 'Kotlin does not support @Inherited annotation, see https://youtrack.jetbrains.com/issue/KT-22265' - value: 'java.lang.annotation.Inherited' - ForbiddenComment: - active: true - comments: - - reason: 'Forbidden FIXME todo marker in comment, please fix the problem.' - value: 'FIXME:' - - reason: 'Forbidden STOPSHIP todo marker in comment, please address the problem before shipping the code.' - value: 'STOPSHIP:' - - reason: 'Forbidden TODO todo marker in comment, please do the changes.' - value: 'TODO:' - allowedPatterns: '' - ForbiddenImport: - active: false - imports: [] - forbiddenPatterns: '' - ForbiddenMethodCall: - active: false - methods: - - reason: 'print does not allow you to configure the output stream. Use a logger instead.' - value: 'kotlin.io.print' - - reason: 'println does not allow you to configure the output stream. Use a logger instead.' - value: 'kotlin.io.println' - - reason: 'using `BigDecimal.(kotlin.Double)` can result in unexpected float point precision behavior. Use `BigDecimal.valueOf(kotlin.Double)` or `BigDecimal.(kotlin.String)` instead.' - value: 'java.math.BigDecimal.(kotlin.Double)' - ForbiddenSuppress: - active: false - rules: [] - ForbiddenVoid: - active: true - ignoreOverridden: false - ignoreUsageInGenerics: false - FunctionOnlyReturningConstant: - active: true - ignoreOverridableFunction: true - ignoreActualFunction: true - excludedFunctions: [] - LoopWithTooManyJumpStatements: - active: true - maxJumpCount: 1 - MagicNumber: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/*.kts'] - ignoreNumbers: - - '-1' - - '0' - - '1' - - '2' - ignoreHashCodeFunction: true - ignorePropertyDeclaration: false - ignoreLocalVariableDeclaration: false - ignoreConstantDeclaration: true - ignoreCompanionObjectPropertyDeclaration: true - ignoreAnnotation: false - ignoreNamedArgument: true - ignoreEnums: false - ignoreRanges: false - ignoreExtensionFunctions: true - MandatoryBracesLoops: - active: false - MaxChainedCallsOnSameLine: - active: false - maxChainedCalls: 5 - MaxLineLength: - active: true - maxLineLength: 120 - excludePackageStatements: true - excludeImportStatements: true - excludeCommentStatements: false - excludeRawStrings: true - MayBeConstant: - active: true - ModifierOrder: - active: true - MultilineLambdaItParameter: - active: false - MultilineRawStringIndentation: - active: false - indentSize: 4 - trimmingMethods: - - 'trimIndent' - - 'trimMargin' - NestedClassesVisibility: - active: true - NewLineAtEndOfFile: - active: true - NoTabs: - active: false - NullableBooleanCheck: - active: false - ObjectLiteralToLambda: - active: true - OptionalAbstractKeyword: - active: true - OptionalUnit: - active: false - PreferToOverPairSyntax: - active: false - ProtectedMemberInFinalClass: - active: true - RangeUntilInsteadOfRangeTo: - active: false - RedundantConstructorKeyword: - active: false - RedundantExplicitType: - active: false - RedundantHigherOrderMapUsage: - active: true - RedundantVisibilityModifierRule: - active: false - ReturnCount: - active: true - max: 2 - excludedFunctions: - - 'equals' - excludeLabeled: false - excludeReturnFromLambda: true - excludeGuardClauses: false - SafeCast: - active: true - SerialVersionUIDInSerializableClass: - active: true - SpacingAfterPackageDeclaration: - active: false - StringShouldBeRawString: - active: false - maxEscapedCharacterCount: 2 - ignoredCharacters: [] - ThrowsCount: - active: true - max: 2 - excludeGuardClauses: false - TrailingWhitespace: - active: false - TrimMultilineRawString: - active: false - trimmingMethods: - - 'trimIndent' - - 'trimMargin' - UnderscoresInNumericLiterals: - active: false - acceptableLength: 4 - allowNonStandardGrouping: false - UnnecessaryAnnotationUseSiteTarget: - active: false - UnnecessaryAny: - active: false - UnnecessaryApply: - active: true - UnnecessaryBackticks: - active: false - UnnecessaryBracesAroundTrailingLambda: - active: false - UnnecessaryFilter: - active: true - UnnecessaryInheritance: - active: true - UnnecessaryInnerClass: - active: false - UnnecessaryLet: - active: false - UnnecessaryParentheses: - active: false - allowForUnclearPrecedence: false - UnusedImport: - active: false - UnusedParameter: - active: true - allowedNames: 'ignored|expected' - UnusedPrivateClass: - active: true - UnusedPrivateMember: - active: true - allowedNames: '' - UnusedPrivateProperty: - active: true - allowedNames: '_|ignored|expected|serialVersionUID' - UseAnyOrNoneInsteadOfFind: - active: true - UseArrayLiteralsInAnnotations: - active: true - UseCheckNotNull: - active: true - UseCheckOrError: - active: true - UseDataClass: - active: false - allowVars: false - UseEmptyCounterpart: - active: false - UseIfEmptyOrIfBlank: - active: false - UseIfInsteadOfWhen: - active: false - ignoreWhenContainingVariableDeclaration: false - UseIsNullOrEmpty: - active: true - UseLet: - active: false - UseOrEmpty: - active: true - UseRequire: - active: true - UseRequireNotNull: - active: true - UseSumOfInsteadOfFlatMapSize: - active: false - UselessCallOnNotNull: - active: true - UtilityClassWithPublicConstructor: - active: true - VarCouldBeVal: - active: true - ignoreLateinitVar: false - WildcardImport: - active: true - excludeImports: - - 'java.util.*' diff --git a/pbj-core/config/markdownlint/.markdownlint.json b/pbj-core/config/markdownlint/.markdownlint.json deleted file mode 100644 index b4d0eebf..00000000 --- a/pbj-core/config/markdownlint/.markdownlint.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "MD013": { - "line_length": 120, - "heading_line_length": 120, - "code_block_line_length": 120 - } -} - diff --git a/pbj-core/config/pmd/ruleset.xml b/pbj-core/config/pmd/ruleset.xml deleted file mode 100644 index b61bd16d..00000000 --- a/pbj-core/config/pmd/ruleset.xml +++ /dev/null @@ -1,15 +0,0 @@ - - PMD Rules - - - - - - - - - - diff --git a/pbj-core/gradle.properties b/pbj-core/gradle.properties index b0b8aa7b..0df028c7 100644 --- a/pbj-core/gradle.properties +++ b/pbj-core/gradle.properties @@ -1,5 +1,5 @@ # Version number -version=0.9.0-SNAPSHOT +version=0.9.1-SNAPSHOT # Need increased heap for running Gradle itself, or SonarQube will run the JVM out of metaspace org.gradle.jvmargs=-Xmx2048m diff --git a/pbj-core/gradle/scripts/clear-gradle-cache.sh b/pbj-core/gradle/scripts/clear-gradle-cache.sh deleted file mode 100755 index 8bfab8ad..00000000 --- a/pbj-core/gradle/scripts/clear-gradle-cache.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -# Cleans build files in a way that './gradlew clean' can only dream of. - -# The location were this script can be found. -SCRIPT_PATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 || exit ; pwd -P )" - -{ - cd "$SCRIPT_PATH"/../.. - - pkill -9 -f gradle - - rm -rvf ~/.gradle - find . -name .gradle -exec rm -rvf {} \; - find . -name build -exec rm -rvf {} \; -} diff --git a/pbj-core/pbj-grpc-helidon/src/main/java/com/hedera/pbj/grpc/helidon/DeadlineDetector.java b/pbj-core/pbj-grpc-helidon/src/main/java/com/hedera/pbj/grpc/helidon/DeadlineDetector.java index 996a806b..80617139 100644 --- a/pbj-core/pbj-grpc-helidon/src/main/java/com/hedera/pbj/grpc/helidon/DeadlineDetector.java +++ b/pbj-core/pbj-grpc-helidon/src/main/java/com/hedera/pbj/grpc/helidon/DeadlineDetector.java @@ -16,10 +16,10 @@ interface DeadlineDetector { * can actually measure elapsed time with nanosecond precision, so the actual deadline may be exceeded by a small * amount of time measuring in the microseconds or even milliseconds. * - * @param deadline The deadline, in nanoseconds, from now. + * @param deadlineNanos The deadline, in nanoseconds, from now. * @param onDeadlineExceeded The callback to invoke when the deadline has been exceeded. * @return A {@link ScheduledFuture} that can be used to cancel the deadline. */ @NonNull - ScheduledFuture scheduleDeadline(long deadline, @NonNull Runnable onDeadlineExceeded); + ScheduledFuture scheduleDeadline(long deadlineNanos, @NonNull Runnable onDeadlineExceeded); } diff --git a/pbj-core/pbj-grpc-helidon/src/main/java/com/hedera/pbj/grpc/helidon/PbjMethodRoute.java b/pbj-core/pbj-grpc-helidon/src/main/java/com/hedera/pbj/grpc/helidon/PbjMethodRoute.java index a3d3b6c9..140dcccd 100644 --- a/pbj-core/pbj-grpc-helidon/src/main/java/com/hedera/pbj/grpc/helidon/PbjMethodRoute.java +++ b/pbj-core/pbj-grpc-helidon/src/main/java/com/hedera/pbj/grpc/helidon/PbjMethodRoute.java @@ -33,7 +33,7 @@ final class PbjMethodRoute extends PbjRoute { @NonNull private final Counter failedHttpRequestCounter; @NonNull private final Counter failedUnknownRequestCounter; @NonNull private final Counter deadlineExceededCounter; - + PbjMethodRoute( @NonNull final ServiceInterface service, @NonNull final ServiceInterface.Method method) { diff --git a/pbj-core/pbj-grpc-helidon/src/main/java/com/hedera/pbj/grpc/helidon/PbjProtocolHandler.java b/pbj-core/pbj-grpc-helidon/src/main/java/com/hedera/pbj/grpc/helidon/PbjProtocolHandler.java index b88e1970..cbcf3d12 100644 --- a/pbj-core/pbj-grpc-helidon/src/main/java/com/hedera/pbj/grpc/helidon/PbjProtocolHandler.java +++ b/pbj-core/pbj-grpc-helidon/src/main/java/com/hedera/pbj/grpc/helidon/PbjProtocolHandler.java @@ -28,14 +28,12 @@ import io.helidon.http.HttpMediaTypes; import io.helidon.http.Status; import io.helidon.http.WritableHeaders; -import io.helidon.http.http2.FlowControl; import io.helidon.http.http2.Http2Flag; import io.helidon.http.http2.Http2FrameData; import io.helidon.http.http2.Http2FrameHeader; import io.helidon.http.http2.Http2FrameTypes; import io.helidon.http.http2.Http2Headers; import io.helidon.http.http2.Http2RstStream; -import io.helidon.http.http2.Http2Settings; import io.helidon.http.http2.Http2StreamState; import io.helidon.http.http2.Http2StreamWriter; import io.helidon.http.http2.Http2WindowUpdate; @@ -65,12 +63,9 @@ final class PbjProtocolHandler implements Http2SubProtocolSelector.SubProtocolHa private static final Pattern GRPC_TIMEOUT_PATTERN = Pattern.compile(GRPC_TIMEOUT_REGEX); // Helidon-specific fields related to the connection itself - private final HttpPrologue prologue; private final Http2Headers headers; private final Http2StreamWriter streamWriter; private final int streamId; - private final Http2Settings serverSettings; - private final Http2Settings clientSettings; private final StreamFlowControl flowControl; private Http2StreamState currentStreamState; @@ -91,14 +86,6 @@ final class PbjProtocolHandler implements Http2SubProtocolSelector.SubProtocolHa * to a non-null no-op future that exists in the infinite future. */ private ScheduledFuture deadlineFuture; - /** The current index into {@link #entityBytes} into which data is to be read. */ - private int entityBytesIndex = 0; - /** - * A future representing the background task detecting deadlines. If there is a deadline, then this future will - * represent the task that will be executed when the deadline is reached. If there is no deadline, then we default - * to a non-null no-op future that exists in the infinite future. - */ - private ScheduledFuture deadlineFuture; /** * The bytes of the next incoming message. This is created dynamically as a message is received, and is never * larger than the system configured {@link PbjConfig#maxMessageSizeBytes()}. @@ -121,8 +108,6 @@ final class PbjProtocolHandler implements Http2SubProtocolSelector.SubProtocolHa this.headers = requireNonNull(headers); this.streamWriter = requireNonNull(streamWriter); this.streamId = streamId; - this.serverSettings = requireNonNull(serverSettings); - this.clientSettings = requireNonNull(clientSettings); this.flowControl = requireNonNull(flowControl); this.currentStreamState = requireNonNull(currentStreamState); this.config = requireNonNull(config); @@ -316,7 +301,6 @@ public void data(Http2FrameHeader header, BufferData data) { if (header.flags(Http2FrameTypes.DATA).endOfStream()) { entityBytesIndex = 0; entityBytes = null; -// listener.onHalfClose(); currentStreamState = Http2StreamState.HALF_CLOSED_LOCAL; incoming.onComplete(); } @@ -415,26 +399,6 @@ private void sendResponseHeaders( grpcHeaders.set(messageEncoding); } - streamWriter.writeData(new Http2FrameData(header, bufferData), flowControl.outbound()); - } catch (final Exception e) { - LOGGER.log(ERROR, "Failed to respond to grpc request: " + route.method(), e); - } - } - - private synchronized void close() { - final var responseHeaders = WritableHeaders.create(); - // Canceling a future that has already completed has no effect. So by canceling here, we are saying: - // "If you have not yet executed, never execute. If you have already executed, then just ignore me". - // The "isCancelled" flag is set if the future was canceled before it was executed. - deadlineFuture.cancel(false); - // If the deadline was canceled, then we have not yet responded to the client. So the response is OK. On the - // other hand, if th deadline was NOT canceled, then the deadline was exceeded. -// if (!deadlineFuture.isCancelled()) { - responseHeaders.set(GrpcStatus.OK); -// } else { -// responseHeaders.set(GrpcStatus.DEADLINE_EXCEEDED); -// } - final var http2Headers = Http2Headers.create(responseHeaders); streamWriter.writeHeaders(http2Headers, streamId, Http2Flag.HeaderFlags.create(Http2Flag.END_OF_HEADERS), diff --git a/pbj-core/pbj-grpc-helidon/src/main/java/com/hedera/pbj/grpc/helidon/PbjProtocolSelector.java b/pbj-core/pbj-grpc-helidon/src/main/java/com/hedera/pbj/grpc/helidon/PbjProtocolSelector.java index 0a72f2e1..9bdf9f2d 100644 --- a/pbj-core/pbj-grpc-helidon/src/main/java/com/hedera/pbj/grpc/helidon/PbjProtocolSelector.java +++ b/pbj-core/pbj-grpc-helidon/src/main/java/com/hedera/pbj/grpc/helidon/PbjProtocolSelector.java @@ -86,18 +86,6 @@ public SubProtocolResult subProtocol(ConnectionContext ctx, return NOT_SUPPORTED; } - // If Content-Type does not begin with "application/grpc", gRPC servers SHOULD respond with HTTP status of - // 415 (Unsupported Media Type). This will prevent other HTTP/2 clients from interpreting a gRPC error - // response, which uses status 200 (OK), as successful. - // See https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md - final var httpHeaders = headers.httpHeaders(); - final var contentType = httpHeaders.value(HeaderNames.CONTENT_TYPE).orElse(""); - if (!contentType.startsWith("application/grpc")) { - return new SubProtocolResult(true, - new PbjErrorProtocolHandler(streamWriter, streamId, currentStreamState, h -> - h.set(Http2Headers.STATUS_NAME, Status.UNSUPPORTED_MEDIA_TYPE_415.code()))); - } - // Look up the route based on the path. If that route does not exist, we return a 200 OK response with // a gRPC status of NOT_FOUND. final var routing = router.routing(PbjRouting.class, EMPTY); @@ -113,8 +101,6 @@ public SubProtocolResult subProtocol(ConnectionContext ctx, new PbjProtocolHandler(headers, streamWriter, streamId, - serverSettings, - clientSettings, flowControl, currentStreamState, config, diff --git a/pbj-core/pbj-grpc-helidon/src/test/java/pbj/PbjTest.java b/pbj-core/pbj-grpc-helidon/src/test/java/pbj/PbjTest.java deleted file mode 100644 index 1f15607b..00000000 --- a/pbj-core/pbj-grpc-helidon/src/test/java/pbj/PbjTest.java +++ /dev/null @@ -1,531 +0,0 @@ -package pbj; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.google.protobuf.util.JsonFormat; -import com.hedera.pbj.grpc.helidon.GrpcStatus; -import com.hedera.pbj.grpc.helidon.PbjRouting; -import com.hedera.pbj.runtime.io.buffer.Bytes; -import com.hedera.pbj.runtime.io.stream.ReadableStreamingData; -import com.hedera.pbj.runtime.io.stream.WritableStreamingData; -import greeter.GreeterGrpc; -import greeter.HelloReply; -import greeter.HelloRequest; -import io.grpc.ManagedChannel; -import io.grpc.ManagedChannelBuilder; -import io.grpc.stub.StreamObserver; -import io.helidon.common.media.type.MediaType; -import io.helidon.common.media.type.MediaTypes; -import io.helidon.http.HeaderNames; -import io.helidon.http.HttpMediaType; -import io.helidon.http.Method; -import io.helidon.webclient.http2.Http2Client; -import io.helidon.webserver.WebServer; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Flow; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -class PbjTest { - private static final MediaType APPLICATION_GRPC = HttpMediaType.create("application/grpc"); - private static final MediaType APPLICATION_GRPC_PROTO = HttpMediaType.create("application/grpc+proto"); - private static final MediaType APPLICATION_GRPC_JSON = HttpMediaType.create("application/grpc+json"); - private static final MediaType APPLICATION_GRPC_STRING = HttpMediaType.create("application/grpc+string"); - private static final MediaType APPLICATION_RANDOM = HttpMediaType.create("application/random"); - private static Http2Client CLIENT; - private static final String SAY_HELLO_PATH = "/greeter.Greeter/sayHello"; - private static final String SAY_HELLO_CLIENT_STREAM_PATH = "/greeter.Greeter/sayHelloStreamRequest"; - - private static final HelloRequest SIMPLE_REQUEST = HelloRequest.newBuilder() - .setName("PBJ") - .build(); - - private static final HelloReply SIMPLE_REPLY = HelloReply.newBuilder() - .setMessage("Hello PBJ") - .build(); - - private static ManagedChannel CHANNEL; - - @BeforeAll - static void setup() { - // Set up the server - WebServer.builder() - .port(8080) - .addRouting(PbjRouting.builder() - .service(new GreeterServiceImpl())) - .build() - .start(); - - CLIENT = Http2Client.builder() - .baseUri("http://localhost:8080") - .build(); - - CHANNEL = ManagedChannelBuilder.forAddress("localhost", 8080) - .usePlaintext() - .build(); - - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // HTTP2 Path - // - // SPEC: - // - // Path is case-sensitive. Some gRPC implementations may allow the Path format shown above to be overridden, but - // this functionality is strongly discouraged. gRPC does not go out of its way to break users that are using this - // kind of override, but we do not actively support it, and some functionality (e.g., service config support) will - // not work when the path is not of the form shown above. - // - // TESTS: - // - Verify the path is case-sensitive - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - /** Verify the path is case-sensitive */ - @Test - void badCaseOnPathIsNotFound() { - try (var response = CLIENT.post() - .contentType(APPLICATION_GRPC_PROTO) - .path(SAY_HELLO_PATH.toUpperCase()) - .submit(messageBytes(SIMPLE_REQUEST))) { - assertThat(response.status().code()).isEqualTo(200); - assertThat(response.headers().get(GrpcStatus.STATUS_NAME)).isEqualTo(GrpcStatus.NOT_FOUND); - } - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // HTTP2 Method - // - // SPEC: - // - // Only POST can be used for gRPC calls. - // - // TESTS: - // - Verify that only POST is supported - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - /** Verify that only POST is supported */ - @ParameterizedTest - @ValueSource(strings = { "GET", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD", "TRACE" }) - void mustUsePost(final String methodName) { - try (var response = CLIENT.method(Method.create(methodName)) - .contentType(APPLICATION_GRPC_PROTO) - .path(SAY_HELLO_PATH) - .request()) { - - // This is consistent with existing behavior on Helidon, but I would have expected the response code - // to be 405 Method Not Allowed instead. See PbjProtocolSelector for the check for POST. - assertThat(response.status().code()).isEqualTo(404); - } - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Content-Type - // - // SPEC: - // - // If Content-Type does not begin with "application/grpc", gRPC servers SHOULD respond with HTTP status of 415 - // (Unsupported Media Type). This will prevent other HTTP/2 clients from interpreting a gRPC error response, which - // uses status 200 (OK), as successful. - // - // TESTS: - // - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - /** Verify that the server responds with 415 when the Content-Type is not specified */ - @Test - void contentTypeMustBeSet() { - try (var response = CLIENT.post() - .path(SAY_HELLO_PATH) - .submit(messageBytes(SIMPLE_REQUEST))) { - - assertThat(response.status().code()).isEqualTo(415); - } - } - - /** Verify that the server responds with 415 when the Content-Type does not start with "application/grpc" */ - @Test - void contentTypeMustStartWithApplicationGrpc() { - try (var response = CLIENT.post() - .path(SAY_HELLO_PATH) - .contentType(APPLICATION_RANDOM) - .submit(messageBytes(SIMPLE_REQUEST))) { - - assertThat(response.status().code()).isEqualTo(415); - } - } - - /** Verify that "application/grpc+json" requests are accepted */ - @Test - void contentTypeCanBeJSON() { - try (var response = CLIENT.post() - .path(SAY_HELLO_PATH) - .contentType(APPLICATION_GRPC_JSON) - .submit(messageBytesJson(SIMPLE_REQUEST))) { - - assertThat(response.status().code()).isEqualTo(200); - assertThat(response.headers().contentType().orElseThrow().text()) - .isEqualTo("application/grpc+json"); - - final var reply = decodeJsonReply(new ReadableStreamingData(response.inputStream())); - assertThat(reply).isEqualTo(SIMPLE_REPLY); - } - } - - /** Verify that "application/grpc+proto" and "application/grpc" both support protobuf encoding */ - @ParameterizedTest - @ValueSource(strings = { "application/grpc+proto", "application/grpc" }) - void contentTypeCanBeProtobuf(final String contentType) { - try (var response = CLIENT.post() - .path(SAY_HELLO_PATH) - .contentType(MediaTypes.create(contentType)) - .submit(messageBytes(SIMPLE_REQUEST))) { - - // TODO Assert that the response is also encoded as JSON - assertThat(response.status().code()).isEqualTo(200); - assertThat(response.headers().contentType().orElseThrow().text()) - .isEqualTo(contentType); - - final var tx = decodeReply(new ReadableStreamingData(response.inputStream())); - assertThat(tx).isEqualTo(SIMPLE_REPLY); - } - } - - /** Verify that a custom suffix of the content type is supported */ - @Test - void contentTypeCanBeCustom() throws IOException { - try (var response = CLIENT.post() - .path(SAY_HELLO_PATH) - .contentType(APPLICATION_GRPC_STRING) - .submit(messageBytes("dude".getBytes(StandardCharsets.UTF_8)))) { - - assertThat(response.status().code()).isEqualTo(200); - assertThat(response.headers().contentType().orElseThrow().text()) - .isEqualTo(APPLICATION_GRPC_STRING.text()); - - // The first five bytes are framing -- compression + length - final var data = response.inputStream().readAllBytes(); - assertThat(new String(data, 5, data.length - 5, StandardCharsets.UTF_8)) - .isEqualTo("Hello dude"); - } - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // METADATA - // - // SPEC: - // - // Custom-Metadata is an arbitrary set of key-value pairs defined by the application layer. Header names starting - // with "grpc-" but not listed here are reserved for future GRPC use and should not be used by applications as - // Custom-Metadata. - // - // Note that HTTP2 does not allow arbitrary octet sequences for header values so binary header values must be - // encoded using Base64 as per https://tools.ietf.org/html/rfc4648#section-4. Implementations MUST accept padded - // and un-padded values and should emit un-padded values. Applications define binary headers by having their names - // end with "-bin". Runtime libraries use this suffix to detect binary headers and properly apply base64 encoding & - // decoding as headers are sent and received. - // - // Custom-Metadata header order is not guaranteed to be preserved except for values with duplicate header names. - // Duplicate header names may have their values joined with "," as the delimiter and be considered semantically - // equivalent. Implementations must split Binary-Headers on "," before decoding the Base64-encoded values. - // - // ASCII-Value should not have leading or trailing whitespace. If it contains leading or trailing whitespace, it - // may be stripped. The ASCII-Value character range defined is stricter than HTTP. Implementations must not error - // due to receiving an invalid ASCII-Value that's a valid field-value in HTTP, but the precise behavior is not - // strictly defined: they may throw the value away or accept the value. If accepted, care must be taken to make - // sure that the application is permitted to echo the value back as metadata. For example, if the metadata is - // provided to the application as a list in a request, the application should not trigger an error by providing - // that same list as the metadata in the response. - // - // TESTS: - // - Not implemented - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Request-Headers - // - // SPEC: - // - // Servers may limit the size of Request-Headers, with a default of 8 KiB suggested. Implementations are encouraged - // to compute total header size like HTTP/2's SETTINGS_MAX_HEADER_LIST_SIZE: the sum of all header fields, for each - // field the sum of the uncompressed field name and value lengths plus 32, with binary values' lengths being - // post-Base64. - // - // TESTS: - // - TBD - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Verify that the server responds with grpc-accept-encoding and UNIMPLEMENTED if unsupported compression schemes - * are used. - */ - @ParameterizedTest - @ValueSource(strings = { "gzip", "deflate", "random" }) - void compressionNotSupported(final String grpcEncoding) { - try (var response = CLIENT.post() - .contentType(APPLICATION_GRPC_PROTO) - .path(SAY_HELLO_PATH) - .header(HeaderNames.create("grpc-encoding"), grpcEncoding) - .submit(messageBytes(SIMPLE_REQUEST))) { - - assertThat(response.status().code()).isEqualTo(200); - assertThat(response.headers().get(GrpcStatus.STATUS_NAME).values()).isEqualTo(GrpcStatus.UNIMPLEMENTED.values()); - assertThat(response.headers().get(HeaderNames.create("grpc-accept-encoding")).get()).isEqualTo("identity"); - } - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Unary Method Calls - // - // TESTS: - // - A correct unary call should return a 200 OK response with a gRPC status of OK - // - A correct unary call to a failed method should return a 200 OK response with an error code - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - // Intentionally uses grpc.io to verify compatibility - @Test - void unaryCall() { - final var stub = GreeterGrpc.newBlockingStub(CHANNEL); - final var reply = stub.sayHello(SIMPLE_REQUEST); - assertThat(reply.getMessage()).isEqualTo("Hello PBJ"); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Server Streaming Method Calls - // - // TESTS: - // - TBD - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - @Test - void streamingServer() { - final var stub = GreeterGrpc.newBlockingStub(CHANNEL); - final var replies = stub.sayHelloStreamReply(SIMPLE_REQUEST); - final var messages = new ArrayList(); - replies.forEachRemaining(messages::add); - assertThat(messages) - .hasSize(10) - .allMatch(reply -> reply.getMessage().equals("Hello!")); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Client Streaming Method Calls - // - // TESTS: - // - TBD - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - @Test - void streamingClient() throws InterruptedException { - final var latch = new CountDownLatch(1); - final var response = new AtomicReference(); - final var requestObserver = GreeterGrpc.newStub(CHANNEL).sayHelloStreamRequest(new StreamObserver() { - @Override - public void onNext(HelloReply helloReply) { - response.set(helloReply); - } - - @Override - public void onError(Throwable throwable) { - // TODO Test fail - System.err.println("Error: " + throwable.getMessage()); - } - - @Override - public void onCompleted() { - latch.countDown(); - } - }); - - requestObserver.onNext(HelloRequest.newBuilder().setName("Alice").build()); - requestObserver.onNext(HelloRequest.newBuilder().setName("Bob").build()); - requestObserver.onNext(HelloRequest.newBuilder().setName("Carol").build()); - requestObserver.onCompleted(); - - latch.await(1, TimeUnit.MINUTES); - - assertThat(response.get()).isEqualTo(HelloReply.newBuilder() - .setMessage("Hello Alice, Bob, Carol") - .build()); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Bidi Streaming Calls - // - // TESTS: - // - TBD - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - @Test - void streamingBidi() throws InterruptedException { - final var latch = new CountDownLatch(1); - final var response = new ArrayList(); - final var requestObserver = GreeterGrpc.newStub(CHANNEL).sayHelloStreamBidi(new StreamObserver() { - @Override - public void onNext(HelloReply helloReply) { - response.add(helloReply); - } - - @Override - public void onError(Throwable throwable) { - // TODO Test fail - System.err.println("Error: " + throwable.getMessage()); - } - - @Override - public void onCompleted() { - latch.countDown(); - } - }); - - requestObserver.onNext(HelloRequest.newBuilder().setName("Alice").build()); - requestObserver.onNext(HelloRequest.newBuilder().setName("Bob").build()); - requestObserver.onNext(HelloRequest.newBuilder().setName("Carol").build()); - requestObserver.onCompleted(); - - latch.await(1, TimeUnit.MINUTES); - - assertThat(response) - .hasSize(3) - .allMatch(reply -> reply.getMessage().startsWith("Hello")); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Utility methods - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - private HelloReply decodeReply(ReadableStreamingData rsd) { - try { - assertThat(rsd.readByte()).isEqualTo((byte) 0); // No Compression - final var responseLength = (int) rsd.readUnsignedInt(); - final var responseData = new byte[responseLength]; - rsd.readBytes(responseData); - return HelloReply.parseFrom(responseData); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private HelloReply decodeJsonReply(ReadableStreamingData rsd) { - try { - assertThat(rsd.readByte()).isEqualTo((byte) 0); // No Compression - final var responseLength = (int) rsd.readUnsignedInt(); - final var responseData = new byte[responseLength]; - rsd.readBytes(responseData); - final var builder = HelloReply.newBuilder(); - JsonFormat.parser().merge(new String(responseData, StandardCharsets.UTF_8), builder); - return builder.build(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private byte[] messageBytes(byte[] data) { - final var out = new ByteArrayOutputStream(); - final WritableStreamingData wsd = new WritableStreamingData(out); - wsd.writeByte((byte) 0); - wsd.writeUnsignedInt(data.length); - wsd.writeBytes(data); - return out.toByteArray(); - } - - private byte[] messageBytes(HelloRequest req) { - final var data = req.toByteArray(); - return messageBytes(data); - } - - private byte[] messageBytesJson(HelloRequest req) { - try { - final var data = JsonFormat.printer().print(req).getBytes(StandardCharsets.UTF_8); - return messageBytes(data); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private static final class GreeterServiceImpl implements GreeterService { - @Override - public HelloReply sayHello(HelloRequest request) { - return HelloReply.newBuilder() - .setMessage("Hello " + request.getName()) - .build(); - } - - // Streams of stuff coming from the client, with a single response. - @Override - public Flow.Subscriber sayHelloStreamRequest(Flow.Subscriber replies) { - final var names = new ArrayList(); - return new Flow.Subscriber<>() { - @Override - public void onSubscribe(Flow.Subscription subscription) { - subscription.request(Long.MAX_VALUE); // turn off flow control - } - - @Override - public void onNext(HelloRequest item) { - names.add(item.getName()); - } - - @Override - public void onError(Throwable throwable) { - replies.onError(throwable); - } - - @Override - public void onComplete() { - final var reply = HelloReply.newBuilder() - .setMessage("Hello " + String.join(", ", names)) - .build(); - replies.onNext(reply); - replies.onComplete(); - } - }; - } - - @Override - public void sayHelloStreamReply(HelloRequest request, Flow.Subscriber replies) { - for (int i = 0; i < 10; i++) { - replies.onNext(HelloReply.newBuilder() - .setMessage("Hello!") - .build()); - } - - replies.onComplete(); - } - - @Override - public Flow.Subscriber sayHelloStreamBidi(Flow.Subscriber replies) { - // Here we receive info from the client. In this case, it is a stream of requests with names. - // We will respond with a stream of replies. - return new Flow.Subscriber<>() { - @Override - public void onSubscribe(Flow.Subscription subscription) { - subscription.request(Long.MAX_VALUE); // turn off flow control - } - - @Override - public void onNext(HelloRequest item) { - replies.onNext(HelloReply.newBuilder() - .setMessage("Hello " + item.getName()) - .build()); - } - - @Override - public void onError(Throwable throwable) { - replies.onError(throwable); - } - - @Override - public void onComplete() { - replies.onComplete(); - } - }; - } - } -} diff --git a/pbj-core/pbj-runtime/src/main/java/com/hedera/pbj/runtime/grpc/Pipelines.java b/pbj-core/pbj-runtime/src/main/java/com/hedera/pbj/runtime/grpc/Pipelines.java index a1405add..1d46b4f0 100644 --- a/pbj-core/pbj-runtime/src/main/java/com/hedera/pbj/runtime/grpc/Pipelines.java +++ b/pbj-core/pbj-runtime/src/main/java/com/hedera/pbj/runtime/grpc/Pipelines.java @@ -413,30 +413,6 @@ private abstract static class PipelineBuilderImpl implements Flow.Subscrib protected Flow.Subscriber replies; private Flow.Subscription sourceSubscription; - @Override - public PipelineBuilder mapRequest(MappingMethod mapper) { - this.requestMapper = mapper; - return this; - } - - @Override - public PipelineBuilder mapResponse(MappingMethod mapper) { - this.responseMapper = mapper; - return this; - } - - @Override - public PipelineBuilder respondTo(Flow.Subscriber replies) { - this.replies = replies; - return this; - } - - @Override - public Flow.Subscriber build() { - replies.onSubscribe(this); - return this; - } - @Override public void request(long n) { // If we supported flow control, we'd pay attention to the number being presented. And we should, ideally,