diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 6488f734..22ad1b44 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -5,7 +5,7 @@ [ballerina] dependencies-toml-version = "2" -distribution-version = "2201.12.0" +distribution-version = "2201.13.0-20250924-081800-3dae8c03" [[package]] org = "ballerina" @@ -107,7 +107,7 @@ modules = [ [[package]] org = "ballerina" name = "time" -version = "2.7.0" +version = "2.8.0" dependencies = [ {org = "ballerina", name = "jballerina.java"} ] diff --git a/ballerina/tests/encrypt_decrypt_pgp_test.bal b/ballerina/tests/encrypt_decrypt_pgp_test.bal index 0b84d0e6..a61d5f7f 100644 --- a/ballerina/tests/encrypt_decrypt_pgp_test.bal +++ b/ballerina/tests/encrypt_decrypt_pgp_test.bal @@ -138,3 +138,19 @@ isolated function testNegativeEncryptAndDecryptStreamWithPgpInvalidPassphrase() test:assertFail("Should return a crypto Error"); } } + +@test:Config +isolated function testInputStreamWithoutCloseForPgpEncrypt() returns error? { + byte[] passphrase = "qCr3bv@5mj5n4eY".toBytes(); + string[] names = ["alice", "charlie"]; + stream inputStream = from string name in names + select name.toString().toBytes(); + stream encryptedStream = check encryptStreamAsPgp(inputStream, PGP_PUBLIC_KEY_PATH); + stream decryptStreamAsPgp = check decryptStreamFromPgp(encryptedStream, PGP_PRIVATE_KEY_PATH, passphrase); + byte[] actualBytes = []; + check from byte[] bytes in decryptStreamAsPgp + do { + actualBytes.push(...bytes); + }; + test:assertEquals(check string:fromBytes(actualBytes), "alicecharlie"); +} diff --git a/changelog.md b/changelog.md index 65d9f8ef..c2787a4e 100644 --- a/changelog.md +++ b/changelog.md @@ -12,6 +12,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - [Update OIDS of NIST approved post quantum algorithms](https://github.com/ballerina-platform/ballerina-library/issues/7678) - [Optimize hardcoded IV detection using semantic model reference counting](https://github.com/ballerina-platform/ballerina-library/issues/8257) +### Fixed +- [Implement optional close method check for BStream](https://github.com/ballerina-platform/ballerina-library/issues/8288) + ## [2.8.0] - 2025-02-11 ### Added diff --git a/native/src/main/java/io/ballerina/stdlib/crypto/BallerinaInputStream.java b/native/src/main/java/io/ballerina/stdlib/crypto/BallerinaInputStream.java index 6c0a5e8f..ec3da25e 100644 --- a/native/src/main/java/io/ballerina/stdlib/crypto/BallerinaInputStream.java +++ b/native/src/main/java/io/ballerina/stdlib/crypto/BallerinaInputStream.java @@ -18,6 +18,9 @@ package io.ballerina.stdlib.crypto; import io.ballerina.runtime.api.Environment; +import io.ballerina.runtime.api.types.MethodType; +import io.ballerina.runtime.api.types.ObjectType; +import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BError; @@ -27,6 +30,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.Objects; /** @@ -48,10 +52,22 @@ public class BallerinaInputStream extends InputStream { private final BStream ballerinaStream; private ByteBuffer buffer = null; private boolean endOfStream = false; + private final boolean hasCloseMethod; public BallerinaInputStream(Environment environment, BStream ballerinaStream) { this.ballerinaStream = ballerinaStream; this.environment = environment; + + // Implementing a close method for a Ballerina stream is optional + // There is no Ballerina runtime API to check if the stream has a close method + // So accessing the iterator object type methods to check if it has a close method + Type iteratorType = ballerinaStream.getIteratorObj().getOriginalType(); + if (iteratorType instanceof ObjectType iteratorObjectType) { + MethodType[] methods = iteratorObjectType.getMethods(); + hasCloseMethod = Arrays.stream(methods).anyMatch(method -> method.getName().equals(BAL_STREAM_CLOSE)); + } else { + hasCloseMethod = false; + } } @Override @@ -71,6 +87,9 @@ public int read() throws IOException { @Override public void close() throws IOException { + if (!hasCloseMethod) { + return; + } Object result = callBalStreamMethod(BAL_STREAM_CLOSE); if (result instanceof BError bError) { throw new IOException((bError).getMessage());