From 3d74396f8085122e2422c662f0d15cfb047aa2a1 Mon Sep 17 00:00:00 2001 From: Denis Kurochkin Date: Tue, 23 Mar 2021 00:08:12 +0200 Subject: [PATCH 1/2] Proper handling of chunked input streams in LoggingInterceptor Signed-off-by: Denis Kurochkin --- core-common/pom.xml | 5 ++ .../jersey/logging/LoggingInterceptor.java | 11 ++- .../logging/LoggingInterceptorTest.java | 76 +++++++++++++++++++ .../src/test/resources/surefire.policy | 5 +- 4 files changed, 95 insertions(+), 2 deletions(-) diff --git a/core-common/pom.xml b/core-common/pom.xml index 3288c5f45b..c5dc543198 100644 --- a/core-common/pom.xml +++ b/core-common/pom.xml @@ -210,6 +210,11 @@ junit test + + org.mockito + mockito-all + test + org.hamcrest hamcrest-library diff --git a/core-common/src/main/java/org/glassfish/jersey/logging/LoggingInterceptor.java b/core-common/src/main/java/org/glassfish/jersey/logging/LoggingInterceptor.java index c90d8b6470..1a42aa4027 100644 --- a/core-common/src/main/java/org/glassfish/jersey/logging/LoggingInterceptor.java +++ b/core-common/src/main/java/org/glassfish/jersey/logging/LoggingInterceptor.java @@ -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..."); diff --git a/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java b/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java index e4e5e91480..db1eae46a1 100644 --- a/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java @@ -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; @@ -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(); + } } diff --git a/core-common/src/test/resources/surefire.policy b/core-common/src/test/resources/surefire.policy index 27602ae4c0..530db3cf62 100644 --- a/core-common/src/test/resources/surefire.policy +++ b/core-common/src/test/resources/surefire.policy @@ -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/-" { From ec9732148795f7ed85af4058e0da97bdd55e1bef Mon Sep 17 00:00:00 2001 From: Denis Kurochkin Date: Tue, 23 Mar 2021 12:32:03 +0200 Subject: [PATCH 2/2] Change Copyright year to 2021 Signed-off-by: Denis Kurochkin --- core-common/pom.xml | 2 +- .../java/org/glassfish/jersey/logging/LoggingInterceptor.java | 2 +- .../org/glassfish/jersey/logging/LoggingInterceptorTest.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core-common/pom.xml b/core-common/pom.xml index c5dc543198..e36288f6c0 100644 --- a/core-common/pom.xml +++ b/core-common/pom.xml @@ -1,7 +1,7 @@