You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2023/04/05 14:45:17 UTC
[commons-io] branch master updated: Let subclasses detect when reading past the maximum is requested
This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-io.git
The following commit(s) were added to refs/heads/master by this push:
new f7e0f2c0 Let subclasses detect when reading past the maximum is requested
f7e0f2c0 is described below
commit f7e0f2c05503e619a64d026e30d6cb08c4917842
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Tue Apr 4 18:37:35 2023 -0400
Let subclasses detect when reading past the maximum is requested
Javadoc
---
.../commons/io/input/BoundedInputStream.java | 102 +++++++++++++++------
.../commons/io/input/BoundedInputStreamTest.java | 55 +++++++++++
2 files changed, 128 insertions(+), 29 deletions(-)
diff --git a/src/main/java/org/apache/commons/io/input/BoundedInputStream.java b/src/main/java/org/apache/commons/io/input/BoundedInputStream.java
index 51bfb2eb..39bfa19e 100644
--- a/src/main/java/org/apache/commons/io/input/BoundedInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/BoundedInputStream.java
@@ -22,15 +22,11 @@ import java.io.IOException;
import java.io.InputStream;
/**
- * This is a stream that will only supply bytes up to a certain length - if its
- * position goes above that, it will stop.
+ * Reads bytes up to a maximum length, if its count goes above that, it stops.
* <p>
- * This is useful to wrap ServletInputStreams. The ServletInputStream will block
- * if you try to read content from it that isn't there, because it doesn't know
- * whether the content hasn't arrived yet or whether the content has finished.
- * So, one of these, initialized with the Content-length sent in the
- * ServletInputStream's header, will stop it blocking, providing it's been sent
- * with a correct content length.
+ * This is useful to wrap ServletInputStreams. The ServletInputStream will block if you try to read content from it that isn't there, because it doesn't know
+ * whether the content hasn't arrived yet or whether the content has finished. So, one of these, initialized with the Content-length sent in the
+ * ServletInputStream's header, will stop it blocking, providing it's been sent with a correct content length.
* </p>
*
* @since 2.0
@@ -40,11 +36,11 @@ public class BoundedInputStream extends InputStream {
/** The wrapped input stream. */
private final InputStream inputStream;
- /** The max length to read. */
- private final long maxLength;
+ /** The max count of bytes to read. */
+ private final long maxCount;
- /** The number of bytes already returned. */
- private long pos;
+ /** The count of bytes read. */
+ private long count;
/** The marked position. */
private long mark = EOF;
@@ -53,26 +49,26 @@ public class BoundedInputStream extends InputStream {
private boolean propagateClose = true;
/**
- * Creates a new {@link BoundedInputStream} that wraps the given input
+ * Constructs a new {@link BoundedInputStream} that wraps the given input
* stream and is unlimited.
*
- * @param in The wrapped input stream
+ * @param in The wrapped input stream.
*/
public BoundedInputStream(final InputStream in) {
this(in, EOF);
}
/**
- * Creates a new {@link BoundedInputStream} that wraps the given input
+ * Constructs a new {@link BoundedInputStream} that wraps the given input
* stream and limits it to a certain size.
*
- * @param inputStream The wrapped input stream
- * @param maxLength The maximum number of bytes to return
+ * @param inputStream The wrapped input stream.
+ * @param maxLength The maximum number of bytes to return.
*/
public BoundedInputStream(final InputStream inputStream, final long maxLength) {
// Some badly designed methods - e.g. the servlet API - overload length
// such that "-1" means stream finished
- this.maxLength = maxLength;
+ this.maxCount = maxLength;
this.inputStream = inputStream;
}
@@ -81,7 +77,8 @@ public class BoundedInputStream extends InputStream {
*/
@Override
public int available() throws IOException {
- if (maxLength >= 0 && pos >= maxLength) {
+ if (isMaxLength()) {
+ onMaxLength(maxCount, count);
return 0;
}
return inputStream.available();
@@ -90,6 +87,7 @@ public class BoundedInputStream extends InputStream {
/**
* Invokes the delegate's {@code close()} method
* if {@link #isPropagateClose()} is {@code true}.
+ *
* @throws IOException if an I/O error occurs.
*/
@Override
@@ -100,7 +98,31 @@ public class BoundedInputStream extends InputStream {
}
/**
- * Indicates whether the {@link #close()} method
+ * Gets the count of bytes read.
+ *
+ * @return The count of bytes read.
+ * @since 2.12.0
+ */
+ public long getCount() {
+ return count;
+ }
+
+ /**
+ * Gets the max count of bytes to read.
+ *
+ * @return The max count of bytes to read.
+ * @since 2.12.0
+ */
+ public long getMaxLength() {
+ return maxCount;
+ }
+
+ private boolean isMaxLength() {
+ return maxCount >= 0 && count >= maxCount;
+ }
+
+ /**
+ * Tests whether the {@link #close()} method
* should propagate to the underling {@link InputStream}.
*
* @return {@code true} if calling {@link #close()}
@@ -113,16 +135,18 @@ public class BoundedInputStream extends InputStream {
/**
* Invokes the delegate's {@code mark(int)} method.
+ *
* @param readlimit read ahead limit
*/
@Override
public synchronized void mark(final int readlimit) {
inputStream.mark(readlimit);
- mark = pos;
+ mark = count;
}
/**
* Invokes the delegate's {@code markSupported()} method.
+ *
* @return true if mark is supported, otherwise false
*/
@Override
@@ -130,25 +154,40 @@ public class BoundedInputStream extends InputStream {
return inputStream.markSupported();
}
+ /**
+ * A caller has caused a request that would cross the {@code maxLength} boundary.
+ *
+ * @param maxLength The max count of bytes to read.
+ * @param count The count of bytes read.
+ * @throws IOException Subclasses may throw.
+ * @since 2.12.0
+ */
+ protected void onMaxLength(final long maxLength, final long count) throws IOException {
+ // for subclasses
+ }
+
/**
* Invokes the delegate's {@code read()} method if
* the current position is less than the limit.
+ *
* @return the byte read or -1 if the end of stream or
* the limit has been reached.
* @throws IOException if an I/O error occurs.
*/
@Override
public int read() throws IOException {
- if (maxLength >= 0 && pos >= maxLength) {
+ if (isMaxLength()) {
+ onMaxLength(maxCount, count);
return EOF;
}
final int result = inputStream.read();
- pos++;
+ count++;
return result;
}
/**
* Invokes the delegate's {@code read(byte[])} method.
+ *
* @param b the buffer to read the bytes into
* @return the number of bytes read or -1 if the end of stream or
* the limit has been reached.
@@ -161,6 +200,7 @@ public class BoundedInputStream extends InputStream {
/**
* Invokes the delegate's {@code read(byte[], int, int)} method.
+ *
* @param b the buffer to read the bytes into
* @param off The start offset
* @param len The number of bytes to read
@@ -170,28 +210,30 @@ public class BoundedInputStream extends InputStream {
*/
@Override
public int read(final byte[] b, final int off, final int len) throws IOException {
- if (maxLength >= 0 && pos >= maxLength) {
+ if (isMaxLength()) {
+ onMaxLength(maxCount, count);
return EOF;
}
- final long maxRead = maxLength >= 0 ? Math.min(len, maxLength - pos) : len;
+ final long maxRead = maxCount >= 0 ? Math.min(len, maxCount - count) : len;
final int bytesRead = inputStream.read(b, off, (int) maxRead);
if (bytesRead == EOF) {
return EOF;
}
- pos += bytesRead;
+ count += bytesRead;
return bytesRead;
}
/**
* Invokes the delegate's {@code reset()} method.
+ *
* @throws IOException if an I/O error occurs.
*/
@Override
public synchronized void reset() throws IOException {
inputStream.reset();
- pos = mark;
+ count = mark;
}
/**
@@ -209,20 +251,22 @@ public class BoundedInputStream extends InputStream {
/**
* Invokes the delegate's {@code skip(long)} method.
+ *
* @param n the number of bytes to skip
* @return the actual number of bytes skipped
* @throws IOException if an I/O error occurs.
*/
@Override
public long skip(final long n) throws IOException {
- final long toSkip = maxLength >= 0 ? Math.min(n, maxLength - pos) : n;
+ final long toSkip = maxCount >= 0 ? Math.min(n, maxCount - count) : n;
final long skippedBytes = inputStream.skip(toSkip);
- pos += skippedBytes;
+ count += skippedBytes;
return skippedBytes;
}
/**
* Invokes the delegate's {@code toString()} method.
+ *
* @return the delegate's {@code toString()}
*/
@Override
diff --git a/src/test/java/org/apache/commons/io/input/BoundedInputStreamTest.java b/src/test/java/org/apache/commons/io/input/BoundedInputStreamTest.java
index 3d47be97..126b519a 100644
--- a/src/test/java/org/apache/commons/io/input/BoundedInputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/input/BoundedInputStreamTest.java
@@ -17,8 +17,11 @@
package org.apache.commons.io.input;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.ByteArrayInputStream;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test;
@@ -35,6 +38,58 @@ public class BoundedInputStreamTest {
}
}
+ @Test
+ public void testOnMaxLength() throws Exception {
+ BoundedInputStream bounded;
+ final byte[] helloWorld = "Hello World".getBytes();
+ final byte[] hello = "Hello".getBytes();
+ final AtomicBoolean boolRef = new AtomicBoolean();
+
+ // limit = length
+ bounded = new BoundedInputStream(new ByteArrayInputStream(helloWorld), helloWorld.length) {
+ @Override
+ protected void onMaxLength(long max, long readCount) {
+ boolRef.set(true);
+ }
+ };
+ assertFalse(boolRef.get());
+ for (int i = 0; i < helloWorld.length; i++) {
+ assertEquals(helloWorld[i], bounded.read(), "limit = length byte[" + i + "]");
+ }
+ assertEquals(-1, bounded.read(), "limit = length end");
+ assertTrue(boolRef.get());
+
+ // limit > length
+ boolRef.set(false);
+ bounded = new BoundedInputStream(new ByteArrayInputStream(helloWorld), helloWorld.length + 1) {
+ @Override
+ protected void onMaxLength(long max, long readCount) {
+ boolRef.set(true);
+ }
+ };
+ assertFalse(boolRef.get());
+ for (int i = 0; i < helloWorld.length; i++) {
+ assertEquals(helloWorld[i], bounded.read(), "limit > length byte[" + i + "]");
+ }
+ assertEquals(-1, bounded.read(), "limit > length end");
+ assertFalse(boolRef.get());
+
+ // limit < length
+ boolRef.set(false);
+ bounded = new BoundedInputStream(new ByteArrayInputStream(helloWorld), hello.length) {
+ @Override
+ protected void onMaxLength(long max, long readCount) {
+ boolRef.set(true);
+ }
+ };
+ assertFalse(boolRef.get());
+ for (int i = 0; i < hello.length; i++) {
+ assertEquals(hello[i], bounded.read(), "limit < length byte[" + i + "]");
+ }
+ assertEquals(-1, bounded.read(), "limit < length end");
+ assertTrue(boolRef.get());
+ }
+
@Test
public void testReadArray() throws Exception {