Skip to content

Commit

Permalink
Proper handling of chunked input streams in LoggingInterceptor
Browse files Browse the repository at this point in the history
  • Loading branch information
dkBrazz authored and Denis Kurochkin committed Mar 22, 2021
1 parent bcfba2b commit 761d330
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 2 deletions.
5 changes: 5 additions & 0 deletions core-common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,11 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,16 @@ InputStream logInboundEntity(final StringBuilder b, InputStream stream, final Ch
}
stream.mark(maxEntitySize + 1);
final byte[] entity = new byte[maxEntitySize + 1];
final int entitySize = stream.read(entity);

int entitySize = 0;
while (entitySize < entity.length) {
int readBytes = stream.read(entity, entitySize, entity.length - entitySize);
if (readBytes < 0) {
break;
}
entitySize += readBytes;
}

b.append(new String(entity, 0, Math.min(entitySize, maxEntitySize), charset));
if (entitySize > maxEntitySize) {
b.append("...more...");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,27 @@

package org.glassfish.jersey.logging;

import org.mockito.stubbing.Answer;

import javax.ws.rs.core.MediaType;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Random;

import org.junit.Test;
import static org.glassfish.jersey.logging.LoggingFeature.Verbosity.HEADERS_ONLY;
import static org.glassfish.jersey.logging.LoggingFeature.Verbosity.PAYLOAD_ANY;
import static org.glassfish.jersey.logging.LoggingFeature.Verbosity.PAYLOAD_TEXT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM_TYPE;
import static javax.ws.rs.core.MediaType.TEXT_HTML_TYPE;
Expand Down Expand Up @@ -106,4 +119,67 @@ public void testVerbosityHeadersPrintBinaryEntity() {
assertFalse(LoggingInterceptor.printEntity(HEADERS_ONLY, APPLICATION_OCTET_STREAM_TYPE));
}

//
// logInboundEntity
//

@Test
public void testLogInboundEntityMockedStream() throws Exception {
int maxEntitySize = 20;
LoggingInterceptor loggingInterceptor = new LoggingInterceptor(null, null, null, maxEntitySize) {};

StringBuilder buffer = new StringBuilder();
InputStream stream = mock(InputStream.class);
when(stream.markSupported()).thenReturn(true);

when(stream.read(any(), eq(0), eq(maxEntitySize + 1)))
.thenAnswer(chunk(4, 'a'));
when(stream.read(any(), eq(4), eq(maxEntitySize + 1 - 4)))
.thenAnswer(chunk(3, 'b'));
when(stream.read(any(), eq(7), eq(maxEntitySize + 1 - 7)))
.thenAnswer(chunk(5, 'c'));
when(stream.read(any(), eq(12), eq(maxEntitySize + 1 - 12)))
.thenReturn(-1);

loggingInterceptor.logInboundEntity(buffer, stream, StandardCharsets.UTF_8);

assertEquals("aaaabbbccccc\n", buffer.toString());
verify(stream).mark(maxEntitySize + 1);
verify(stream).reset();
}

private Answer<?> chunk(int size, char filler) {
return invocation -> {
byte[] buf = invocation.getArgumentAt(0, byte[].class);
int offset = invocation.getArgumentAt(1, Integer.class);
Arrays.fill(buf, offset, offset + size, (byte) filler);
return size;
};
}

@Test
public void testLogInboundEntityRealStream() throws Exception {
int maxEntitySize = 2000;
String inputString = getRandomString(maxEntitySize * 2);

LoggingInterceptor loggingInterceptor = new LoggingInterceptor(null, null, null, maxEntitySize) {};
StringBuilder buffer = new StringBuilder();
InputStream stream = new ByteArrayInputStream(inputString.getBytes());

loggingInterceptor.logInboundEntity(buffer, stream, StandardCharsets.UTF_8);

assertEquals(inputString.substring(0, maxEntitySize) + "...more...\n", buffer.toString());
}

private static String getRandomString(int length) {
final String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 _";
StringBuilder result = new StringBuilder();

while (length > 0) {
Random rand = new Random();
result.append(characters.charAt(rand.nextInt(characters.length())));
length--;
}
return result.toString();
}
}
5 changes: 4 additions & 1 deletion core-common/src/test/resources/surefire.policy
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,15 @@ grant codebase "file:${settings.localRepository}/-" {
grant codebase "file:${project.build.directory}/test-classes/-" {
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
permission java.lang.RuntimePermission "modifyThread";
permission java.util.PropertyPermission "*", "write";
permission java.util.PropertyPermission "*", "read,write";
permission java.io.FilePermission "${java.io.tmpdir}/-", "read,write,delete";
permission java.lang.RuntimePermission "getClassLoader";
permission java.lang.RuntimePermission "accessClassInPackage.sun.misc";
permission java.lang.RuntimePermission "accessClassInPackage.sun.misc.*";
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
permission java.lang.RuntimePermission "accessDeclaredMembers";
permission java.lang.RuntimePermission "accessClassInPackage.sun.reflect";
permission java.lang.RuntimePermission "reflectionFactoryAccess";
};

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

0 comments on commit 761d330

Please sign in to comment.