You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by dk...@apache.org on 2011/10/26 17:41:28 UTC

svn commit: r1189272 - in /cxf/trunk: api/src/main/java/org/apache/cxf/io/ rt/core/src/main/java/org/apache/cxf/attachment/ rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/ rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/ rt/fron...

Author: dkulp
Date: Wed Oct 26 15:41:28 2011
New Revision: 1189272

URL: http://svn.apache.org/viewvc?rev=1189272&view=rev
Log:
[CXF-3879] Allow controlling the maximum size of incoming attachments.
Patch from Sam Meder applied.

Modified:
    cxf/trunk/api/src/main/java/org/apache/cxf/io/CachedOutputStream.java
    cxf/trunk/rt/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContextImpl.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/FormEncodingProvider.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/MultipartProvider.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/multipart/AttachmentUtils.java
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/MultipartServer.java

Modified: cxf/trunk/api/src/main/java/org/apache/cxf/io/CachedOutputStream.java
URL: http://svn.apache.org/viewvc/cxf/trunk/api/src/main/java/org/apache/cxf/io/CachedOutputStream.java?rev=1189272&r1=1189271&r2=1189272&view=diff
==============================================================================
--- cxf/trunk/api/src/main/java/org/apache/cxf/io/CachedOutputStream.java (original)
+++ cxf/trunk/api/src/main/java/org/apache/cxf/io/CachedOutputStream.java Wed Oct 26 15:41:28 2011
@@ -43,6 +43,7 @@ import org.apache.cxf.helpers.LoadingByt
 public class CachedOutputStream extends OutputStream {
     private static final File DEFAULT_TEMP_DIR;
     private static final int DEFAULT_THRESHOLD;
+    private static final long DEFAULT_MAX_SIZE;
     static {
         String s = SystemPropertyAction.getProperty("org.apache.cxf.io.CachedOutputStream.Threshold",
                                                    "-1");
@@ -63,12 +64,17 @@ public class CachedOutputStream extends 
         } else {
             DEFAULT_TEMP_DIR = null;
         }
+
+        s = System.getProperty("org.apache.cxf.io.CachedOutputStream.MaxSize",
+                               "-1");
+        DEFAULT_MAX_SIZE = Long.parseLong(s);
     }
 
     protected boolean outputLocked;
     protected OutputStream currentStream;
 
     private long threshold = DEFAULT_THRESHOLD;
+    private long maxSize = DEFAULT_MAX_SIZE;
 
     private int totalLength;
 
@@ -383,13 +389,20 @@ public class CachedOutputStream extends 
 
     }
 
+    private  void enforceLimits() throws IOException {
+        if (maxSize > 0 && totalLength > maxSize) {
+            throw new CacheSizeExceededException();
+        }
+        if (inmem && totalLength > threshold && currentStream instanceof ByteArrayOutputStream) {
+            createFileOutputStream();
+        }       
+    }
+
     public void write(byte[] b, int off, int len) throws IOException {
         if (!outputLocked) {
             onWrite();
             this.totalLength += len;
-            if (inmem && totalLength > threshold && currentStream instanceof ByteArrayOutputStream) {
-                createFileOutputStream();
-            }
+            enforceLimits();
             currentStream.write(b, off, len);
         }
     }
@@ -398,9 +411,7 @@ public class CachedOutputStream extends 
         if (!outputLocked) {
             onWrite();
             this.totalLength += b.length;
-            if (inmem && totalLength > threshold && currentStream instanceof ByteArrayOutputStream) {
-                createFileOutputStream();
-            }
+            enforceLimits();
             currentStream.write(b);
         }
     }
@@ -409,9 +420,7 @@ public class CachedOutputStream extends 
         if (!outputLocked) {
             onWrite();
             this.totalLength++;
-            if (inmem && totalLength > threshold && currentStream instanceof ByteArrayOutputStream) {
-                createFileOutputStream();
-            }
+            enforceLimits();
             currentStream.write(b);
         }
     }
@@ -498,4 +507,8 @@ public class CachedOutputStream extends 
     public void setThreshold(long threshold) {
         this.threshold = threshold;
     }
+
+    public void setMaxSize(long maxSize) {
+        this.maxSize = maxSize;
+    }
 }

Modified: cxf/trunk/rt/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java?rev=1189272&r1=1189271&r2=1189272&view=diff
==============================================================================
--- cxf/trunk/rt/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java (original)
+++ cxf/trunk/rt/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java Wed Oct 26 15:41:28 2011
@@ -48,6 +48,8 @@ public class AttachmentDeserializer {
 
     public static final String ATTACHMENT_MEMORY_THRESHOLD = "attachment-memory-threshold";
 
+    public static final String ATTACHMENT_MAX_SIZE = "attachment-max-size";
+
     public static final int THRESHOLD = 1024 * 100; //100K (byte unit)
 
     private static final Pattern CONTENT_TYPE_BOUNDARY_PATTERN = Pattern.compile("boundary=\"?([^\";]*)");
@@ -186,6 +188,15 @@ public class AttachmentDeserializer {
         } else {
             bos.setThreshold(THRESHOLD);
         }
+
+        Object maxSize = message.getContextualProperty(ATTACHMENT_MAX_SIZE);
+        if (maxSize != null) {
+            if (maxSize instanceof Long) {
+                bos.setMaxSize((Long) maxSize);
+            } else {
+                bos.setMaxSize(Long.valueOf((String)maxSize));
+            }
+        }
     }
 
     public AttachmentImpl readNext() throws IOException {

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContextImpl.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContextImpl.java?rev=1189272&r1=1189271&r2=1189272&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContextImpl.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContextImpl.java Wed Oct 26 15:41:28 2011
@@ -46,6 +46,7 @@ import org.apache.cxf.attachment.Attachm
 import org.apache.cxf.attachment.AttachmentUtil;
 import org.apache.cxf.endpoint.Endpoint;
 import org.apache.cxf.interceptor.AttachmentOutInterceptor;
+import org.apache.cxf.io.CacheSizeExceededException;
 import org.apache.cxf.jaxrs.ext.multipart.Attachment;
 import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
 import org.apache.cxf.jaxrs.impl.ProvidersImpl;
@@ -67,7 +68,11 @@ public class MessageContextImpl implemen
         String keyValue = key.toString();
         if (MultipartBody.INBOUND_MESSAGE_ATTACHMENTS.equals(keyValue)
             || (MultipartBody.INBOUND_MESSAGE_ATTACHMENTS + ".embedded").equals(keyValue)) {
-            return createAttachments(key.toString());
+            try {
+                return createAttachments(key.toString());
+            } catch (CacheSizeExceededException e) {
+                throw new WebApplicationException(413);
+            }
         }
         if (keyValue.equals("WRITE-" + Message.ATTACHMENTS)) {
             return m.getExchange().getOutMessage().get(Message.ATTACHMENTS);
@@ -216,6 +221,8 @@ public class MessageContextImpl implemen
                 m.getExchange().getInMessage().get(AttachmentDeserializer.ATTACHMENT_DIRECTORY));
             inMessage.put(AttachmentDeserializer.ATTACHMENT_MEMORY_THRESHOLD, 
                 m.getExchange().getInMessage().get(AttachmentDeserializer.ATTACHMENT_MEMORY_THRESHOLD));
+            inMessage.put(AttachmentDeserializer.ATTACHMENT_MAX_SIZE,
+                m.getExchange().getInMessage().get(AttachmentDeserializer.ATTACHMENT_MAX_SIZE));
             inMessage.setContent(InputStream.class, 
                 m.getExchange().getInMessage().get("org.apache.cxf.multipart.embedded.input"));
             inMessage.put(Message.CONTENT_TYPE, 

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/FormEncodingProvider.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/FormEncodingProvider.java?rev=1189272&r1=1189271&r2=1189272&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/FormEncodingProvider.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/FormEncodingProvider.java Wed Oct 26 15:41:28 2011
@@ -61,6 +61,7 @@ public class FormEncodingProvider implem
     @Context private MessageContext mc;
     private String attachmentDir;
     private String attachmentThreshold;
+    private String attachmentMaxSize;
 
     private boolean expectEncoded;
     
@@ -76,6 +77,10 @@ public class FormEncodingProvider implem
         attachmentThreshold = threshold;
     }
     
+    public void setAttachmentMaxSize(String maxSize) {
+        attachmentMaxSize = maxSize;
+    }
+
     public void setValidator(FormValidator formValidator) {
         validator = formValidator;
     }
@@ -151,7 +156,7 @@ public class FormEncodingProvider implem
                                InputStream is, MediaType mt, boolean decode) {
         if (mt.isCompatible(MediaType.MULTIPART_FORM_DATA_TYPE)) {
             MultipartBody body = 
-                AttachmentUtils.getMultipartBody(mc, attachmentDir, attachmentThreshold);
+                AttachmentUtils.getMultipartBody(mc, attachmentDir, attachmentThreshold, attachmentMaxSize);
             FormUtils.populateMapFromMultipart(params, body, decode);
         } else {
             String enc = HttpUtils.getEncoding(mt, "UTF-8");

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/MultipartProvider.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/MultipartProvider.java?rev=1189272&r1=1189271&r2=1189272&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/MultipartProvider.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/MultipartProvider.java Wed Oct 26 15:41:28 2011
@@ -82,6 +82,7 @@ public class MultipartProvider extends A
     private MessageContext mc;
     private String attachmentDir;
     private String attachmentThreshold;
+    private String attachmentMaxSize;
 
     public void setMessageContext(MessageContext context) {
         this.mc = context;
@@ -95,6 +96,10 @@ public class MultipartProvider extends A
         attachmentThreshold = threshold;
     }
     
+    public void setAttachmentMaxSize(String maxSize) {
+        attachmentMaxSize = maxSize;
+    }
+
     public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, 
                               MediaType mt) {
         return isSupported(type, genericType, annotations, mt);
@@ -131,8 +136,8 @@ public class MultipartProvider extends A
                            MultivaluedMap<String, String> headers, InputStream is) 
         throws IOException, WebApplicationException {
         checkContentLength();
-        List<Attachment> infos = 
-            AttachmentUtils.getAttachments(mc, attachmentDir, attachmentThreshold);
+        List<Attachment> infos = AttachmentUtils.getAttachments(
+                mc, attachmentDir, attachmentThreshold, attachmentMaxSize);
         
         if (Collection.class.isAssignableFrom(c) 
             && AnnotationUtils.getAnnotation(anns, Multipart.class) == null) {

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/multipart/AttachmentUtils.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/multipart/AttachmentUtils.java?rev=1189272&r1=1189271&r2=1189272&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/multipart/AttachmentUtils.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/multipart/AttachmentUtils.java Wed Oct 26 15:41:28 2011
@@ -72,13 +72,16 @@ public final class AttachmentUtils {
     }
     
     public static MultipartBody getMultipartBody(MessageContext mc,
-        String attachmentDir, String attachmentThreshold) {
+        String attachmentDir, String attachmentThreshold, String attachmentMaxSize) {
         if (attachmentDir != null) {
             mc.put(AttachmentDeserializer.ATTACHMENT_DIRECTORY, attachmentDir);
         }
         if (attachmentThreshold != null) {
             mc.put(AttachmentDeserializer.ATTACHMENT_MEMORY_THRESHOLD, attachmentThreshold);
         }
+        if (attachmentMaxSize != null) {
+            mc.put(AttachmentDeserializer.ATTACHMENT_MAX_SIZE, attachmentMaxSize);
+        }
         
         boolean embeddedAttachment = mc.get("org.apache.cxf.multipart.embedded") != null;
         String propertyName = embeddedAttachment ? MultipartBody.INBOUND_MESSAGE_ATTACHMENTS + ".embedded"
@@ -88,8 +91,11 @@ public final class AttachmentUtils {
     }
     
     public static List<Attachment> getAttachments(MessageContext mc, 
-        String attachmentDir, String attachmentThreshold) {
-        return getMultipartBody(mc, attachmentDir, attachmentThreshold).getAllAttachments();
+        String attachmentDir, String attachmentThreshold, String attachmentMaxSize) {
+        return getMultipartBody(mc, 
+                                attachmentDir, 
+                                attachmentThreshold, 
+                                attachmentMaxSize).getAllAttachments();
     }
     
     public static Attachment getMultipart(Class<Object> c, Annotation[] anns, 

Modified: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java?rev=1189272&r1=1189271&r2=1189272&view=diff
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java (original)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java Wed Oct 26 15:41:28 2011
@@ -43,6 +43,10 @@ import org.apache.commons.httpclient.Htt
 import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
 import org.apache.commons.httpclient.methods.PostMethod;
 import org.apache.commons.httpclient.methods.RequestEntity;
+import org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource;
+import org.apache.commons.httpclient.methods.multipart.FilePart;
+import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
+import org.apache.commons.httpclient.methods.multipart.Part;
 import org.apache.cxf.helpers.IOUtils;
 import org.apache.cxf.io.CachedOutputStream;
 import org.apache.cxf.jaxrs.client.JAXRSClientFactory;
@@ -526,6 +530,28 @@ public class JAXRSMultipartTest extends 
         }
     }
     
+    @Test
+    public void testMultipartRequestTooLarge() throws Exception {
+        PostMethod post = new PostMethod("http://localhost:" + PORT + "/bookstore/books/image");
+        String ct = "multipart/mixed";
+        post.setRequestHeader("Content-Type", ct);
+        Part[] parts = new Part[1];
+        parts[0] = new FilePart("image",
+                new ByteArrayPartSource("testfile.png", new byte[1024 * 1024 * 15]),
+                "image/png", null);
+        post.setRequestEntity(new MultipartRequestEntity(parts, post.getParams()));
+
+        HttpClient httpclient = new HttpClient();
+
+        try {
+            int result = httpclient.executeMethod(post);
+            assertEquals(413, result);
+        } finally {
+            // Release current connection to the connection pool once you are done
+            post.releaseConnection();
+        }
+    }
+
     private void doAddBook(String address, String resourceName, int status) throws Exception {
         doAddBook("multipart/related", address, resourceName, status);
     }

Modified: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/MultipartServer.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/MultipartServer.java?rev=1189272&r1=1189271&r2=1189272&view=diff
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/MultipartServer.java (original)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/MultipartServer.java Wed Oct 26 15:41:28 2011
@@ -19,15 +19,22 @@
 
 package org.apache.cxf.systest.jaxrs;
 
+import java.util.Collections;
+
+import org.apache.cxf.attachment.AttachmentDeserializer;
 import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
 import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
 import org.apache.cxf.testutil.common.AbstractBusTestServerBase;
     
+
 public class MultipartServer extends AbstractBusTestServerBase {
     public static final String PORT = allocatePort(MultipartServer.class);
     protected void run() {
         JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
         sf.setResourceClasses(MultipartStore.class);
+        sf.setProperties(Collections.<String, Object>singletonMap(
+                AttachmentDeserializer.ATTACHMENT_MAX_SIZE,
+                String.valueOf(1024 * 1024 * 10)));
         //default lifecycle is per-request, change it to singleton
         sf.setResourceProvider(MultipartStore.class,
                                new SingletonResourceProvider(new MultipartStore()));