Return-Path: X-Original-To: apmail-chemistry-commits-archive@www.apache.org Delivered-To: apmail-chemistry-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 4B10D17AFC for ; Wed, 1 Oct 2014 14:14:36 +0000 (UTC) Received: (qmail 5136 invoked by uid 500); 1 Oct 2014 14:14:36 -0000 Delivered-To: apmail-chemistry-commits-archive@chemistry.apache.org Received: (qmail 5078 invoked by uid 500); 1 Oct 2014 14:14:36 -0000 Mailing-List: contact commits-help@chemistry.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@chemistry.apache.org Delivered-To: mailing list commits@chemistry.apache.org Received: (qmail 5063 invoked by uid 99); 1 Oct 2014 14:14:36 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 01 Oct 2014 14:14:36 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 01 Oct 2014 14:14:34 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id EB0DB2388868; Wed, 1 Oct 2014 14:14:13 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1628709 - in /chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src: main/java/org/apache/chemistry/opencmis/server/impl/browser/ main/java/org/apache/chemistry/opencmis/server/shared/ test/java/org/apac... Date: Wed, 01 Oct 2014 14:14:13 -0000 To: commits@chemistry.apache.org From: fmui@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20141001141413.EB0DB2388868@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: fmui Date: Wed Oct 1 14:14:13 2014 New Revision: 1628709 URL: http://svn.apache.org/r1628709 Log: simple reimplementation of the form data parser including _charset_ field support Modified: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/POSTHttpServletRequestWrapper.java chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/shared/QueryStringHttpServletRequestWrapper.java chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/CheckServletInputStreamTest.java chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/HttpRequestMockHelper.java chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/MultipartParserTest.java 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=1628709&r1=1628708&r2=1628709&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 Wed Oct 1 14:14:13 2014 @@ -20,12 +20,16 @@ package org.apache.chemistry.opencmis.se import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.math.BigInteger; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; 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.IOUtils; import org.apache.chemistry.opencmis.server.shared.HttpUtils; @@ -33,19 +37,24 @@ import org.apache.chemistry.opencmis.ser import org.apache.chemistry.opencmis.server.shared.ThresholdOutputStreamFactory; public final class POSTHttpServletRequestWrapper extends QueryStringHttpServletRequestWrapper { + + public static final String FORM_URLENCODED = "application/x-www-form-urlencoded"; + private static final int MAX_CONTENT_BYTES = 10 * 1024 * 1024; + private static final int BUFFER_SIZE = 64 * 1024; + private static final String CHARSET_FIELD = "_charset_"; + private String filename; private String contentType; private BigInteger size; private InputStream stream; + @SuppressWarnings("unchecked") public POSTHttpServletRequestWrapper(HttpServletRequest request, ThresholdOutputStreamFactory streamFactory) throws IOException { super(request); - // check multipart - boolean isMultipart = MultipartParser.isMultipartContent(request); - - if (isMultipart) { + if (MultipartParser.isMultipartContent(request)) { + // multipart processing MultipartParser parser = new MultipartParser(request, streamFactory); parser.parse(); @@ -69,18 +78,14 @@ public final class POSTHttpServletReques if ((contentTypeControl != null) && (contentTypeControl.trim().length() > 0)) { contentType = contentTypeControl; } - } else { + } else if (isFormUrlencodedContent(request)) { // form data processing - StringBuilder sb = new StringBuilder(); - - InputStreamReader sr = new InputStreamReader(request.getInputStream(), IOUtils.UTF8); - char[] buffer = new char[4096]; - int c = 0; - while ((c = sr.read(buffer)) > -1) { - sb.append(buffer, 0, c); + if (!parseFormUrlEncodedData(request)) { + parameters.putAll(request.getParameterMap()); } - - parseFormData(sb.toString()); + } else { + // spec incompliant form encoding + throw new CmisInvalidArgumentException("Invalid form encoding!"); } } @@ -99,4 +104,167 @@ public final class POSTHttpServletReques public InputStream getStream() { return stream; } + + /** + * Parses a form data request + * + * @param request + * the request + * @return {@code true} if the body contained data, {@code false} otherwise + */ + protected boolean parseFormUrlEncodedData(HttpServletRequest request) throws IOException { + byte data[] = new byte[BUFFER_SIZE]; + int dataPos = 0; + + InputStream stream = request.getInputStream(); + int b; + byte buffer[] = new byte[BUFFER_SIZE]; + + // read stream + while ((b = stream.read(buffer)) != -1) { + if (dataPos + b > MAX_CONTENT_BYTES) { + throw new CmisInvalidArgumentException("Limit exceeded!"); + } + + if (data.length - dataPos < b) { + // expand buffer + int newSize = ((data.length + b) * 2 < MAX_CONTENT_BYTES ? (data.length + b * 2) : MAX_CONTENT_BYTES); + byte[] newbuf = new byte[newSize]; + System.arraycopy(data, 0, newbuf, 0, dataPos); + data = newbuf; + } + + System.arraycopy(buffer, 0, data, dataPos, b); + dataPos += b; + } + + if (dataPos == 0) { + // empty stream + return false; + } + + // parse parameters + boolean parseName = true; + boolean parseCharset = false; + int startPos = 0; + + List rawParameters = new ArrayList(); + String rawName = null; + String rawValue = null; + + String charset = null; + + for (int i = 0; i < dataPos; i++) { + switch (data[i]) { + case '=': + if (startPos < i) { + rawName = new String(data, startPos, i - startPos, IOUtils.ISO_8859_1); + if (CHARSET_FIELD.equalsIgnoreCase(rawName)) { + parseCharset = true; + } + } + + parseName = false; + startPos = i + 1; + break; + + case '&': + if (parseName) { + if (startPos < i) { + rawName = new String(data, startPos, i - startPos, IOUtils.ISO_8859_1); + rawParameters.add(new String[] { rawName, null }); + } + } else { + if (rawName != null) { + rawValue = new String(data, startPos, i - startPos, IOUtils.ISO_8859_1); + rawParameters.add(new String[] { rawName, rawValue }); + if (parseCharset) { + charset = rawValue; + } + } + } + + rawName = null; + rawValue = null; + + parseName = true; + parseCharset = false; + startPos = i + 1; + break; + + default: + break; + } + } + + if (startPos < dataPos) { + // there is a final parameter after the last '&' + if (parseName) { + rawName = new String(data, startPos, dataPos - startPos, IOUtils.ISO_8859_1); + rawParameters.add(new String[] { rawName, null }); + } else { + if (rawName != null) { + rawValue = new String(data, startPos, dataPos - startPos, IOUtils.ISO_8859_1); + rawParameters.add(new String[] { rawName, rawValue }); + if (parseCharset) { + charset = rawValue; + } + } + } + } else if (!parseName) { + // the stream ended with '=' + rawParameters.add(new String[] { rawName, "" }); + } + + data = null; + + // find charset + if (charset == null) { + // check charset in content type + String contentType = request.getContentType(); + if (contentType != null) { + String[] parts = contentType.split(";"); + for (int i = 1; i < parts.length; i++) { + String part = parts[i].trim().toLowerCase(Locale.ENGLISH); + if (part.startsWith("charset")) { + int x = part.indexOf('='); + charset = part.substring(x + 1).trim(); + break; + } + } + } + } + + if (charset == null) { + // set default charset + charset = IOUtils.UTF8; + } + + // decode parameters + for (String[] rawParameter : rawParameters) { + String name = URLDecoder.decode(rawParameter[0], charset); + + String value = null; + if (rawParameter[1] != null) { + value = URLDecoder.decode(rawParameter[1], charset); + } + + addParameter(name, value); + } + + return true; + } + + /** + * Returns if the request is a form-urlencoded request. + */ + public static final boolean isFormUrlencodedContent(HttpServletRequest request) { + String contentType = request.getContentType(); + + if (contentType != null && contentType.toLowerCase(Locale.ENGLISH).startsWith(FORM_URLENCODED)) { + return true; + } + + return false; + } } Modified: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/shared/QueryStringHttpServletRequestWrapper.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/shared/QueryStringHttpServletRequestWrapper.java?rev=1628709&r1=1628708&r2=1628709&view=diff ============================================================================== --- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/shared/QueryStringHttpServletRequestWrapper.java (original) +++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/shared/QueryStringHttpServletRequestWrapper.java Wed Oct 1 14:14:13 2014 @@ -35,7 +35,7 @@ import org.apache.chemistry.opencmis.com */ public class QueryStringHttpServletRequestWrapper extends HttpServletRequestWrapper { - private Map parameters; + protected Map parameters; public QueryStringHttpServletRequestWrapper(HttpServletRequest request) throws IOException { super(request); Modified: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/CheckServletInputStreamTest.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/CheckServletInputStreamTest.java?rev=1628709&r1=1628708&r2=1628709&view=diff ============================================================================== --- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/CheckServletInputStreamTest.java (original) +++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/CheckServletInputStreamTest.java Wed Oct 1 14:14:13 2014 @@ -106,8 +106,8 @@ public class CheckServletInputStreamTest ByteArrayInputStream originStream = new ByteArrayInputStream(byteBuffer); try { - ProtectionRequestWrapper prw = new ProtectionRequestWrapper(HttpRequestMockHelper.createRequest(BOUNDARY, - originStream), max); + ProtectionRequestWrapper prw = new ProtectionRequestWrapper(HttpRequestMockHelper.createMultipartRequest( + BOUNDARY, originStream), max); InputStream stream = prw.getInputStream(); int countS = 0; @@ -143,8 +143,8 @@ public class CheckServletInputStreamTest originStream = new ByteArrayInputStream(byteBuffer); try { - ProtectionRequestWrapper prw = new ProtectionRequestWrapper(HttpRequestMockHelper.createRequest(BOUNDARY, - originStream), max); + ProtectionRequestWrapper prw = new ProtectionRequestWrapper(HttpRequestMockHelper.createMultipartRequest( + BOUNDARY, originStream), max); InputStream stream = prw.getInputStream(); int countS = 0; Modified: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/HttpRequestMockHelper.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/HttpRequestMockHelper.java?rev=1628709&r1=1628708&r2=1628709&view=diff ============================================================================== --- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/HttpRequestMockHelper.java (original) +++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/HttpRequestMockHelper.java Wed Oct 1 14:14:13 2014 @@ -29,7 +29,7 @@ import org.mockito.Mockito; public class HttpRequestMockHelper { - public static HttpServletRequest createRequest(String boundary, byte[] content) throws IOException { + public static HttpServletRequest createMultipartRequest(String boundary, byte[] content) throws IOException { FakeServletInputStream stream = new FakeServletInputStream(content); HttpServletRequest request = Mockito.mock(HttpServletRequest.class); @@ -39,7 +39,8 @@ public class HttpRequestMockHelper { return request; } - public static HttpServletRequest createRequest(String boundary, InputStream inputStream) throws IOException { + public static HttpServletRequest createMultipartRequest(String boundary, InputStream inputStream) + throws IOException { FakeServletInputStream stream = new FakeServletInputStream(inputStream); HttpServletRequest request = Mockito.mock(HttpServletRequest.class); @@ -49,6 +50,17 @@ public class HttpRequestMockHelper { return request; } + public static HttpServletRequest createFormRequest(String encoding, byte[] content) throws IOException { + FakeServletInputStream stream = new FakeServletInputStream(content); + + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + Mockito.when(request.getContentType()).thenReturn( + "application/x-www-form-urlencoded" + (encoding == null ? "" : ";charset=" + encoding)); + Mockito.when(request.getInputStream()).thenReturn(stream); + + return request; + } + private static class FakeServletInputStream extends ServletInputStream { private InputStream stream; Modified: 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=1628709&r1=1628708&r2=1628709&view=diff ============================================================================== --- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/MultipartParserTest.java (original) +++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/MultipartParserTest.java Wed Oct 1 14:14:13 2014 @@ -408,7 +408,7 @@ public class MultipartParserTest { // ---- helpers ---- private MultipartParser prepareParser(String boundary, byte[] content) throws Exception { - HttpServletRequest request = HttpRequestMockHelper.createRequest(boundary, content); + HttpServletRequest request = HttpRequestMockHelper.createMultipartRequest(boundary, content); ThresholdOutputStreamFactory streamFactory = ThresholdOutputStreamFactory.newInstance(null, THRESHOLD, MAX_SIZE, false);