This is an automated email from the ASF dual-hosted git repository.
reta pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/main by this push:
new da5edccdb85 Add maxSize to BinaryDataProvider so the payload size
could be constrained (to 1Gb by default) (#3301)
da5edccdb85 is described below
commit da5edccdb85138bad83858f05eba10dfe5df4212
Author: Andriy Redko <[email protected]>
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)
---
.../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 | 8 +++++-
7 files changed, 80 insertions(+), 11 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 d9604618d10..4d521f806b7 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
@@ -71,6 +71,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) {
@@ -105,7 +106,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"
@@ -259,6 +260,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 02d648c0971..b3c8395d3a5 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 c3f303921d2..04729bce260 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 37bbc3ac573..bdb8cab1a8a 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,7 +39,7 @@ import jakarta.annotation.PreDestroy;
import jakarta.ws.rs.core.Feature;
import jakarta.ws.rs.core.FeatureContext;
import jakarta.ws.rs.ext.Provider;
-
+import org.apache.cxf.helpers.IOUtils;
@Provider
public class CacheControlFeature implements Feature, Closeable {
@@ -47,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) {
@@ -55,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)) {