This is an automated email from the ASF dual-hosted git repository. peterlee pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-compress.git
The following commit(s) were added to refs/heads/master by this push: new 2fe5825 [COMPRESS-584] [COMPRESS-585] Fix IOUtils.readRange() can read more from a channel than asked for 2fe5825 is described below commit 2fe5825828f7a96b0676d11972f9a13892075b4d Author: Matthijs Laan <matthij...@gmail.com> AuthorDate: Wed Aug 4 12:04:29 2021 +0200 [COMPRESS-584] [COMPRESS-585] Fix IOUtils.readRange() can read more from a channel than asked for --- .../org/apache/commons/compress/utils/IOUtils.java | 2 ++ .../apache/commons/compress/utils/IOUtilsTest.java | 38 ++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/main/java/org/apache/commons/compress/utils/IOUtils.java b/src/main/java/org/apache/commons/compress/utils/IOUtils.java index 3a1f582..d07f3d8 100644 --- a/src/main/java/org/apache/commons/compress/utils/IOUtils.java +++ b/src/main/java/org/apache/commons/compress/utils/IOUtils.java @@ -367,6 +367,8 @@ public final class IOUtils { final ByteBuffer b = ByteBuffer.allocate(Math.min(len, COPY_BUF_SIZE)); int read = 0; while (read < len) { + // Make sure we never read more than len bytes + b.limit(Math.min(len - read, b.capacity())); final int readNow = input.read(b); if (readNow <= 0) { break; diff --git a/src/test/java/org/apache/commons/compress/utils/IOUtilsTest.java b/src/test/java/org/apache/commons/compress/utils/IOUtilsTest.java index a692ef0..f5d00dc 100644 --- a/src/test/java/org/apache/commons/compress/utils/IOUtilsTest.java +++ b/src/test/java/org/apache/commons/compress/utils/IOUtilsTest.java @@ -23,6 +23,7 @@ import java.io.EOFException; import java.io.FilterInputStream; import java.io.InputStream; import java.io.IOException; +import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; @@ -149,6 +150,43 @@ public class IOUtilsTest { } @Test + public void readRangeFromChannelDoesntReadMoreThanAskedForWhenItGotLessInFirstReadCall() throws IOException { + try (ReadableByteChannel in = new SeekableInMemoryByteChannel(new byte[] { 1, 2, 3, 4, 5, 6, 7 }) { + @Override + public int read(ByteBuffer buf) throws IOException { + // Trickle max 2 bytes at a time to trigger COMPRESS-584 + final ByteBuffer temp = ByteBuffer.allocate(Math.min(2, buf.remaining())); + final int read = super.read(temp); + if (read > 0) { + buf.put(temp.array(), 0, read); + } + return read; + } + }) { + final byte[] read = IOUtils.readRange(in, 5); + Assert.assertArrayEquals(new byte[] { 1, 2, 3, 4, 5 }, read); + } + } + + @Test + public void readRangeMoreThanCopyBufferSize() throws Exception { + final Field COPY_BUF_SIZE = IOUtils.class.getDeclaredField("COPY_BUF_SIZE"); + COPY_BUF_SIZE.setAccessible(true); + final int copyBufSize = (int)COPY_BUF_SIZE.get(null); + + // Make an input that requires two read loops to trigger COMPRESS-585 + final byte[] input = new byte[copyBufSize + 10]; + + try (SeekableInMemoryByteChannel in = new SeekableInMemoryByteChannel(input)) { + // Ask for less than the input length, but more than the buffer size + final int toRead = copyBufSize + 1; + final byte[] read = IOUtils.readRange(in, toRead); + Assert.assertEquals(toRead, read.length); + Assert.assertEquals(toRead, in.position()); + } + } + + @Test public void readRangeFromChannelStopsIfThereIsNothingToReadAnymore() throws IOException { try (ReadableByteChannel in = new SeekableInMemoryByteChannel(new byte[] { 1, 2, 3, 4, 5 })) { byte[] read = IOUtils.readRange(in, 10);