Skip to content

Commit 3d74396

Browse files
committed
Proper handling of chunked input streams in LoggingInterceptor
Signed-off-by: Denis Kurochkin <d.k.brazz@gmail.com>
1 parent bcfba2b commit 3d74396

File tree

4 files changed

+95
-2
lines changed

4 files changed

+95
-2
lines changed

core-common/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,11 @@
210210
<artifactId>junit</artifactId>
211211
<scope>test</scope>
212212
</dependency>
213+
<dependency>
214+
<groupId>org.mockito</groupId>
215+
<artifactId>mockito-all</artifactId>
216+
<scope>test</scope>
217+
</dependency>
213218
<dependency>
214219
<groupId>org.hamcrest</groupId>
215220
<artifactId>hamcrest-library</artifactId>

core-common/src/main/java/org/glassfish/jersey/logging/LoggingInterceptor.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,16 @@ InputStream logInboundEntity(final StringBuilder b, InputStream stream, final Ch
193193
}
194194
stream.mark(maxEntitySize + 1);
195195
final byte[] entity = new byte[maxEntitySize + 1];
196-
final int entitySize = stream.read(entity);
196+
197+
int entitySize = 0;
198+
while (entitySize < entity.length) {
199+
int readBytes = stream.read(entity, entitySize, entity.length - entitySize);
200+
if (readBytes < 0) {
201+
break;
202+
}
203+
entitySize += readBytes;
204+
}
205+
197206
b.append(new String(entity, 0, Math.min(entitySize, maxEntitySize), charset));
198207
if (entitySize > maxEntitySize) {
199208
b.append("...more...");

core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,27 @@
1616

1717
package org.glassfish.jersey.logging;
1818

19+
import org.mockito.stubbing.Answer;
20+
1921
import javax.ws.rs.core.MediaType;
22+
import java.io.ByteArrayInputStream;
23+
import java.io.InputStream;
24+
import java.nio.charset.StandardCharsets;
25+
import java.util.Arrays;
26+
import java.util.Random;
2027

2128
import org.junit.Test;
2229
import static org.glassfish.jersey.logging.LoggingFeature.Verbosity.HEADERS_ONLY;
2330
import static org.glassfish.jersey.logging.LoggingFeature.Verbosity.PAYLOAD_ANY;
2431
import static org.glassfish.jersey.logging.LoggingFeature.Verbosity.PAYLOAD_TEXT;
32+
import static org.junit.Assert.assertEquals;
2533
import static org.junit.Assert.assertFalse;
2634
import static org.junit.Assert.assertTrue;
35+
import static org.mockito.Matchers.any;
36+
import static org.mockito.Matchers.eq;
37+
import static org.mockito.Mockito.mock;
38+
import static org.mockito.Mockito.verify;
39+
import static org.mockito.Mockito.when;
2740

2841
import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM_TYPE;
2942
import static javax.ws.rs.core.MediaType.TEXT_HTML_TYPE;
@@ -106,4 +119,67 @@ public void testVerbosityHeadersPrintBinaryEntity() {
106119
assertFalse(LoggingInterceptor.printEntity(HEADERS_ONLY, APPLICATION_OCTET_STREAM_TYPE));
107120
}
108121

122+
//
123+
// logInboundEntity
124+
//
125+
126+
@Test
127+
public void testLogInboundEntityMockedStream() throws Exception {
128+
int maxEntitySize = 20;
129+
LoggingInterceptor loggingInterceptor = new LoggingInterceptor(null, null, null, maxEntitySize) {};
130+
131+
StringBuilder buffer = new StringBuilder();
132+
InputStream stream = mock(InputStream.class);
133+
when(stream.markSupported()).thenReturn(true);
134+
135+
when(stream.read(any(), eq(0), eq(maxEntitySize + 1)))
136+
.thenAnswer(chunk(4, 'a'));
137+
when(stream.read(any(), eq(4), eq(maxEntitySize + 1 - 4)))
138+
.thenAnswer(chunk(3, 'b'));
139+
when(stream.read(any(), eq(7), eq(maxEntitySize + 1 - 7)))
140+
.thenAnswer(chunk(5, 'c'));
141+
when(stream.read(any(), eq(12), eq(maxEntitySize + 1 - 12)))
142+
.thenReturn(-1);
143+
144+
loggingInterceptor.logInboundEntity(buffer, stream, StandardCharsets.UTF_8);
145+
146+
assertEquals("aaaabbbccccc\n", buffer.toString());
147+
verify(stream).mark(maxEntitySize + 1);
148+
verify(stream).reset();
149+
}
150+
151+
private Answer<?> chunk(int size, char filler) {
152+
return invocation -> {
153+
byte[] buf = invocation.getArgumentAt(0, byte[].class);
154+
int offset = invocation.getArgumentAt(1, Integer.class);
155+
Arrays.fill(buf, offset, offset + size, (byte) filler);
156+
return size;
157+
};
158+
}
159+
160+
@Test
161+
public void testLogInboundEntityRealStream() throws Exception {
162+
int maxEntitySize = 2000;
163+
String inputString = getRandomString(maxEntitySize * 2);
164+
165+
LoggingInterceptor loggingInterceptor = new LoggingInterceptor(null, null, null, maxEntitySize) {};
166+
StringBuilder buffer = new StringBuilder();
167+
InputStream stream = new ByteArrayInputStream(inputString.getBytes());
168+
169+
loggingInterceptor.logInboundEntity(buffer, stream, StandardCharsets.UTF_8);
170+
171+
assertEquals(inputString.substring(0, maxEntitySize) + "...more...\n", buffer.toString());
172+
}
173+
174+
private static String getRandomString(int length) {
175+
final String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 _";
176+
StringBuilder result = new StringBuilder();
177+
178+
while (length > 0) {
179+
Random rand = new Random();
180+
result.append(characters.charAt(rand.nextInt(characters.length())));
181+
length--;
182+
}
183+
return result.toString();
184+
}
109185
}

core-common/src/test/resources/surefire.policy

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,15 @@ grant codebase "file:${settings.localRepository}/-" {
2929
grant codebase "file:${project.build.directory}/test-classes/-" {
3030
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
3131
permission java.lang.RuntimePermission "modifyThread";
32-
permission java.util.PropertyPermission "*", "write";
32+
permission java.util.PropertyPermission "*", "read,write";
3333
permission java.io.FilePermission "${java.io.tmpdir}/-", "read,write,delete";
3434
permission java.lang.RuntimePermission "getClassLoader";
3535
permission java.lang.RuntimePermission "accessClassInPackage.sun.misc";
3636
permission java.lang.RuntimePermission "accessClassInPackage.sun.misc.*";
3737
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
38+
permission java.lang.RuntimePermission "accessDeclaredMembers";
39+
permission java.lang.RuntimePermission "accessClassInPackage.sun.reflect";
40+
permission java.lang.RuntimePermission "reflectionFactoryAccess";
3841
};
3942

4043
grant codebase "file:${project.build.directory}/classes/-" {

0 commit comments

Comments
 (0)