chemistry-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From f...@apache.org
Subject svn commit: r1348063 - in /chemistry/opencmis/trunk: chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/browser/ chemistry-opencmis-commons/chemistry-opencmis-commons-impl/src/ma...
Date Fri, 08 Jun 2012 13:29:15 GMT
Author: fmui
Date: Fri Jun  8 13:29:14 2012
New Revision: 1348063

URL: http://svn.apache.org/viewvc?rev=1348063&view=rev
Log:
CMIS-532: Brower Binding: fix issue with special characters in filename

Added:
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/MultipartParser.java   (with props)
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/MultipartParserTest.java   (with props)
Modified:
    chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/browser/FormDataWriter.java
    chemistry/opencmis/trunk/chemistry-opencmis-commons/chemistry-opencmis-commons-impl/src/main/java/org/apache/chemistry/opencmis/commons/impl/MimeHelper.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/pom.xml
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/AtomEntryParser.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/POSTHttpServletRequestWrapper.java

Modified: chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/browser/FormDataWriter.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/browser/FormDataWriter.java?rev=1348063&r1=1348062&r2=1348063&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/browser/FormDataWriter.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/browser/FormDataWriter.java Fri Jun  8 13:29:14 2012
@@ -176,6 +176,8 @@ public class FormDataWriter {
                 out.write((param.getKey() + "=" + URLEncoder.encode(param.getValue(), "UTF-8")).getBytes("UTF-8"));
             }
         } else {
+            writeLine(out);
+            
             // parameters
             for (Map.Entry<String, String> param : parameters.entrySet()) {
                 writeLine(out, "--" + boundary);

Modified: chemistry/opencmis/trunk/chemistry-opencmis-commons/chemistry-opencmis-commons-impl/src/main/java/org/apache/chemistry/opencmis/commons/impl/MimeHelper.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-commons/chemistry-opencmis-commons-impl/src/main/java/org/apache/chemistry/opencmis/commons/impl/MimeHelper.java?rev=1348063&r1=1348062&r2=1348063&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-commons/chemistry-opencmis-commons-impl/src/main/java/org/apache/chemistry/opencmis/commons/impl/MimeHelper.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-commons/chemistry-opencmis-commons-impl/src/main/java/org/apache/chemistry/opencmis/commons/impl/MimeHelper.java Fri Jun  8 13:29:14 2012
@@ -35,13 +35,15 @@ public class MimeHelper {
     public static final String CONTENT_DISPOSITION = "Content-Disposition";
 
     public static final String DISPOSITION_ATTACHMENT = "attachment";
-    
+
     public static final String DISPOSITION_INLINE = "inline";
 
-    public static final String DISPOSITION_FORM_DATA_CONTENT = "form-data; name=\"content\"";
-    
     public static final String DISPOSITION_FILENAME = "filename";
 
+    public static final String DISPOSITION_NAME = "name";
+
+    public static final String DISPOSITION_FORM_DATA_CONTENT = "form-data; " + DISPOSITION_NAME + "=\"content\"";
+
     // RFC 2045
     private static final String MIME_SPECIALS = "()<>@,;:\\\"/[]?=" + "\t ";
 
@@ -70,9 +72,11 @@ public class MimeHelper {
      * <p>
      * See <a href="http://tools.ietf.org/html/rfc2231">RFC 2231</a> for
      * details.
-     *
-     * @param value the value to encode
-     * @param buf the buffer to fill
+     * 
+     * @param value
+     *            the value to encode
+     * @param buf
+     *            the buffer to fill
      * @return {@code true} if an encoding was needed, or {@code false} if no
      *         encoding was actually needed
      */
@@ -108,11 +112,12 @@ public class MimeHelper {
      * <p>
      * See <a href="http://tools.ietf.org/html/rfc2231">RFC 2231</a> for
      * details.
-     *
-     * @param value the string to encode
+     * 
+     * @param value
+     *            the string to encode
      * @return the encoded string
      */
-     protected static String encodeRFC2231(String key, String value) {
+    protected static String encodeRFC2231(String key, String value) {
         StringBuilder buf = new StringBuilder();
         boolean encoded = encodeRFC2231value(value, buf);
         if (encoded) {
@@ -128,13 +133,14 @@ public class MimeHelper {
      * <p>
      * See <a href="http://tools.ietf.org/html/rfc2231">RFC 2231</a> for
      * details.
-     *
-     * @param disposition the disposition
-     * @param filename the file name
+     * 
+     * @param disposition
+     *            the disposition
+     * @param filename
+     *            the file name
      * @return the encoded header value
      */
-    public static String encodeContentDisposition(String disposition,
-            String filename) {
+    public static String encodeContentDisposition(String disposition, String filename) {
         if (disposition == null) {
             disposition = DISPOSITION_ATTACHMENT;
         }
@@ -147,8 +153,9 @@ public class MimeHelper {
      * <p>
      * See <a href="http://tools.ietf.org/html/rfc2231">RFC 2231</a> for
      * details.
-     *
-     * @param value the header value to decode
+     * 
+     * @param value
+     *            the header value to decode
      * @return the filename
      */
     public static String decodeContentDispositionFilename(String value) {
@@ -165,14 +172,15 @@ public class MimeHelper {
      * <p>
      * See <a href="http://tools.ietf.org/html/rfc2231">RFC 2231</a> for
      * details.
-     *
-     * @param value the header value to decode
-     * @param params the map of parameters to fill
+     * 
+     * @param value
+     *            the header value to decode
+     * @param params
+     *            the map of parameters to fill
      * @return the disposition
-     *
+     * 
      */
-    public static String decodeContentDisposition(String value,
-            Map<String, String> params) {
+    public static String decodeContentDisposition(String value, Map<String, String> params) {
         try {
             HeaderTokenizer tokenizer = new HeaderTokenizer(value);
             // get the first token, which must be an ATOM
@@ -194,6 +202,86 @@ public class MimeHelper {
         }
     }
 
+    /**
+     * Gets charset from a content type header.
+     * 
+     * @param value
+     *            the header value to decode
+     * @return the charset or <code>null</code> if no valid boundary available
+     */
+    public static String getCharsetFromContentType(String value) {
+        try {
+            HeaderTokenizer tokenizer = new HeaderTokenizer(value, ";", true);
+
+            // get the first token, which must be an ATOM
+            Token token = tokenizer.next();
+            if (token.getType() != Token.ATOM) {
+                return null;
+            }
+
+            // the remainder is the parameters
+            String remainder = tokenizer.getRemainder();
+            Map<String, String> params;
+            if (remainder != null) {
+                params = new HashMap<String, String>();
+                getParameters(remainder, params);
+
+                return params.get("charset");
+            }
+        } catch (ParseException e) {
+            return null;
+        }
+
+        return null;
+    }
+
+    /**
+     * Gets the boundary from a <code>multipart/formdata</code> content type
+     * header.
+     * 
+     * @param value
+     *            the header value to decode
+     * @return the boundary as a byte array or <code>null</code> if no valid
+     *         boundary available
+     */
+    public static byte[] getBoundaryFromMultiPart(String value) {
+        try {
+            HeaderTokenizer tokenizer = new HeaderTokenizer(value, ";", true);
+
+            // get the first token, which must be an ATOM
+            Token token = tokenizer.next();
+            if (token.getType() != Token.ATOM) {
+                return null;
+            }
+
+            // check content type
+            String multipartContentType = token.getValue();
+            if (multipartContentType == null || !multipartContentType.equalsIgnoreCase("multipart/form-data")) {
+                return null;
+            }
+
+            // the remainder is the parameters
+            String remainder = tokenizer.getRemainder();
+            if (remainder != null) {
+                Map<String, String> params = new HashMap<String, String>();
+                getParameters(remainder, params);
+
+                String boundaryStr = params.get("boundary");
+                if (boundaryStr != null && boundaryStr.length() > 0) {
+                    try {
+                        return boundaryStr.getBytes("ISO-8859-1");
+                    } catch (UnsupportedEncodingException e) {
+                        return boundaryStr.getBytes();
+                    }
+                }
+            }
+        } catch (ParseException e) {
+            return null;
+        }
+
+        return null;
+    }
+
     protected static class ParseException extends Exception {
         private static final long serialVersionUID = 1L;
 
@@ -256,8 +344,7 @@ public class MimeHelper {
             this(header, MIME_SPECIALS, true);
         }
 
-        protected HeaderTokenizer(String header, String delimiters,
-                boolean skipComments) {
+        protected HeaderTokenizer(String header, String delimiters, boolean skipComments) {
             this.header = header;
             this.delimiters = delimiters;
             this.skipComments = skipComments;
@@ -273,7 +360,7 @@ public class MimeHelper {
 
         /**
          * Read an ATOM token from the parsed header.
-         *
+         * 
          * @return A token containing the value of the atom token.
          */
         private Token readAtomicToken() {
@@ -282,8 +369,7 @@ public class MimeHelper {
             while (++pos < header.length()) {
                 // break on the first non-atom character.
                 char ch = header.charAt(pos);
-                if (delimiters.indexOf(header.charAt(pos)) != -1 || ch < 32
-                        || ch >= 127) {
+                if (delimiters.indexOf(header.charAt(pos)) != -1 || ch < 32 || ch >= 127) {
                     break;
                 }
             }
@@ -292,7 +378,7 @@ public class MimeHelper {
 
         /**
          * Read the next token from the header.
-         *
+         * 
          * @return The next token from the header. White space is skipped, and
          *         comment tokens are also skipped if indicated.
          */
@@ -331,13 +417,14 @@ public class MimeHelper {
         /**
          * Extract a substring from the header string and apply any
          * escaping/folding rules to the string.
-         *
-         * @param start The starting offset in the header.
-         * @param end The header end offset + 1.
+         * 
+         * @param start
+         *            The starting offset in the header.
+         * @param end
+         *            The header end offset + 1.
          * @return The processed string value.
          */
-        private String getEscapedValue(int start, int end)
-                throws ParseException {
+        private String getEscapedValue(int start, int end) throws ParseException {
             StringBuffer value = new StringBuffer();
             for (int i = start; i < end; i++) {
                 char ch = header.charAt(i);
@@ -369,7 +456,7 @@ public class MimeHelper {
         /**
          * Read a comment from the header, applying nesting and escape rules to
          * the content.
-         *
+         * 
          * @return A comment token with the token value.
          */
         private Token readComment() throws ParseException {
@@ -410,7 +497,7 @@ public class MimeHelper {
         /**
          * Parse out a quoted string from the header, applying escaping rules to
          * the value.
-         *
+         * 
          * @return The QUOTEDSTRING token with the value.
          * @exception ParseException
          */
@@ -445,8 +532,7 @@ public class MimeHelper {
          */
         private void eatWhiteSpace() {
             // skip to end of whitespace
-            while (++pos < header.length()
-                    && WHITE.indexOf(header.charAt(pos)) != -1)
+            while (++pos < header.length() && WHITE.indexOf(header.charAt(pos)) != -1)
                 ;
         }
     }
@@ -454,8 +540,7 @@ public class MimeHelper {
     /*
      * Tweaked from geronimo-javamail_1.4_spec-1.7.1. ParameterList
      */
-    protected static Map<String, String> getParameters(String list,
-            Map<String, String> params) throws ParseException {
+    protected static Map<String, String> getParameters(String list, Map<String, String> params) throws ParseException {
         HeaderTokenizer tokenizer = new HeaderTokenizer(list);
         while (true) {
             Token token = tokenizer.next();
@@ -476,8 +561,7 @@ public class MimeHelper {
                 }
 
                 if (token.getType() != Token.ATOM) {
-                    throw new ParseException("Invalid parameter name: "
-                            + token.getValue());
+                    throw new ParseException("Invalid parameter name: " + token.getValue());
                 }
 
                 // get the parameter name as a lower case version for better
@@ -494,10 +578,8 @@ public class MimeHelper {
                 // now the value, which may be an atom or a literal
                 token = tokenizer.next();
 
-                if (token.getType() != Token.ATOM
-                        && token.getType() != Token.QUOTEDSTRING) {
-                    throw new ParseException("Invalid parameter value: "
-                            + token.getValue());
+                if (token.getType() != Token.ATOM && token.getType() != Token.QUOTEDSTRING) {
+                    throw new ParseException("Invalid parameter value: " + token.getValue());
                 }
 
                 String value = token.getValue();

Modified: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/pom.xml
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/pom.xml?rev=1348063&r1=1348062&r2=1348063&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/pom.xml (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/pom.xml Fri Jun  8 13:29:14 2012
@@ -84,11 +84,6 @@
             <version>${project.version}</version>
         </dependency>
         <dependency>
-            <groupId>commons-fileupload</groupId>
-            <artifactId>commons-fileupload</artifactId>
-            <version>1.2.1</version>
-        </dependency>
-        <dependency>
             <groupId>commons-io</groupId>
             <artifactId>commons-io</artifactId>
             <version>2.0.1</version>

Modified: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/AtomEntryParser.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/AtomEntryParser.java?rev=1348063&r1=1348062&r2=1348063&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/AtomEntryParser.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/AtomEntryParser.java Fri Jun  8 13:29:14 2012
@@ -181,6 +181,7 @@ public class AtomEntryParser {
 
         XMLInputFactory factory = XMLInputFactory.newInstance();
         factory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.FALSE);
+        factory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
         XMLStreamReader parser = factory.createXMLStreamReader(stream);
 
         while (true) {

Added: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/MultipartParser.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/MultipartParser.java?rev=1348063&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/MultipartParser.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/MultipartParser.java Fri Jun  8 13:29:14 2012
@@ -0,0 +1,511 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.chemistry.opencmis.server.impl.browser;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
+import org.apache.chemistry.opencmis.commons.impl.Constants;
+import org.apache.chemistry.opencmis.commons.impl.MimeHelper;
+import org.apache.chemistry.opencmis.server.shared.ThresholdOutputStream;
+
+/**
+ * Simple multi-part parser, following all necessary standards for the CMIS
+ * browser binding.
+ */
+public class MultipartParser {
+
+    public static final String MULTIPART = "multipart/";
+
+    private static final int MAX_FIELD_BYTES = 10 * 1024 * 1024;
+
+    private static final int BUFFER_SIZE = 64 * 1024;
+
+    private static final byte CR = 0x0D;
+    private static final byte LF = 0x0A;
+    private static final byte DASH = 0x2D;
+    private static final byte[] BOUNDARY_PREFIX = { CR, LF, DASH, DASH };
+
+    private final HttpServletRequest request;
+    private final File tempDir;
+    private final int memoryThreshold;
+    private final long maxContentSize;
+    private final InputStream requestStream;
+
+    private byte[] boundary;
+    private int[] badCharacters;
+    private int[] goodSuffixes;
+
+    private byte[] buffer;
+    private byte[] buffer2;
+    private int bufferPosition;
+    private int bufferCount;
+    private boolean eof;
+
+    private int fieldBytes;
+    private boolean hasContent;
+
+    private Map<String, String> headers;
+
+    private boolean isContent;
+    private String name;
+    private String filename;
+    private String contentType;
+    private BigInteger contentSize;
+    private InputStream contentStream;
+    private String value;
+
+    public MultipartParser(HttpServletRequest request, File tempDir, int memoryThreshold, long maxContentSize)
+            throws IOException {
+        this.request = request;
+        this.tempDir = tempDir;
+        this.memoryThreshold = memoryThreshold;
+        this.maxContentSize = maxContentSize;
+
+        this.requestStream = request.getInputStream();
+
+        extractBoundary();
+
+        buffer = new byte[BUFFER_SIZE + boundary.length];
+        buffer2 = new byte[buffer.length];
+        bufferPosition = 0;
+        bufferCount = 0;
+        eof = false;
+
+        hasContent = false;
+        fieldBytes = 0;
+
+        skipPreamble();
+    }
+
+    private void extractBoundary() {
+        String contentType = request.getContentType();
+
+        // parse content type and extract boundary
+        byte[] extractedBoundary = MimeHelper.getBoundaryFromMultiPart(contentType);
+        if (extractedBoundary == null) {
+            throw new CmisInvalidArgumentException("Invalid multipart request!");
+        }
+
+        boundary = new byte[BOUNDARY_PREFIX.length + extractedBoundary.length];
+        System.arraycopy(BOUNDARY_PREFIX, 0, boundary, 0, BOUNDARY_PREFIX.length);
+        System.arraycopy(extractedBoundary, 0, boundary, BOUNDARY_PREFIX.length, extractedBoundary.length);
+
+        // prepare boundary search
+
+        int m = boundary.length;
+
+        badCharacters = new int[256];
+        Arrays.fill(badCharacters, -1);
+
+        for (int j = 0; j < m; j++) {
+            badCharacters[boundary[j] & 0xff] = j;
+        }
+
+        int[] f = new int[m + 1];
+        goodSuffixes = new int[m + 1];
+        int i = m;
+        int j = m + 1;
+        f[i] = j;
+        while (i > 0) {
+            while (j <= m && boundary[i - 1] != boundary[j - 1]) {
+                if (goodSuffixes[j] == 0) {
+                    goodSuffixes[j] = j - i;
+                }
+                j = f[j];
+            }
+            i--;
+            j--;
+            f[i] = j;
+        }
+
+        j = f[0];
+        for (i = 0; i <= m; i++) {
+            if (goodSuffixes[i] == 0) {
+                goodSuffixes[i] = j;
+            }
+
+            if (i == j) {
+                j = f[j];
+            }
+        }
+    }
+
+    private int findBoundary() {
+        if (bufferCount < boundary.length) {
+            if (eof) {
+                throw new CmisInvalidArgumentException("Unexpected end of stream!");
+            } else {
+                return -1;
+            }
+        }
+
+        int m = boundary.length;
+
+        int i = 0;
+        while (i <= bufferCount - m) {
+            int j = m - 1;
+            while (j >= 0 && boundary[j] == buffer[i + j]) {
+                j--;
+            }
+
+            if (j < 0) {
+                return i;
+            } else {
+                i += Math.max(goodSuffixes[j + 1], j - badCharacters[buffer[i + j] & 0xff]);
+            }
+        }
+
+        return -1;
+    }
+
+    private void readBuffer() throws IOException {
+        if (bufferPosition < bufferCount) {
+            System.arraycopy(buffer, bufferPosition, buffer2, 0, bufferCount - bufferPosition);
+            bufferCount = bufferCount - bufferPosition;
+
+            byte[] tmpBuffer = buffer2;
+            buffer2 = buffer;
+            buffer = tmpBuffer;
+        } else {
+            bufferCount = 0;
+        }
+
+        bufferPosition = 0;
+
+        if (eof) {
+            return;
+        }
+
+        while (true) {
+            int r = requestStream.read(buffer, bufferCount, buffer.length - bufferCount);
+            if (r == -1) {
+                eof = true;
+                break;
+            }
+
+            bufferCount += r;
+            if (buffer.length == bufferCount) {
+                break;
+            }
+        }
+    }
+
+    private int nextByte() throws IOException {
+        if (bufferCount == 0) {
+            if (eof) {
+                return -1;
+            } else {
+                readBuffer();
+                return nextByte();
+            }
+        }
+
+        if (bufferCount > bufferPosition) {
+            return buffer[bufferPosition++];
+        }
+
+        readBuffer();
+        return nextByte();
+    }
+
+    private String readLine() throws IOException {
+        StringBuilder sb = new StringBuilder();
+
+        int r;
+        while ((r = nextByte()) > -1) {
+            if (r == CR) {
+                if (nextByte() != LF) {
+                    throw new CmisInvalidArgumentException("Invalid multipart request!");
+                }
+                break;
+            }
+
+            sb.append((char) r);
+        }
+
+        return sb.toString();
+    }
+
+    private void readHeaders() throws IOException {
+        int b = nextByte();
+        if (b == -1) {
+            throw new CmisInvalidArgumentException("Unexpected end of stream!");
+        }
+
+        if (b == DASH) {
+            b = nextByte();
+            if (b == DASH) {
+                // expected end of stream
+                headers = null;
+                return;
+            }
+        } else if (b == CR) {
+            b = nextByte();
+            if (b == LF) {
+                parseHeaders();
+                return;
+            }
+        }
+
+        throw new CmisInvalidArgumentException("Invalid multipart request!");
+    }
+
+    private void parseHeaders() throws IOException {
+        headers = new HashMap<String, String>();
+
+        String line;
+        while ((line = readLine()).length() > 0) {
+            int x = line.indexOf(':');
+            if (x > 0) {
+                headers.put(line.substring(0, x).toLowerCase().trim(), line.substring(x + 1).trim());
+            }
+        }
+    }
+
+    public void readBodyAsString(String charset) throws IOException {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        while (true) {
+            readBuffer();
+
+            int boundaryPosition = findBoundary();
+
+            if (boundaryPosition > -1) {
+                bos.write(buffer, bufferPosition, boundaryPosition - bufferPosition);
+                bufferPosition = boundaryPosition + boundary.length;
+                break;
+            } else {
+                int len = Math.min(BUFFER_SIZE, bufferCount) - bufferPosition;
+                bos.write(buffer, bufferPosition, len);
+                bufferPosition = bufferPosition + len;
+            }
+
+            fieldBytes += bos.size();
+            if (fieldBytes > MAX_FIELD_BYTES) {
+                throw new CmisInvalidArgumentException("Limit exceeded!");
+            }
+        }
+
+        try {
+            value = bos.toString(charset);
+        } catch (UnsupportedEncodingException uee) {
+            throw new CmisInvalidArgumentException("Unknown endcoding!");
+        }
+    }
+
+    public void readBodyAsStream() throws Exception {
+        ThresholdOutputStream stream = new ThresholdOutputStream(tempDir, memoryThreshold, maxContentSize);
+
+        try {
+            while (true) {
+                readBuffer();
+
+                int boundaryPosition = findBoundary();
+
+                if (boundaryPosition > -1) {
+                    stream.write(buffer, bufferPosition, boundaryPosition - bufferPosition);
+                    bufferPosition = boundaryPosition + boundary.length;
+                    break;
+                } else {
+                    int len = Math.min(BUFFER_SIZE, bufferCount) - bufferPosition;
+                    stream.write(buffer, bufferPosition, len);
+                    bufferPosition = bufferPosition + len;
+                }
+            }
+
+            stream.close();
+
+            contentSize = BigInteger.valueOf(stream.getSize());
+            contentStream = stream.getInputStream();
+        } catch (Exception e) {
+            // if something went wrong, make sure the temp file will
+            // be deleted
+            stream.destroy();
+            throw e;
+        }
+    }
+
+    private void readBody() throws Exception {
+        String contentDisposition = headers.get("content-disposition");
+
+        if (contentDisposition == null) {
+            throw new CmisInvalidArgumentException("Invalid multipart request!");
+        }
+
+        Map<String, String> params = new HashMap<String, String>();
+        MimeHelper.decodeContentDisposition(contentDisposition, params);
+
+        name = params.get(MimeHelper.DISPOSITION_NAME);
+        filename = params.get(MimeHelper.DISPOSITION_FILENAME);
+        isContent = (filename != null);
+        contentType = headers.get("content-type");
+
+        if (isContent) {
+            if (hasContent) {
+                throw new CmisInvalidArgumentException("Only one content expected!");
+            }
+
+            hasContent = true;
+
+            if (contentType == null) {
+                contentType = Constants.MEDIATYPE_OCTETSTREAM;
+            }
+
+            readBodyAsStream();
+        } else {
+            contentSize = BigInteger.ZERO;
+
+            String charset = null;
+
+            if (contentType != null) {
+                charset = MimeHelper.getCharsetFromContentType(contentType);
+            }
+
+            if (charset == null) {
+                charset = "ISO-8859-1";
+            }
+
+            readBodyAsString(charset);
+        }
+    }
+
+    private void skipPreamble() throws IOException {
+        readBuffer();
+
+        if (bufferCount < boundary.length - 2) {
+            throw new CmisInvalidArgumentException("Invalid multipart request!");
+        }
+
+        for (int i = 2; i < boundary.length; i++) {
+            if (boundary[i] != buffer[i - 2]) {
+                break;
+            }
+
+            if (i == boundary.length - 1) {
+                bufferPosition = boundary.length - 2;
+                readBuffer();
+                return;
+            }
+        }
+
+        while (true) {
+            int boundaryPosition = findBoundary();
+
+            if (boundaryPosition > -1) {
+                bufferPosition = boundaryPosition + boundary.length;
+                readBuffer();
+                break;
+            }
+
+            bufferPosition = BUFFER_SIZE + 1;
+            readBuffer();
+        }
+    }
+
+    private void skipEpilogue() {
+        try {
+            // read to the end of stream
+            byte[] tmpBuf = new byte[4096];
+            while (requestStream.read(tmpBuf) > -1) {
+            }
+        } catch (IOException e) {
+            // ignore
+        }
+    }
+
+    public boolean readNext() throws Exception {
+        try {
+            readHeaders();
+
+            // no headers -> end of request
+            if (headers == null) {
+                skipEpilogue();
+                return false;
+            }
+
+            readBody();
+
+            return true;
+        } catch (Exception e) {
+            if (contentStream != null) {
+                try {
+                    contentStream.close();
+                } catch (Exception e2) {
+                    // ignore
+                }
+            }
+
+            skipEpilogue();
+
+            throw e;
+        }
+    }
+
+    public boolean isContent() {
+        return isContent;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public String getFilename() {
+        return filename;
+    }
+
+    public String getContentType() {
+        return contentType;
+    }
+
+    public BigInteger getSize() {
+        return contentSize;
+    }
+
+    public InputStream getStream() {
+        return contentStream;
+    }
+
+    /**
+     * Returns if the request is a multi-part request
+     */
+    public static final boolean isMultipartContent(HttpServletRequest request) {
+        String contentType = request.getContentType();
+
+        if (contentType != null && contentType.toLowerCase().startsWith(MULTIPART)) {
+            return true;
+        }
+
+        return false;
+    }
+}

Propchange: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/MultipartParser.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/POSTHttpServletRequestWrapper.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/POSTHttpServletRequestWrapper.java?rev=1348063&r1=1348062&r2=1348063&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/POSTHttpServletRequestWrapper.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/POSTHttpServletRequestWrapper.java Fri Jun  8 13:29:14 2012
@@ -18,7 +18,6 @@
  */
 package org.apache.chemistry.opencmis.server.impl.browser;
 
-import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -34,10 +33,6 @@ import javax.servlet.http.HttpServletReq
 
 import org.apache.chemistry.opencmis.commons.impl.Constants;
 import org.apache.chemistry.opencmis.server.shared.HttpUtils;
-import org.apache.chemistry.opencmis.server.shared.ThresholdOutputStream;
-import org.apache.commons.fileupload.FileItemIterator;
-import org.apache.commons.fileupload.FileItemStream;
-import org.apache.commons.fileupload.servlet.ServletFileUpload;
 
 public class POSTHttpServletRequestWrapper extends HttpServletRequestWrapper {
     private final boolean isMultipart;
@@ -47,8 +42,8 @@ public class POSTHttpServletRequestWrapp
     private BigInteger size;
     private InputStream stream;
 
-    public POSTHttpServletRequestWrapper(HttpServletRequest request, File tempDir, int memoryThreshold, long maxContentSize)
-            throws Exception {
+    public POSTHttpServletRequestWrapper(HttpServletRequest request, File tempDir, int memoryThreshold,
+            long maxContentSize) throws Exception {
         super(request);
 
         parameters = new HashMap<String, String[]>();
@@ -57,67 +52,19 @@ public class POSTHttpServletRequestWrapp
         parseFormData(request.getQueryString());
 
         // check multipart
-        isMultipart = ServletFileUpload.isMultipartContent(request);
+        isMultipart = MultipartParser.isMultipartContent(request);
 
         if (isMultipart) {
-            ServletFileUpload upload = new ServletFileUpload();
-            FileItemIterator iter = upload.getItemIterator(request);
+            MultipartParser parser = new MultipartParser(request, tempDir, memoryThreshold, maxContentSize);
 
-            while (iter.hasNext()) {
-                FileItemStream item = iter.next();
-                String name = item.getFieldName();
-                InputStream itemStream = new BufferedInputStream(item.openStream());
-
-                if (item.isFormField()) {
-                    InputStreamReader reader = new InputStreamReader(itemStream, "UTF-8");
-
-                    try {
-                        StringBuilder sb = new StringBuilder();
-
-                        char[] buffer = new char[64 * 1024];
-                        int b = 0;
-                        while ((b = reader.read(buffer)) > -1) {
-                            sb.append(buffer, 0, b);
-                        }
-
-                        addParameter(name, sb.toString());
-                    } finally {
-                        try {
-                            reader.close();
-                        } catch (Exception e) {
-                            // ignore
-                        }
-                    }
+            while (parser.readNext()) {
+                if (parser.isContent()) {
+                    filename = parser.getFilename();
+                    contentType = parser.getContentType();
+                    size = parser.getSize();
+                    stream = parser.getStream();
                 } else {
-                    filename = item.getName();
-                    contentType = (item.getContentType() == null ? Constants.MEDIATYPE_OCTETSTREAM : item
-                            .getContentType());
-
-                    ThresholdOutputStream os = new ThresholdOutputStream(tempDir, memoryThreshold, maxContentSize);
-
-                    try {
-                        byte[] buffer = new byte[64 * 1024];
-                        int b = 0;
-                        while ((b = itemStream.read(buffer)) > -1) {
-                            os.write(buffer, 0, b);
-                        }
-
-                        os.close();
-
-                        size = BigInteger.valueOf(os.getSize());
-                        stream = os.getInputStream();
-                    } catch (Exception e) {
-                        // if something went wrong, make sure the temp file will
-                        // be deleted
-                        os.destroy();
-                        throw e;
-                    } finally {
-                        try {
-                            itemStream.close();
-                        } catch (Exception e) {
-                            // ignore
-                        }
-                    }
+                    addParameter(parser.getName(), parser.getValue());
                 }
             }
 

Added: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/MultipartParserTest.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/MultipartParserTest.java?rev=1348063&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/MultipartParserTest.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/MultipartParserTest.java Fri Jun  8 13:29:14 2012
@@ -0,0 +1,338 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.chemistry.opencmis.server.impl;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
+import org.apache.chemistry.opencmis.server.impl.browser.MultipartParser;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+/**
+ * Tests the multipart parser.
+ */
+public class MultipartParserTest {
+
+    private static final int THRESHOLD = 4 * 1024 * 1024;
+    private static final int MAX_SIZE = -1;
+
+    @Test
+    public void testMultipartParser() throws Exception {
+        String boundary = "---- next ----";
+        byte[] content = "This is content!".getBytes();
+        byte[] formdata = ("\r\n--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n"
+                + "\r\n" + "value1\r\n" + "--" + boundary + "\r\n"
+                + "content-disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "value2\r\n" + "--" + boundary
+                + "\r\n" + "content-disposition: form-data; name=\"field3\"\r\n" + "\r\n" + "value3\r\n" + "--"
+                + boundary + "\r\n"
+                + "Content-Disposition: form-data; name=\"content\"; filename=test-filename.txt\r\n"
+                + "Content-Type: text/plain\r\n" + "Content-Transfer-Encoding: binary\r\n" + "\r\n"
+                + new String(content) + "\r\n" + "--" + boundary + "--").getBytes();
+
+        MultipartParser parser = prepareParser(boundary, formdata);
+
+        Map<String, String> values = new HashMap<String, String>();
+        values.put("field1", "value1");
+        values.put("field2", "value2");
+        values.put("field3", "value3");
+
+        assertMultipartBasics(parser, 4, values, true, "test-filename.txt", "text/plain", content);
+    }
+
+    @Test
+    public void testNoPreamble() throws Exception {
+        String boundary = "BoUnDaRy--987654320";
+        byte[] formdata = ("--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n"
+                + "value1\r\n" + "--" + boundary + "--").getBytes();
+
+        MultipartParser parser = prepareParser(boundary, formdata);
+
+        Map<String, String> values = new HashMap<String, String>();
+        values.put("field1", "value1");
+
+        assertMultipartBasics(parser, 1, values, false, null, null, null);
+    }
+
+    @Test
+    public void testPreamble() throws Exception {
+        String boundary = "BoUnDaRy--987654320";
+        byte[] formdata = ("This is a preamble.\r\n--" + boundary + "\r\n"
+                + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "value1\r\n" + "--" + boundary + "--")
+                .getBytes();
+
+        MultipartParser parser = prepareParser(boundary, formdata);
+
+        Map<String, String> values = new HashMap<String, String>();
+        values.put("field1", "value1");
+
+        assertMultipartBasics(parser, 1, values, false, null, null, null);
+    }
+
+    @Test
+    public void testEpilogue() throws Exception {
+        String boundary = "BoUnDaRy--987654320";
+        byte[] formdata = ("\r\n--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n"
+                + "\r\n" + "value1\r\n" + "--" + boundary + "--This is an epilogue.").getBytes();
+
+        MultipartParser parser = prepareParser(boundary, formdata);
+
+        Map<String, String> values = new HashMap<String, String>();
+        values.put("field1", "value1");
+
+        assertMultipartBasics(parser, 1, values, false, null, null, null);
+    }
+
+    @Test
+    public void testEmpty() throws Exception {
+        String boundary = "BoUnDaRy--987654320";
+        byte[] formdata = ("\r\n--" + boundary + "--").getBytes();
+
+        MultipartParser parser = prepareParser(boundary, formdata);
+
+        Map<String, String> values = new HashMap<String, String>();
+
+        assertMultipartBasics(parser, 0, values, false, null, null, null);
+    }
+
+    @Test
+    public void testContentOnly() throws Exception {
+        String boundary = "ABCD-1234";
+        byte[] content = "abcŠšŸ".getBytes();
+        byte[] formdata = ("\r\n--" + boundary + "\r\n"
+                + "Content-Disposition: form-data; name=\"content\"; filename=\"a new file\"\r\n"
+                + "Content-Type: application/something\r\n" + "Content-Transfer-Encoding: binary\r\n" + "\r\n"
+                + new String(content) + "\r\n" + "--" + boundary + "--").getBytes();
+
+        MultipartParser parser = prepareParser(boundary, formdata);
+
+        Map<String, String> values = new HashMap<String, String>();
+
+        assertMultipartBasics(parser, 1, values, true, "a new file", "application/something", content);
+    }
+
+    @Test
+    public void testBigContent() throws Exception {
+        String boundary = "---- next ----";
+        byte[] content = new byte[2 * 1024 * 1024];
+
+        Random rnd = new Random();
+        for (int i = 0; i < content.length; i++) {
+            content[i] = (byte) ('a' + rnd.nextInt('z' - 'a'));
+        }
+
+        byte[] formdata = ("\r\n--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n"
+                + "\r\n" + "value1\r\n" + "--" + boundary + "\r\n"
+                + "content-disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "value2\r\n" + "--" + boundary
+                + "\r\n" + "content-disposition: form-data; name=\"field3\"\r\n" + "\r\n" + "value3\r\n" + "--"
+                + boundary + "\r\n" + "Content-Disposition: form-data; name=\"content\"; filename=bigtest.txt\r\n"
+                + "Content-Type: text/plain\r\n" + "Content-Transfer-Encoding: binary\r\n" + "\r\n"
+                + new String(content) + "\r\n" + "--" + boundary + "--").getBytes();
+
+        MultipartParser parser = prepareParser(boundary, formdata);
+
+        Map<String, String> values = new HashMap<String, String>();
+        values.put("field1", "value1");
+        values.put("field2", "value2");
+        values.put("field3", "value3");
+
+        assertMultipartBasics(parser, 4, values, true, "bigtest.txt", "text/plain", content);
+    }
+
+    @Test
+    public void testManyField() throws Exception {
+        String boundary = "============";
+
+        StringBuilder sb = new StringBuilder("\r\n");
+
+        Map<String, String> values = new HashMap<String, String>();
+
+        for (int i = 0; i < 10000; i++) {
+            String name = "field" + i;
+            String value = "value " + i * i;
+
+            values.put(name, value);
+
+            sb.append("\r\n--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"" + name + "\"\r\n"
+                    + "\r\n" + value);
+        }
+
+        sb.append("\r\n" + "--" + boundary + "--");
+
+        MultipartParser parser = prepareParser(boundary, sb.toString().getBytes());
+
+        assertMultipartBasics(parser, values.size(), values, false, null, null, null);
+    }
+
+    @Test
+    public void testCharsets() throws Exception {
+        String[] charsets = new String[] { "utf-8", "iso-8859-1", "utf-16" };
+
+        String boundary = "ldchqeriuvoqeirbvxipu  eckqnqklwjcnqwklcqwncqewlciqecqwecevoipooei cqwcoewcq";
+        StringBuilder value = new StringBuilder();
+
+        for (int i = 1; i < 255; i++) {
+            value.append((char) i);
+        }
+
+        Map<String, String> values = new HashMap<String, String>();
+        values.put("field1", value.toString());
+
+        for (String charset : charsets) {
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            bos.write(("\r\n--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n"
+                    + "Content-Type: text/plain; charset=" + charset + "\r\n" + "\r\n").getBytes());
+            bos.write(value.toString().getBytes(charset));
+            bos.write(("\r\n" + "--" + boundary + "--This is an epilogue.").getBytes());
+
+            MultipartParser parser = prepareParser(boundary, bos.toByteArray());
+
+            assertMultipartBasics(parser, 1, values, false, null, null, null);
+        }
+    }
+
+    @Test(expected = CmisInvalidArgumentException.class)
+    public void testNoBoundary() throws Exception {
+        String boundary = "";
+        byte[] formdata = ("\r\n--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n"
+                + "\r\n" + "value1\r\n" + "--" + boundary + "--").getBytes();
+
+        prepareParser(boundary, formdata);
+    }
+
+    @Test(expected = CmisInvalidArgumentException.class)
+    public void testInvalidCharset() throws Exception {
+        String boundary = "15983409582340582340";
+        byte[] formdata = ("\r\n--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n"
+                + "Content-Type: text/plain; charset=xyz\r\n" + "\r\n" + "value1\r\n" + "--" + boundary + "--")
+                .getBytes();
+
+        MultipartParser parser = prepareParser(boundary, formdata);
+
+        assertMultipartBasics(parser, 1, null, false, null, null, null);
+    }
+
+    @Test(expected = CmisInvalidArgumentException.class)
+    public void testTwoContentParts() throws Exception {
+        String boundary = "-?-";
+        byte[] content = "abcŠšŸ".getBytes();
+        byte[] formdata = ("\r\n--" + boundary + "\r\n"
+                + "Content-Disposition: form-data; name=\"content1\"; filename=\"file1\"\r\n"
+                + "Content-Type: application/something\r\n" + "Content-Transfer-Encoding: binary\r\n" + "\r\n"
+                + new String(content) + "\r\n--" + boundary + "\r\n"
+                + "Content-Disposition: form-data; name=\"content2\"; filename=\"file2\"\r\n"
+                + "Content-Type: application/something\r\n" + "Content-Transfer-Encoding: binary\r\n" + "\r\n"
+                + new String(content) + "\r\n" + "--" + boundary + "--").getBytes();
+
+        MultipartParser parser = prepareParser(boundary, formdata);
+
+        assertMultipartBasics(parser, 2, null, true, "file1", "application/something", content);
+    }
+
+    // ---- helpers ----
+
+    private MultipartParser prepareParser(String boundary, byte[] content) throws Exception {
+        FakeServletInputStream stream = new FakeServletInputStream(content);
+
+        HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+        Mockito.when(request.getContentType()).thenReturn("multipart/form-data; boundary=\"" + boundary + "\"");
+        Mockito.when(request.getInputStream()).thenReturn(stream);
+
+        return new MultipartParser(request, null, THRESHOLD, MAX_SIZE);
+    }
+
+    private byte[] readBytesFromStream(InputStream is) throws IOException {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        byte[] buffer = new byte[4096];
+        int b;
+        while ((b = is.read(buffer)) > -1) {
+            bos.write(buffer, 0, b);
+        }
+
+        is.close();
+
+        return bos.toByteArray();
+    }
+
+    private void assertMultipartBasics(MultipartParser parser, int count, Map<String, String> values,
+            boolean hasContent, String filename, String contentType, byte[] content) throws Exception {
+        Map<String, String> parameters = new HashMap<String, String>();
+
+        int counter = 0;
+        while (parser.readNext()) {
+            counter++;
+
+            if (parser.isContent()) {
+                assertEquals(filename, parser.getFilename());
+                assertEquals(contentType, parser.getContentType());
+                assertEquals(content.length, parser.getSize().intValue());
+                assertArrayEquals(content, readBytesFromStream(parser.getStream()));
+            } else {
+                parameters.put(parser.getName(), parser.getValue());
+            }
+        }
+
+        assertEquals(count, counter);
+        assertEquals(counter - (hasContent ? 1 : 0), parameters.size());
+        assertEquals(values.size(), parameters.size());
+
+        if (values != null) {
+            for (Map.Entry<String, String> e : values.entrySet()) {
+                assertEquals(e.getValue(), parameters.get(e.getKey()));
+            }
+        }
+    }
+
+    private static class FakeServletInputStream extends ServletInputStream {
+
+        private ByteArrayInputStream stream;
+
+        public FakeServletInputStream(byte[] content) {
+            stream = new ByteArrayInputStream(content);
+        }
+
+        @Override
+        public int read() throws IOException {
+            return stream.read();
+        }
+
+        @Override
+        public int read(byte[] b, int off, int len) throws IOException {
+            return stream.read(b, off, len);
+        }
+
+        @Override
+        public int read(byte[] b) throws IOException {
+            return stream.read(b);
+        }
+    }
+}

Propchange: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/MultipartParserTest.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message