From f071de7889f82e56f560d215a9e5be2e14099149 Mon Sep 17 00:00:00 2001 From: huangpingzhi <3231811510@qq.com> Date: Mon, 26 Oct 2026 00:59:03 +0800 Subject: [PATCH 1/3] Hash only the bytes read from a stream in Bytes.getMD5 --- .../org/apache/dubbo/common/io/Bytes.java | 11 ++----- .../org/apache/dubbo/common/io/BytesTest.java | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/io/Bytes.java b/dubbo-common/src/main/java/org/apache/dubbo/common/io/Bytes.java index d3c615b75532..abb2339ee2a4 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/io/Bytes.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/io/Bytes.java @@ -909,15 +909,8 @@ private static byte[] decodeTable(String code) { private static byte[] getMD5(InputStream is, int bs) throws IOException { MessageDigest md = getMessageDigest(); byte[] buf = new byte[bs]; - while (is.available() > 0) { - int read, total = 0; - do { - if ((read = is.read(buf, total, bs - total)) <= 0) { - break; - } - total += read; - } while (total < bs); - md.update(buf); + for (int read = is.read(buf); read != -1; read = is.read(buf)) { + md.update(buf, 0, read); } return md.digest(); } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/io/BytesTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/io/BytesTest.java index 29e6d31fe498..8b3f02addd1c 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/io/BytesTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/io/BytesTest.java @@ -16,8 +16,11 @@ */ package org.apache.dubbo.common.io; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.util.Random; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -127,6 +130,33 @@ void testMD5ForFile() throws IOException { assertThat(md5, is(Bytes.base642bytes("iNZ+5qHafVNPLJxHwLKJ3w=="))); } + @Test + void testMD5ForStreamWithShortFinalRead() throws IOException { + byte[] source = new byte[1024 * 8 + 17]; + new Random().nextBytes(source); + assertThat(Bytes.getMD5(new ByteArrayInputStream(source)), is(Bytes.getMD5(source))); + } + + @Test + void testMD5ForStreamWithZeroAvailable() throws IOException { + byte[] source = new byte[1024]; + new Random().nextBytes(source); + InputStream is = new InputStream() { + private int pos; + + @Override + public int available() { + return 0; + } + + @Override + public int read() { + return pos < source.length ? source[pos++] & 0xff : -1; + } + }; + assertThat(Bytes.getMD5(is), is(Bytes.getMD5(source))); + } + @Test void testZip() throws IOException { String s = "hello world"; From a38a5b42c9e8f5dda152075458e1cb3e75ff6476 Mon Sep 17 00:00:00 2001 From: HhH666-ux <> Date: Tue, 27 Oct 2026 20:18:33 +0800 Subject: [PATCH 2/3] Fix stale MD5 expectation in BytesTest.testMD5ForFile md5.testfile.txt is 12 bytes while getMD5(File) reads through an 8 KiB buffer. The old getMD5 copied the whole buffer into the digest on every iteration, so the expectation in this test was the digest of the file followed by ~8180 zero bytes rather than the digest of the file. Now that only the bytes actually read are hashed, the expectation has to be the real digest ("/D/5joxqDTCH1RXARz+Gdw=="). Assert it against getMD5(byte[]) as well, so the test states the intent instead of pinning whatever the buffer handling happens to produce. --- .../java/org/apache/dubbo/common/io/BytesTest.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/io/BytesTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/io/BytesTest.java index 8b3f02addd1c..707cc19a3f65 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/io/BytesTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/io/BytesTest.java @@ -20,6 +20,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; import java.util.Random; import org.junit.jupiter.api.Assertions; @@ -125,9 +126,13 @@ void testMD5ForString() { @Test void testMD5ForFile() throws IOException { - byte[] md5 = Bytes.getMD5(new File( - getClass().getClassLoader().getResource("md5.testfile.txt").getFile())); - assertThat(md5, is(Bytes.base642bytes("iNZ+5qHafVNPLJxHwLKJ3w=="))); + File file = new File(getClass().getClassLoader().getResource("md5.testfile.txt").getFile()); + byte[] md5 = Bytes.getMD5(file); + // md5.testfile.txt is much shorter than the 8 KiB read buffer, so this pins down that the + // digest covers only the bytes read from the file. The previous expectation was the digest + // of the whole buffer, i.e. the file content followed by ~8180 zero bytes. + assertThat(md5, is(Bytes.base642bytes("/D/5joxqDTCH1RXARz+Gdw=="))); + assertThat(md5, is(Bytes.getMD5(Files.readAllBytes(file.toPath())))); } @Test From 24b1b7979aca037cc0ee604b56e8fbaa0a445d15 Mon Sep 17 00:00:00 2001 From: HhH666-ux Date: Tue, 27 Oct 2026 20:40:35 +0800 Subject: [PATCH 3/3] Wrap the resource lookup line to satisfy spotless --- .../src/test/java/org/apache/dubbo/common/io/BytesTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/io/BytesTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/io/BytesTest.java index 707cc19a3f65..3d525ce671c8 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/io/BytesTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/io/BytesTest.java @@ -126,7 +126,8 @@ void testMD5ForString() { @Test void testMD5ForFile() throws IOException { - File file = new File(getClass().getClassLoader().getResource("md5.testfile.txt").getFile()); + File file = new File( + getClass().getClassLoader().getResource("md5.testfile.txt").getFile()); byte[] md5 = Bytes.getMD5(file); // md5.testfile.txt is much shorter than the 8 KiB read buffer, so this pins down that the // digest covers only the bytes read from the file. The previous expectation was the digest