Skip to content

Commit d74fc31

Browse files
author
Julius Davies
committed
Fix for CODEC-101: java.io.InputStreamReader hates it when InputStream.read(byte[]) returns a zero.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/codec/trunk@1061974 13f79535-47bb-0310-9956-ffa450edef68
1 parent 5a0d6b1 commit d74fc31

3 files changed

Lines changed: 53 additions & 13 deletions

File tree

src/java/org/apache/commons/codec/binary/Base64InputStream.java

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -145,21 +145,41 @@ public int read(byte b[], int offset, int len) throws IOException {
145145
} else if (len == 0) {
146146
return 0;
147147
} else {
148-
if (!base64.hasData()) {
149-
byte[] buf = new byte[doEncode ? 4096 : 8192];
150-
int c = in.read(buf);
151-
// A little optimization to avoid System.arraycopy()
152-
// when possible.
153-
if (c > 0 && b.length == len) {
154-
base64.setInitialBuffer(b, offset, len);
155-
}
156-
if (doEncode) {
157-
base64.encode(buf, 0, c);
158-
} else {
159-
base64.decode(buf, 0, c);
148+
int readLen = 0;
149+
/*
150+
Rationale for while-loop on (readLen == 0):
151+
-----
152+
Base64.readResults() usually returns > 0 or EOF (-1). In the
153+
rare case where it returns 0, we just keep trying.
154+
155+
This is essentially an undocumented contract for InputStream
156+
implementors that want their code to work properly with
157+
java.io.InputStreamReader, since the latter hates it when
158+
InputStream.read(byte[]) returns a zero. Unfortunately our
159+
readResults() call must return 0 if a large amount of the data
160+
being decoded was non-base64, so this while-loop enables proper
161+
interop with InputStreamReader for that scenario.
162+
-----
163+
This is a fix for CODEC-101
164+
*/
165+
while (readLen == 0) {
166+
if (!base64.hasData()) {
167+
byte[] buf = new byte[doEncode ? 4096 : 8192];
168+
int c = in.read(buf);
169+
// A little optimization to avoid System.arraycopy()
170+
// when possible.
171+
if (c > 0 && b.length == len) {
172+
base64.setInitialBuffer(b, offset, len);
173+
}
174+
if (doEncode) {
175+
base64.encode(buf, 0, c);
176+
} else {
177+
base64.decode(buf, 0, c);
178+
}
160179
}
180+
readLen = base64.readResults(b, offset, len);
161181
}
162-
return base64.readResults(b, offset, len);
182+
return readLen;
163183
}
164184
}
165185

src/test/org/apache/commons/codec/binary/Base64InputStreamTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,24 @@ public Base64InputStreamTest(String name) {
4646
super(name);
4747
}
4848

49+
/**
50+
* Test for the CODEC-101 bug: InputStream.read(byte[]) should never return 0
51+
* because Java's builtin InputStreamReader hates that.
52+
*
53+
* @throws Exception for some failure scenarios.
54+
*/
55+
public void testCodec101() throws Exception {
56+
byte[] codec101 = StringUtils.getBytesUtf8(Base64TestData.CODEC_101_MULTIPLE_OF_3);
57+
ByteArrayInputStream bais = new ByteArrayInputStream(codec101);
58+
Base64InputStream in = new Base64InputStream(bais);
59+
byte[] result = new byte[8192];
60+
int c = in.read(result);
61+
assertTrue("Codec101: First read successful [c=" + c + "]", c > 0);
62+
63+
c = in.read(result);
64+
assertTrue("Codec101: Second read should report end-of-stream [c=" + c + "]", c < 0);
65+
}
66+
4967
/**
5068
* Test the Base64InputStream implementation against the special NPE inducing input
5169
* identified in the CODEC-98 bug.

src/test/org/apache/commons/codec/binary/Base64TestData.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
*/
3333
public class Base64TestData {
3434

35+
public static final String CODEC_101_MULTIPLE_OF_3 = "123";
36+
3537
public static final String CODEC_98_NPE
3638
= "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM";
3739

0 commit comments

Comments
 (0)