chemistry-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From f...@apache.org
Subject svn commit: r1439343 - in /chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src: main/java/org/apache/chemistry/opencmis/server/impl/atompub/ main/java/org/apache/chemistry/opencmis/server/shared/ test/java/org/apac...
Date Mon, 28 Jan 2013 11:18:01 GMT
Author: fmui
Date: Mon Jan 28 11:18:01 2013
New Revision: 1439343

URL: http://svn.apache.org/viewvc?rev=1439343&view=rev
Log:
Server: Catch oversized requests as early as possible

Added:
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/shared/CappedInputStream.java
  (with props)
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/CappedInputStreamTest.java
  (with props)
Modified:
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/AtomEntryParser.java

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=1439343&r1=1439342&r2=1439343&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
Mon Jan 28 11:18:01 2013
@@ -55,6 +55,7 @@ import org.apache.chemistry.opencmis.com
 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl;
 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringImpl;
 import org.apache.chemistry.opencmis.commons.impl.jaxb.CmisObjectType;
+import org.apache.chemistry.opencmis.server.shared.CappedInputStream;
 import org.apache.chemistry.opencmis.server.shared.ThresholdOutputStream;
 
 /**
@@ -62,6 +63,8 @@ import org.apache.chemistry.opencmis.ser
  */
 public class AtomEntryParser {
 
+    private static final long MAX_STREAM_LENGTH = 10 * 1024 * 1024;
+
     private static final String TAG_ENTRY = "entry";
     private static final String TAG_TITLE = "title";
     private static final String TAG_OBJECT = "object";
@@ -75,6 +78,8 @@ public class AtomEntryParser {
 
     protected boolean ignoreAtomContentSrc;
 
+    private CappedInputStream cappedStream;
+
     private final File tempDir;
     private final int memoryThreshold;
     private final long maxContentSize;
@@ -184,10 +189,12 @@ public class AtomEntryParser {
             return;
         }
 
+        cappedStream = new CappedInputStream(stream, MAX_STREAM_LENGTH);
+
         XMLInputFactory factory = XMLInputFactory.newInstance();
         factory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.FALSE);
         factory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
-        XMLStreamReader parser = factory.createXMLStreamReader(stream);
+        XMLStreamReader parser = factory.createXMLStreamReader(cappedStream);
 
         while (true) {
             int event = parser.getEventType();
@@ -296,17 +303,21 @@ public class AtomEntryParser {
             }
         }
 
+        ThresholdOutputStream ths = null;
         byte[] bytes = null;
         if (type.equals("text") || type.equals("html")) {
-            bytes = readText(parser).getBytes("UTF-8");
+            ths = readContentBytes(parser);
         } else if (type.equals("xhtml")) {
             bytes = copy(parser);
         } else if (type.endsWith("/xml") || type.endsWith("+xml")) {
             bytes = copy(parser);
         } else if (type.startsWith("text/")) {
-            bytes = readText(parser).getBytes("UTF-8");
+            ths = readContentBytes(parser);
         } else {
-            ThresholdOutputStream ths = readBase64(parser);
+            ths = readBase64(parser);
+        }
+
+        if (ths != null) {
             atomContentStream.setStream(ths.getInputStream());
             atomContentStream.setLength(BigInteger.valueOf(ths.getSize()));
         }
@@ -314,6 +325,7 @@ public class AtomEntryParser {
         if (bytes != null) {
             atomContentStream.setStream(new ByteArrayInputStream(bytes));
             atomContentStream.setLength(BigInteger.valueOf(bytes.length));
+            cappedStream.deductBytes(bytes.length);
         }
     }
 
@@ -365,7 +377,7 @@ public class AtomEntryParser {
     /**
      * Parses a tag that contains text.
      */
-    private static String readText(XMLStreamReader parser) throws Exception {
+    private String readText(XMLStreamReader parser) throws Exception {
         StringBuilder sb = new StringBuilder();
 
         next(parser);
@@ -394,6 +406,40 @@ public class AtomEntryParser {
     }
 
     /**
+     * Parses a tag that contains content bytes.
+     */
+    private ThresholdOutputStream readContentBytes(XMLStreamReader parser) throws Exception
{
+        ThresholdOutputStream bufferStream = new ThresholdOutputStream(tempDir, memoryThreshold,
maxContentSize,
+                encrypt);
+
+        next(parser);
+
+        while (true) {
+            int event = parser.getEventType();
+            if (event == XMLStreamReader.END_ELEMENT) {
+                break;
+            } else if (event == XMLStreamReader.CHARACTERS) {
+                String s = parser.getText();
+                if (s != null) {
+                    byte[] bytes = s.getBytes("UTF-8");
+                    bufferStream.write(bytes);
+                    cappedStream.deductBytes(bytes.length);
+                }
+            } else if (event == XMLStreamReader.START_ELEMENT) {
+                throw new RuntimeException("Unexpected tag: " + parser.getName());
+            }
+
+            if (!next(parser)) {
+                break;
+            }
+        }
+
+        next(parser);
+
+        return bufferStream;
+    }
+
+    /**
      * Parses a tag that contains base64 encoded content.
      */
     private ThresholdOutputStream readBase64(XMLStreamReader parser) throws Exception {
@@ -412,7 +458,9 @@ public class AtomEntryParser {
                 } else if (event == XMLStreamReader.CHARACTERS) {
                     String s = parser.getText();
                     if (s != null) {
-                        b64stream.write(s.getBytes("US-ASCII"));
+                        byte[] bytes = s.getBytes("US-ASCII");
+                        b64stream.write(bytes);
+                        cappedStream.deductBytes(bytes.length);
                     }
                 } else if (event == XMLStreamReader.START_ELEMENT) {
                     throw new RuntimeException("Unexpected tag: " + parser.getName());

Added: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/shared/CappedInputStream.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/CappedInputStream.java?rev=1439343&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/shared/CappedInputStream.java
(added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/shared/CappedInputStream.java
Mon Jan 28 11:18:01 2013
@@ -0,0 +1,139 @@
+/*
+ * 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.shared;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
+
+/**
+ * A stream that counts bytes and throws an exception if the given maximum is
+ * reached. Counted bytes can be deducted to excludes parts of stream from the
+ * length limitation.
+ */
+public class CappedInputStream extends InputStream {
+
+    private InputStream stream;
+    private long max;
+    private long counter;
+
+    public CappedInputStream(InputStream stream, long max) {
+        this.stream = stream;
+        this.max = max;
+        this.counter = 0;
+    }
+
+    /**
+     * Returns the counter;
+     */
+    public long getCounter() {
+        return counter;
+    }
+
+    /**
+     * Deducts the byte counter.
+     */
+    public void deductBytes(int byteCount) {
+        counter -= byteCount;
+    }
+
+    /**
+     * Deducts the byte counter.
+     */
+    public void deductString(String s, String encoding) throws UnsupportedEncodingException
{
+        if (encoding == null) {
+            counter -= s.getBytes("UTF-8").length;
+        } else {
+            counter -= s.getBytes(encoding).length;
+        }
+    }
+
+    private void checkLength() throws IOException {
+        if (counter > max) {
+            throw new CmisInvalidArgumentException("Limit exceeded!");
+        }
+    }
+
+    @Override
+    public int available() throws IOException {
+        return stream.available();
+    }
+
+    @Override
+    public synchronized void mark(int readlimit) {
+    }
+
+    @Override
+    public synchronized void reset() throws IOException {
+    }
+
+    @Override
+    public boolean markSupported() {
+        return false;
+    }
+
+    @Override
+    public int read() throws IOException {
+        checkLength();
+
+        int b = stream.read();
+        if (b > -1) {
+            counter++;
+        }
+
+        return b;
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        checkLength();
+
+        int l = stream.read(b, off, len);
+        counter += l;
+
+        return l;
+    }
+
+    @Override
+    public int read(byte[] b) throws IOException {
+        checkLength();
+
+        int l = stream.read(b);
+        counter += l;
+
+        return l;
+    }
+
+    @Override
+    public long skip(long n) throws IOException {
+        checkLength();
+
+        long l = stream.skip(n);
+        counter += l;
+
+        return l;
+    }
+
+    @Override
+    public void close() throws IOException {
+        stream.close();
+    }
+}

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

Added: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/CappedInputStreamTest.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/CappedInputStreamTest.java?rev=1439343&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/CappedInputStreamTest.java
(added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/CappedInputStreamTest.java
Mon Jan 28 11:18:01 2013
@@ -0,0 +1,92 @@
+/*
+ * 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.fail;
+
+import java.io.ByteArrayInputStream;
+
+import org.apache.chemistry.opencmis.server.shared.CappedInputStream;
+import org.junit.Test;
+
+public class CappedInputStreamTest {
+
+    @Test
+    public void testStream1() {
+        baseTest(64 * 1024, 10 * 1024, 1000, 60 * 1024, true);
+    }
+
+    @Test
+    public void testStream2() {
+        baseTest(64 * 1024, 10 * 1024, 1000, 2000, false);
+    }
+
+    @Test
+    public void testStream3() {
+        baseTest(100, 20, 0, 79, true);
+    }
+
+    @Test
+    public void testStream4() {
+        baseTest(100, 20, 10, 89, true);
+    }
+
+    @Test
+    public void testStream5() {
+        baseTest(100, 20, 0, 30, false);
+    }
+
+    @Test
+    public void testStream6() {
+        baseTest(100, 20, 19, 99, true);
+    }
+
+    private void baseTest(int bufferSize, int max, int goodBegin, int goodEnd, boolean success)
{
+        byte[] byteBuffer = new byte[bufferSize];
+        for (int i = 0; i < byteBuffer.length; i++) {
+            byteBuffer[i] = (byte) (i >= goodBegin && i <= goodEnd ? 1 : 0);
+        }
+
+        ByteArrayInputStream originStream = new ByteArrayInputStream(byteBuffer);
+
+        try {
+            CappedInputStream stream = new CappedInputStream(originStream, max);
+
+            int b = 0;
+            byte[] buffer = new byte[10];
+            while ((b = stream.read(buffer)) > -1) {
+                int counter = 0;
+
+                for (int i = 0; i < b; i++) {
+                    if (buffer[i] == 1) {
+                        counter++;
+                    }
+                }
+
+                stream.deductBytes(counter);
+            }
+
+            stream.close();
+        } catch (Exception e) {
+            if (success) {
+                fail();
+            }
+        }
+    }
+}

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



Mime
View raw message