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