From 9e20b5563abf76c6de4049bce1a05463f18eb58a Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 27 Oct 2023 12:10:33 +0200 Subject: [PATCH 1/3] [MRESOLVER-295] ChecksumUtils should be gone (deprecated) Shuffle it's pieces along to their final place, and deprecate the whole class, but leave it in place, as Resolver 1.x did not deprecate it. To be removed in 2.1 of Resolver. --- https://issues.apache.org/jira/browse/MRESOLVER-295 --- .../basic/TestChecksumAlgorithmSelector.java | 4 +- .../internal/impl/DefaultFileProcessor.java | 46 +++- ...DigestChecksumAlgorithmFactorySupport.java | 4 +- .../impl/DefaultFileProcessorTest.java | 30 +++ .../internal/test/util/TestChecksumUtils.java | 89 +++++++ .../aether/transport/http/HttpServer.java | 4 +- .../eclipse/aether/util/ChecksumUtils.java | 36 +-- .../eclipse/aether/util/StringDigestUtil.java | 57 ++++- .../eclipse/aether/util/ChecksumUtilTest.java | 239 ------------------ .../aether/util/StringDigestUtilTest.java | 20 ++ 10 files changed, 243 insertions(+), 286 deletions(-) create mode 100644 maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestChecksumUtils.java delete mode 100644 maven-resolver-util/src/test/java/org/eclipse/aether/util/ChecksumUtilTest.java diff --git a/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/TestChecksumAlgorithmSelector.java b/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/TestChecksumAlgorithmSelector.java index 59d8a9913..c9dedc8d7 100644 --- a/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/TestChecksumAlgorithmSelector.java +++ b/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/TestChecksumAlgorithmSelector.java @@ -31,7 +31,7 @@ import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySupport; -import org.eclipse.aether.util.ChecksumUtils; +import org.eclipse.aether.util.StringDigestUtil; import static java.util.stream.Collectors.toList; @@ -104,7 +104,7 @@ public void update(final ByteBuffer input) { @Override public String checksum() { - return ChecksumUtils.toHexString(messageDigest.digest()); + return StringDigestUtil.toHexString(messageDigest.digest()); } }; } catch (NoSuchAlgorithmException e) { diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultFileProcessor.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultFileProcessor.java index d02f73701..b58479237 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultFileProcessor.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultFileProcessor.java @@ -21,20 +21,13 @@ import javax.inject.Named; import javax.inject.Singleton; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.UncheckedIOException; +import java.io.*; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import org.eclipse.aether.spi.io.FileProcessor; -import org.eclipse.aether.util.ChecksumUtils; import org.eclipse.aether.util.FileUtils; /** @@ -142,7 +135,42 @@ public void move(File source, File target) throws IOException { @Override public String readChecksum(final File checksumFile) throws IOException { // for now do exactly same as happened before, but FileProcessor is a component and can be replaced - return ChecksumUtils.read(checksumFile); + return read(checksumFile); + } + + /** + * Extracts the checksum from the specified file, applies several tricks to be able to read GNU Coreutils format. + * Code copied in unmodified form from ChecksumUtils that is deprecated. + */ + private static String read(File checksumFile) throws IOException { + String checksum = ""; + try (BufferedReader br = new BufferedReader( + new InputStreamReader(Files.newInputStream(checksumFile.toPath()), StandardCharsets.UTF_8), 512)) { + while (true) { + String line = br.readLine(); + if (line == null) { + break; + } + line = line.trim(); + if (!line.isEmpty()) { + checksum = line; + break; + } + } + } + + if (checksum.matches(".+= [0-9A-Fa-f]+")) { + int lastSpacePos = checksum.lastIndexOf(' '); + checksum = checksum.substring(lastSpacePos + 1); + } else { + int spacePos = checksum.indexOf(' '); + + if (spacePos != -1) { + checksum = checksum.substring(0, spacePos); + } + } + + return checksum; } @Override diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/MessageDigestChecksumAlgorithmFactorySupport.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/MessageDigestChecksumAlgorithmFactorySupport.java index e335fec16..d704a4f5c 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/MessageDigestChecksumAlgorithmFactorySupport.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/MessageDigestChecksumAlgorithmFactorySupport.java @@ -25,7 +25,7 @@ import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithm; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySupport; -import org.eclipse.aether.util.ChecksumUtils; +import org.eclipse.aether.util.StringDigestUtil; /** * Support class to implement {@link ChecksumAlgorithmFactory} based on Java {@link MessageDigest}. @@ -49,7 +49,7 @@ public void update(final ByteBuffer input) { @Override public String checksum() { - return ChecksumUtils.toHexString(messageDigest.digest()); + return StringDigestUtil.toHexString(messageDigest.digest()); } }; } catch (NoSuchAlgorithmException e) { diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultFileProcessorTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultFileProcessorTest.java index 3c18d5d34..2af17e859 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultFileProcessorTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultFileProcessorTest.java @@ -134,4 +134,34 @@ void testWriteStream() throws IOException { target.delete(); } + + @Test + void testReadChecksumEmptyFile() throws IOException { + File emptyFile = TestFileUtils.createTempFile(""); + String read = fileProcessor.readChecksum(emptyFile); + assertEquals("", read); + } + + @Test + void testReadChecksum() throws IOException { + String checksum = "da39a3ee5e6b4b0d3255bfef95601890afd80709"; + File checksumFile = TestFileUtils.createTempFile(checksum); + String read = fileProcessor.readChecksum(checksumFile); + assertEquals(checksum, read); + } + + @Test + void testReadChecksumWhitespace() throws IOException { + String checksum = "da39a3ee5e6b4b0d3255bfef95601890afd80709"; + File checksumFile; + String read; + + checksumFile = TestFileUtils.createTempFile("foobar(alg) = " + checksum); + read = fileProcessor.readChecksum(checksumFile); + assertEquals(checksum, read); + + checksumFile = TestFileUtils.createTempFile(checksum + " foobar"); + read = fileProcessor.readChecksum(checksumFile); + assertEquals(checksum, read); + } } diff --git a/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestChecksumUtils.java b/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestChecksumUtils.java new file mode 100644 index 000000000..f0d2591a7 --- /dev/null +++ b/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestChecksumUtils.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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. + */ +package org.eclipse.aether.internal.test.util; + +import java.io.*; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.*; + +/** + * Provides utility methods to calculate checksums. + */ +public class TestChecksumUtils { + public static Map calc(File dataFile, Collection algos) throws IOException { + return calc(new FileInputStream(dataFile), algos); + } + + public static Map calc(byte[] dataBytes, Collection algos) throws IOException { + return calc(new ByteArrayInputStream(dataBytes), algos); + } + + private static Map calc(InputStream data, Collection algos) throws IOException { + Map results = new LinkedHashMap<>(); + + Map digests = new LinkedHashMap<>(); + for (String algo : algos) { + try { + digests.put(algo, MessageDigest.getInstance(algo)); + } catch (NoSuchAlgorithmException e) { + results.put(algo, e); + } + } + + try (InputStream in = data) { + for (byte[] buffer = new byte[32 * 1024]; ; ) { + int read = in.read(buffer); + if (read < 0) { + break; + } + for (MessageDigest digest : digests.values()) { + digest.update(buffer, 0, read); + } + } + } + + for (Map.Entry entry : digests.entrySet()) { + byte[] bytes = entry.getValue().digest(); + + results.put(entry.getKey(), toHexString(bytes)); + } + + return results; + } + + @SuppressWarnings("checkstyle:magicnumber") + private static String toHexString(byte[] bytes) { + if (bytes == null) { + return null; + } + + StringBuilder buffer = new StringBuilder(bytes.length * 2); + + for (byte aByte : bytes) { + int b = aByte & 0xFF; + if (b < 0x10) { + buffer.append('0'); + } + buffer.append(Integer.toHexString(b)); + } + + return buffer.toString(); + } +} diff --git a/maven-resolver-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpServer.java b/maven-resolver-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpServer.java index 74f3c9e24..d1fcce9a9 100644 --- a/maven-resolver-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpServer.java +++ b/maven-resolver-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpServer.java @@ -37,7 +37,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.eclipse.aether.util.ChecksumUtils; +import org.eclipse.aether.internal.test.util.TestChecksumUtils; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.server.Request; @@ -334,7 +334,7 @@ public void handle(String target, Request req, HttpServletRequest request, HttpS "bytes " + offset + "-" + (file.length() - 1L) + "/" + file.length()); } if (checksumHeader != null) { - Map checksums = ChecksumUtils.calc(file, Collections.singleton("SHA-1")); + Map checksums = TestChecksumUtils.calc(file, Collections.singleton("SHA-1")); if (checksumHeader == ChecksumHeader.NEXUS) { response.setHeader(HttpHeader.ETAG.asString(), "{SHA1{" + checksums.get("SHA-1") + "}}"); } else if (checksumHeader == ChecksumHeader.XCHECKSUM) { diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/ChecksumUtils.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/ChecksumUtils.java index d3acf2252..da1a597de 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/ChecksumUtils.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/ChecksumUtils.java @@ -34,7 +34,10 @@ /** * A utility class to assist in the verification and generation of checksums. + * + * @deprecated The use of class should be avoided, see {@link StringDigestUtil} and file processor in SPI module. */ +@Deprecated public final class ChecksumUtils { private ChecksumUtils() { @@ -147,21 +150,7 @@ private static Map calc(InputStream data, Collection alg */ @SuppressWarnings("checkstyle:magicnumber") public static String toHexString(byte[] bytes) { - if (bytes == null) { - return null; - } - - StringBuilder buffer = new StringBuilder(bytes.length * 2); - - for (byte aByte : bytes) { - int b = aByte & 0xFF; - if (b < 0x10) { - buffer.append('0'); - } - buffer.append(Integer.toHexString(b)); - } - - return buffer.toString(); + return StringDigestUtil.toHexString(bytes); } /** @@ -174,21 +163,6 @@ public static String toHexString(byte[] bytes) { */ @SuppressWarnings("checkstyle:magicnumber") public static byte[] fromHexString(String hexString) { - if (hexString == null) { - return null; - } - if (hexString.isEmpty()) { - return new byte[] {}; - } - int len = hexString.length(); - if (len % 2 != 0) { - throw new IllegalArgumentException("hexString length not even"); - } - byte[] data = new byte[len / 2]; - for (int i = 0; i < len; i += 2) { - data[i / 2] = (byte) - ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16)); - } - return data; + return StringDigestUtil.fromHexString(hexString); } } diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/StringDigestUtil.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/StringDigestUtil.java index 6f89246fe..91e8aae5a 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/StringDigestUtil.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/StringDigestUtil.java @@ -63,7 +63,7 @@ public StringDigestUtil update(String data) { * @see MessageDigest#digest() */ public String digest() { - return ChecksumUtils.toHexString(digest.digest()); + return toHexString(digest.digest()); } /** @@ -79,4 +79,59 @@ public static StringDigestUtil sha1() { public static String sha1(final String string) { return sha1().update(string).digest(); } + + /** + * Creates a hexadecimal representation of the specified bytes. Each byte is converted into a two-digit hex number + * and appended to the result with no separator between consecutive bytes. + * + * @param bytes The bytes to represent in hex notation, may be {@code null}. + * @return The hexadecimal representation of the input or {@code null} if the input was {@code null}. + * @since 2.0.0 + */ + @SuppressWarnings("checkstyle:magicnumber") + public static String toHexString(byte[] bytes) { + if (bytes == null) { + return null; + } + + StringBuilder buffer = new StringBuilder(bytes.length * 2); + + for (byte aByte : bytes) { + int b = aByte & 0xFF; + if (b < 0x10) { + buffer.append('0'); + } + buffer.append(Integer.toHexString(b)); + } + + return buffer.toString(); + } + + /** + * Creates a byte array out of hexadecimal representation of the specified bytes. If input string is {@code null}, + * {@code null} is returned. Input value must have even length (due hex encoding = 2 chars one byte). + * + * @param hexString The hexString to convert to byte array, may be {@code null}. + * @return The byte array of the input or {@code null} if the input was {@code null}. + * @since 2.0.0 + */ + @SuppressWarnings("checkstyle:magicnumber") + public static byte[] fromHexString(String hexString) { + if (hexString == null) { + return null; + } + if (hexString.isEmpty()) { + return new byte[] {}; + } + int len = hexString.length(); + if (len % 2 != 0) { + throw new IllegalArgumentException("hexString length not even"); + } + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) + ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16)); + } + return data; + } } diff --git a/maven-resolver-util/src/test/java/org/eclipse/aether/util/ChecksumUtilTest.java b/maven-resolver-util/src/test/java/org/eclipse/aether/util/ChecksumUtilTest.java deleted file mode 100644 index a00c9a620..000000000 --- a/maven-resolver-util/src/test/java/org/eclipse/aether/util/ChecksumUtilTest.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF 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. - */ -package org.eclipse.aether.util; - -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.eclipse.aether.internal.test.util.TestFileUtils.*; -import static org.junit.jupiter.api.Assertions.*; - -public class ChecksumUtilTest { - private static final String EMPTY = "EMPTY"; - private static final String PATTERN = "PATTERN"; - private static final String TEXT = "TEXT"; - - private final Map files = new HashMap<>(3); - - private final Map bytes = new HashMap<>(3); - - private static final Map emptyChecksums = new HashMap<>(); - - private static final Map patternChecksums = new HashMap<>(); - - private static final Map textChecksums = new HashMap<>(); - - private final Map> sums = new HashMap<>(); - - @BeforeAll - static void beforeClass() { - emptyChecksums.put("MD5", "d41d8cd98f00b204e9800998ecf8427e"); - emptyChecksums.put("SHA-1", "da39a3ee5e6b4b0d3255bfef95601890afd80709"); - emptyChecksums.put("SHA-256", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); - emptyChecksums.put( - "SHA-512", - "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"); - patternChecksums.put("MD5", "14f01d6c7de7d4cf0a4887baa3528b5a"); - patternChecksums.put("SHA-1", "feeeda19f626f9b0ef6cbf5948c1ec9531694295"); - patternChecksums.put("SHA-256", "81d480a714840ab206dc8de62ca6119036f65499ad9e2e227c2465551bed684d"); - patternChecksums.put( - "SHA-512", - "931aa34118d9a85b9514e0224046d736a5bd7e2b2f366505fe1ad07ed85e1a4ac0cbc18e9b9a7fe36ce835be2a18cb571202a4975d182553faff336970eb0b7e"); - textChecksums.put("MD5", "12582d1a662cefe3385f2113998e43ed"); - textChecksums.put("SHA-1", "a8ae272db549850eef2ff54376f8cac2770745ee"); - textChecksums.put("SHA-256", "35829adced2979761ba521dc2bb7d166e92ebed7342319d041398e509d481a46"); - textChecksums.put( - "SHA-512", - "2d6d19570b26080fa88101af2256ce3dae63512b06864cd36a05371c81d6dbd0ec226dd75f22e8d46a9582e1fc40ee6e7a02d43c852f3c92255982b835db6e7c"); - } - - @BeforeEach - void before() throws IOException { - sums.clear(); - - byte[] emptyBytes = new byte[0]; - bytes.put(EMPTY, emptyBytes); - files.put(EMPTY, createTempFile(emptyBytes, 0)); - sums.put(EMPTY, emptyChecksums); - - byte[] patternBytes = - writeBytes(new byte[] {0, 1, 2, 4, 8, 16, 32, 64, 127, -1, -2, -4, -8, -16, -32, -64, -127}, 1000); - bytes.put(PATTERN, patternBytes); - files.put(PATTERN, createTempFile(patternBytes, 1)); - sums.put(PATTERN, patternChecksums); - - byte[] textBytes = - writeBytes("the quick brown fox jumps over the lazy dog\n".getBytes(StandardCharsets.UTF_8), 500); - bytes.put(TEXT, textBytes); - files.put(TEXT, createTempFile(textBytes, 1)); - sums.put(TEXT, textChecksums); - } - - @Test - void testEquality() throws Throwable { - Map checksums; - - for (Map.Entry fileEntry : files.entrySet()) { - - checksums = ChecksumUtils.calc(fileEntry.getValue(), Arrays.asList("SHA-512", "SHA-256", "SHA-1", "MD5")); - - for (Entry entry : checksums.entrySet()) { - if (entry.getValue() instanceof Throwable) { - throw (Throwable) entry.getValue(); - } - String actual = entry.getValue().toString(); - String expected = sums.get(fileEntry.getKey()).get(entry.getKey()); - assertEquals( - expected, - actual, - String.format( - "checksums do not match for '%s', algorithm '%s'", - fileEntry.getValue().getName(), entry.getKey())); - } - assertTrue(fileEntry.getValue().delete(), "Could not delete file"); - } - } - - @Test - void testFileHandleLeakage() throws IOException { - for (File file : files.values()) { - for (int i = 0; i < 150; i++) { - ChecksumUtils.calc(file, Arrays.asList("SHA-512", "SHA-256", "SHA-1", "MD5")); - } - assertTrue(file.delete(), "Could not delete file"); - } - } - - @Test - void testRead() throws IOException { - for (Map checksums : sums.values()) { - String sha512 = checksums.get("SHA-512"); - String sha256 = checksums.get("SHA-256"); - String sha1 = checksums.get("SHA-1"); - String md5 = checksums.get("MD5"); - - File sha512File = createTempFile(sha512); - File sha256File = createTempFile(sha256); - File sha1File = createTempFile(sha1); - File md5File = createTempFile(md5); - - assertEquals(sha512, ChecksumUtils.read(sha512File)); - assertEquals(sha256, ChecksumUtils.read(sha256File)); - assertEquals(sha1, ChecksumUtils.read(sha1File)); - assertEquals(md5, ChecksumUtils.read(md5File)); - - assertTrue(sha512File.delete(), "ChecksumUtils leaks file handles (cannot delete checksums.sha512)"); - assertTrue(sha256File.delete(), "ChecksumUtils leaks file handles (cannot delete checksums.sha256)"); - assertTrue(sha1File.delete(), "ChecksumUtils leaks file handles (cannot delete checksums.sha1)"); - assertTrue(md5File.delete(), "ChecksumUtils leaks file handles (cannot delete checksums.md5)"); - } - } - - @Test - void testReadSpaces() throws IOException { - for (Map checksums : sums.values()) { - String sha512 = checksums.get("SHA-512"); - String sha256 = checksums.get("SHA-256"); - String sha1 = checksums.get("SHA-1"); - String md5 = checksums.get("MD5"); - - File sha512File = createTempFile("sha512-checksum = " + sha512); - File sha256File = createTempFile("sha256-checksum = " + sha256); - File sha1File = createTempFile("sha1-checksum = " + sha1); - File md5File = createTempFile(md5 + " test"); - - assertEquals(sha512, ChecksumUtils.read(sha512File)); - assertEquals(sha256, ChecksumUtils.read(sha256File)); - assertEquals(sha1, ChecksumUtils.read(sha1File)); - assertEquals(md5, ChecksumUtils.read(md5File)); - - assertTrue(sha512File.delete(), "ChecksumUtils leaks file handles (cannot delete checksums.sha512)"); - assertTrue(sha256File.delete(), "ChecksumUtils leaks file handles (cannot delete checksums.sha256)"); - assertTrue(sha1File.delete(), "ChecksumUtils leaks file handles (cannot delete checksums.sha1)"); - assertTrue(md5File.delete(), "ChecksumUtils leaks file handles (cannot delete checksums.md5)"); - } - } - - @Test - void testReadEmptyFile() throws IOException { - File file = createTempFile(""); - - assertEquals("", ChecksumUtils.read(file)); - - assertTrue(file.delete(), "ChecksumUtils leaks file handles (cannot delete checksum.empty)"); - } - - @Test - void testToHexString() { - assertNull(ChecksumUtils.toHexString(null)); - assertEquals("", ChecksumUtils.toHexString(new byte[] {})); - assertEquals("00", ChecksumUtils.toHexString(new byte[] {0})); - assertEquals("ff", ChecksumUtils.toHexString(new byte[] {-1})); - assertEquals("00017f", ChecksumUtils.toHexString(new byte[] {0, 1, 127})); - } - - @Test - void testFromHexString() { - assertNull(ChecksumUtils.toHexString(null)); - assertArrayEquals(new byte[] {}, ChecksumUtils.fromHexString("")); - assertArrayEquals(new byte[] {0}, ChecksumUtils.fromHexString("00")); - assertArrayEquals(new byte[] {-1}, ChecksumUtils.fromHexString("ff")); - assertArrayEquals(new byte[] {0, 1, 127}, ChecksumUtils.fromHexString("00017f")); - } - - @Test - void testCalcWithByteArray() throws Throwable { - Map checksums; - - for (Map.Entry bytesEntry : bytes.entrySet()) { - checksums = ChecksumUtils.calc(bytesEntry.getValue(), Arrays.asList("SHA-512", "SHA-256", "SHA-1", "MD5")); - - for (Entry entry : checksums.entrySet()) { - if (entry.getValue() instanceof Throwable) { - throw (Throwable) entry.getValue(); - } - String actual = entry.getValue().toString(); - String expected = sums.get(bytesEntry.getKey()).get(entry.getKey()); - assertEquals( - expected, - actual, - String.format( - "checksums do not match for '%s', algorithm '%s'", - bytesEntry.getKey(), entry.getKey())); - } - } - } - - private byte[] writeBytes(byte[] pattern, int repeat) { - byte[] result = new byte[pattern.length * repeat]; - for (int i = 0; i < repeat; i++) { - System.arraycopy(pattern, 0, result, i * pattern.length, pattern.length); - } - return result; - } -} diff --git a/maven-resolver-util/src/test/java/org/eclipse/aether/util/StringDigestUtilTest.java b/maven-resolver-util/src/test/java/org/eclipse/aether/util/StringDigestUtilTest.java index 595841583..3edae4e47 100644 --- a/maven-resolver-util/src/test/java/org/eclipse/aether/util/StringDigestUtilTest.java +++ b/maven-resolver-util/src/test/java/org/eclipse/aether/util/StringDigestUtilTest.java @@ -20,6 +20,8 @@ import org.junit.jupiter.api.Test; +import static org.eclipse.aether.util.StringDigestUtil.fromHexString; +import static org.eclipse.aether.util.StringDigestUtil.toHexString; import static org.junit.jupiter.api.Assertions.*; public class StringDigestUtilTest { @@ -73,4 +75,22 @@ void unsupportedAlg() { // good } } + + @Test + void testToHexString() { + assertNull(toHexString(null)); + assertEquals("", toHexString(new byte[] {})); + assertEquals("00", toHexString(new byte[] {0})); + assertEquals("ff", toHexString(new byte[] {-1})); + assertEquals("00017f", toHexString(new byte[] {0, 1, 127})); + } + + @Test + void testFromHexString() { + assertNull(fromHexString(null)); + assertArrayEquals(new byte[] {}, fromHexString("")); + assertArrayEquals(new byte[] {0}, fromHexString("00")); + assertArrayEquals(new byte[] {-1}, fromHexString("ff")); + assertArrayEquals(new byte[] {0, 1, 127}, fromHexString("00017f")); + } } From 5b4208a6dd6ebb8dbe9e9daf877e526d6c3711cb Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 27 Oct 2023 12:31:34 +0200 Subject: [PATCH 2/3] Why not use impl in test? --- .../internal/impl/DefaultFileProcessor.java | 8 -- .../impl/DefaultFileProcessorTest.java | 7 ++ .../internal/test/util/TestChecksumUtils.java | 89 ------------------- maven-resolver-transport-http/pom.xml | 5 ++ .../aether/transport/http/HttpServer.java | 9 +- 5 files changed, 17 insertions(+), 101 deletions(-) delete mode 100644 maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestChecksumUtils.java diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultFileProcessor.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultFileProcessor.java index b58479237..12bd4797b 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultFileProcessor.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultFileProcessor.java @@ -135,14 +135,6 @@ public void move(File source, File target) throws IOException { @Override public String readChecksum(final File checksumFile) throws IOException { // for now do exactly same as happened before, but FileProcessor is a component and can be replaced - return read(checksumFile); - } - - /** - * Extracts the checksum from the specified file, applies several tricks to be able to read GNU Coreutils format. - * Code copied in unmodified form from ChecksumUtils that is deprecated. - */ - private static String read(File checksumFile) throws IOException { String checksum = ""; try (BufferedReader br = new BufferedReader( new InputStreamReader(Files.newInputStream(checksumFile.toPath()), StandardCharsets.UTF_8), 512)) { diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultFileProcessorTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultFileProcessorTest.java index 2af17e859..b3a6d002b 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultFileProcessorTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultFileProcessorTest.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.nio.file.NoSuchFileException; import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.aether.internal.test.util.TestFileUtils; @@ -135,6 +136,12 @@ void testWriteStream() throws IOException { target.delete(); } + @Test + void testReadChecksumNonExistentFile() { + // This is NIO2 exception, maybe assert only IOEx is enough? + assertThrows(NoSuchFileException.class, () -> fileProcessor.readChecksum(new File("non existent"))); + } + @Test void testReadChecksumEmptyFile() throws IOException { File emptyFile = TestFileUtils.createTempFile(""); diff --git a/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestChecksumUtils.java b/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestChecksumUtils.java deleted file mode 100644 index f0d2591a7..000000000 --- a/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestChecksumUtils.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF 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. - */ -package org.eclipse.aether.internal.test.util; - -import java.io.*; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.*; - -/** - * Provides utility methods to calculate checksums. - */ -public class TestChecksumUtils { - public static Map calc(File dataFile, Collection algos) throws IOException { - return calc(new FileInputStream(dataFile), algos); - } - - public static Map calc(byte[] dataBytes, Collection algos) throws IOException { - return calc(new ByteArrayInputStream(dataBytes), algos); - } - - private static Map calc(InputStream data, Collection algos) throws IOException { - Map results = new LinkedHashMap<>(); - - Map digests = new LinkedHashMap<>(); - for (String algo : algos) { - try { - digests.put(algo, MessageDigest.getInstance(algo)); - } catch (NoSuchAlgorithmException e) { - results.put(algo, e); - } - } - - try (InputStream in = data) { - for (byte[] buffer = new byte[32 * 1024]; ; ) { - int read = in.read(buffer); - if (read < 0) { - break; - } - for (MessageDigest digest : digests.values()) { - digest.update(buffer, 0, read); - } - } - } - - for (Map.Entry entry : digests.entrySet()) { - byte[] bytes = entry.getValue().digest(); - - results.put(entry.getKey(), toHexString(bytes)); - } - - return results; - } - - @SuppressWarnings("checkstyle:magicnumber") - private static String toHexString(byte[] bytes) { - if (bytes == null) { - return null; - } - - StringBuilder buffer = new StringBuilder(bytes.length * 2); - - for (byte aByte : bytes) { - int b = aByte & 0xFF; - if (b < 0x10) { - buffer.append('0'); - } - buffer.append(Integer.toHexString(b)); - } - - return buffer.toString(); - } -} diff --git a/maven-resolver-transport-http/pom.xml b/maven-resolver-transport-http/pom.xml index 2df00f96e..88f6feed3 100644 --- a/maven-resolver-transport-http/pom.xml +++ b/maven-resolver-transport-http/pom.xml @@ -105,6 +105,11 @@ maven-resolver-test-util test + + org.apache.maven.resolver + maven-resolver-impl + test + org.eclipse.jetty jetty-server diff --git a/maven-resolver-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpServer.java b/maven-resolver-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpServer.java index d1fcce9a9..b2880a4f0 100644 --- a/maven-resolver-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpServer.java +++ b/maven-resolver-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpServer.java @@ -37,7 +37,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.eclipse.aether.internal.test.util.TestChecksumUtils; +import org.eclipse.aether.internal.impl.checksum.Sha1ChecksumAlgorithmFactory; +import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmHelper; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.server.Request; @@ -334,12 +335,12 @@ public void handle(String target, Request req, HttpServletRequest request, HttpS "bytes " + offset + "-" + (file.length() - 1L) + "/" + file.length()); } if (checksumHeader != null) { - Map checksums = TestChecksumUtils.calc(file, Collections.singleton("SHA-1")); + Map checksums = ChecksumAlgorithmHelper.calculate( + file, Collections.singletonList(new Sha1ChecksumAlgorithmFactory())); if (checksumHeader == ChecksumHeader.NEXUS) { response.setHeader(HttpHeader.ETAG.asString(), "{SHA1{" + checksums.get("SHA-1") + "}}"); } else if (checksumHeader == ChecksumHeader.XCHECKSUM) { - response.setHeader( - "x-checksum-sha1", checksums.get("SHA-1").toString()); + response.setHeader("x-checksum-sha1", checksums.get(Sha1ChecksumAlgorithmFactory.NAME)); } } if (HttpMethod.HEAD.is(req.getMethod())) { From ad317b5bd0756c2aac7cf02bfb484877ba5ea99c Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 27 Oct 2023 12:39:21 +0200 Subject: [PATCH 3/3] Assert IOEx is enough? --- .../aether/internal/impl/DefaultFileProcessorTest.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultFileProcessorTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultFileProcessorTest.java index b3a6d002b..7de414b86 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultFileProcessorTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultFileProcessorTest.java @@ -23,7 +23,6 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -import java.nio.file.NoSuchFileException; import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.aether.internal.test.util.TestFileUtils; @@ -138,8 +137,7 @@ void testWriteStream() throws IOException { @Test void testReadChecksumNonExistentFile() { - // This is NIO2 exception, maybe assert only IOEx is enough? - assertThrows(NoSuchFileException.class, () -> fileProcessor.readChecksum(new File("non existent"))); + assertThrows(IOException.class, () -> fileProcessor.readChecksum(new File("non existent"))); } @Test