diff --git a/ballerina-tests/tests/78_server_error_types.proto b/ballerina-tests/tests/78_server_error_types.proto new file mode 100644 index 000000000..10e890e60 --- /dev/null +++ b/ballerina-tests/tests/78_server_error_types.proto @@ -0,0 +1,24 @@ +// Copyright (c) 2023 WSO2 LLC. (http://www.wso2.com) All Rights Reserved. +// +// WSO2 LLC. licenses this file to you 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. + +syntax = "proto3"; + +import "google/protobuf/empty.proto"; +import "google/protobuf/wrappers.proto"; + +service ServerErrorTypesService { + rpc GetServerError(google.protobuf.Int32Value) returns (google.protobuf.Empty); +} diff --git a/ballerina-tests/tests/78_server_error_types_client.bal b/ballerina-tests/tests/78_server_error_types_client.bal new file mode 100644 index 000000000..21e7b7f07 --- /dev/null +++ b/ballerina-tests/tests/78_server_error_types_client.bal @@ -0,0 +1,225 @@ +// Copyright (c) 2023 WSO2 LLC. (http://www.wso2.com) All Rights Reserved. +// +// WSO2 LLC. licenses this file to you 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 ballerina/io; +import ballerina/grpc; +import ballerina/test; + +@test:Config {enable: true} +function testErrorTypesCancelledError() returns error? { + ServerErrorTypesServiceClient errorClient = check new ("http://localhost:9178"); + error? response = errorClient->GetServerError(1); + io:println(response); + if response is grpc:CancelledError { + test:assertEquals(response.message(), "Cancelled execution"); + } else { + test:assertFail("Expected error type is not returned"); + } +} + +@test:Config {enable: true} +function testErrorTypesUnKnownError() returns error? { + ServerErrorTypesServiceClient errorClient = check new ("http://localhost:9178"); + error? response = errorClient->GetServerError(2); + io:println(response); + if response is grpc:UnKnownError { + test:assertEquals(response.message(), "Unknown request"); + } else { + test:assertFail("Expected error type is not returned"); + } +} + +@test:Config {enable: true} +function testErrorTypesInvalidArgumentError() returns error? { + ServerErrorTypesServiceClient errorClient = check new ("http://localhost:9178"); + error? response = errorClient->GetServerError(3); + io:println(response); + if response is grpc:InvalidArgumentError { + test:assertEquals(response.message(), "Invalid argument"); + } else { + test:assertFail("Expected error type is not returned"); + } +} + +@test:Config {enable: true} +function testErrorTypesDeadlineExceededError() returns error? { + ServerErrorTypesServiceClient errorClient = check new ("http://localhost:9178"); + error? response = errorClient->GetServerError(4); + io:println(response); + if response is grpc:DeadlineExceededError { + test:assertEquals(response.message(), "Deadline exceeded"); + } else { + test:assertFail("Expected error type is not returned"); + } +} + +@test:Config {enable: true} +function testErrorTypesNotFoundError() returns error? { + ServerErrorTypesServiceClient errorClient = check new ("http://localhost:9178"); + error? response = errorClient->GetServerError(5); + io:println(response); + if response is grpc:NotFoundError { + test:assertEquals(response.message(), "Not found"); + } else { + test:assertFail("Expected error type is not returned"); + } +} + +@test:Config {enable: true} +function testErrorTypesAlreadyExistsError() returns error? { + ServerErrorTypesServiceClient errorClient = check new ("http://localhost:9178"); + error? response = errorClient->GetServerError(6); + io:println(response); + if response is grpc:AlreadyExistsError { + test:assertEquals(response.message(), "Already exists"); + } else { + test:assertFail("Expected error type is not returned"); + } +} + +@test:Config {enable: true} +function testErrorTypesPermissionDeniedError() returns error? { + ServerErrorTypesServiceClient errorClient = check new ("http://localhost:9178"); + error? response = errorClient->GetServerError(7); + io:println(response); + if response is grpc:PermissionDeniedError { + test:assertEquals(response.message(), "Permission denied"); + } else { + test:assertFail("Expected error type is not returned"); + } +} + +@test:Config {enable: true} +function testErrorTypesUnauthenticatedError() returns error? { + ServerErrorTypesServiceClient errorClient = check new ("http://localhost:9178"); + error? response = errorClient->GetServerError(8); + io:println(response); + if response is grpc:UnauthenticatedError { + test:assertEquals(response.message(), "Unauthenticated"); + } else { + test:assertFail("Expected error type is not returned"); + } +} + +@test:Config {enable: true} +function testErrorTypesResourceExhaustedError() returns error? { + ServerErrorTypesServiceClient errorClient = check new ("http://localhost:9178"); + error? response = errorClient->GetServerError(9); + io:println(response); + if response is grpc:ResourceExhaustedError { + test:assertEquals(response.message(), "Resource exhausted"); + } else { + test:assertFail("Expected error type is not returned"); + } +} + +@test:Config {enable: true} +function testErrorTypesFailedPreconditionError() returns error? { + ServerErrorTypesServiceClient errorClient = check new ("http://localhost:9178"); + error? response = errorClient->GetServerError(10); + io:println(response); + if response is grpc:FailedPreconditionError { + test:assertEquals(response.message(), "Failed precondition"); + } else { + test:assertFail("Expected error type is not returned"); + } +} + +@test:Config {enable: true} +function testErrorTypesAbortedError() returns error? { + ServerErrorTypesServiceClient errorClient = check new ("http://localhost:9178"); + error? response = errorClient->GetServerError(11); + io:println(response); + if response is grpc:AbortedError { + test:assertEquals(response.message(), "Aborted execution"); + } else { + test:assertFail("Expected error type is not returned"); + } +} + +@test:Config {enable: true} +function testErrorTypesOutOfRangeError() returns error? { + ServerErrorTypesServiceClient errorClient = check new ("http://localhost:9178"); + error? response = errorClient->GetServerError(12); + io:println(response); + if response is grpc:OutOfRangeError { + test:assertEquals(response.message(), "Out of range"); + } else { + test:assertFail("Expected error type is not returned"); + } +} + +@test:Config {enable: true} +function testErrorTypesUnimplementedError() returns error? { + ServerErrorTypesServiceClient errorClient = check new ("http://localhost:9178"); + error? response = errorClient->GetServerError(13); + io:println(response); + if response is grpc:UnimplementedError { + test:assertEquals(response.message(), "Unimplemented"); + } else { + test:assertFail("Expected error type is not returned"); + } +} + +@test:Config {enable: true} +function testErrorTypesInternalError() returns error? { + ServerErrorTypesServiceClient errorClient = check new ("http://localhost:9178"); + error? response = errorClient->GetServerError(14); + io:println(response); + if response is grpc:InternalError { + test:assertEquals(response.message(), "Internal error"); + } else { + test:assertFail("Expected error type is not returned"); + } +} + +@test:Config {enable: true} +function testErrorTypesUnavailableError() returns error? { + ServerErrorTypesServiceClient errorClient = check new ("http://localhost:9178"); + error? response = errorClient->GetServerError(15); + io:println(response); + if response is grpc:UnavailableError { + test:assertEquals(response.message(), "Unavailable"); + } else { + test:assertFail("Expected error type is not returned"); + } +} + +@test:Config {enable: true} +function testErrorTypesDataLossError() returns error? { + ServerErrorTypesServiceClient errorClient = check new ("http://localhost:9178"); + error? response = errorClient->GetServerError(16); + io:println(response); + if response is grpc:DataLossError { + test:assertEquals(response.message(), "Data loss"); + } else { + test:assertFail("Expected error type is not returned"); + } +} + +@test:Config {enable: true} +function testErrorTypesUnknownErrorType() returns error? { + ServerErrorTypesServiceClient errorClient = check new ("http://localhost:9178"); + error? response = errorClient->GetServerError(22); + io:println(response); + if response is error { + test:assertEquals(response.message(), "Unknown error type"); + } else { + test:assertFail("Expected error type is not returned"); + } +} + + diff --git a/ballerina-tests/tests/78_server_error_types_pb.bal b/ballerina-tests/tests/78_server_error_types_pb.bal new file mode 100644 index 000000000..2c3c1a776 --- /dev/null +++ b/ballerina-tests/tests/78_server_error_types_pb.bal @@ -0,0 +1,83 @@ +// Copyright (c) 2023 WSO2 LLC. (http://www.wso2.com) All Rights Reserved. +// +// WSO2 LLC. licenses this file to you 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 ballerina/grpc; +import ballerina/protobuf.types.empty; +import ballerina/protobuf.types.wrappers; + +public const string SERVER_ERROR_TYPES_DESC = "0A1B37385F7365727665725F6572726F725F74797065732E70726F746F1A1B676F6F676C652F70726F746F6275662F656D7074792E70726F746F1A1E676F6F676C652F70726F746F6275662F77726170706572732E70726F746F32600A175365727665724572726F7254797065735365727669636512450A0E4765745365727665724572726F72121B2E676F6F676C652E70726F746F6275662E496E74333256616C75651A162E676F6F676C652E70726F746F6275662E456D707479620670726F746F33"; + +public isolated client class ServerErrorTypesServiceClient { + *grpc:AbstractClientEndpoint; + + private final grpc:Client grpcClient; + + public isolated function init(string url, *grpc:ClientConfiguration config) returns grpc:Error? { + self.grpcClient = check new (url, config); + check self.grpcClient.initStub(self, SERVER_ERROR_TYPES_DESC); + } + + isolated remote function GetServerError(int|wrappers:ContextInt req) returns grpc:Error? { + map headers = {}; + int message; + if req is wrappers:ContextInt { + message = req.content; + headers = req.headers; + } else { + message = req; + } + _ = check self.grpcClient->executeSimpleRPC("ServerErrorTypesService/GetServerError", message, headers); + } + + isolated remote function GetServerErrorContext(int|wrappers:ContextInt req) returns empty:ContextNil|grpc:Error { + map headers = {}; + int message; + if req is wrappers:ContextInt { + message = req.content; + headers = req.headers; + } else { + message = req; + } + var payload = check self.grpcClient->executeSimpleRPC("ServerErrorTypesService/GetServerError", message, headers); + [anydata, map] [_, respHeaders] = payload; + return {headers: respHeaders}; + } +} + +public client class ServerErrorTypesServiceNilCaller { + private grpc:Caller caller; + + public isolated function init(grpc:Caller caller) { + self.caller = caller; + } + + public isolated function getId() returns int { + return self.caller.getId(); + } + + isolated remote function sendError(grpc:Error response) returns grpc:Error? { + return self.caller->sendError(response); + } + + isolated remote function complete() returns grpc:Error? { + return self.caller->complete(); + } + + public isolated function isCancelled() returns boolean { + return self.caller.isCancelled(); + } +} + diff --git a/ballerina-tests/tests/78_server_error_types_service.bal b/ballerina-tests/tests/78_server_error_types_service.bal new file mode 100644 index 000000000..7870526b9 --- /dev/null +++ b/ballerina-tests/tests/78_server_error_types_service.bal @@ -0,0 +1,76 @@ +// Copyright (c) 2023 WSO2 LLC. (http://www.wso2.com) All Rights Reserved. +// +// WSO2 LLC. licenses this file to you 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 ballerina/grpc; + +listener grpc:Listener ep78 = new (9178); + +@grpc:Descriptor {value: SERVER_ERROR_TYPES_DESC} +service "ServerErrorTypesService" on ep78 { + + remote function GetServerError(int errorType) returns error? { + if errorType == 1 { + return error grpc:CancelledError("Cancelled execution"); + } + if errorType == 2 { + return error grpc:UnKnownError("Unknown request"); + } + if errorType == 3 { + return error grpc:InvalidArgumentError("Invalid argument"); + } + if errorType == 4 { + return error grpc:DeadlineExceededError("Deadline exceeded"); + } + if errorType == 5 { + return error grpc:NotFoundError("Not found"); + } + if errorType == 6 { + return error grpc:AlreadyExistsError("Already exists"); + } + if errorType == 7 { + return error grpc:PermissionDeniedError("Permission denied"); + } + if errorType == 8 { + return error grpc:UnauthenticatedError("Unauthenticated"); + } + if errorType == 9 { + return error grpc:ResourceExhaustedError("Resource exhausted"); + } + if errorType == 10 { + return error grpc:FailedPreconditionError("Failed precondition"); + } + if errorType == 11 { + return error grpc:AbortedError("Aborted execution"); + } + if errorType == 12 { + return error grpc:OutOfRangeError("Out of range"); + } + if errorType == 13 { + return error grpc:UnimplementedError("Unimplemented"); + } + if errorType == 14 { + return error grpc:InternalError("Internal error"); + } + if errorType == 15 { + return error grpc:UnavailableError("Unavailable"); + } + if errorType == 16 { + return error grpc:DataLossError("Data loss"); + } + return error("Unknown error type"); + } +} + diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index 4204eb207..033d9b1c6 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -1,7 +1,7 @@ [package] org = "ballerina" name = "grpc" -version = "1.10.0" +version = "1.10.1" distribution = "2201.8.0" authors = ["Ballerina"] keywords = ["network", "grpc", "protobuf", "server-streaming", "client-streaming", "bidirectional-streaming"] @@ -16,11 +16,11 @@ graalvmCompatible = true [[platform.java17.dependency]] groupId = "io.ballerina.stdlib" artifactId = "grpc-native" -version = "1.10.0" -path = "../native/build/libs/grpc-native-1.10.0.jar" +version = "1.10.1" +path = "../native/build/libs/grpc-native-1.10.1-SNAPSHOT.jar" [[platform.java17.dependency]] -path = "../test-utils/build/libs/grpc-test-utils-1.10.0.jar" +path = "../test-utils/build/libs/grpc-test-utils-1.10.1-SNAPSHOT.jar" scope = "testOnly" [[platform.java17.dependency]] diff --git a/ballerina/CompilerPlugin.toml b/ballerina/CompilerPlugin.toml index 756a7531a..722abc199 100644 --- a/ballerina/CompilerPlugin.toml +++ b/ballerina/CompilerPlugin.toml @@ -3,4 +3,4 @@ id = "grpc-compiler-plugin" class = "io.ballerina.stdlib.grpc.plugin.GrpcCompilerPlugin" [[dependency]] -path = "../compiler-plugin/build/libs/grpc-compiler-plugin-1.10.0.jar" +path = "../compiler-plugin/build/libs/grpc-compiler-plugin-1.10.1-SNAPSHOT.jar" diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index e04d076df..85bd3802d 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -68,7 +68,7 @@ dependencies = [ [[package]] org = "ballerina" name = "grpc" -version = "1.10.0" +version = "1.10.1" dependencies = [ {org = "ballerina", name = "auth"}, {org = "ballerina", name = "crypto"}, diff --git a/changelog.md b/changelog.md index 039967c93..a51eca470 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,15 @@ This file contains all the notable changes done to the Ballerina gRPC package th The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased +### Fixed +- [Fixed error response from backend not getting properly propegated to the client](https://github.com/ballerina-platform/ballerina-standard-library/issues/4833) + +## [1.9.1] - 2023-09-11 +### Fixed +- [Address CVE-2023-33201 bouncy castle vulnerability](https://github.com/ballerina-platform/ballerina-standard-library/issues/4776) +- [Fixed performance issue due to `tcpnodelay` configuration](https://github.com/ballerina-platform/ballerina-standard-library/issues/4768) + ## [1.9.0] - 2023-06-30 ### Fixed - [Add descriptor map to `grpc:Descriptor` and stub initialization](https://github.com/ballerina-platform/ballerina-standard-library/issues/4555) diff --git a/native/src/main/java/io/ballerina/stdlib/grpc/GrpcConstants.java b/native/src/main/java/io/ballerina/stdlib/grpc/GrpcConstants.java index 2697489fe..abf8f2858 100644 --- a/native/src/main/java/io/ballerina/stdlib/grpc/GrpcConstants.java +++ b/native/src/main/java/io/ballerina/stdlib/grpc/GrpcConstants.java @@ -193,7 +193,7 @@ private GrpcConstants() { public static final String INVALID_ARGUMENT_ERROR = "InvalidArgumentError"; public static final String DEADLINE_EXCEEDED_ERROR = "DeadlineExceededError"; public static final String NOT_FOUND_ERROR = "NotFoundError"; - public static final String ALREADY_EXISTS_ERROR = "AleadyExistsError"; + public static final String ALREADY_EXISTS_ERROR = "AlreadyExistsError"; public static final String PERMISSION_DENIED_ERROR = "PermissionDeniedError"; public static final String RESOURCE_EXHAUSTED_ERROR = "ResourceExhaustedError"; public static final String FAILED_PRECONDITION_ERROR = "FailedPreconditionError";