Skip to content

Commit 6b87643

Browse files
author
Denis Kurochkin
authored
Proper handling of chunked input streams in LoggingInterceptor (#4753)
Signed-off-by: Denis Kurochkin <d.k.brazz@gmail.com>
1 parent f0e0106 commit 6b87643

File tree

4 files changed

+98
-5
lines changed

4 files changed

+98
-5
lines changed

core-common/pom.xml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!--
33
4-
Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
4+
Copyright (c) 2010, 2021 Oracle and/or its affiliates. All rights reserved.
55
66
This program and the accompanying materials are made available under the
77
terms of the Eclipse Public License v. 2.0, which is available at
@@ -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: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -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: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2016, 2019 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -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)