Improve performance for large multi-part boundaries.
Patch provided by Felix Schumacher.
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 0c9241f..3e53518 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -58,6 +58,7 @@
<action issue="FILEUPLOAD-245" dev="sebb" type="fix">DiskFileItem.get() may not fully read the data</action>
<action issue="FILEUPLOAD-243" dev="sebb" type="update" due-to="Ville Skyttä">Make some MultipartStream private fields final</action>
<action dev="ecki" type="add">Site: added security report</action>
+ <action dev="markt" due-to="Felix Schumacher">Improve performance for large multi-part boundaries</action>
</release>
<release version="1.3.2" description="Bugfix release for 1.3.1" date="2016-05-26">
diff --git a/src/main/java/org/apache/commons/fileupload/MultipartStream.java b/src/main/java/org/apache/commons/fileupload/MultipartStream.java
index 0d05b71..851c6eb 100644
--- a/src/main/java/org/apache/commons/fileupload/MultipartStream.java
+++ b/src/main/java/org/apache/commons/fileupload/MultipartStream.java
@@ -230,6 +230,11 @@
private final byte[] boundary;
/**
+ * The table for Knuth-Morris-Pratt search algorithm
+ */
+ private int[] boundaryTable;
+
+ /**
* The length of the buffer used for processing the request.
*/
private final int bufSize;
@@ -339,12 +344,14 @@
this.notifier = pNotifier;
this.boundary = new byte[this.boundaryLength];
+ this.boundaryTable = new int[this.boundaryLength + 1];
this.keepRegion = this.boundary.length;
System.arraycopy(BOUNDARY_PREFIX, 0, this.boundary, 0,
BOUNDARY_PREFIX.length);
System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length,
boundary.length);
+ computeBoundaryTable();
head = 0;
tail = 0;
@@ -506,6 +513,31 @@
}
System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length,
boundary.length);
+ computeBoundaryTable();
+ }
+
+ /**
+ * Compute the table used for Knuth-Morris-Pratt search algorithm.
+ */
+ private void computeBoundaryTable() {
+ int position = 2;
+ int candidate = 0;
+
+ boundaryTable[0] = -1;
+ boundaryTable[1] = 0;
+
+ while (position <= boundaryLength) {
+ if (boundary[position - 1] == boundary[candidate]) {
+ boundaryTable[position] = candidate + 1;
+ candidate++;
+ position++;
+ } else if (candidate > 0) {
+ candidate = boundaryTable[candidate];
+ } else {
+ boundaryTable[position] = 0;
+ position++;
+ }
+ }
}
/**
@@ -628,6 +660,7 @@
// First delimiter may be not preceeded with a CRLF.
System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2);
boundaryLength = boundary.length - 2;
+ computeBoundaryTable();
try {
// Discard all data up to the delimiter.
discardBodyData();
@@ -643,6 +676,7 @@
boundaryLength = boundary.length;
boundary[0] = CR;
boundary[1] = LF;
+ computeBoundaryTable();
}
}
@@ -698,23 +732,20 @@
* not found.
*/
protected int findSeparator() {
- int first;
- int match = 0;
- int maxpos = tail - boundaryLength;
- for (first = head; first <= maxpos && match != boundaryLength; first++) {
- first = findByte(boundary[0], first);
- if (first == -1 || first > maxpos) {
- return -1;
+
+ int bufferPos = this.head;
+ int tablePos = 0;
+
+ while (bufferPos < this.tail) {
+ while (tablePos >= 0 && buffer[bufferPos] != boundary[tablePos]) {
+ tablePos = boundaryTable[tablePos];
}
- for (match = 1; match < boundaryLength; match++) {
- if (buffer[first + match] != boundary[match]) {
- break;
- }
+ bufferPos++;
+ tablePos++;
+ if (tablePos == boundaryLength) {
+ return bufferPos - boundaryLength;
}
}
- if (match == boundaryLength) {
- return first - 1;
- }
return -1;
}