Skip to content

Commit b5c1049

Browse files
committed
BOMInputStream.read() should return -1 after the stream is closed
1 parent dfd2a06 commit b5c1049

5 files changed

Lines changed: 67 additions & 8 deletions

File tree

src/changes/changes.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ The <action> type attribute can be add,update,fix,remove.
7070
<action dev="ggregory" type="add" due-to="Gary Gregory">InfiniteCircularInputStream.available() should return 0 when the stream is closed.</action>
7171
<action dev="ggregory" type="add" due-to="Gary Gregory">ChecksumInputStream(InputStream, Checksum, long, long) should fail-fast on null Checksum input.</action>
7272
<action dev="ggregory" type="add" due-to="Gary Gregory">BufferedFileChannelInputStream.read() should return -1 after the stream is closed.</action>
73+
<action dev="ggregory" type="add" due-to="Gary Gregory">BOMInputStream.read() should return -1 after the stream is closed.</action>
7374
<!-- UPDATE -->
7475
<action dev="ggregory" type="update" due-to="Dependabot">Bump tests commons.bytebuddy.version from 1.14.13 to 1.14.17 #615, #621, #631, #635.</action>
7576
<action dev="ggregory" type="update" due-to="Dependabot">Bump tests commons-codec:commons-codec from 1.16.1 to 1.17.0.</action>

src/main/java/org/apache/commons/io/input/BOMInputStream.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,9 @@ private boolean matches(final ByteOrderMark bom) {
419419
*/
420420
@Override
421421
public int read() throws IOException {
422+
if (isClosed()) {
423+
return EOF;
424+
}
422425
final int b = readFirstBytes();
423426
return b >= 0 ? b : in.read();
424427
}

src/main/java/org/apache/commons/io/input/ProxyInputStream.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,22 @@
3939
* <li>notify a subclass that an exception was caught through {@link #handleIOException(IOException)}</li>
4040
* <li>{@link #unwrap()} itself</li>
4141
* </ul>
42-
* <p>
43-
* This class does not add any state (no additional instance variables).
44-
* </p>
4542
*/
4643
public abstract class ProxyInputStream extends FilterInputStream {
4744

45+
/**
46+
* Tracks whether {@link #close()} has been called or not.
47+
*/
48+
private boolean closed;
49+
4850
/**
4951
* Constructs a new ProxyInputStream.
5052
*
5153
* @param proxy the InputStream to delegate to
5254
*/
5355
public ProxyInputStream(final InputStream proxy) {
54-
super(proxy);
5556
// the proxy is stored in a protected superclass variable named 'in'
57+
super(proxy);
5658
}
5759

5860
/**
@@ -87,7 +89,7 @@ protected void afterRead(final int n) throws IOException {
8789
*/
8890
@Override
8991
public int available() throws IOException {
90-
if (in != null) {
92+
if (in != null && !isClosed()) {
9193
try {
9294
return in.available();
9395
} catch (final IOException e) {
@@ -130,6 +132,7 @@ protected void beforeRead(final int n) throws IOException {
130132
@Override
131133
public void close() throws IOException {
132134
IOUtils.close(in, this::handleIOException);
135+
closed = true;
133136
}
134137

135138
/**
@@ -147,6 +150,15 @@ protected void handleIOException(final IOException e) throws IOException {
147150
throw e;
148151
}
149152

153+
/**
154+
* Tests whether this instance is closed.
155+
*
156+
* @return whether this instance is closed.
157+
*/
158+
boolean isClosed() {
159+
return closed;
160+
}
161+
150162
/**
151163
* Invokes the delegate's {@code mark(int)} method.
152164
*

src/test/java/org/apache/commons/io/input/BOMInputStreamTest.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import javax.xml.parsers.ParserConfigurationException;
3838

3939
import org.apache.commons.io.ByteOrderMark;
40+
import org.apache.commons.io.IOUtils;
4041
import org.apache.commons.lang3.SystemProperties;
4142
import org.junit.jupiter.api.Test;
4243
import org.w3c.dom.Document;
@@ -207,13 +208,35 @@ private void readFile(final BOMInputStream bomInputStream) throws Exception {
207208
}
208209

209210
@Test
210-
public void testAvailableWithBOM() throws Exception {
211+
public void testAvailableWithBOMAfterOpen() throws Exception {
211212
final byte[] data = { 'A', 'B', 'C', 'D' };
212213
try (InputStream in = BOMInputStream.builder().setInputStream(createUtf8Input(data, true)).get()) {
213214
assertEquals(7, in.available());
214215
}
215216
}
216217

218+
@Test
219+
public void testAvailableWithBOMAfterClose() throws Exception {
220+
final byte[] data = { 'A', 'B', 'C', 'D' };
221+
final InputStream shadow;
222+
try (InputStream in = BOMInputStream.builder().setInputStream(createUtf8Input(data, true)).get()) {
223+
assertEquals(7, in.available());
224+
shadow = in;
225+
}
226+
assertEquals(0, shadow.available());
227+
}
228+
229+
@Test
230+
public void testReadAfterClose() throws Exception {
231+
final byte[] data = { 'A', 'B', 'C', 'D' };
232+
final InputStream shadow;
233+
try (InputStream in = BOMInputStream.builder().setInputStream(createUtf8Input(data, true)).get()) {
234+
assertEquals(7, in.available());
235+
shadow = in;
236+
}
237+
assertEquals(IOUtils.EOF, shadow.read());
238+
}
239+
217240
@Test
218241
public void testAvailableWithoutBOM() throws Exception {
219242
final byte[] data = { 'A', 'B', 'C', 'D' };

src/test/java/org/apache/commons/io/input/ProxyInputStreamTest.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,14 @@ protected InputStream createProxySource() {
5656
return CharSequenceInputStream.builder().setCharSequence("abc").get();
5757
}
5858

59-
protected void testEos(final T inputStream) {
60-
// empty
59+
@SuppressWarnings("resource")
60+
@Test
61+
public void testAvailableAfterClose() throws IOException {
62+
final T shadow;
63+
try (T inputStream = createFixture()) {
64+
shadow = inputStream;
65+
}
66+
assertEquals(0, shadow.available());
6167
}
6268

6369
@Test
@@ -82,6 +88,10 @@ public void testAvailableNull() throws IOException {
8288
}
8389
}
8490

91+
protected void testEos(final T inputStream) {
92+
// empty
93+
}
94+
8595
@Test
8696
public void testRead() throws IOException {
8797
try (T inputStream = createFixture()) {
@@ -97,6 +107,16 @@ public void testRead() throws IOException {
97107
}
98108
}
99109

110+
@SuppressWarnings("resource")
111+
@Test
112+
public void testReadAfterClose() throws IOException {
113+
final T shadow;
114+
try (T inputStream = createFixture()) {
115+
shadow = inputStream;
116+
}
117+
assertEquals(IOUtils.EOF, shadow.read());
118+
}
119+
100120
@Test
101121
public void testReadArrayAtMiddleFully() throws IOException {
102122
try (T inputStream = createFixture()) {

0 commit comments

Comments
 (0)