diff --git a/FIPS/pom.xml b/FIPS/pom.xml index 04fa6a5f5..dd994a297 100644 --- a/FIPS/pom.xml +++ b/FIPS/pom.xml @@ -429,6 +429,10 @@ org.jsoup ${shadeBase}.org.jsoup + + com.github.luben.zstd + ${shadeBase}.com.github.luben.zstd + com.nimbusds ${shadeBase}.com.nimbusds diff --git a/FIPS/scripts/check_content.sh b/FIPS/scripts/check_content.sh index 8b818b1b4..fa675655e 100755 --- a/FIPS/scripts/check_content.sh +++ b/FIPS/scripts/check_content.sh @@ -1,12 +1,12 @@ #!/bin/bash -e -# scripts used to check if all dependency is shaded into snowflake internal path +# scripts used to check if all dependencies are shaded into snowflake internal path set -o pipefail DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" -if jar tvf $DIR/../target/snowflake-jdbc-fips.jar | awk '{print $8}' | grep -v -E "^(net|com)/snowflake" | grep -v -E "(com|net)/\$" | grep -v -E "^META-INF" | grep -v -E "^mozilla" | grep -v -E "^com/sun/jna" | grep -v com/sun/ | grep -v mime.types; then +if jar tvf $DIR/../target/snowflake-jdbc-fips.jar | awk '{print $8}' | grep -v -E "^(net|com)/snowflake" | grep -v -E "(com|net)/\$" | grep -v -E "^META-INF" | grep -v -E "^mozilla" | grep -v -E "^com/sun/jna" | grep -v com/sun/ | grep -v mime.types | grep -v -E "^aix/" | grep -v -E "^darwin/" | grep -v -E "^freebsd/" | grep -v -E "^linux/" | grep -v -E "^win/"; then echo "[ERROR] JDBC jar includes class not under the snowflake namespace" exit 1 fi diff --git a/ci/scripts/check_content.sh b/ci/scripts/check_content.sh index a9c0768b6..ce6f34180 100755 --- a/ci/scripts/check_content.sh +++ b/ci/scripts/check_content.sh @@ -8,12 +8,12 @@ set -o pipefail DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" -if jar tvf $DIR/../../target/snowflake-jdbc${package_modifier}.jar | awk '{print $8}' | grep -v -E "^(net|com)/snowflake" | grep -v -E "(com|net)/\$" | grep -v -E "^META-INF" | grep -v -E "^mozilla" | grep -v -E "^com/sun/jna" | grep -v com/sun/ | grep -v mime.types; then +if jar tvf $DIR/../../target/snowflake-jdbc${package_modifier}.jar | awk '{print $8}' | grep -v -E "^(net|com)/snowflake" | grep -v -E "(com|net)/\$" | grep -v -E "^META-INF" | grep -v -E "^mozilla" | grep -v -E "^com/sun/jna" | grep -v com/sun/ | grep -v mime.types | grep -v -E "^aix/" | grep -v -E "^darwin/" | grep -v -E "^freebsd/" | grep -v -E "^linux/" | grep -v -E "^win/"; then echo "[ERROR] JDBC jar includes class not under the snowflake namespace" exit 1 fi -if jar tvf $DIR/../../target/snowflake-jdbc${package_modifier}.jar | awk '{print $8}' | grep -E "^META-INF/versions/.*.class" | grep -v -E "^META-INF/versions/.*/(net|com)/snowflake"; then - echo "[ERROR] JDBC jar includes multi release classes not under the snowflake namespace" +if jar tvf $DIR/../../target/snowflake-jdbc${package_modifier}.jar | awk '{print $8}' | grep -E "^META-INF/versions/.*.class" | grep -v -E "^META-INF/versions/.*/(net|com)/snowflake" | grep -v -E "^aix/" | grep -v -E "^darwin/" | grep -v -E "^freebsd/" | grep -v -E "^linux/" | grep -v -E "^win/"; then + echo "[ERROR] JDBC jar includes multi-release classes not under the snowflake namespace" exit 1 fi diff --git a/linkage-checker-exclusion-rules.xml b/linkage-checker-exclusion-rules.xml index 8bad89714..0f6275008 100644 --- a/linkage-checker-exclusion-rules.xml +++ b/linkage-checker-exclusion-rules.xml @@ -19,11 +19,6 @@ Optional - - - - Optional - diff --git a/parent-pom.xml b/parent-pom.xml index 28412d161..04dc541e0 100644 --- a/parent-pom.xml +++ b/parent-pom.xml @@ -19,6 +19,7 @@ 1.10.0 4.5.14 4.4.16 + 1.5.6-5 17.0.0 9.3 1.8.1 @@ -327,6 +328,11 @@ httpcore ${apache.httpcore.version} + + com.github.luben + zstd-jni + ${zstd-jni.version} + org.apache.tika tika-core @@ -644,6 +650,10 @@ org.apache.httpcomponents httpcore + + com.github.luben + zstd-jni + org.apache.tika tika-core diff --git a/pom.xml b/pom.xml index 096641174..e41cb7f62 100644 --- a/pom.xml +++ b/pom.xml @@ -947,6 +947,10 @@ android.annotation ${shadeBase}.android.annotation + + com.github.luben.zstd + ${shadeBase}.com.github.luben.zstd + diff --git a/src/main/java/net/snowflake/client/jdbc/DefaultResultStreamProvider.java b/src/main/java/net/snowflake/client/jdbc/DefaultResultStreamProvider.java index 3ee556bb4..567db8fa1 100644 --- a/src/main/java/net/snowflake/client/jdbc/DefaultResultStreamProvider.java +++ b/src/main/java/net/snowflake/client/jdbc/DefaultResultStreamProvider.java @@ -2,6 +2,7 @@ import static net.snowflake.client.core.Constants.MB; +import com.github.luben.zstd.ZstdInputStream; import java.io.IOException; import java.io.InputStream; import java.io.PushbackInputStream; @@ -153,6 +154,8 @@ private InputStream detectContentEncodingAndGetInputStream(HttpResponse response if ("gzip".equalsIgnoreCase(encoding.getValue())) { /* specify buffer size for GZIPInputStream */ inputStream = new GZIPInputStream(is, STREAM_BUFFER_SIZE); + } else if ("zstd".equalsIgnoreCase(encoding.getValue())) { + inputStream = new ZstdInputStream(is); } else { throw new SnowflakeSQLException( SqlState.INTERNAL_ERROR, diff --git a/src/test/java/net/snowflake/client/jdbc/DefaultResultStreamProviderTest.java b/src/test/java/net/snowflake/client/jdbc/DefaultResultStreamProviderTest.java new file mode 100644 index 000000000..b78bf3a5e --- /dev/null +++ b/src/test/java/net/snowflake/client/jdbc/DefaultResultStreamProviderTest.java @@ -0,0 +1,120 @@ +package net.snowflake.client.jdbc; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.github.luben.zstd.ZstdInputStream; +import com.github.luben.zstd.ZstdOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.junit.Before; +import org.junit.Test; + +public class DefaultResultStreamProviderTest { + + private DefaultResultStreamProvider resultStreamProvider; + private HttpResponse mockResponse; + + @Before + public void setUp() { + resultStreamProvider = new DefaultResultStreamProvider(); + mockResponse = mock(HttpResponse.class); + } + + private InputStream invokeDetectContentEncodingAndGetInputStream( + HttpResponse response, InputStream inputStream) throws Exception { + Method method = + DefaultResultStreamProvider.class.getDeclaredMethod( + "detectContentEncodingAndGetInputStream", HttpResponse.class, InputStream.class); + method.setAccessible(true); + return (InputStream) method.invoke(resultStreamProvider, response, inputStream); + } + + @Test + public void testDetectContentEncodingAndGetInputStream_Gzip() throws Exception { + // Mocking gzip content encoding + Header encodingHeader = mock(Header.class); + when(encodingHeader.getValue()).thenReturn("gzip"); + when(mockResponse.getFirstHeader("Content-Encoding")).thenReturn(encodingHeader); + + // Original data to compress and validate + String originalData = "Some data in GZIP"; + + // Creating a gzip byte array using GZIPOutputStream + byte[] gzipData; + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream)) { + gzipOutputStream.write(originalData.getBytes(StandardCharsets.UTF_8)); + gzipOutputStream.close(); // close to flush and finish the compression + gzipData = byteArrayOutputStream.toByteArray(); + } + + // Mocking input stream with the gzip data + InputStream gzipStream = new ByteArrayInputStream(gzipData); + + // Call the private method using reflection + InputStream resultStream = + invokeDetectContentEncodingAndGetInputStream(mockResponse, gzipStream); + + // Decompress and validate the data matches original + ByteArrayOutputStream decompressedOutput = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int bytesRead; + try (GZIPInputStream gzipInputStream = (GZIPInputStream) resultStream) { + while ((bytesRead = gzipInputStream.read(buffer)) != -1) { + decompressedOutput.write(buffer, 0, bytesRead); + } + } + String decompressedData = new String(decompressedOutput.toByteArray(), StandardCharsets.UTF_8); + + assertEquals(originalData, decompressedData); + } + + @Test + public void testDetectContentEncodingAndGetInputStream_Zstd() throws Exception { + // Mocking zstd content encoding + Header encodingHeader = mock(Header.class); + when(encodingHeader.getValue()).thenReturn("zstd"); + when(mockResponse.getFirstHeader("Content-Encoding")).thenReturn(encodingHeader); + + // Original data to compress and validate + String originalData = "Some data in ZSTD"; + + // Creating a zstd byte array using ZstdOutputStream + byte[] zstdData; + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ZstdOutputStream zstdOutputStream = new ZstdOutputStream(byteArrayOutputStream)) { + zstdOutputStream.write(originalData.getBytes(StandardCharsets.UTF_8)); + zstdOutputStream.close(); // close to flush and finish the compression + zstdData = byteArrayOutputStream.toByteArray(); + } + + // Mocking input stream with the zstd data + InputStream zstdStream = new ByteArrayInputStream(zstdData); + + // Call the private method using reflection + InputStream resultStream = + invokeDetectContentEncodingAndGetInputStream(mockResponse, zstdStream); + + // Decompress and validate the data matches original + ByteArrayOutputStream decompressedOutput = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int bytesRead; + try (ZstdInputStream zstdInputStream = (ZstdInputStream) resultStream) { + while ((bytesRead = zstdInputStream.read(buffer)) != -1) { + decompressedOutput.write(buffer, 0, bytesRead); + } + } + String decompressedData = new String(decompressedOutput.toByteArray(), StandardCharsets.UTF_8); + + assertEquals(originalData, decompressedData); + } +} diff --git a/thin_public_pom.xml b/thin_public_pom.xml index 31a1aedee..a781a376d 100644 --- a/thin_public_pom.xml +++ b/thin_public_pom.xml @@ -64,6 +64,7 @@ UTF-8 2.0.13 1.6.9 + 1.5.6-5 @@ -262,6 +263,11 @@ jsoup ${jsoup.version} + + com.github.luben + zstd-jni + ${zstd-jni.version} + org.slf4j slf4j-api