You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by jo...@apache.org on 2019/09/23 13:46:27 UTC

[commons-fileupload] branch master updated (e0bfe29 -> 273cb11)

This is an automated email from the ASF dual-hosted git repository.

jochen pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/commons-fileupload.git.


    from e0bfe29  PR: FILEUPLOAD-300
     new f597668  PR: FILEUPLOAD-300
     new 273cb11  PR: FILEUPLOAD-300

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../commons/fileupload2/FileItemIterator.java      |   3 +-
 .../apache/commons/fileupload2/FileUploadBase.java | 663 +--------------------
 .../commons/fileupload2/MultipartStream.java       |   8 +-
 .../fileupload2/impl/FileItemIteratorImpl.java     | 348 +++++++++++
 .../fileupload2/impl/FileItemStreamImpl.java       |  13 +-
 .../impl/FileSizeLimitExceededException.java       |  94 +++
 .../fileupload2/impl/FileUploadIOException.java    |  62 ++
 .../fileupload2/impl/IOFileUploadException.java    |  61 ++
 .../impl/InvalidContentTypeException.java          |  62 ++
 .../commons/fileupload2/impl/SizeException.java    |  75 +++
 .../SizeLimitExceededException.java}               |  81 +--
 .../fileupload2/impl/UnknownSizeException.java     |  56 ++
 .../commons/fileupload2/DiskFileUploadTest.java    |   3 +-
 .../org/apache/commons/fileupload2/SizesTest.java  | 582 +++++++++---------
 .../apache/commons/fileupload2/StreamingTest.java  |   6 +-
 15 files changed, 1113 insertions(+), 1004 deletions(-)
 create mode 100644 src/main/java/org/apache/commons/fileupload2/impl/FileItemIteratorImpl.java
 create mode 100644 src/main/java/org/apache/commons/fileupload2/impl/FileSizeLimitExceededException.java
 create mode 100644 src/main/java/org/apache/commons/fileupload2/impl/FileUploadIOException.java
 create mode 100644 src/main/java/org/apache/commons/fileupload2/impl/IOFileUploadException.java
 create mode 100644 src/main/java/org/apache/commons/fileupload2/impl/InvalidContentTypeException.java
 create mode 100644 src/main/java/org/apache/commons/fileupload2/impl/SizeException.java
 copy src/main/java/org/apache/commons/fileupload2/{util/mime/ParseException.java => impl/SizeLimitExceededException.java} (52%)
 create mode 100644 src/main/java/org/apache/commons/fileupload2/impl/UnknownSizeException.java


[commons-fileupload] 02/02: PR: FILEUPLOAD-300

Posted by jo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jochen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-fileupload.git

commit 273cb114512f79dfe6cbce281a22976f174b588e
Author: Jochen Wiedmann <jo...@gmail.com>
AuthorDate: Mon Sep 23 15:40:41 2019 +0200

    PR: FILEUPLOAD-300
    
    Moved exception classes to impl package.
---
 .../commons/fileupload2/FileItemIterator.java      |   3 +-
 .../apache/commons/fileupload2/FileUploadBase.java | 341 +-----------
 .../commons/fileupload2/MultipartStream.java       |   2 +-
 .../fileupload2/impl/FileItemIteratorImpl.java     |   3 -
 .../fileupload2/impl/FileItemStreamImpl.java       |   7 +-
 .../impl/FileSizeLimitExceededException.java       |  94 ++++
 .../fileupload2/impl/FileUploadIOException.java    |  62 +++
 .../fileupload2/impl/IOFileUploadException.java    |  61 +++
 .../impl/InvalidContentTypeException.java          |  62 +++
 .../commons/fileupload2/impl/SizeException.java    |  75 +++
 .../impl/SizeLimitExceededException.java           |  43 ++
 .../fileupload2/impl/UnknownSizeException.java     |  56 ++
 .../commons/fileupload2/DiskFileUploadTest.java    |   3 +-
 .../org/apache/commons/fileupload2/SizesTest.java  | 582 ++++++++++-----------
 .../apache/commons/fileupload2/StreamingTest.java  |   2 +-
 15 files changed, 754 insertions(+), 642 deletions(-)

diff --git a/src/main/java/org/apache/commons/fileupload2/FileItemIterator.java b/src/main/java/org/apache/commons/fileupload2/FileItemIterator.java
index b56706d..7b39f35 100644
--- a/src/main/java/org/apache/commons/fileupload2/FileItemIterator.java
+++ b/src/main/java/org/apache/commons/fileupload2/FileItemIterator.java
@@ -19,7 +19,8 @@ package org.apache.commons.fileupload2;
 import java.io.IOException;
 import java.util.List;
 
-import org.apache.commons.fileupload2.FileUploadBase.FileSizeLimitExceededException;
+import org.apache.commons.fileupload2.impl.FileSizeLimitExceededException;
+import org.apache.commons.fileupload2.impl.SizeLimitExceededException;
 
 /**
  * An iterator, as returned by
diff --git a/src/main/java/org/apache/commons/fileupload2/FileUploadBase.java b/src/main/java/org/apache/commons/fileupload2/FileUploadBase.java
index 4ba37ac..5b03531 100644
--- a/src/main/java/org/apache/commons/fileupload2/FileUploadBase.java
+++ b/src/main/java/org/apache/commons/fileupload2/FileUploadBase.java
@@ -31,6 +31,8 @@ import javax.servlet.http.HttpServletRequest;
 
 import org.apache.commons.fileupload2.impl.FileItemIteratorImpl;
 import org.apache.commons.fileupload2.impl.FileItemStreamImpl;
+import org.apache.commons.fileupload2.impl.FileUploadIOException;
+import org.apache.commons.fileupload2.impl.IOFileUploadException;
 import org.apache.commons.fileupload2.servlet.ServletFileUpload;
 import org.apache.commons.fileupload2.servlet.ServletRequestContext;
 import org.apache.commons.fileupload2.util.FileItemHeadersImpl;
@@ -699,345 +701,6 @@ public abstract class FileUploadBase {
     }
 
     /**
-     * This exception is thrown for hiding an inner
-     * {@link FileUploadException} in an {@link IOException}.
-     */
-    public static class FileUploadIOException extends IOException {
-
-        /**
-         * The exceptions UID, for serializing an instance.
-         */
-        private static final long serialVersionUID = -7047616958165584154L;
-
-        /**
-         * The exceptions cause; we overwrite the parent
-         * classes field, which is available since Java
-         * 1.4 only.
-         */
-        private final FileUploadException cause;
-
-        /**
-         * Creates a <code>FileUploadIOException</code> with the
-         * given cause.
-         *
-         * @param pCause The exceptions cause, if any, or null.
-         */
-        public FileUploadIOException(FileUploadException pCause) {
-            // We're not doing super(pCause) cause of 1.3 compatibility.
-            cause = pCause;
-        }
-
-        /**
-         * Returns the exceptions cause.
-         *
-         * @return The exceptions cause, if any, or null.
-         */
-        @Override
-        public Throwable getCause() {
-            return cause;
-        }
-
-    }
-
-    /**
-     * Thrown to indicate that the request is not a multipart request.
-     */
-    public static class InvalidContentTypeException
-            extends FileUploadException {
-
-        /**
-         * The exceptions UID, for serializing an instance.
-         */
-        private static final long serialVersionUID = -9073026332015646668L;
-
-        /**
-         * Constructs a <code>InvalidContentTypeException</code> with no
-         * detail message.
-         */
-        public InvalidContentTypeException() {
-            super();
-        }
-
-        /**
-         * Constructs an <code>InvalidContentTypeException</code> with
-         * the specified detail message.
-         *
-         * @param message The detail message.
-         */
-        public InvalidContentTypeException(String message) {
-            super(message);
-        }
-
-        /**
-         * Constructs an <code>InvalidContentTypeException</code> with
-         * the specified detail message and cause.
-         *
-         * @param msg The detail message.
-         * @param cause the original cause
-         *
-         * @since 1.3.1
-         */
-        public InvalidContentTypeException(String msg, Throwable cause) {
-            super(msg, cause);
-        }
-    }
-
-    /**
-     * Thrown to indicate an IOException.
-     */
-    public static class IOFileUploadException extends FileUploadException {
-
-        /**
-         * The exceptions UID, for serializing an instance.
-         */
-        private static final long serialVersionUID = 1749796615868477269L;
-
-        /**
-         * The exceptions cause; we overwrite the parent
-         * classes field, which is available since Java
-         * 1.4 only.
-         */
-        private final IOException cause;
-
-        /**
-         * Creates a new instance with the given cause.
-         *
-         * @param pMsg The detail message.
-         * @param pException The exceptions cause.
-         */
-        public IOFileUploadException(String pMsg, IOException pException) {
-            super(pMsg);
-            cause = pException;
-        }
-
-        /**
-         * Returns the exceptions cause.
-         *
-         * @return The exceptions cause, if any, or null.
-         */
-        @Override
-        public Throwable getCause() {
-            return cause;
-        }
-
-    }
-
-    /**
-     * This exception is thrown, if a requests permitted size
-     * is exceeded.
-     */
-    protected abstract static class SizeException extends FileUploadException {
-
-        /**
-         * Serial version UID, being used, if serialized.
-         */
-        private static final long serialVersionUID = -8776225574705254126L;
-
-        /**
-         * The actual size of the request.
-         */
-        private final long actual;
-
-        /**
-         * The maximum permitted size of the request.
-         */
-        private final long permitted;
-
-        /**
-         * Creates a new instance.
-         *
-         * @param message The detail message.
-         * @param actual The actual number of bytes in the request.
-         * @param permitted The requests size limit, in bytes.
-         */
-        protected SizeException(String message, long actual, long permitted) {
-            super(message);
-            this.actual = actual;
-            this.permitted = permitted;
-        }
-
-        /**
-         * Retrieves the actual size of the request.
-         *
-         * @return The actual size of the request.
-         * @since 1.3
-         */
-        public long getActualSize() {
-            return actual;
-        }
-
-        /**
-         * Retrieves the permitted size of the request.
-         *
-         * @return The permitted size of the request.
-         * @since 1.3
-         */
-        public long getPermittedSize() {
-            return permitted;
-        }
-
-    }
-
-    /**
-     * Thrown to indicate that the request size is not specified. In other
-     * words, it is thrown, if the content-length header is missing or
-     * contains the value -1.
-     *
-     * @deprecated 1.2 As of commons-fileupload 1.2, the presence of a
-     *   content-length header is no longer required.
-     */
-    @Deprecated
-    public static class UnknownSizeException
-        extends FileUploadException {
-
-        /**
-         * The exceptions UID, for serializing an instance.
-         */
-        private static final long serialVersionUID = 7062279004812015273L;
-
-        /**
-         * Constructs a <code>UnknownSizeException</code> with no
-         * detail message.
-         */
-        public UnknownSizeException() {
-            super();
-        }
-
-        /**
-         * Constructs an <code>UnknownSizeException</code> with
-         * the specified detail message.
-         *
-         * @param message The detail message.
-         */
-        public UnknownSizeException(String message) {
-            super(message);
-        }
-
-    }
-
-    /**
-     * Thrown to indicate that the request size exceeds the configured maximum.
-     */
-    public static class SizeLimitExceededException
-            extends SizeException {
-
-        /**
-         * The exceptions UID, for serializing an instance.
-         */
-        private static final long serialVersionUID = -2474893167098052828L;
-
-        /**
-         * @deprecated 1.2 Replaced by
-         * {@link #SizeLimitExceededException(String, long, long)}
-         */
-        @Deprecated
-        public SizeLimitExceededException() {
-            this(null, 0, 0);
-        }
-
-        /**
-         * @deprecated 1.2 Replaced by
-         * {@link #SizeLimitExceededException(String, long, long)}
-         * @param message The exceptions detail message.
-         */
-        @Deprecated
-        public SizeLimitExceededException(String message) {
-            this(message, 0, 0);
-        }
-
-        /**
-         * Constructs a <code>SizeExceededException</code> with
-         * the specified detail message, and actual and permitted sizes.
-         *
-         * @param message   The detail message.
-         * @param actual    The actual request size.
-         * @param permitted The maximum permitted request size.
-         */
-        public SizeLimitExceededException(String message, long actual,
-                long permitted) {
-            super(message, actual, permitted);
-        }
-
-    }
-
-    /**
-     * Thrown to indicate that A files size exceeds the configured maximum.
-     */
-    public static class FileSizeLimitExceededException
-            extends SizeException {
-
-        /**
-         * The exceptions UID, for serializing an instance.
-         */
-        private static final long serialVersionUID = 8150776562029630058L;
-
-        /**
-         * File name of the item, which caused the exception.
-         */
-        private String fileName;
-
-        /**
-         * Field name of the item, which caused the exception.
-         */
-        private String fieldName;
-
-        /**
-         * Constructs a <code>SizeExceededException</code> with
-         * the specified detail message, and actual and permitted sizes.
-         *
-         * @param message   The detail message.
-         * @param actual    The actual request size.
-         * @param permitted The maximum permitted request size.
-         */
-        public FileSizeLimitExceededException(String message, long actual,
-                long permitted) {
-            super(message, actual, permitted);
-        }
-
-        /**
-         * Returns the file name of the item, which caused the
-         * exception.
-         *
-         * @return File name, if known, or null.
-         */
-        public String getFileName() {
-            return fileName;
-        }
-
-        /**
-         * Sets the file name of the item, which caused the
-         * exception.
-         *
-         * @param pFileName the file name of the item, which caused the exception.
-         */
-        public void setFileName(String pFileName) {
-            fileName = pFileName;
-        }
-
-        /**
-         * Returns the field name of the item, which caused the
-         * exception.
-         *
-         * @return Field name, if known, or null.
-         */
-        public String getFieldName() {
-            return fieldName;
-        }
-
-        /**
-         * Sets the field name of the item, which caused the
-         * exception.
-         *
-         * @param pFieldName the field name of the item,
-         *        which caused the exception.
-         */
-        public void setFieldName(String pFieldName) {
-            fieldName = pFieldName;
-        }
-
-    }
-
-    /**
      * Returns the progress listener.
      *
      * @return The progress listener, if any, or null.
diff --git a/src/main/java/org/apache/commons/fileupload2/MultipartStream.java b/src/main/java/org/apache/commons/fileupload2/MultipartStream.java
index ff36d9d..4d14b01 100644
--- a/src/main/java/org/apache/commons/fileupload2/MultipartStream.java
+++ b/src/main/java/org/apache/commons/fileupload2/MultipartStream.java
@@ -24,7 +24,7 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
 
-import org.apache.commons.fileupload2.FileUploadBase.FileUploadIOException;
+import org.apache.commons.fileupload2.impl.FileUploadIOException;
 import org.apache.commons.fileupload2.util.Closeable;
 import org.apache.commons.fileupload2.util.Streams;
 
diff --git a/src/main/java/org/apache/commons/fileupload2/impl/FileItemIteratorImpl.java b/src/main/java/org/apache/commons/fileupload2/impl/FileItemIteratorImpl.java
index 2f0396e..1fc7185 100644
--- a/src/main/java/org/apache/commons/fileupload2/impl/FileItemIteratorImpl.java
+++ b/src/main/java/org/apache/commons/fileupload2/impl/FileItemIteratorImpl.java
@@ -35,9 +35,6 @@ import org.apache.commons.fileupload2.MultipartStream;
 import org.apache.commons.fileupload2.ProgressListener;
 import org.apache.commons.fileupload2.RequestContext;
 import org.apache.commons.fileupload2.UploadContext;
-import org.apache.commons.fileupload2.FileUploadBase.FileUploadIOException;
-import org.apache.commons.fileupload2.FileUploadBase.InvalidContentTypeException;
-import org.apache.commons.fileupload2.FileUploadBase.SizeLimitExceededException;
 import org.apache.commons.fileupload2.util.LimitedInputStream;
 import org.apache.commons.io.IOUtils;
 
diff --git a/src/main/java/org/apache/commons/fileupload2/impl/FileItemStreamImpl.java b/src/main/java/org/apache/commons/fileupload2/impl/FileItemStreamImpl.java
index 9a40a25..a4db26b 100644
--- a/src/main/java/org/apache/commons/fileupload2/impl/FileItemStreamImpl.java
+++ b/src/main/java/org/apache/commons/fileupload2/impl/FileItemStreamImpl.java
@@ -23,11 +23,8 @@ import java.io.InputStream;
 
 import org.apache.commons.fileupload2.FileItemHeaders;
 import org.apache.commons.fileupload2.FileItemStream;
-import org.apache.commons.fileupload2.FileUploadBase;
 import org.apache.commons.fileupload2.FileUploadException;
 import org.apache.commons.fileupload2.InvalidFileNameException;
-import org.apache.commons.fileupload2.FileUploadBase.FileSizeLimitExceededException;
-import org.apache.commons.fileupload2.FileUploadBase.FileUploadIOException;
 import org.apache.commons.fileupload2.MultipartStream.ItemInputStream;
 import org.apache.commons.fileupload2.util.Closeable;
 import org.apache.commons.fileupload2.util.LimitedInputStream;
@@ -97,7 +94,7 @@ public class FileItemStreamImpl implements FileItemStream {
         if (fileSizeMax != -1) { // Check if limit is already exceeded
             if (pContentLength != -1
                     && pContentLength > fileSizeMax) {
-                FileUploadBase.FileSizeLimitExceededException e =
+                FileSizeLimitExceededException e =
                         new FileSizeLimitExceededException(
                                 format("The field %s exceeds its maximum permitted size of %s bytes.",
                                         fieldName, Long.valueOf(fileSizeMax)),
@@ -116,7 +113,7 @@ public class FileItemStreamImpl implements FileItemStream {
                 protected void raiseError(long pSizeMax, long pCount)
                         throws IOException {
                     itemStream.close(true);
-                    FileUploadBase.FileSizeLimitExceededException e =
+                    FileSizeLimitExceededException e =
                         new FileSizeLimitExceededException(
                             format("The field %s exceeds its maximum permitted size of %s bytes.",
                                    fieldName, Long.valueOf(pSizeMax)),
diff --git a/src/main/java/org/apache/commons/fileupload2/impl/FileSizeLimitExceededException.java b/src/main/java/org/apache/commons/fileupload2/impl/FileSizeLimitExceededException.java
new file mode 100644
index 0000000..096c03c
--- /dev/null
+++ b/src/main/java/org/apache/commons/fileupload2/impl/FileSizeLimitExceededException.java
@@ -0,0 +1,94 @@
+/*
+ * 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.commons.fileupload2.impl;
+
+/**
+ * Thrown to indicate that A files size exceeds the configured maximum.
+ */
+public class FileSizeLimitExceededException
+        extends SizeException {
+
+    /**
+     * The exceptions UID, for serializing an instance.
+     */
+    private static final long serialVersionUID = 8150776562029630058L;
+
+    /**
+     * File name of the item, which caused the exception.
+     */
+    private String fileName;
+
+    /**
+     * Field name of the item, which caused the exception.
+     */
+    private String fieldName;
+
+    /**
+     * Constructs a <code>SizeExceededException</code> with
+     * the specified detail message, and actual and permitted sizes.
+     *
+     * @param message   The detail message.
+     * @param actual    The actual request size.
+     * @param permitted The maximum permitted request size.
+     */
+    public FileSizeLimitExceededException(String message, long actual,
+            long permitted) {
+        super(message, actual, permitted);
+    }
+
+    /**
+     * Returns the file name of the item, which caused the
+     * exception.
+     *
+     * @return File name, if known, or null.
+     */
+    public String getFileName() {
+        return fileName;
+    }
+
+    /**
+     * Sets the file name of the item, which caused the
+     * exception.
+     *
+     * @param pFileName the file name of the item, which caused the exception.
+     */
+    public void setFileName(String pFileName) {
+        fileName = pFileName;
+    }
+
+    /**
+     * Returns the field name of the item, which caused the
+     * exception.
+     *
+     * @return Field name, if known, or null.
+     */
+    public String getFieldName() {
+        return fieldName;
+    }
+
+    /**
+     * Sets the field name of the item, which caused the
+     * exception.
+     *
+     * @param pFieldName the field name of the item,
+     *        which caused the exception.
+     */
+    public void setFieldName(String pFieldName) {
+        fieldName = pFieldName;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/commons/fileupload2/impl/FileUploadIOException.java b/src/main/java/org/apache/commons/fileupload2/impl/FileUploadIOException.java
new file mode 100644
index 0000000..e1b214e
--- /dev/null
+++ b/src/main/java/org/apache/commons/fileupload2/impl/FileUploadIOException.java
@@ -0,0 +1,62 @@
+/*
+ * 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.commons.fileupload2.impl;
+
+import java.io.IOException;
+
+import org.apache.commons.fileupload2.FileUploadException;
+
+/**
+ * This exception is thrown for hiding an inner
+ * {@link FileUploadException} in an {@link IOException}.
+ */
+public class FileUploadIOException extends IOException {
+
+    /**
+     * The exceptions UID, for serializing an instance.
+     */
+    private static final long serialVersionUID = -7047616958165584154L;
+
+    /**
+     * The exceptions cause; we overwrite the parent
+     * classes field, which is available since Java
+     * 1.4 only.
+     */
+    private final FileUploadException cause;
+
+    /**
+     * Creates a <code>FileUploadIOException</code> with the
+     * given cause.
+     *
+     * @param pCause The exceptions cause, if any, or null.
+     */
+    public FileUploadIOException(FileUploadException pCause) {
+        // We're not doing super(pCause) cause of 1.3 compatibility.
+        cause = pCause;
+    }
+
+    /**
+     * Returns the exceptions cause.
+     *
+     * @return The exceptions cause, if any, or null.
+     */
+    @Override
+    public Throwable getCause() {
+        return cause;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/commons/fileupload2/impl/IOFileUploadException.java b/src/main/java/org/apache/commons/fileupload2/impl/IOFileUploadException.java
new file mode 100644
index 0000000..de7b44d
--- /dev/null
+++ b/src/main/java/org/apache/commons/fileupload2/impl/IOFileUploadException.java
@@ -0,0 +1,61 @@
+/*
+ * 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.commons.fileupload2.impl;
+
+import java.io.IOException;
+
+import org.apache.commons.fileupload2.FileUploadException;
+
+/**
+ * Thrown to indicate an IOException.
+ */
+public class IOFileUploadException extends FileUploadException {
+
+    /**
+     * The exceptions UID, for serializing an instance.
+     */
+    private static final long serialVersionUID = 1749796615868477269L;
+
+    /**
+     * The exceptions cause; we overwrite the parent
+     * classes field, which is available since Java
+     * 1.4 only.
+     */
+    private final IOException cause;
+
+    /**
+     * Creates a new instance with the given cause.
+     *
+     * @param pMsg The detail message.
+     * @param pException The exceptions cause.
+     */
+    public IOFileUploadException(String pMsg, IOException pException) {
+        super(pMsg);
+        cause = pException;
+    }
+
+    /**
+     * Returns the exceptions cause.
+     *
+     * @return The exceptions cause, if any, or null.
+     */
+    @Override
+    public Throwable getCause() {
+        return cause;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/commons/fileupload2/impl/InvalidContentTypeException.java b/src/main/java/org/apache/commons/fileupload2/impl/InvalidContentTypeException.java
new file mode 100644
index 0000000..e76f311
--- /dev/null
+++ b/src/main/java/org/apache/commons/fileupload2/impl/InvalidContentTypeException.java
@@ -0,0 +1,62 @@
+/*
+ * 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.commons.fileupload2.impl;
+
+import org.apache.commons.fileupload2.FileUploadException;
+
+/**
+ * Thrown to indicate that the request is not a multipart request.
+ */
+public class InvalidContentTypeException
+        extends FileUploadException {
+
+    /**
+     * The exceptions UID, for serializing an instance.
+     */
+    private static final long serialVersionUID = -9073026332015646668L;
+
+    /**
+     * Constructs a <code>InvalidContentTypeException</code> with no
+     * detail message.
+     */
+    public InvalidContentTypeException() {
+        super();
+    }
+
+    /**
+     * Constructs an <code>InvalidContentTypeException</code> with
+     * the specified detail message.
+     *
+     * @param message The detail message.
+     */
+    public InvalidContentTypeException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs an <code>InvalidContentTypeException</code> with
+     * the specified detail message and cause.
+     *
+     * @param msg The detail message.
+     * @param cause the original cause
+     *
+     * @since 1.3.1
+     */
+    public InvalidContentTypeException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/commons/fileupload2/impl/SizeException.java b/src/main/java/org/apache/commons/fileupload2/impl/SizeException.java
new file mode 100644
index 0000000..d030c30
--- /dev/null
+++ b/src/main/java/org/apache/commons/fileupload2/impl/SizeException.java
@@ -0,0 +1,75 @@
+/*
+ * 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.commons.fileupload2.impl;
+
+import org.apache.commons.fileupload2.FileUploadException;
+
+/**
+ * This exception is thrown, if a requests permitted size
+ * is exceeded.
+ */
+abstract class SizeException extends FileUploadException {
+
+    /**
+     * Serial version UID, being used, if serialized.
+     */
+    private static final long serialVersionUID = -8776225574705254126L;
+
+    /**
+     * The actual size of the request.
+     */
+    private final long actual;
+
+    /**
+     * The maximum permitted size of the request.
+     */
+    private final long permitted;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param message The detail message.
+     * @param actual The actual number of bytes in the request.
+     * @param permitted The requests size limit, in bytes.
+     */
+    protected SizeException(String message, long actual, long permitted) {
+        super(message);
+        this.actual = actual;
+        this.permitted = permitted;
+    }
+
+    /**
+     * Retrieves the actual size of the request.
+     *
+     * @return The actual size of the request.
+     * @since 1.3
+     */
+    public long getActualSize() {
+        return actual;
+    }
+
+    /**
+     * Retrieves the permitted size of the request.
+     *
+     * @return The permitted size of the request.
+     * @since 1.3
+     */
+    public long getPermittedSize() {
+        return permitted;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/commons/fileupload2/impl/SizeLimitExceededException.java b/src/main/java/org/apache/commons/fileupload2/impl/SizeLimitExceededException.java
new file mode 100644
index 0000000..0b34fae
--- /dev/null
+++ b/src/main/java/org/apache/commons/fileupload2/impl/SizeLimitExceededException.java
@@ -0,0 +1,43 @@
+/*
+ * 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.commons.fileupload2.impl;
+
+/**
+ * Thrown to indicate that the request size exceeds the configured maximum.
+ */
+public class SizeLimitExceededException
+        extends SizeException {
+
+    /**
+     * The exceptions UID, for serializing an instance.
+     */
+    private static final long serialVersionUID = -2474893167098052828L;
+
+    /**
+     * Constructs a <code>SizeExceededException</code> with
+     * the specified detail message, and actual and permitted sizes.
+     *
+     * @param message   The detail message.
+     * @param actual    The actual request size.
+     * @param permitted The maximum permitted request size.
+     */
+    public SizeLimitExceededException(String message, long actual,
+            long permitted) {
+        super(message, actual, permitted);
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/commons/fileupload2/impl/UnknownSizeException.java b/src/main/java/org/apache/commons/fileupload2/impl/UnknownSizeException.java
new file mode 100644
index 0000000..f4068df
--- /dev/null
+++ b/src/main/java/org/apache/commons/fileupload2/impl/UnknownSizeException.java
@@ -0,0 +1,56 @@
+/*
+ * 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.commons.fileupload2.impl;
+
+import org.apache.commons.fileupload2.FileUploadException;
+
+/**
+ * Thrown to indicate that the request size is not specified. In other
+ * words, it is thrown, if the content-length header is missing or
+ * contains the value -1.
+ *
+ * @deprecated 1.2 As of commons-fileupload 1.2, the presence of a
+ *   content-length header is no longer required.
+ */
+@Deprecated
+public class UnknownSizeException
+    extends FileUploadException {
+
+    /**
+     * The exceptions UID, for serializing an instance.
+     */
+    private static final long serialVersionUID = 7062279004812015273L;
+
+    /**
+     * Constructs a <code>UnknownSizeException</code> with no
+     * detail message.
+     */
+    public UnknownSizeException() {
+        super();
+    }
+
+    /**
+     * Constructs an <code>UnknownSizeException</code> with
+     * the specified detail message.
+     *
+     * @param message The detail message.
+     */
+    public UnknownSizeException(String message) {
+        super(message);
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/fileupload2/DiskFileUploadTest.java b/src/test/java/org/apache/commons/fileupload2/DiskFileUploadTest.java
index fad393c..19ce621 100644
--- a/src/test/java/org/apache/commons/fileupload2/DiskFileUploadTest.java
+++ b/src/test/java/org/apache/commons/fileupload2/DiskFileUploadTest.java
@@ -26,6 +26,7 @@ import javax.servlet.http.HttpServletRequest;
 import org.apache.commons.fileupload2.DiskFileUpload;
 import org.apache.commons.fileupload2.FileUploadException;
 import org.apache.commons.fileupload2.disk.DiskFileItem;
+import org.apache.commons.fileupload2.impl.InvalidContentTypeException;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -63,7 +64,7 @@ public class DiskFileUploadTest {
         try {
             upload.parseRequest(req);
             fail("testWithNullContentType: expected exception was not thrown");
-        } catch (DiskFileUpload.InvalidContentTypeException expected) {
+        } catch (InvalidContentTypeException expected) {
             // this exception is expected
         } catch (FileUploadException unexpected) {
             fail("testWithNullContentType: unexpected exception was thrown");
diff --git a/src/test/java/org/apache/commons/fileupload2/SizesTest.java b/src/test/java/org/apache/commons/fileupload2/SizesTest.java
index ab1388b..9f2308d 100644
--- a/src/test/java/org/apache/commons/fileupload2/SizesTest.java
+++ b/src/test/java/org/apache/commons/fileupload2/SizesTest.java
@@ -1,291 +1,291 @@
-/*
- * 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.commons.fileupload2;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Iterator;
-import java.util.List;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.apache.commons.fileupload2.FileItem;
-import org.apache.commons.fileupload2.FileItemIterator;
-import org.apache.commons.fileupload2.FileItemStream;
-import org.apache.commons.fileupload2.FileUploadBase;
-import org.apache.commons.fileupload2.FileUploadException;
-import org.apache.commons.fileupload2.FileUploadBase.FileUploadIOException;
-import org.apache.commons.fileupload2.FileUploadBase.SizeException;
-import org.apache.commons.fileupload2.disk.DiskFileItemFactory;
-import org.apache.commons.fileupload2.servlet.ServletFileUpload;
-import org.apache.commons.fileupload2.util.Streams;
-import org.junit.Test;
-
-/**
- * Unit test for items with varying sizes.
- */
-public class SizesTest {
-
-    /**
-     * Runs a test with varying file sizes.
-     */
-    @Test
-    public void testFileUpload()
-            throws IOException, FileUploadException {
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        int add = 16;
-        int num = 0;
-        for (int i = 0;  i < 16384;  i += add) {
-            if (++add == 32) {
-                add = 16;
-            }
-            String header = "-----1234\r\n"
-                + "Content-Disposition: form-data; name=\"field" + (num++) + "\"\r\n"
-                + "\r\n";
-            baos.write(header.getBytes("US-ASCII"));
-            for (int j = 0;  j < i;  j++) {
-                baos.write((byte) j);
-            }
-            baos.write("\r\n".getBytes("US-ASCII"));
-        }
-        baos.write("-----1234--\r\n".getBytes("US-ASCII"));
-
-        List<FileItem> fileItems =
-                Util.parseUpload(new ServletFileUpload(new DiskFileItemFactory()), baos.toByteArray());
-        Iterator<FileItem> fileIter = fileItems.iterator();
-        add = 16;
-        num = 0;
-        for (int i = 0;  i < 16384;  i += add) {
-            if (++add == 32) {
-                add = 16;
-            }
-            FileItem item = fileIter.next();
-            assertEquals("field" + (num++), item.getFieldName());
-            byte[] bytes = item.get();
-            assertEquals(i, bytes.length);
-            for (int j = 0;  j < i;  j++) {
-                assertEquals((byte) j, bytes[j]);
-            }
-        }
-        assertTrue(!fileIter.hasNext());
-    }
-
-    /** Checks, whether limiting the file size works.
-     */
-    @Test
-    public void testFileSizeLimit()
-            throws IOException, FileUploadException {
-        final String request =
-            "-----1234\r\n" +
-            "Content-Disposition: form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
-            "Content-Type: text/whatever\r\n" +
-            "\r\n" +
-            "This is the content of the file\n" +
-            "\r\n" +
-            "-----1234--\r\n";
-
-        ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
-        upload.setFileSizeMax(-1);
-        HttpServletRequest req = new MockHttpServletRequest(
-                request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
-        List<FileItem> fileItems = upload.parseRequest(req);
-        assertEquals(1, fileItems.size());
-        FileItem item = fileItems.get(0);
-        assertEquals("This is the content of the file\n", new String(item.get()));
-
-        upload = new ServletFileUpload(new DiskFileItemFactory());
-        upload.setFileSizeMax(40);
-        req = new MockHttpServletRequest(request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
-        fileItems = upload.parseRequest(req);
-        assertEquals(1, fileItems.size());
-        item = fileItems.get(0);
-        assertEquals("This is the content of the file\n", new String(item.get()));
-
-        upload = new ServletFileUpload(new DiskFileItemFactory());
-        upload.setFileSizeMax(30);
-        req = new MockHttpServletRequest(request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
-        try {
-            upload.parseRequest(req);
-            fail("Expected exception.");
-        } catch (FileUploadBase.FileSizeLimitExceededException e) {
-            assertEquals(30, e.getPermittedSize());
-        }
-    }
-
-    /** Checks, whether a faked Content-Length header is detected.
-     */
-    @Test
-    public void testFileSizeLimitWithFakedContentLength()
-            throws IOException, FileUploadException {
-        final String request =
-            "-----1234\r\n" +
-            "Content-Disposition: form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
-            "Content-Type: text/whatever\r\n" +
-            "Content-Length: 10\r\n" +
-            "\r\n" +
-            "This is the content of the file\n" +
-            "\r\n" +
-            "-----1234--\r\n";
-
-        ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
-        upload.setFileSizeMax(-1);
-        HttpServletRequest req = new MockHttpServletRequest(
-                request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
-        List<FileItem> fileItems = upload.parseRequest(req);
-        assertEquals(1, fileItems.size());
-        FileItem item = fileItems.get(0);
-        assertEquals("This is the content of the file\n", new String(item.get()));
-
-        upload = new ServletFileUpload(new DiskFileItemFactory());
-        upload.setFileSizeMax(40);
-        req = new MockHttpServletRequest(request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
-        fileItems = upload.parseRequest(req);
-        assertEquals(1, fileItems.size());
-        item = fileItems.get(0);
-        assertEquals("This is the content of the file\n", new String(item.get()));
-
-        // provided Content-Length is larger than the FileSizeMax -> handled by ctor
-        upload = new ServletFileUpload(new DiskFileItemFactory());
-        upload.setFileSizeMax(5);
-        req = new MockHttpServletRequest(request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
-        try {
-            upload.parseRequest(req);
-            fail("Expected exception.");
-        } catch (FileUploadBase.FileSizeLimitExceededException e) {
-            assertEquals(5, e.getPermittedSize());
-        }
-
-        // provided Content-Length is wrong, actual content is larger -> handled by LimitedInputStream
-        upload = new ServletFileUpload(new DiskFileItemFactory());
-        upload.setFileSizeMax(15);
-        req = new MockHttpServletRequest(request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
-        try {
-            upload.parseRequest(req);
-            fail("Expected exception.");
-        } catch (FileUploadBase.FileSizeLimitExceededException e) {
-            assertEquals(15, e.getPermittedSize());
-        }
-    }
-
-    /** Checks, whether the maxSize works.
-     */
-    @Test
-    public void testMaxSizeLimit()
-            throws IOException, FileUploadException {
-        final String request =
-            "-----1234\r\n" +
-            "Content-Disposition: form-data; name=\"file1\"; filename=\"foo1.tab\"\r\n" +
-            "Content-Type: text/whatever\r\n" +
-            "Content-Length: 10\r\n" +
-            "\r\n" +
-            "This is the content of the file\n" +
-            "\r\n" +
-            "-----1234\r\n" +
-            "Content-Disposition: form-data; name=\"file2\"; filename=\"foo2.tab\"\r\n" +
-            "Content-Type: text/whatever\r\n" +
-            "\r\n" +
-            "This is the content of the file\n" +
-            "\r\n" +
-            "-----1234--\r\n";
-
-        ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
-        upload.setFileSizeMax(-1);
-        upload.setSizeMax(200);
-
-        MockHttpServletRequest req = new MockHttpServletRequest(
-                request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
-        try {
-            upload.parseRequest(req);
-            fail("Expected exception.");
-        } catch (FileUploadBase.SizeLimitExceededException e) {
-            assertEquals(200, e.getPermittedSize());
-        }
-    }
-
-    @Test
-    public void testMaxSizeLimitUnknownContentLength()
-            throws IOException, FileUploadException {
-        final String request =
-            "-----1234\r\n" +
-            "Content-Disposition: form-data; name=\"file1\"; filename=\"foo1.tab\"\r\n" +
-            "Content-Type: text/whatever\r\n" +
-            "Content-Length: 10\r\n" +
-            "\r\n" +
-            "This is the content of the file\n" +
-            "\r\n" +
-            "-----1234\r\n" +
-            "Content-Disposition: form-data; name=\"file2\"; filename=\"foo2.tab\"\r\n" +
-            "Content-Type: text/whatever\r\n" +
-            "\r\n" +
-            "This is the content of the file\n" +
-            "\r\n" +
-            "-----1234--\r\n";
-
-        ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
-        upload.setFileSizeMax(-1);
-        upload.setSizeMax(300);
-
-        // the first item should be within the max size limit
-        // set the read limit to 10 to simulate a "real" stream
-        // otherwise the buffer would be immediately filled
-
-        MockHttpServletRequest req = new MockHttpServletRequest(
-                request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
-        req.setContentLength(-1);
-        req.setReadLimit(10);
-
-        FileItemIterator it = upload.getItemIterator(req);
-        assertTrue(it.hasNext());
-
-        FileItemStream item = it.next();
-        assertFalse(item.isFormField());
-        assertEquals("file1", item.getFieldName());
-        assertEquals("foo1.tab", item.getName());
-
-        {
-            InputStream stream = item.openStream();
-            ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            Streams.copy(stream, baos, true);
-        }
-
-        // the second item is over the size max, thus we expect an error
-        try {
-            // the header is still within size max -> this shall still succeed
-            assertTrue(it.hasNext());
-        } catch (SizeException e) {
-            fail();
-        }
-
-        item = it.next();
-
-        try {
-            InputStream stream = item.openStream();
-            ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            Streams.copy(stream, baos, true);
-            fail();
-        } catch (FileUploadIOException e) {
-            // expected
-        }
-    }
-
-}
+/*
+ * 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.commons.fileupload2;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.fileupload2.FileItem;
+import org.apache.commons.fileupload2.FileItemIterator;
+import org.apache.commons.fileupload2.FileItemStream;
+import org.apache.commons.fileupload2.FileUploadException;
+import org.apache.commons.fileupload2.disk.DiskFileItemFactory;
+import org.apache.commons.fileupload2.impl.FileSizeLimitExceededException;
+import org.apache.commons.fileupload2.impl.FileUploadIOException;
+import org.apache.commons.fileupload2.impl.SizeLimitExceededException;
+import org.apache.commons.fileupload2.servlet.ServletFileUpload;
+import org.apache.commons.fileupload2.util.Streams;
+import org.junit.Test;
+
+/**
+ * Unit test for items with varying sizes.
+ */
+public class SizesTest {
+
+    /**
+     * Runs a test with varying file sizes.
+     */
+    @Test
+    public void testFileUpload()
+            throws IOException, FileUploadException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        int add = 16;
+        int num = 0;
+        for (int i = 0;  i < 16384;  i += add) {
+            if (++add == 32) {
+                add = 16;
+            }
+            String header = "-----1234\r\n"
+                + "Content-Disposition: form-data; name=\"field" + (num++) + "\"\r\n"
+                + "\r\n";
+            baos.write(header.getBytes("US-ASCII"));
+            for (int j = 0;  j < i;  j++) {
+                baos.write((byte) j);
+            }
+            baos.write("\r\n".getBytes("US-ASCII"));
+        }
+        baos.write("-----1234--\r\n".getBytes("US-ASCII"));
+
+        List<FileItem> fileItems =
+                Util.parseUpload(new ServletFileUpload(new DiskFileItemFactory()), baos.toByteArray());
+        Iterator<FileItem> fileIter = fileItems.iterator();
+        add = 16;
+        num = 0;
+        for (int i = 0;  i < 16384;  i += add) {
+            if (++add == 32) {
+                add = 16;
+            }
+            FileItem item = fileIter.next();
+            assertEquals("field" + (num++), item.getFieldName());
+            byte[] bytes = item.get();
+            assertEquals(i, bytes.length);
+            for (int j = 0;  j < i;  j++) {
+                assertEquals((byte) j, bytes[j]);
+            }
+        }
+        assertTrue(!fileIter.hasNext());
+    }
+
+    /** Checks, whether limiting the file size works.
+     */
+    @Test
+    public void testFileSizeLimit()
+            throws IOException, FileUploadException {
+        final String request =
+            "-----1234\r\n" +
+            "Content-Disposition: form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
+            "Content-Type: text/whatever\r\n" +
+            "\r\n" +
+            "This is the content of the file\n" +
+            "\r\n" +
+            "-----1234--\r\n";
+
+        ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
+        upload.setFileSizeMax(-1);
+        HttpServletRequest req = new MockHttpServletRequest(
+                request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
+        List<FileItem> fileItems = upload.parseRequest(req);
+        assertEquals(1, fileItems.size());
+        FileItem item = fileItems.get(0);
+        assertEquals("This is the content of the file\n", new String(item.get()));
+
+        upload = new ServletFileUpload(new DiskFileItemFactory());
+        upload.setFileSizeMax(40);
+        req = new MockHttpServletRequest(request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
+        fileItems = upload.parseRequest(req);
+        assertEquals(1, fileItems.size());
+        item = fileItems.get(0);
+        assertEquals("This is the content of the file\n", new String(item.get()));
+
+        upload = new ServletFileUpload(new DiskFileItemFactory());
+        upload.setFileSizeMax(30);
+        req = new MockHttpServletRequest(request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
+        try {
+            upload.parseRequest(req);
+            fail("Expected exception.");
+        } catch (FileSizeLimitExceededException e) {
+            assertEquals(30, e.getPermittedSize());
+        }
+    }
+
+    /** Checks, whether a faked Content-Length header is detected.
+     */
+    @Test
+    public void testFileSizeLimitWithFakedContentLength()
+            throws IOException, FileUploadException {
+        final String request =
+            "-----1234\r\n" +
+            "Content-Disposition: form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
+            "Content-Type: text/whatever\r\n" +
+            "Content-Length: 10\r\n" +
+            "\r\n" +
+            "This is the content of the file\n" +
+            "\r\n" +
+            "-----1234--\r\n";
+
+        ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
+        upload.setFileSizeMax(-1);
+        HttpServletRequest req = new MockHttpServletRequest(
+                request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
+        List<FileItem> fileItems = upload.parseRequest(req);
+        assertEquals(1, fileItems.size());
+        FileItem item = fileItems.get(0);
+        assertEquals("This is the content of the file\n", new String(item.get()));
+
+        upload = new ServletFileUpload(new DiskFileItemFactory());
+        upload.setFileSizeMax(40);
+        req = new MockHttpServletRequest(request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
+        fileItems = upload.parseRequest(req);
+        assertEquals(1, fileItems.size());
+        item = fileItems.get(0);
+        assertEquals("This is the content of the file\n", new String(item.get()));
+
+        // provided Content-Length is larger than the FileSizeMax -> handled by ctor
+        upload = new ServletFileUpload(new DiskFileItemFactory());
+        upload.setFileSizeMax(5);
+        req = new MockHttpServletRequest(request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
+        try {
+            upload.parseRequest(req);
+            fail("Expected exception.");
+        } catch (FileSizeLimitExceededException e) {
+            assertEquals(5, e.getPermittedSize());
+        }
+
+        // provided Content-Length is wrong, actual content is larger -> handled by LimitedInputStream
+        upload = new ServletFileUpload(new DiskFileItemFactory());
+        upload.setFileSizeMax(15);
+        req = new MockHttpServletRequest(request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
+        try {
+            upload.parseRequest(req);
+            fail("Expected exception.");
+        } catch (FileSizeLimitExceededException e) {
+            assertEquals(15, e.getPermittedSize());
+        }
+    }
+
+    /** Checks, whether the maxSize works.
+     */
+    @Test
+    public void testMaxSizeLimit()
+            throws IOException, FileUploadException {
+        final String request =
+            "-----1234\r\n" +
+            "Content-Disposition: form-data; name=\"file1\"; filename=\"foo1.tab\"\r\n" +
+            "Content-Type: text/whatever\r\n" +
+            "Content-Length: 10\r\n" +
+            "\r\n" +
+            "This is the content of the file\n" +
+            "\r\n" +
+            "-----1234\r\n" +
+            "Content-Disposition: form-data; name=\"file2\"; filename=\"foo2.tab\"\r\n" +
+            "Content-Type: text/whatever\r\n" +
+            "\r\n" +
+            "This is the content of the file\n" +
+            "\r\n" +
+            "-----1234--\r\n";
+
+        ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
+        upload.setFileSizeMax(-1);
+        upload.setSizeMax(200);
+
+        MockHttpServletRequest req = new MockHttpServletRequest(
+                request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
+        try {
+            upload.parseRequest(req);
+            fail("Expected exception.");
+        } catch (SizeLimitExceededException e) {
+            assertEquals(200, e.getPermittedSize());
+        }
+    }
+
+    @Test
+    public void testMaxSizeLimitUnknownContentLength()
+            throws IOException, FileUploadException {
+        final String request =
+            "-----1234\r\n" +
+            "Content-Disposition: form-data; name=\"file1\"; filename=\"foo1.tab\"\r\n" +
+            "Content-Type: text/whatever\r\n" +
+            "Content-Length: 10\r\n" +
+            "\r\n" +
+            "This is the content of the file\n" +
+            "\r\n" +
+            "-----1234\r\n" +
+            "Content-Disposition: form-data; name=\"file2\"; filename=\"foo2.tab\"\r\n" +
+            "Content-Type: text/whatever\r\n" +
+            "\r\n" +
+            "This is the content of the file\n" +
+            "\r\n" +
+            "-----1234--\r\n";
+
+        ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
+        upload.setFileSizeMax(-1);
+        upload.setSizeMax(300);
+
+        // the first item should be within the max size limit
+        // set the read limit to 10 to simulate a "real" stream
+        // otherwise the buffer would be immediately filled
+
+        MockHttpServletRequest req = new MockHttpServletRequest(
+                request.getBytes("US-ASCII"), Constants.CONTENT_TYPE);
+        req.setContentLength(-1);
+        req.setReadLimit(10);
+
+        FileItemIterator it = upload.getItemIterator(req);
+        assertTrue(it.hasNext());
+
+        FileItemStream item = it.next();
+        assertFalse(item.isFormField());
+        assertEquals("file1", item.getFieldName());
+        assertEquals("foo1.tab", item.getName());
+
+        {
+            InputStream stream = item.openStream();
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            Streams.copy(stream, baos, true);
+        }
+
+        // the second item is over the size max, thus we expect an error
+        try {
+            // the header is still within size max -> this shall still succeed
+            assertTrue(it.hasNext());
+        } catch (SizeLimitExceededException e) {
+            fail();
+        }
+
+        item = it.next();
+
+        try {
+            InputStream stream = item.openStream();
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            Streams.copy(stream, baos, true);
+            fail();
+        } catch (FileUploadIOException e) {
+            // expected
+        }
+    }
+
+}
diff --git a/src/test/java/org/apache/commons/fileupload2/StreamingTest.java b/src/test/java/org/apache/commons/fileupload2/StreamingTest.java
index b80bee0..d173bdf 100644
--- a/src/test/java/org/apache/commons/fileupload2/StreamingTest.java
+++ b/src/test/java/org/apache/commons/fileupload2/StreamingTest.java
@@ -33,8 +33,8 @@ import org.apache.commons.fileupload2.FileUploadBase;
 import org.apache.commons.fileupload2.FileUploadException;
 import org.apache.commons.fileupload2.InvalidFileNameException;
 import org.apache.commons.fileupload2.MultipartStream;
-import org.apache.commons.fileupload2.FileUploadBase.IOFileUploadException;
 import org.apache.commons.fileupload2.disk.DiskFileItemFactory;
+import org.apache.commons.fileupload2.impl.IOFileUploadException;
 import org.apache.commons.fileupload2.servlet.ServletFileUpload;
 import org.apache.commons.fileupload2.servlet.ServletRequestContext;
 


[commons-fileupload] 01/02: PR: FILEUPLOAD-300

Posted by jo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jochen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-fileupload.git

commit f59766841400a41a0f416ab669a249515179ab94
Author: Jochen Wiedmann <jo...@gmail.com>
AuthorDate: Mon Sep 23 15:31:00 2019 +0200

    PR: FILEUPLOAD-300
    
    Move FileItemIteratorImpl class to item package.
---
 .../apache/commons/fileupload2/FileUploadBase.java | 322 +------------------
 .../commons/fileupload2/MultipartStream.java       |   6 +-
 .../fileupload2/impl/FileItemIteratorImpl.java     | 351 +++++++++++++++++++++
 .../fileupload2/impl/FileItemStreamImpl.java       |   6 +-
 .../apache/commons/fileupload2/StreamingTest.java  |   4 +-
 5 files changed, 362 insertions(+), 327 deletions(-)

diff --git a/src/main/java/org/apache/commons/fileupload2/FileUploadBase.java b/src/main/java/org/apache/commons/fileupload2/FileUploadBase.java
index 0a3a19b..4ba37ac 100644
--- a/src/main/java/org/apache/commons/fileupload2/FileUploadBase.java
+++ b/src/main/java/org/apache/commons/fileupload2/FileUploadBase.java
@@ -19,7 +19,6 @@ package org.apache.commons.fileupload2;
 import static java.lang.String.format;
 
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -27,17 +26,15 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.NoSuchElementException;
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.apache.commons.fileupload2.impl.FileItemIteratorImpl;
 import org.apache.commons.fileupload2.impl.FileItemStreamImpl;
 import org.apache.commons.fileupload2.servlet.ServletFileUpload;
 import org.apache.commons.fileupload2.servlet.ServletRequestContext;
 import org.apache.commons.fileupload2.util.FileItemHeadersImpl;
-import org.apache.commons.fileupload2.util.LimitedInputStream;
 import org.apache.commons.fileupload2.util.Streams;
-import org.apache.commons.io.IOUtils;
 
 /**
  * <p>High level API for processing file uploads.</p>
@@ -415,7 +412,7 @@ public abstract class FileUploadBase {
      *
      * @return The boundary, as a byte array.
      */
-    protected byte[] getBoundary(String contentType) {
+    public byte[] getBoundary(String contentType) {
         ParameterParser parser = new ParameterParser();
         parser.setLowerCaseNames(true);
         // Parameter parser can handle null input
@@ -456,7 +453,7 @@ public abstract class FileUploadBase {
      *
      * @return The file name for the current <code>encapsulation</code>.
      */
-    protected String getFileName(FileItemHeaders headers) {
+    public String getFileName(FileItemHeaders headers) {
         return getFileName(headers.getHeader(CONTENT_DISPOSITION));
     }
 
@@ -498,7 +495,7 @@ public abstract class FileUploadBase {
      *
      * @return The field name for the current <code>encapsulation</code>.
      */
-    protected String getFieldName(FileItemHeaders headers) {
+    public String getFieldName(FileItemHeaders headers) {
         return getFieldName(headers.getHeader(CONTENT_DISPOSITION));
     }
 
@@ -574,7 +571,7 @@ public abstract class FileUploadBase {
      *
      * @return A <code>Map</code> containing the parsed HTTP request headers.
      */
-    protected FileItemHeaders getParsedHeaders(String headerPart) {
+    public FileItemHeaders getParsedHeaders(String headerPart) {
         final int len = headerPart.length();
         FileItemHeadersImpl headers = newFileItemHeaders();
         int start = 0;
@@ -702,315 +699,6 @@ public abstract class FileUploadBase {
     }
 
     /**
-     * The iterator, which is returned by
-     * {@link FileUploadBase#getItemIterator(RequestContext)}.
-     */
-    public static class FileItemIteratorImpl implements FileItemIterator {
-    	private final FileUploadBase fileUploadBase;
-    	private final RequestContext ctx;
-    	private long sizeMax, fileSizeMax;
-
-
-    	@Override
-    	public long getSizeMax() {
-			return sizeMax;
-		}
-
-    	@Override
-		public void setSizeMax(long sizeMax) {
-			this.sizeMax = sizeMax;
-		}
-
-    	@Override
-		public long getFileSizeMax() {
-			return fileSizeMax;
-		}
-
-    	@Override
-		public void setFileSizeMax(long fileSizeMax) {
-			this.fileSizeMax = fileSizeMax;
-		}
-
-		/**
-         * The multi part stream to process.
-         */
-        private MultipartStream multiPartStream;
-
-        /**
-         * The notifier, which used for triggering the
-         * {@link ProgressListener}.
-         */
-        private MultipartStream.ProgressNotifier progressNotifier;
-
-        /**
-         * The boundary, which separates the various parts.
-         */
-        private byte[] multiPartBoundary;
-
-        /**
-         * The item, which we currently process.
-         */
-        private FileItemStreamImpl currentItem;
-
-        /**
-         * The current items field name.
-         */
-        private String currentFieldName;
-
-        /**
-         * Whether we are currently skipping the preamble.
-         */
-        private boolean skipPreamble;
-
-        /**
-         * Whether the current item may still be read.
-         */
-        private boolean itemValid;
-
-        /**
-         * Whether we have seen the end of the file.
-         */
-        private boolean eof;
-
-        /**
-         * Creates a new instance.
-         *
-         * @param ctx The request context.
-         * @throws FileUploadException An error occurred while
-         *   parsing the request.
-         * @throws IOException An I/O error occurred.
-         */
-        FileItemIteratorImpl(FileUploadBase pFileUploadBase, RequestContext pRequestContext)
-                throws FileUploadException, IOException {
-        	fileUploadBase = pFileUploadBase;
-        	sizeMax = fileUploadBase.getSizeMax();
-        	fileSizeMax = fileUploadBase.getFileSizeMax();
-        	ctx = pRequestContext;
-            if (ctx == null) {
-                throw new NullPointerException("ctx parameter");
-            }
-
-
-            skipPreamble = true;
-            findNextItem();
-        }
-
-        protected void init(FileUploadBase fileUploadBase, RequestContext pRequestContext)
-                throws FileUploadException, IOException {
-            String contentType = ctx.getContentType();
-            if ((null == contentType)
-                    || (!contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART))) {
-                throw new InvalidContentTypeException(
-                        format("the request doesn't contain a %s or %s stream, content type header is %s",
-                               MULTIPART_FORM_DATA, MULTIPART_MIXED, contentType));
-            }
-
-
-            @SuppressWarnings("deprecation") // still has to be backward compatible
-            final int contentLengthInt = ctx.getContentLength();
-
-            final long requestSize = UploadContext.class.isAssignableFrom(ctx.getClass())
-                                     // Inline conditional is OK here CHECKSTYLE:OFF
-                                     ? ((UploadContext) ctx).contentLength()
-                                     : contentLengthInt;
-                                     // CHECKSTYLE:ON
-
-            InputStream input; // N.B. this is eventually closed in MultipartStream processing
-            if (sizeMax >= 0) {
-                if (requestSize != -1 && requestSize > sizeMax) {
-                    throw new SizeLimitExceededException(
-                        format("the request was rejected because its size (%s) exceeds the configured maximum (%s)",
-                                Long.valueOf(requestSize), Long.valueOf(sizeMax)),
-                               requestSize, sizeMax);
-                }
-                // N.B. this is eventually closed in MultipartStream processing
-                input = new LimitedInputStream(ctx.getInputStream(), sizeMax) {
-                    @Override
-                    protected void raiseError(long pSizeMax, long pCount)
-                            throws IOException {
-                        FileUploadException ex = new SizeLimitExceededException(
-                        format("the request was rejected because its size (%s) exceeds the configured maximum (%s)",
-                                Long.valueOf(pCount), Long.valueOf(pSizeMax)),
-                               pCount, pSizeMax);
-                        throw new FileUploadIOException(ex);
-                    }
-                };
-            } else {
-                input = ctx.getInputStream();
-            }
-
-            String charEncoding = fileUploadBase.getHeaderEncoding();
-            if (charEncoding == null) {
-                charEncoding = ctx.getCharacterEncoding();
-            }
-
-            multiPartBoundary = fileUploadBase.getBoundary(contentType);
-            if (multiPartBoundary == null) {
-                IOUtils.closeQuietly(input); // avoid possible resource leak
-                throw new FileUploadException("the request was rejected because no multipart boundary was found");
-            }
-
-            progressNotifier = new MultipartStream.ProgressNotifier(fileUploadBase.getProgressListener(), requestSize);
-            try {
-                multiPartStream = new MultipartStream(input, multiPartBoundary, progressNotifier);
-            } catch (IllegalArgumentException iae) {
-                IOUtils.closeQuietly(input); // avoid possible resource leak
-                throw new InvalidContentTypeException(
-                        format("The boundary specified in the %s header is too long", CONTENT_TYPE), iae);
-            }
-            multiPartStream.setHeaderEncoding(charEncoding);
-        }
-
-        public MultipartStream getMultiPartStream() throws FileUploadException, IOException {
-        	if (multiPartStream == null) {
-        		init(fileUploadBase, ctx);
-        	}
-        	return multiPartStream;
-        }
- 
-        /**
-         * Called for finding the next item, if any.
-         *
-         * @return True, if an next item was found, otherwise false.
-         * @throws IOException An I/O error occurred.
-         */
-        private boolean findNextItem() throws FileUploadException, IOException {
-            if (eof) {
-                return false;
-            }
-            if (currentItem != null) {
-                currentItem.close();
-                currentItem = null;
-            }
-            final MultipartStream multi = getMultiPartStream();
-            for (;;) {
-                boolean nextPart;
-                if (skipPreamble) {
-                    nextPart = multi.skipPreamble();
-                } else {
-                    nextPart = multi.readBoundary();
-                }
-                if (!nextPart) {
-                    if (currentFieldName == null) {
-                        // Outer multipart terminated -> No more data
-                        eof = true;
-                        return false;
-                    }
-                    // Inner multipart terminated -> Return to parsing the outer
-                    multi.setBoundary(multiPartBoundary);
-                    currentFieldName = null;
-                    continue;
-                }
-                FileItemHeaders headers = fileUploadBase.getParsedHeaders(multi.readHeaders());
-                if (currentFieldName == null) {
-                    // We're parsing the outer multipart
-                    String fieldName = fileUploadBase.getFieldName(headers);
-                    if (fieldName != null) {
-                        String subContentType = headers.getHeader(CONTENT_TYPE);
-                        if (subContentType != null
-                                &&  subContentType.toLowerCase(Locale.ENGLISH)
-                                        .startsWith(MULTIPART_MIXED)) {
-                            currentFieldName = fieldName;
-                            // Multiple files associated with this field name
-                            byte[] subBoundary = fileUploadBase.getBoundary(subContentType);
-                            multi.setBoundary(subBoundary);
-                            skipPreamble = true;
-                            continue;
-                        }
-                        String fileName = fileUploadBase.getFileName(headers);
-                        currentItem = new FileItemStreamImpl(this, fileName,
-                                fieldName, headers.getHeader(CONTENT_TYPE),
-                                fileName == null, getContentLength(headers));
-                        currentItem.setHeaders(headers);
-                        progressNotifier.noteItem();
-                        itemValid = true;
-                        return true;
-                    }
-                } else {
-                    String fileName = fileUploadBase.getFileName(headers);
-                    if (fileName != null) {
-                        currentItem = new FileItemStreamImpl(this, fileName,
-                                currentFieldName,
-                                headers.getHeader(CONTENT_TYPE),
-                                false, getContentLength(headers));
-                        currentItem.setHeaders(headers);
-                        progressNotifier.noteItem();
-                        itemValid = true;
-                        return true;
-                    }
-                }
-                multi.discardBodyData();
-            }
-        }
-
-        private long getContentLength(FileItemHeaders pHeaders) {
-            try {
-                return Long.parseLong(pHeaders.getHeader(CONTENT_LENGTH));
-            } catch (Exception e) {
-                return -1;
-            }
-        }
-
-        /**
-         * Returns, whether another instance of {@link FileItemStream}
-         * is available.
-         *
-         * @throws FileUploadException Parsing or processing the
-         *   file item failed.
-         * @throws IOException Reading the file item failed.
-         * @return True, if one or more additional file items
-         *   are available, otherwise false.
-         */
-        @Override
-        public boolean hasNext() throws FileUploadException, IOException {
-            if (eof) {
-                return false;
-            }
-            if (itemValid) {
-                return true;
-            }
-            try {
-                return findNextItem();
-            } catch (FileUploadIOException e) {
-                // unwrap encapsulated SizeException
-                throw (FileUploadException) e.getCause();
-            }
-        }
-
-        /**
-         * Returns the next available {@link FileItemStream}.
-         *
-         * @throws java.util.NoSuchElementException No more items are
-         *   available. Use {@link #hasNext()} to prevent this exception.
-         * @throws FileUploadException Parsing or processing the
-         *   file item failed.
-         * @throws IOException Reading the file item failed.
-         * @return FileItemStream instance, which provides
-         *   access to the next file item.
-         */
-        @Override
-        public FileItemStream next() throws FileUploadException, IOException {
-            if (eof  ||  (!itemValid && !hasNext())) {
-                throw new NoSuchElementException();
-            }
-            itemValid = false;
-            return currentItem;
-        }
-
-		@Override
-		public List<FileItem> getFileItems() throws FileUploadException, IOException {
-			final List<FileItem> items = new ArrayList<FileItem>();
-			while (hasNext()) {
-				final FileItemStream fis = next();
-				final FileItem fi = fileUploadBase.getFileItemFactory().createItem(fis.getFieldName(), fis.getContentType(), fis.isFormField(), fis.getName());
-				items.add(fi);
-			}
-			return items;
-		}
-
-    }
-
-    /**
      * This exception is thrown for hiding an inner
      * {@link FileUploadException} in an {@link IOException}.
      */
diff --git a/src/main/java/org/apache/commons/fileupload2/MultipartStream.java b/src/main/java/org/apache/commons/fileupload2/MultipartStream.java
index 05f344a..ff36d9d 100644
--- a/src/main/java/org/apache/commons/fileupload2/MultipartStream.java
+++ b/src/main/java/org/apache/commons/fileupload2/MultipartStream.java
@@ -116,7 +116,7 @@ public class MultipartStream {
          * @param pListener The listener to invoke.
          * @param pContentLength The expected content length.
          */
-        ProgressNotifier(ProgressListener pListener, long pContentLength) {
+        public ProgressNotifier(ProgressListener pListener, long pContentLength) {
             listener = pListener;
             contentLength = pContentLength;
         }
@@ -137,7 +137,7 @@ public class MultipartStream {
         /**
          * Called to indicate, that a new file item has been detected.
          */
-        void noteItem() {
+        public void noteItem() {
             ++items;
             notifyListener();
         }
@@ -366,7 +366,7 @@ public class MultipartStream {
      *
      * @see #MultipartStream(InputStream, byte[], int, ProgressNotifier)
      */
-    MultipartStream(InputStream input,
+    public MultipartStream(InputStream input,
             byte[] boundary,
             ProgressNotifier pNotifier) {
         this(input, boundary, DEFAULT_BUFSIZE, pNotifier);
diff --git a/src/main/java/org/apache/commons/fileupload2/impl/FileItemIteratorImpl.java b/src/main/java/org/apache/commons/fileupload2/impl/FileItemIteratorImpl.java
new file mode 100644
index 0000000..2f0396e
--- /dev/null
+++ b/src/main/java/org/apache/commons/fileupload2/impl/FileItemIteratorImpl.java
@@ -0,0 +1,351 @@
+/*
+ * 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.commons.fileupload2.impl;
+
+import static java.lang.String.format;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.NoSuchElementException;
+
+import org.apache.commons.fileupload2.FileItem;
+import org.apache.commons.fileupload2.FileItemHeaders;
+import org.apache.commons.fileupload2.FileItemIterator;
+import org.apache.commons.fileupload2.FileItemStream;
+import org.apache.commons.fileupload2.FileUploadBase;
+import org.apache.commons.fileupload2.FileUploadException;
+import org.apache.commons.fileupload2.MultipartStream;
+import org.apache.commons.fileupload2.ProgressListener;
+import org.apache.commons.fileupload2.RequestContext;
+import org.apache.commons.fileupload2.UploadContext;
+import org.apache.commons.fileupload2.FileUploadBase.FileUploadIOException;
+import org.apache.commons.fileupload2.FileUploadBase.InvalidContentTypeException;
+import org.apache.commons.fileupload2.FileUploadBase.SizeLimitExceededException;
+import org.apache.commons.fileupload2.util.LimitedInputStream;
+import org.apache.commons.io.IOUtils;
+
+/**
+ * The iterator, which is returned by
+ * {@link FileUploadBase#getItemIterator(RequestContext)}.
+ */
+public class FileItemIteratorImpl implements FileItemIterator {
+	private final FileUploadBase fileUploadBase;
+	private final RequestContext ctx;
+	private long sizeMax, fileSizeMax;
+
+
+	@Override
+	public long getSizeMax() {
+		return sizeMax;
+	}
+
+	@Override
+	public void setSizeMax(long sizeMax) {
+		this.sizeMax = sizeMax;
+	}
+
+	@Override
+	public long getFileSizeMax() {
+		return fileSizeMax;
+	}
+
+	@Override
+	public void setFileSizeMax(long fileSizeMax) {
+		this.fileSizeMax = fileSizeMax;
+	}
+
+	/**
+     * The multi part stream to process.
+     */
+    private MultipartStream multiPartStream;
+
+    /**
+     * The notifier, which used for triggering the
+     * {@link ProgressListener}.
+     */
+    private MultipartStream.ProgressNotifier progressNotifier;
+
+    /**
+     * The boundary, which separates the various parts.
+     */
+    private byte[] multiPartBoundary;
+
+    /**
+     * The item, which we currently process.
+     */
+    private FileItemStreamImpl currentItem;
+
+    /**
+     * The current items field name.
+     */
+    private String currentFieldName;
+
+    /**
+     * Whether we are currently skipping the preamble.
+     */
+    private boolean skipPreamble;
+
+    /**
+     * Whether the current item may still be read.
+     */
+    private boolean itemValid;
+
+    /**
+     * Whether we have seen the end of the file.
+     */
+    private boolean eof;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param ctx The request context.
+     * @throws FileUploadException An error occurred while
+     *   parsing the request.
+     * @throws IOException An I/O error occurred.
+     */
+    public FileItemIteratorImpl(FileUploadBase pFileUploadBase, RequestContext pRequestContext)
+            throws FileUploadException, IOException {
+    	fileUploadBase = pFileUploadBase;
+    	sizeMax = fileUploadBase.getSizeMax();
+    	fileSizeMax = fileUploadBase.getFileSizeMax();
+    	ctx = pRequestContext;
+        if (ctx == null) {
+            throw new NullPointerException("ctx parameter");
+        }
+
+
+        skipPreamble = true;
+        findNextItem();
+    }
+
+    protected void init(FileUploadBase fileUploadBase, RequestContext pRequestContext)
+            throws FileUploadException, IOException {
+        String contentType = ctx.getContentType();
+        if ((null == contentType)
+                || (!contentType.toLowerCase(Locale.ENGLISH).startsWith(FileUploadBase.MULTIPART))) {
+            throw new InvalidContentTypeException(
+                    format("the request doesn't contain a %s or %s stream, content type header is %s",
+                           FileUploadBase.MULTIPART_FORM_DATA, FileUploadBase.MULTIPART_MIXED, contentType));
+        }
+
+
+        @SuppressWarnings("deprecation") // still has to be backward compatible
+        final int contentLengthInt = ctx.getContentLength();
+
+        final long requestSize = UploadContext.class.isAssignableFrom(ctx.getClass())
+                                 // Inline conditional is OK here CHECKSTYLE:OFF
+                                 ? ((UploadContext) ctx).contentLength()
+                                 : contentLengthInt;
+                                 // CHECKSTYLE:ON
+
+        InputStream input; // N.B. this is eventually closed in MultipartStream processing
+        if (sizeMax >= 0) {
+            if (requestSize != -1 && requestSize > sizeMax) {
+                throw new SizeLimitExceededException(
+                    format("the request was rejected because its size (%s) exceeds the configured maximum (%s)",
+                            Long.valueOf(requestSize), Long.valueOf(sizeMax)),
+                           requestSize, sizeMax);
+            }
+            // N.B. this is eventually closed in MultipartStream processing
+            input = new LimitedInputStream(ctx.getInputStream(), sizeMax) {
+                @Override
+                protected void raiseError(long pSizeMax, long pCount)
+                        throws IOException {
+                    FileUploadException ex = new SizeLimitExceededException(
+                    format("the request was rejected because its size (%s) exceeds the configured maximum (%s)",
+                            Long.valueOf(pCount), Long.valueOf(pSizeMax)),
+                           pCount, pSizeMax);
+                    throw new FileUploadIOException(ex);
+                }
+            };
+        } else {
+            input = ctx.getInputStream();
+        }
+
+        String charEncoding = fileUploadBase.getHeaderEncoding();
+        if (charEncoding == null) {
+            charEncoding = ctx.getCharacterEncoding();
+        }
+
+        multiPartBoundary = fileUploadBase.getBoundary(contentType);
+        if (multiPartBoundary == null) {
+            IOUtils.closeQuietly(input); // avoid possible resource leak
+            throw new FileUploadException("the request was rejected because no multipart boundary was found");
+        }
+
+        progressNotifier = new MultipartStream.ProgressNotifier(fileUploadBase.getProgressListener(), requestSize);
+        try {
+            multiPartStream = new MultipartStream(input, multiPartBoundary, progressNotifier);
+        } catch (IllegalArgumentException iae) {
+            IOUtils.closeQuietly(input); // avoid possible resource leak
+            throw new InvalidContentTypeException(
+                    format("The boundary specified in the %s header is too long", FileUploadBase.CONTENT_TYPE), iae);
+        }
+        multiPartStream.setHeaderEncoding(charEncoding);
+    }
+
+    public MultipartStream getMultiPartStream() throws FileUploadException, IOException {
+    	if (multiPartStream == null) {
+    		init(fileUploadBase, ctx);
+    	}
+    	return multiPartStream;
+    }
+
+    /**
+     * Called for finding the next item, if any.
+     *
+     * @return True, if an next item was found, otherwise false.
+     * @throws IOException An I/O error occurred.
+     */
+    private boolean findNextItem() throws FileUploadException, IOException {
+        if (eof) {
+            return false;
+        }
+        if (currentItem != null) {
+            currentItem.close();
+            currentItem = null;
+        }
+        final MultipartStream multi = getMultiPartStream();
+        for (;;) {
+            boolean nextPart;
+            if (skipPreamble) {
+                nextPart = multi.skipPreamble();
+            } else {
+                nextPart = multi.readBoundary();
+            }
+            if (!nextPart) {
+                if (currentFieldName == null) {
+                    // Outer multipart terminated -> No more data
+                    eof = true;
+                    return false;
+                }
+                // Inner multipart terminated -> Return to parsing the outer
+                multi.setBoundary(multiPartBoundary);
+                currentFieldName = null;
+                continue;
+            }
+            FileItemHeaders headers = fileUploadBase.getParsedHeaders(multi.readHeaders());
+            if (currentFieldName == null) {
+                // We're parsing the outer multipart
+                String fieldName = fileUploadBase.getFieldName(headers);
+                if (fieldName != null) {
+                    String subContentType = headers.getHeader(FileUploadBase.CONTENT_TYPE);
+                    if (subContentType != null
+                            &&  subContentType.toLowerCase(Locale.ENGLISH)
+                                    .startsWith(FileUploadBase.MULTIPART_MIXED)) {
+                        currentFieldName = fieldName;
+                        // Multiple files associated with this field name
+                        byte[] subBoundary = fileUploadBase.getBoundary(subContentType);
+                        multi.setBoundary(subBoundary);
+                        skipPreamble = true;
+                        continue;
+                    }
+                    String fileName = fileUploadBase.getFileName(headers);
+                    currentItem = new FileItemStreamImpl(this, fileName,
+                            fieldName, headers.getHeader(FileUploadBase.CONTENT_TYPE),
+                            fileName == null, getContentLength(headers));
+                    currentItem.setHeaders(headers);
+                    progressNotifier.noteItem();
+                    itemValid = true;
+                    return true;
+                }
+            } else {
+                String fileName = fileUploadBase.getFileName(headers);
+                if (fileName != null) {
+                    currentItem = new FileItemStreamImpl(this, fileName,
+                            currentFieldName,
+                            headers.getHeader(FileUploadBase.CONTENT_TYPE),
+                            false, getContentLength(headers));
+                    currentItem.setHeaders(headers);
+                    progressNotifier.noteItem();
+                    itemValid = true;
+                    return true;
+                }
+            }
+            multi.discardBodyData();
+        }
+    }
+
+    private long getContentLength(FileItemHeaders pHeaders) {
+        try {
+            return Long.parseLong(pHeaders.getHeader(FileUploadBase.CONTENT_LENGTH));
+        } catch (Exception e) {
+            return -1;
+        }
+    }
+
+    /**
+     * Returns, whether another instance of {@link FileItemStream}
+     * is available.
+     *
+     * @throws FileUploadException Parsing or processing the
+     *   file item failed.
+     * @throws IOException Reading the file item failed.
+     * @return True, if one or more additional file items
+     *   are available, otherwise false.
+     */
+    @Override
+    public boolean hasNext() throws FileUploadException, IOException {
+        if (eof) {
+            return false;
+        }
+        if (itemValid) {
+            return true;
+        }
+        try {
+            return findNextItem();
+        } catch (FileUploadIOException e) {
+            // unwrap encapsulated SizeException
+            throw (FileUploadException) e.getCause();
+        }
+    }
+
+    /**
+     * Returns the next available {@link FileItemStream}.
+     *
+     * @throws java.util.NoSuchElementException No more items are
+     *   available. Use {@link #hasNext()} to prevent this exception.
+     * @throws FileUploadException Parsing or processing the
+     *   file item failed.
+     * @throws IOException Reading the file item failed.
+     * @return FileItemStream instance, which provides
+     *   access to the next file item.
+     */
+    @Override
+    public FileItemStream next() throws FileUploadException, IOException {
+        if (eof  ||  (!itemValid && !hasNext())) {
+            throw new NoSuchElementException();
+        }
+        itemValid = false;
+        return currentItem;
+    }
+
+	@Override
+	public List<FileItem> getFileItems() throws FileUploadException, IOException {
+		final List<FileItem> items = new ArrayList<FileItem>();
+		while (hasNext()) {
+			final FileItemStream fis = next();
+			final FileItem fi = fileUploadBase.getFileItemFactory().createItem(fis.getFieldName(), fis.getContentType(), fis.isFormField(), fis.getName());
+			items.add(fi);
+		}
+		return items;
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/commons/fileupload2/impl/FileItemStreamImpl.java b/src/main/java/org/apache/commons/fileupload2/impl/FileItemStreamImpl.java
index be48435..9a40a25 100644
--- a/src/main/java/org/apache/commons/fileupload2/impl/FileItemStreamImpl.java
+++ b/src/main/java/org/apache/commons/fileupload2/impl/FileItemStreamImpl.java
@@ -26,8 +26,6 @@ import org.apache.commons.fileupload2.FileItemStream;
 import org.apache.commons.fileupload2.FileUploadBase;
 import org.apache.commons.fileupload2.FileUploadException;
 import org.apache.commons.fileupload2.InvalidFileNameException;
-import org.apache.commons.fileupload2.MultipartStream;
-import org.apache.commons.fileupload2.FileItemStream.ItemSkippedException;
 import org.apache.commons.fileupload2.FileUploadBase.FileSizeLimitExceededException;
 import org.apache.commons.fileupload2.FileUploadBase.FileUploadIOException;
 import org.apache.commons.fileupload2.MultipartStream.ItemInputStream;
@@ -40,7 +38,7 @@ import org.apache.commons.fileupload2.util.Streams;
  * Default implementation of {@link FileItemStream}.
  */
 public class FileItemStreamImpl implements FileItemStream {
-	private final FileUploadBase.FileItemIteratorImpl fileItemIteratorImpl;
+	private final FileItemIteratorImpl fileItemIteratorImpl;
 
     /**
      * The file items content type.
@@ -87,7 +85,7 @@ public class FileItemStreamImpl implements FileItemStream {
      * @param pContentLength The items content length, if known, or -1
      * @throws IOException Creating the file item failed.
      */
-    public FileItemStreamImpl(FileUploadBase.FileItemIteratorImpl pFileItemIterator, String pName, String pFieldName,
+    public FileItemStreamImpl(FileItemIteratorImpl pFileItemIterator, String pName, String pFieldName,
             String pContentType, boolean pFormField,
             long pContentLength) throws FileUploadException, IOException {
     	fileItemIteratorImpl = pFileItemIterator;
diff --git a/src/test/java/org/apache/commons/fileupload2/StreamingTest.java b/src/test/java/org/apache/commons/fileupload2/StreamingTest.java
index 0fb11f5..b80bee0 100644
--- a/src/test/java/org/apache/commons/fileupload2/StreamingTest.java
+++ b/src/test/java/org/apache/commons/fileupload2/StreamingTest.java
@@ -267,10 +267,8 @@ public class StreamingTest extends TestCase {
             assertTrue(e.getMessage().indexOf("foo.exe\\0.png") != -1);
         }
 
-        List<FileItem> fileItems = parseUpload(reqBytes);
-        final FileItem fileItem = fileItems.get(0);
         try {
-            fileItem.getName();
+            List<FileItem> fileItems = parseUpload(reqBytes);
             fail("Expected exception");
         } catch (InvalidFileNameException e) {
             assertEquals(fileName, e.getName());