diff --git a/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java b/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java index d5c6409cb56..dc0e735315a 100644 --- a/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java +++ b/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java @@ -61,6 +61,8 @@ public class AttachmentDeserializer { * The maximum size of the attachment. Allowed value is any of {@link Number} or {@link String}. */ public static final String ATTACHMENT_MAX_SIZE = "attachment-max-size"; + public static final long DEFAULT_ATTACHMENT_MAX_SIZE = + SystemPropertyAction.getInteger("org.apache.cxf.attachment-max-size", 50 * 1024 * 1024 /* 50 Mb */); /** * The maximum number of attachments permitted in a message. The default is 50. diff --git a/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java b/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java index 42cffc8c027..79f34d81101 100644 --- a/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java +++ b/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java @@ -214,24 +214,25 @@ public static void setStreamedAttachmentProperties(Message message, CachedOutput } Object maxSize = message.getContextualProperty(AttachmentDeserializer.ATTACHMENT_MAX_SIZE); - if (maxSize != null) { - if (maxSize instanceof Number) { - long size = ((Number) maxSize).longValue(); - if (size >= 0) { - bos.setMaxSize(size); - } else { - LOG.warning("Max size value overflowed long. Do not set max size!"); - } - } else if (maxSize instanceof String) { - try { - bos.setMaxSize(Long.parseLong((String) maxSize)); - } catch (NumberFormatException e) { - throw new IOException("Provided threshold String is not a number", e); - } + if (maxSize == null) { + maxSize = AttachmentDeserializer.DEFAULT_ATTACHMENT_MAX_SIZE; + } + if (maxSize instanceof Number) { + long size = ((Number) maxSize).longValue(); + if (size >= 0) { + bos.setMaxSize(size); } else { - throw new IOException("The value set as " + AttachmentDeserializer.ATTACHMENT_MAX_SIZE - + " should be either an instance of Number or String"); + LOG.warning("The max size value is set to unlimited."); } + } else if (maxSize instanceof String) { + try { + bos.setMaxSize(Long.parseLong((String) maxSize)); + } catch (NumberFormatException e) { + throw new IOException("Provided max size String is not a number", e); + } + } else { + throw new IOException("The value set as " + AttachmentDeserializer.ATTACHMENT_MAX_SIZE + + " should be either an instance of Number or String"); } } diff --git a/core/src/test/java/org/apache/cxf/attachment/AttachmentDeserializerTest.java b/core/src/test/java/org/apache/cxf/attachment/AttachmentDeserializerTest.java index eec76dbba9c..1d6e2dd756e 100644 --- a/core/src/test/java/org/apache/cxf/attachment/AttachmentDeserializerTest.java +++ b/core/src/test/java/org/apache/cxf/attachment/AttachmentDeserializerTest.java @@ -31,7 +31,9 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import java.util.stream.IntStream; +import java.util.stream.LongStream; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; @@ -41,6 +43,7 @@ import jakarta.activation.DataSource; import jakarta.activation.URLDataSource; import org.apache.cxf.helpers.IOUtils; +import org.apache.cxf.io.CacheSizeExceededException; import org.apache.cxf.message.Attachment; import org.apache.cxf.message.Exchange; import org.apache.cxf.message.ExchangeImpl; @@ -377,6 +380,25 @@ public void testSmallStream() throws Exception { assertEquals(-1, m.read(new byte[1000])); m.close(); } + + @Test + public void testDefaultAttachmentMaxSize() throws Exception { + final byte[] messageBytes = ("------=_Part_1\n\nJJJJ\n------=_Part_1\n\n" + + "Content-Transfer-Encoding: binary\n\n" + LongStream + .range(0, AttachmentDeserializer.DEFAULT_ATTACHMENT_MAX_SIZE / 3 + 1) + .mapToObj(i -> "=3D") + .collect(Collectors.joining()) + + "\n------=_Part_1\n").getBytes(); + + msg = new MessageImpl(); + msg.setContent(InputStream.class, new ByteArrayInputStream(messageBytes)); + msg.put(Message.CONTENT_TYPE, "multipart/related"); + AttachmentDeserializer ad = new AttachmentDeserializer(msg); + ad.initializeAttachments(); + + // Force it to load the attachments + assertThrows(CacheSizeExceededException.class, () -> msg.getAttachments().size()); + } @Test public void testCXF2542() throws Exception { diff --git a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/FormEncodingProviderTest.java b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/FormEncodingProviderTest.java index ffb489a7d64..c103bf6bd24 100644 --- a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/FormEncodingProviderTest.java +++ b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/FormEncodingProviderTest.java @@ -25,19 +25,34 @@ import java.lang.annotation.Annotation; import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.LongStream; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.Encoded; import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.core.Form; import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MultivaluedHashMap; import jakarta.ws.rs.core.MultivaluedMap; +import org.apache.cxf.Bus; +import org.apache.cxf.attachment.AttachmentDeserializer; +import org.apache.cxf.bus.extension.ExtensionManagerBus; +import org.apache.cxf.jaxrs.ext.multipart.MultipartBody; import org.apache.cxf.jaxrs.impl.MetadataMap; +import org.apache.cxf.jaxrs.model.ProviderInfo; import org.apache.cxf.jaxrs.utils.HttpUtils; +import org.apache.cxf.jaxrs.utils.InjectionUtils; +import org.apache.cxf.message.ExchangeImpl; +import org.apache.cxf.message.Message; +import org.apache.cxf.message.MessageImpl; import org.junit.Test; +import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -141,6 +156,40 @@ public void testMultiLines() throws Exception { assertEquals("Wrong entry for baz", "4", mvMap.getFirst("baz")); } + + @Test + public void testWriteMultipartTooLarge() throws Exception { + final MultivaluedMap headers = new MultivaluedHashMap<>(); + headers.add("Content-Transfer-Encoding", "binary"); + + final Bus bus = new ExtensionManagerBus(); + final Message m = new MessageImpl(); + m.put(AttachmentDeserializer.ATTACHMENT_PART_HEADERS, headers); + m.put(Message.CONTENT_TYPE, "multipart/related"); + + final ExchangeImpl exchange = new ExchangeImpl(); + m.setExchange(exchange); + exchange.setInMessage(m); + + FormEncodingProvider ferp = new FormEncodingProvider<>(); + InjectionUtils.injectContextFields(ferp, new ProviderInfo<>(ferp, bus, false), m); + + final byte[] messageBytes = ("------=_Part_1\n\nJJJJ\n------=_Part_1\n\n" + + "Content-Transfer-Encoding: binary\n\n" + LongStream + .range(0, AttachmentDeserializer.DEFAULT_ATTACHMENT_MAX_SIZE / 3 + 1) + .mapToObj(i -> "=3D") + .collect(Collectors.joining()) + + "\n------=_Part_1\n").getBytes(); + try (ByteArrayInputStream in = new ByteArrayInputStream(messageBytes)) { + m.setContent(InputStream.class, in); + + final WebApplicationException ex = assertThrows(WebApplicationException.class, + () -> ferp.readFrom(MultipartBody.class, null, new Annotation[]{}, + MediaType.MULTIPART_FORM_DATA_TYPE, null, in)); + + assertThat(ex.getResponse().getStatus(), equalTo(413) /* Request Entity Too Large */); + } + } @Test public void testWriteMultipleValues() throws Exception { diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSEntityPartTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSEntityPartTest.java index c7ecd4b1a0b..cbd81f2f9fa 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSEntityPartTest.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSEntityPartTest.java @@ -19,6 +19,7 @@ package org.apache.cxf.systest.jaxrs; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; @@ -132,6 +133,28 @@ public void testBookJaxbForm() throws Exception { doAddFormBook(address, "attachmentFormJaxb", "bookXML", MediaType.APPLICATION_XML, 200); } + @Test + public void testMultipartRequestTooLarge() throws Exception { + final String address = "http://localhost:" + PORT + "/bookstore/books/images"; + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + PrintWriter writer = new PrintWriter(output, true); + LoggingOutInterceptor out = new LoggingOutInterceptor(writer); + + final WebClient client = WebClient.create(address); + client.getConfiguration().getOutInterceptors().add(out); + client.type(MediaType.MULTIPART_FORM_DATA).accept(MediaType.MULTIPART_FORM_DATA); + + final EntityPart part = EntityPart + .withFileName("testfile.png") + .content(new ByteArrayInputStream(new byte[1024 * 11])) + .build(); + + try (Response response = client.postCollection(List.of(part), EntityPart.class)) { + assertEquals(413, response.getStatus()); + } + } + private void doAddFormBook(String address, String resourceName, String name, String mt, int status) throws Exception {