Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion core/src/main/java/org/apache/cxf/helpers/CastUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,13 @@ public static <T, U> Map.Entry<T, U> cast(Map.Entry<?, ?> p, Class<T> pc, Class<
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;
}
}
}
30 changes: 25 additions & 5 deletions core/src/main/java/org/apache/cxf/helpers/IOUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,14 @@
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 =
Comment thread
coheigea marked this conversation as resolved.
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;

Expand Down Expand Up @@ -176,7 +180,7 @@ public static int copy(final InputStream input, final OutputStream output)
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,
Expand All @@ -189,7 +193,7 @@ public static int copyAndCloseInput(final InputStream input,
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);
}
}

Expand All @@ -206,9 +210,13 @@ public static void copyAndCloseInput(final Reader input,
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();
Expand All @@ -228,6 +236,10 @@ public static int copy(final InputStream input, final OutputStream output,
output.write(buffer, 0, n);
total += n;
n = input.read(buffer);

if (maxSize > 0 /* -1 sets to unlimited */ && total > maxSize) {
Comment thread
reta marked this conversation as resolved.
throw new IOException("The total limit of " + maxSize + " bytes exceeded, data is too large");
}
Comment thread
reta marked this conversation as resolved.
}
return total;
}
Expand Down Expand Up @@ -426,15 +438,23 @@ public static void consume(final InputStream input,
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;
}
Comment thread
reta marked this conversation as resolved.
try (InputStream input = in; ByteArrayOutputStream bos = new ByteArrayOutputStream(i)) {
copy(input, bos);
copy(input, bos, DEFAULT_BUFFER_SIZE, maxSize);
return bos.toByteArray();
}
}
Expand Down
8 changes: 7 additions & 1 deletion core/src/main/java/org/apache/cxf/io/CachedOutputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
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;
Expand Down Expand Up @@ -334,9 +335,14 @@ public byte[] getBytes() throws IOException {
}
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));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -105,7 +106,7 @@ public T readFrom(Class<T> clazz, Type genericType, Annotation[] annotations, Me
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)) {
Comment thread
reta marked this conversation as resolved.
LOG.warning("Reading data into File objects with the help of pre-packaged"
Expand Down Expand Up @@ -259,6 +260,10 @@ public void setBufferSize(int bufferSize) {
this.bufferSize = bufferSize;
}

public void setMaxSize(int maxSize) {
this.maxSize = maxSize;
}

protected NioWriteHandler getNioHandler(final InputStream in) {

return new NioWriteHandler() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@

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 {
Expand Down Expand Up @@ -133,6 +136,22 @@ public void testWriteTo() throws Exception {
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 {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -63,6 +64,10 @@ public CacheControlClientReaderInterceptor setCache(final Cache<Key, Entry> c) {
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);
Expand Down Expand Up @@ -97,7 +102,7 @@ public Object aroundReadFrom(final ReaderInterceptorContext context) throws IOEx
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
Expand All @@ -117,7 +122,7 @@ public Object aroundReadFrom(final ReaderInterceptorContext context) throws IOEx
} 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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,15 @@
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 {
private CachingProvider provider;
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) {
Expand All @@ -55,10 +56,15 @@ public boolean configure(final FeatureContext context) {
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)) {
Expand Down
Loading