Skip to content

Commit 122a77d

Browse files
committed
Propagate IO exceptions in TeeServletInputStream
1 parent a06ccd7 commit 122a77d

File tree

2 files changed

+123
-2
lines changed

2 files changed

+123
-2
lines changed

logback-access-common/src/main/java/ch/qos/logback/access/common/servlet/TeeServletInputStream.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,15 @@ private void duplicateInputStream(HttpServletRequest request) {
3636
try {
3737
originalSIS = request.getInputStream();
3838
inputBuffer = consumeBufferAndReturnAsByteArray(originalSIS);
39-
this.in = new ByteArrayInputStream(inputBuffer);
39+
in = new ByteArrayInputStream(inputBuffer);
4040
} catch (IOException e) {
41-
e.printStackTrace();
41+
inputBuffer = new byte[0];
42+
in = new InputStream() {
43+
@Override
44+
public int read() throws IOException {
45+
throw e;
46+
}
47+
};
4248
} finally {
4349
closeStream(originalSIS);
4450
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package ch.qos.logback.access.common.servlet;
2+
3+
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
4+
import static org.junit.jupiter.api.Assertions.assertEquals;
5+
import static org.junit.jupiter.api.Assertions.assertThrows;
6+
7+
import java.io.IOException;
8+
import java.lang.reflect.Proxy;
9+
import java.nio.ByteBuffer;
10+
import org.junit.jupiter.api.Test;
11+
12+
import jakarta.servlet.ReadListener;
13+
import jakarta.servlet.ServletInputStream;
14+
import jakarta.servlet.http.HttpServletRequest;
15+
16+
public class TeeServletInputStreamTest {
17+
18+
@Test
19+
public void testReadsInputStreamFullyAndProvidesInputBuffer() throws IOException {
20+
// arrange
21+
byte[] bytes = "GET / HTTP/1.1".getBytes();
22+
HttpServletRequest request = mockServletRequest(bytes);
23+
24+
// act
25+
try (TeeServletInputStream is = new TeeServletInputStream(request)) {
26+
// assert
27+
assertArrayEquals(bytes, is.getInputBuffer());
28+
}
29+
}
30+
31+
@Test
32+
public void testProvidesReadableInputStream() throws IOException {
33+
// arrange
34+
byte[] bytes = "GET / HTTP/1.1".getBytes();
35+
HttpServletRequest request = mockServletRequest(bytes);
36+
37+
// act
38+
try (TeeServletInputStream is = new TeeServletInputStream(request)) {
39+
// assert
40+
for (byte nextByte : bytes) {
41+
assertEquals(nextByte, is.read());
42+
}
43+
assertEquals(-1, is.read());
44+
assertEquals(-1, is.read());
45+
assertEquals(-1, is.read());
46+
}
47+
}
48+
49+
@Test
50+
public void testPropagatesIOExceptionOnRead() throws IOException {
51+
// arrange
52+
byte[] bytes = "GET / ...".getBytes();
53+
HttpServletRequest request = mockServletRequest(bytes, "Read timed out");
54+
55+
// act
56+
try (TeeServletInputStream is = new TeeServletInputStream(request)) {
57+
// assert
58+
IOException e = assertThrows(IOException.class, () -> { while(is.read() != -1); });
59+
assertEquals("Read timed out", e.getMessage());
60+
assertArrayEquals(new byte[0], is.getInputBuffer());
61+
}
62+
}
63+
64+
private static HttpServletRequest mockServletRequest(byte[] bytes) {
65+
return mockServletRequest(bytes, null);
66+
}
67+
68+
private static HttpServletRequest mockServletRequest(byte[] bytes, String ioError) {
69+
ServletInputStream inputStream = mockServletInputStream(bytes, ioError);
70+
71+
ClassLoader classLoader = TeeServletInputStreamTest.class.getClassLoader();
72+
Class<?>[] interfaces = new Class<?>[] {HttpServletRequest.class};
73+
74+
Object servletRequest = Proxy.newProxyInstance(classLoader, interfaces, (object, method, arg) -> {
75+
switch (method.getName()) {
76+
case "getInputStream": return inputStream;
77+
default: throw new UnsupportedOperationException();
78+
}
79+
});
80+
81+
return (HttpServletRequest) servletRequest;
82+
}
83+
84+
private static ServletInputStream mockServletInputStream(byte[] bytes, String ioError) {
85+
ByteBuffer buffer = ByteBuffer.wrap(bytes);
86+
87+
return new ServletInputStream() {
88+
@Override
89+
public int read() throws IOException {
90+
if (buffer.hasRemaining()) {
91+
return buffer.get();
92+
} else if (ioError == null) {
93+
return -1;
94+
}
95+
throw new IOException(ioError);
96+
}
97+
98+
@Override
99+
public void setReadListener(ReadListener readListener) {
100+
throw new UnsupportedOperationException();
101+
}
102+
103+
@Override
104+
public boolean isReady() {
105+
return true;
106+
}
107+
108+
@Override
109+
public boolean isFinished() {
110+
return buffer.hasRemaining();
111+
}
112+
};
113+
}
114+
115+
}

0 commit comments

Comments
 (0)