You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by re...@apache.org on 2026/07/12 20:14:00 UTC
(cxf) 02/03: Add maxSize to BinaryDataProvider so the payload size could be constrained (to 1Gb by default) (#3301)
This is an automated email from the ASF dual-hosted git repository.
reta pushed a commit to branch 3.6.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git
commit 95dd0e63cc72db7be4ccbbf2552bd5b4cd584215
Author: Andriy Redko <dr...@gmail.com>
AuthorDate: Sat Jul 11 11:58:23 2026 -0400
Add maxSize to BinaryDataProvider so the payload size could be constrained (to 1Gb by default) (#3301)
(cherry picked from commit da5edccdb85138bad83858f05eba10dfe5df4212)
---
.../java/org/apache/cxf/helpers/CastUtils.java | 10 +++++++-
.../main/java/org/apache/cxf/helpers/IOUtils.java | 30 ++++++++++++++++++----
.../java/org/apache/cxf/io/CachedOutputStream.java | 8 +++++-
.../cxf/jaxrs/provider/BinaryDataProvider.java | 7 ++++-
.../cxf/jaxrs/provider/BinaryDataProviderTest.java | 19 ++++++++++++++
.../cache/CacheControlClientReaderInterceptor.java | 9 +++++--
.../jaxrs/client/cache/CacheControlFeature.java | 7 +++++
7 files changed, 80 insertions(+), 10 deletions(-)
diff --git a/core/src/main/java/org/apache/cxf/helpers/CastUtils.java b/core/src/main/java/org/apache/cxf/helpers/CastUtils.java
index 958ef5f5e44..74c3db05da9 100644
--- a/core/src/main/java/org/apache/cxf/helpers/CastUtils.java
+++ b/core/src/main/java/org/apache/cxf/helpers/CastUtils.java
@@ -92,5 +92,13 @@ public final class CastUtils {
return (Map.Entry<T, U>)p;
}
-
+ public static int cast(long value) {
+ if (value > Integer.MAX_VALUE) {
+ return Integer.MAX_VALUE;
+ } else if (value < Integer.MIN_VALUE) {
+ return Integer.MIN_VALUE;
+ } else {
+ return (int) value;
+ }
+ }
}
diff --git a/core/src/main/java/org/apache/cxf/helpers/IOUtils.java b/core/src/main/java/org/apache/cxf/helpers/IOUtils.java
index be05fe9981b..37507334e2d 100644
--- a/core/src/main/java/org/apache/cxf/helpers/IOUtils.java
+++ b/core/src/main/java/org/apache/cxf/helpers/IOUtils.java
@@ -34,10 +34,14 @@ import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.Objects;
+import org.apache.cxf.common.util.SystemPropertyAction;
import org.apache.cxf.io.CopyingOutputStream;
import org.apache.cxf.io.Transferable;
public final class IOUtils {
+ public static final int DEFAULT_BINARY_MAX_SIZE =
+ SystemPropertyAction.getInteger("org.apache.cxf.binary-max-size", 1073741824 /* 1Gb */);
+
public static final Charset UTF8_CHARSET = java.nio.charset.StandardCharsets.UTF_8;
public static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
@@ -176,7 +180,7 @@ public final class IOUtils {
if (output instanceof CopyingOutputStream) {
return ((CopyingOutputStream)output).copyFrom(input);
}
- return copy(input, output, DEFAULT_BUFFER_SIZE);
+ return copy(input, output, DEFAULT_BUFFER_SIZE, -1);
}
public static int copyAndCloseInput(final InputStream input,
@@ -189,7 +193,7 @@ public final class IOUtils {
public static int copyAndCloseInput(final InputStream input,
final OutputStream output, int bufferSize) throws IOException {
try (InputStream in = input) {
- return copy(in, output, bufferSize);
+ return copy(in, output, bufferSize, -1);
}
}
@@ -206,9 +210,13 @@ public final class IOUtils {
copy(r, output, bufferSize);
}
}
+
+ public static int copy(final InputStream input, final OutputStream output, int bufferSize) throws IOException {
+ return copy(input, output, bufferSize, -1);
+ }
public static int copy(final InputStream input, final OutputStream output,
- int bufferSize) throws IOException {
+ int bufferSize, int maxSize) throws IOException {
Objects.requireNonNull(input, "The inputStream is required but null value was provided");
Objects.requireNonNull(output, "The outputStream is required but null value was provided");
int avail = input.available();
@@ -228,6 +236,10 @@ public final class IOUtils {
output.write(buffer, 0, n);
total += n;
n = input.read(buffer);
+
+ if (maxSize > 0 /* -1 sets to unlimited */ && total > maxSize) {
+ throw new IOException("The total limit of " + maxSize + " bytes exceeded, data is too large");
+ }
}
return total;
}
@@ -426,15 +438,23 @@ public final class IOUtils {
n = input.read(buffer, 0, n);
}
}
-
+
+ /**
+ * @deprecated use {@link #readBytesFromStream(InputStream, int)}
+ */
+ @Deprecated(forRemoval = true)
public static byte[] readBytesFromStream(InputStream in) throws IOException {
+ return readBytesFromStream(in, -1);
+ }
+
+ public static byte[] readBytesFromStream(InputStream in, int maxSize) throws IOException {
Objects.requireNonNull(in, "The inputStream is required but null value was provided");
int i = in.available();
if (i < DEFAULT_BUFFER_SIZE) {
i = DEFAULT_BUFFER_SIZE;
}
try (InputStream input = in; ByteArrayOutputStream bos = new ByteArrayOutputStream(i)) {
- copy(input, bos);
+ copy(input, bos, DEFAULT_BUFFER_SIZE, maxSize);
return bos.toByteArray();
}
}
diff --git a/core/src/main/java/org/apache/cxf/io/CachedOutputStream.java b/core/src/main/java/org/apache/cxf/io/CachedOutputStream.java
index c19e7cfbe8b..200b0dfe248 100644
--- a/core/src/main/java/org/apache/cxf/io/CachedOutputStream.java
+++ b/core/src/main/java/org/apache/cxf/io/CachedOutputStream.java
@@ -44,6 +44,7 @@ import javax.crypto.CipherOutputStream;
import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.common.util.SystemPropertyAction;
+import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.helpers.FileUtils;
import org.apache.cxf.helpers.IOUtils;
import org.apache.cxf.helpers.LoadingByteArrayOutputStream;
@@ -334,9 +335,14 @@ public class CachedOutputStream extends OutputStream {
}
throw new IOException("Unknown format of currentStream");
}
+
+ if (totalLength > Integer.MAX_VALUE) {
+ throw new IOException("The total limit of " + Integer.MAX_VALUE + " bytes exceeded, data is too large");
+ }
+
// read the file
try (InputStream fin = createInputStream(tempFile)) {
- return IOUtils.readBytesFromStream(fin);
+ return IOUtils.readBytesFromStream(fin, CastUtils.cast(maxSize));
}
}
diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/BinaryDataProvider.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/BinaryDataProvider.java
index f920988b467..80d534c58b4 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/BinaryDataProvider.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/BinaryDataProvider.java
@@ -72,6 +72,7 @@ public class BinaryDataProvider<T> extends AbstractConfigurableProvider
private static final Logger LOG = LogUtils.getL7dLogger(BinaryDataProvider.class);
private int bufferSize = IOUtils.DEFAULT_BUFFER_SIZE;
+ private int maxSize = IOUtils.DEFAULT_BINARY_MAX_SIZE;
private boolean reportByteArraySize;
private boolean closeResponseInputStream = true;
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mt) {
@@ -106,7 +107,7 @@ public class BinaryDataProvider<T> extends AbstractConfigurableProvider
return clazz.cast(new InputStreamReader(is, getEncoding(type)));
}
if (byte[].class.isAssignableFrom(clazz)) {
- return clazz.cast(IOUtils.readBytesFromStream(is));
+ return clazz.cast(IOUtils.readBytesFromStream(is, maxSize));
}
if (File.class.isAssignableFrom(clazz)) {
LOG.warning("Reading data into File objects with the help of pre-packaged"
@@ -260,6 +261,10 @@ public class BinaryDataProvider<T> extends AbstractConfigurableProvider
this.bufferSize = bufferSize;
}
+ public void setMaxSize(int maxSize) {
+ this.maxSize = maxSize;
+ }
+
protected NioWriteHandler getNioHandler(final InputStream in) {
return new NioWriteHandler() {
diff --git a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/BinaryDataProviderTest.java b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/BinaryDataProviderTest.java
index c8f13010806..b60e27615fa 100644
--- a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/BinaryDataProviderTest.java
+++ b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/BinaryDataProviderTest.java
@@ -39,8 +39,11 @@ import org.apache.cxf.jaxrs.impl.MetadataMap;
import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
public class BinaryDataProviderTest {
@@ -133,6 +136,22 @@ public class BinaryDataProviderTest {
assertArrayEquals(os.toByteArray(), new String("hi").getBytes());
}
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Test
+ public void testReadBytesMaxSize() throws Exception {
+ final int maxSize = 4096;
+ final BinaryDataProvider p = new BinaryDataProvider();
+ p.setMaxSize(maxSize);
+
+ final byte[] bytes = new byte[maxSize + 1]; /* maxSize + 1 byte */
+ final IOException ex = assertThrows(IOException.class,
+ () -> p.readFrom(byte[].class, byte[].class, new Annotation[]{},
+ MediaType.APPLICATION_OCTET_STREAM_TYPE,
+ new MetadataMap<String, Object>(),
+ new ByteArrayInputStream(bytes)));
+
+ assertThat(ex.getMessage(), equalTo("The total limit of 4096 bytes exceeded, data is too large"));
+ }
private static final class StreamingOutputImpl implements StreamingOutput {
diff --git a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/cache/CacheControlClientReaderInterceptor.java b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/cache/CacheControlClientReaderInterceptor.java
index 3da097aa99b..8fc8cea373b 100644
--- a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/cache/CacheControlClientReaderInterceptor.java
+++ b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/cache/CacheControlClientReaderInterceptor.java
@@ -49,6 +49,7 @@ public class CacheControlClientReaderInterceptor implements ReaderInterceptor {
@Context
private UriInfo uriInfo;
private boolean cacheResponseInputStream;
+ private int maxSize = IOUtils.DEFAULT_BINARY_MAX_SIZE;
public CacheControlClientReaderInterceptor(final Cache<Key, Entry> cache) {
this.cache = cache;
@@ -63,6 +64,10 @@ public class CacheControlClientReaderInterceptor implements ReaderInterceptor {
return this;
}
+ public void setMaxSize(int maxSize) {
+ this.maxSize = maxSize;
+ }
+
@Override
public Object aroundReadFrom(final ReaderInterceptorContext context) throws IOException, WebApplicationException {
Object cachedEntity = context.getProperty(CacheControlClientRequestFilter.CACHED_ENTITY_PROPERTY);
@@ -97,7 +102,7 @@ public class CacheControlClientReaderInterceptor implements ReaderInterceptor {
final boolean validCacheControl = isCacheControlValid(context, cacheControl);
if (validCacheControl && cacheResponseInputStream) {
// if Cache-Control is set and the stream needs to be cached then do it
- cachedBytes = IOUtils.readBytesFromStream(context.getInputStream());
+ cachedBytes = IOUtils.readBytesFromStream(context.getInputStream(), maxSize);
context.setInputStream(new ByteArrayInputStream(cachedBytes));
}
// Read the stream and get the actual entity
@@ -117,7 +122,7 @@ public class CacheControlClientReaderInterceptor implements ReaderInterceptor {
} else if (responseEntity instanceof InputStream) {
// read the stream, cache it, the cached bytes will be returned immediately
// when a client cache will return them
- byte[] bytes = IOUtils.readBytesFromStream((InputStream)responseEntity);
+ byte[] bytes = IOUtils.readBytesFromStream((InputStream)responseEntity, maxSize);
ser = new BytesEntity(bytes, true);
responseEntity = new ByteArrayInputStream(bytes);
}
diff --git a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/cache/CacheControlFeature.java b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/cache/CacheControlFeature.java
index 0b2877889d7..841e3c4a005 100644
--- a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/cache/CacheControlFeature.java
+++ b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/cache/CacheControlFeature.java
@@ -39,6 +39,7 @@ import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.Provider;
+import org.apache.cxf.helpers.IOUtils;
@Provider
public class CacheControlFeature implements Feature, Closeable {
@@ -46,6 +47,7 @@ public class CacheControlFeature implements Feature, Closeable {
private CacheManager manager;
private Cache<Key, Entry> cache;
private boolean cacheResponseInputStream;
+ private int maxSize = IOUtils.DEFAULT_BINARY_MAX_SIZE;
@Override
public boolean configure(final FeatureContext context) {
@@ -54,10 +56,15 @@ public class CacheControlFeature implements Feature, Closeable {
context.register(new CacheControlClientRequestFilter(entryCache));
CacheControlClientReaderInterceptor reader = new CacheControlClientReaderInterceptor(entryCache);
reader.setCacheResponseInputStream(cacheResponseInputStream);
+ reader.setMaxSize(maxSize);
context.register(reader);
return true;
}
+ public void setMaxSize(int maxSize) {
+ this.maxSize = maxSize;
+ }
+
@PreDestroy // TODO: check it is called
public void close() {
for (final Closeable c : Arrays.asList(cache, manager, provider)) {