You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2019/12/07 22:30:35 UTC

[tomcat] 03/14: Merge in FileUpload changes to 2317552 (2019-12-06, 2.0-SNAPSHOT)

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

markt pushed a commit to branch 7.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit be8f0e3a1417cce57d42d9727d4fea4619db6fd9
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Fri Dec 6 15:30:48 2019 +0000

    Merge in FileUpload changes to 2317552 (2019-12-06, 2.0-SNAPSHOT)
---
 java/org/apache/catalina/connector/Request.java    |   6 +-
 .../util/http/fileupload/FileItemIterator.java     |  49 +-
 .../util/http/fileupload/FileUploadBase.java       | 708 +--------------------
 .../util/http/fileupload/MultipartStream.java      |  10 +-
 .../util/http/fileupload/disk/DiskFileItem.java    |   3 +-
 .../http/fileupload/impl/FileItemIteratorImpl.java | 339 ++++++++++
 .../http/fileupload/impl/FileItemStreamImpl.java   | 222 +++++++
 .../impl/FileSizeLimitExceededException.java       |  94 +++
 .../fileupload/impl/FileUploadIOException.java     |  62 ++
 .../fileupload/impl/IOFileUploadException.java     |  61 ++
 .../impl/InvalidContentTypeException.java          |  62 ++
 .../util/http/fileupload/impl/SizeException.java   |  75 +++
 .../impl/SizeLimitExceededException.java           |  43 ++
 webapps/docs/changelog.xml                         |   4 +
 14 files changed, 1029 insertions(+), 709 deletions(-)

diff --git a/java/org/apache/catalina/connector/Request.java b/java/org/apache/catalina/connector/Request.java
index f916ad7..954c0a2 100644
--- a/java/org/apache/catalina/connector/Request.java
+++ b/java/org/apache/catalina/connector/Request.java
@@ -94,10 +94,10 @@ import org.apache.tomcat.util.http.Parameters;
 import org.apache.tomcat.util.http.Parameters.FailReason;
 import org.apache.tomcat.util.http.ServerCookie;
 import org.apache.tomcat.util.http.fileupload.FileItem;
-import org.apache.tomcat.util.http.fileupload.FileUploadBase;
-import org.apache.tomcat.util.http.fileupload.FileUploadBase.InvalidContentTypeException;
 import org.apache.tomcat.util.http.fileupload.FileUploadException;
 import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory;
+import org.apache.tomcat.util.http.fileupload.impl.InvalidContentTypeException;
+import org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException;
 import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
 import org.apache.tomcat.util.http.fileupload.servlet.ServletRequestContext;
 import org.apache.tomcat.util.http.mapper.MappingData;
@@ -2996,7 +2996,7 @@ public class Request implements HttpServletRequest {
             } catch (InvalidContentTypeException e) {
                 parameters.setParseFailedReason(FailReason.INVALID_CONTENT_TYPE);
                 partsParseException = new ServletException(e);
-            } catch (FileUploadBase.SizeException e) {
+            } catch (SizeLimitExceededException e) {
                 parameters.setParseFailedReason(FailReason.POST_TOO_LARGE);
                 checkSwallowInput();
                 partsParseException = new IllegalStateException(e);
diff --git a/java/org/apache/tomcat/util/http/fileupload/FileItemIterator.java b/java/org/apache/tomcat/util/http/fileupload/FileItemIterator.java
index 4f331ad..9665312 100644
--- a/java/org/apache/tomcat/util/http/fileupload/FileItemIterator.java
+++ b/java/org/apache/tomcat/util/http/fileupload/FileItemIterator.java
@@ -17,12 +17,56 @@
 package org.apache.tomcat.util.http.fileupload;
 
 import java.io.IOException;
+import java.util.List;
+
+import org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException;
+import org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException;
 
 /**
  * An iterator, as returned by
  * {@link FileUploadBase#getItemIterator(RequestContext)}.
  */
 public interface FileItemIterator {
+    /** Returns the maximum size of a single file. An {@link FileSizeLimitExceededException}
+     * will be thrown, if there is an uploaded file, which is exceeding this value.
+     * By default, this value will be copied from the {@link FileUploadBase#getFileSizeMax()
+     * FileUploadBase} object, however, the user may replace the default value with a
+     * request specific value by invoking {@link #setFileSizeMax(long)} on this object.
+     * @return The maximum size of a single, uploaded file. The value -1 indicates "unlimited".
+     */
+    public long getFileSizeMax();
+
+    /** Sets the maximum size of a single file. An {@link FileSizeLimitExceededException}
+     * will be thrown, if there is an uploaded file, which is exceeding this value.
+     * By default, this value will be copied from the {@link FileUploadBase#getFileSizeMax()
+     * FileUploadBase} object, however, the user may replace the default value with a
+     * request specific value by invoking {@link #setFileSizeMax(long)} on this object, so
+     * there is no need to configure it here.
+     * <em>Note:</em>Changing this value doesn't affect files, that have already been uploaded.
+     * @param pFileSizeMax The maximum size of a single, uploaded file. The value -1 indicates "unlimited".
+     */
+    public void setFileSizeMax(long pFileSizeMax);
+
+    /** Returns the maximum size of the complete HTTP request. A {@link SizeLimitExceededException}
+     * will be thrown, if the HTTP request will exceed this value.
+     * By default, this value will be copied from the {@link FileUploadBase#getSizeMax()
+     * FileUploadBase} object, however, the user may replace the default value with a
+     * request specific value by invoking {@link #setSizeMax(long)} on this object.
+     * @return The maximum size of the complete HTTP requqest. The value -1 indicates "unlimited".
+     */
+    public long getSizeMax();
+
+    /** Returns the maximum size of the complete HTTP request. A {@link SizeLimitExceededException}
+     * will be thrown, if the HTTP request will exceed this value.
+     * By default, this value will be copied from the {@link FileUploadBase#getSizeMax()
+     * FileUploadBase} object, however, the user may replace the default value with a
+     * request specific value by invoking {@link #setSizeMax(long)} on this object.
+     * <em>Note:</em> Setting the maximum size on this object will work only, if the iterator is not
+     * yet initialized. In other words: If the methods {@link #hasNext()}, {@link #next()} have not
+     * yet been invoked.
+     * @param pSizeMax The maximum size of the complete HTTP request. The value -1 indicates "unlimited".
+     */
+    public void setSizeMax(long pSizeMax);
 
     /**
      * Returns, whether another instance of {@link FileItemStream}
@@ -34,7 +78,7 @@ public interface FileItemIterator {
      * @return True, if one or more additional file items
      *   are available, otherwise false.
      */
-    boolean hasNext() throws FileUploadException, IOException;
+    public boolean hasNext() throws FileUploadException, IOException;
 
     /**
      * Returns the next available {@link FileItemStream}.
@@ -47,6 +91,7 @@ public interface FileItemIterator {
      * @return FileItemStream instance, which provides
      *   access to the next file item.
      */
-    FileItemStream next() throws FileUploadException, IOException;
+    public FileItemStream next() throws FileUploadException, IOException;
 
+    public List<FileItem> getFileItems() throws FileUploadException, IOException;
 }
diff --git a/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java b/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java
index 5480a33..8869b9e 100644
--- a/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java
+++ b/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java
@@ -17,19 +17,18 @@
 package org.apache.tomcat.util.http.fileupload;
 
 import java.io.IOException;
-import java.io.InputStream;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.NoSuchElementException;
 
-import org.apache.tomcat.util.http.fileupload.MultipartStream.ItemInputStream;
-import org.apache.tomcat.util.http.fileupload.util.Closeable;
+import org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl;
+import org.apache.tomcat.util.http.fileupload.impl.FileItemStreamImpl;
+import org.apache.tomcat.util.http.fileupload.impl.FileUploadIOException;
+import org.apache.tomcat.util.http.fileupload.impl.IOFileUploadException;
 import org.apache.tomcat.util.http.fileupload.util.FileItemHeadersImpl;
-import org.apache.tomcat.util.http.fileupload.util.LimitedInputStream;
 import org.apache.tomcat.util.http.fileupload.util.Streams;
 
 
@@ -256,7 +255,7 @@ public abstract class FileUploadBase {
     public FileItemIterator getItemIterator(RequestContext ctx)
     throws FileUploadException, IOException {
         try {
-            return new FileItemIteratorImpl(ctx);
+            return new FileItemIteratorImpl(this, ctx);
         } catch (FileUploadIOException e) {
             // unwrap encapsulated SizeException
             throw (FileUploadException) e.getCause();
@@ -288,7 +287,7 @@ public abstract class FileUploadBase {
             while (iter.hasNext()) {
                 final FileItemStream item = iter.next();
                 // Don't use getName() here to prevent an InvalidFileNameException.
-                final String fileName = ((FileItemIteratorImpl.FileItemStreamImpl) item).name;
+                final String fileName = ((FileItemStreamImpl) item).getName();
                 FileItem fileItem = fac.createItem(item.getFieldName(), item.getContentType(),
                                                    item.isFormField(), fileName);
                 items.add(fileItem);
@@ -366,7 +365,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
@@ -390,7 +389,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));
     }
 
@@ -433,7 +432,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));
     }
 
@@ -471,7 +470,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;
@@ -553,693 +552,6 @@ public abstract class FileUploadBase {
     }
 
     /**
-     * The iterator, which is returned by
-     * {@link FileUploadBase#getItemIterator(RequestContext)}.
-     */
-    private class FileItemIteratorImpl implements FileItemIterator {
-
-        /**
-         * Default implementation of {@link FileItemStream}.
-         */
-        class FileItemStreamImpl implements FileItemStream {
-
-            /**
-             * The file items content type.
-             */
-            private final String contentType;
-
-            /**
-             * The file items field name.
-             */
-            private final String fieldName;
-
-            /**
-             * The file items file name.
-             */
-            private final String name;
-
-            /**
-             * Whether the file item is a form field.
-             */
-            private final boolean formField;
-
-            /**
-             * The file items input stream.
-             */
-            private final InputStream stream;
-
-            /**
-             * Whether the file item was already opened.
-             */
-            private boolean opened;
-
-            /**
-             * The headers, if any.
-             */
-            private FileItemHeaders headers;
-
-            /**
-             * Creates a new instance.
-             *
-             * @param pName The items file name, or null.
-             * @param pFieldName The items field name.
-             * @param pContentType The items content type, or null.
-             * @param pFormField Whether the item is a form field.
-             * @param pContentLength The items content length, if known, or -1
-             * @throws IOException Creating the file item failed.
-             */
-            FileItemStreamImpl(String pName, String pFieldName,
-                    String pContentType, boolean pFormField,
-                    long pContentLength) throws IOException {
-                name = pName;
-                fieldName = pFieldName;
-                contentType = pContentType;
-                formField = pFormField;
-                final ItemInputStream itemStream = multi.newInputStream();
-                InputStream istream = itemStream;
-                if (fileSizeMax != -1) {
-                    if (pContentLength != -1
-                            &&  pContentLength > fileSizeMax) {
-                        FileSizeLimitExceededException e =
-                            new FileSizeLimitExceededException(
-                                String.format("The field %s exceeds its maximum permitted size of %s bytes.",
-                                        fieldName, Long.valueOf(fileSizeMax)),
-                                pContentLength, fileSizeMax);
-                        e.setFileName(pName);
-                        e.setFieldName(pFieldName);
-                        throw new FileUploadIOException(e);
-                    }
-                    istream = new LimitedInputStream(istream, fileSizeMax) {
-                        @Override
-                        protected void raiseError(long pSizeMax, long pCount)
-                                throws IOException {
-                            itemStream.close(true);
-                            FileSizeLimitExceededException e =
-                                new FileSizeLimitExceededException(
-                                    String.format("The field %s exceeds its maximum permitted size of %s bytes.",
-                                           fieldName, Long.valueOf(pSizeMax)),
-                                    pCount, pSizeMax);
-                            e.setFieldName(fieldName);
-                            e.setFileName(name);
-                            throw new FileUploadIOException(e);
-                        }
-                    };
-                }
-                stream = istream;
-            }
-
-            /**
-             * Returns the items content type, or null.
-             *
-             * @return Content type, if known, or null.
-             */
-            @Override
-            public String getContentType() {
-                return contentType;
-            }
-
-            /**
-             * Returns the items field name.
-             *
-             * @return Field name.
-             */
-            @Override
-            public String getFieldName() {
-                return fieldName;
-            }
-
-            /**
-             * Returns the items file name.
-             *
-             * @return File name, if known, or null.
-             * @throws InvalidFileNameException The file name contains a NUL character,
-             *   which might be an indicator of a security attack. If you intend to
-             *   use the file name anyways, catch the exception and use
-             *   InvalidFileNameException#getName().
-             */
-            @Override
-            public String getName() {
-                return Streams.checkFileName(name);
-            }
-
-            /**
-             * Returns, whether this is a form field.
-             *
-             * @return True, if the item is a form field,
-             *   otherwise false.
-             */
-            @Override
-            public boolean isFormField() {
-                return formField;
-            }
-
-            /**
-             * Returns an input stream, which may be used to
-             * read the items contents.
-             *
-             * @return Opened input stream.
-             * @throws IOException An I/O error occurred.
-             */
-            @Override
-            public InputStream openStream() throws IOException {
-                if (opened) {
-                    throw new IllegalStateException(
-                            "The stream was already opened.");
-                }
-                if (((Closeable) stream).isClosed()) {
-                    throw new FileItemStream.ItemSkippedException();
-                }
-                return stream;
-            }
-
-            /**
-             * Closes the file item.
-             *
-             * @throws IOException An I/O error occurred.
-             */
-            void close() throws IOException {
-                stream.close();
-            }
-
-            /**
-             * Returns the file item headers.
-             *
-             * @return The items header object
-             */
-            @Override
-            public FileItemHeaders getHeaders() {
-                return headers;
-            }
-
-            /**
-             * Sets the file item headers.
-             *
-             * @param pHeaders The items header object
-             */
-            @Override
-            public void setHeaders(FileItemHeaders pHeaders) {
-                headers = pHeaders;
-            }
-
-        }
-
-        /**
-         * The multi part stream to process.
-         */
-        private final MultipartStream multi;
-
-        /**
-         * The notifier, which used for triggering the
-         * {@link ProgressListener}.
-         */
-        private final MultipartStream.ProgressNotifier notifier;
-
-        /**
-         * The boundary, which separates the various parts.
-         */
-        private final byte[] boundary;
-
-        /**
-         * 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(RequestContext ctx)
-                throws FileUploadException, IOException {
-            if (ctx == null) {
-                throw new NullPointerException("ctx parameter");
-            }
-
-            String contentType = ctx.getContentType();
-            if ((null == contentType)
-                    || (!contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART))) {
-                throw new InvalidContentTypeException(String.format(
-                        "the request doesn't contain a %s or %s stream, content type header is %s",
-                        MULTIPART_FORM_DATA, MULTIPART_MIXED, contentType));
-            }
-
-
-            final long requestSize = ((UploadContext) ctx).contentLength();
-
-            InputStream input; // N.B. this is eventually closed in MultipartStream processing
-            if (sizeMax >= 0) {
-                if (requestSize != -1 && requestSize > sizeMax) {
-                    throw new SizeLimitExceededException(String.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(
-                        String.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 = headerEncoding;
-            if (charEncoding == null) {
-                charEncoding = ctx.getCharacterEncoding();
-            }
-
-            boundary = getBoundary(contentType);
-            if (boundary == null) {
-                IOUtils.closeQuietly(input); // avoid possible resource leak
-                throw new FileUploadException("the request was rejected because no multipart boundary was found");
-            }
-
-            notifier = new MultipartStream.ProgressNotifier(listener, requestSize);
-            try {
-                multi = new MultipartStream(input, boundary, notifier);
-            } catch (IllegalArgumentException iae) {
-                IOUtils.closeQuietly(input); // avoid possible resource leak
-                throw new InvalidContentTypeException(
-                        String.format("The boundary specified in the %s header is too long", CONTENT_TYPE), iae);
-            }
-            multi.setHeaderEncoding(charEncoding);
-
-            skipPreamble = true;
-            findNextItem();
-        }
-
-        /**
-         * 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 IOException {
-            if (eof) {
-                return false;
-            }
-            if (currentItem != null) {
-                currentItem.close();
-                currentItem = null;
-            }
-            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(boundary);
-                    currentFieldName = null;
-                    continue;
-                }
-                FileItemHeaders headers = getParsedHeaders(multi.readHeaders());
-                if (currentFieldName == null) {
-                    // We're parsing the outer multipart
-                    String fieldName = 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 = getBoundary(subContentType);
-                            multi.setBoundary(subBoundary);
-                            skipPreamble = true;
-                            continue;
-                        }
-                        String fileName = getFileName(headers);
-                        currentItem = new FileItemStreamImpl(fileName,
-                                fieldName, headers.getHeader(CONTENT_TYPE),
-                                fileName == null, getContentLength(headers));
-                        currentItem.setHeaders(headers);
-                        notifier.noteItem();
-                        itemValid = true;
-                        return true;
-                    }
-                } else {
-                    String fileName = getFileName(headers);
-                    if (fileName != null) {
-                        currentItem = new FileItemStreamImpl(fileName,
-                                currentFieldName,
-                                headers.getHeader(CONTENT_TYPE),
-                                false, getContentLength(headers));
-                        currentItem.setHeaders(headers);
-                        notifier.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;
-        }
-
-    }
-
-    /**
-     * This exception is thrown for hiding an inner
-     * {@link FileUploadException} in an {@link IOException}.
-     */
-    public static class FileUploadIOException extends IOException {
-
-        private static final long serialVersionUID = -3082868232248803474L;
-
-        public FileUploadIOException() {
-            super();
-        }
-
-        public FileUploadIOException(String message, Throwable cause) {
-            super(message, cause);
-        }
-
-        public FileUploadIOException(String message) {
-            super(message);
-        }
-
-        public FileUploadIOException(Throwable cause) {
-            super(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 {
-
-        private static final long serialVersionUID = -5858565745868986701L;
-
-        public IOFileUploadException() {
-            super();
-        }
-
-        public IOFileUploadException(String message, Throwable cause) {
-            super(message, cause);
-        }
-
-        public IOFileUploadException(String message) {
-            super(message);
-        }
-
-        public IOFileUploadException(Throwable cause) {
-            super(cause);
-        }
-    }
-
-    /**
-     * This exception is thrown, if a requests permitted size
-     * is exceeded.
-     */
-    public 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 exceeds the configured maximum.
-     */
-    public static 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);
-        }
-
-    }
-
-    /**
-     * 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/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java b/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
index 351fa02..c3616c3 100644
--- a/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
+++ b/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
@@ -22,7 +22,7 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
 
-import org.apache.tomcat.util.http.fileupload.FileUploadBase.FileUploadIOException;
+import org.apache.tomcat.util.http.fileupload.impl.FileUploadIOException;
 import org.apache.tomcat.util.http.fileupload.util.Closeable;
 import org.apache.tomcat.util.http.fileupload.util.Streams;
 
@@ -115,7 +115,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;
         }
@@ -136,7 +136,7 @@ public class MultipartStream {
         /**
          * Called to indicate, that a new file item has been detected.
          */
-        void noteItem() {
+        public void noteItem() {
             ++items;
             notifyListener();
         }
@@ -332,7 +332,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);
@@ -576,7 +576,7 @@ public class MultipartStream {
      * Creates a new {@link ItemInputStream}.
      * @return A new instance of {@link ItemInputStream}.
      */
-    ItemInputStream newInputStream() {
+    public ItemInputStream newInputStream() {
         return new ItemInputStream();
     }
 
diff --git a/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java b/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java
index e899ef3..ac2eb25 100644
--- a/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java
+++ b/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java
@@ -540,7 +540,7 @@ public class DiskFileItem
      * Removes the file contents from the temporary storage.
      */
     @Override
-    protected void finalize() {
+    protected void finalize() throws Throwable {
         if (dfos == null || dfos.isInMemory()) {
             return;
         }
@@ -549,6 +549,7 @@ public class DiskFileItem
         if (outputFile != null && outputFile.exists()) {
             outputFile.delete();
         }
+        super.finalize();
     }
 
     /**
diff --git a/java/org/apache/tomcat/util/http/fileupload/impl/FileItemIteratorImpl.java b/java/org/apache/tomcat/util/http/fileupload/impl/FileItemIteratorImpl.java
new file mode 100644
index 0000000..58b46ca
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/fileupload/impl/FileItemIteratorImpl.java
@@ -0,0 +1,339 @@
+/*
+ * 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.tomcat.util.http.fileupload.impl;
+
+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.tomcat.util.http.fileupload.FileItem;
+import org.apache.tomcat.util.http.fileupload.FileItemHeaders;
+import org.apache.tomcat.util.http.fileupload.FileItemIterator;
+import org.apache.tomcat.util.http.fileupload.FileItemStream;
+import org.apache.tomcat.util.http.fileupload.FileUploadBase;
+import org.apache.tomcat.util.http.fileupload.FileUploadException;
+import org.apache.tomcat.util.http.fileupload.IOUtils;
+import org.apache.tomcat.util.http.fileupload.MultipartStream;
+import org.apache.tomcat.util.http.fileupload.ProgressListener;
+import org.apache.tomcat.util.http.fileupload.RequestContext;
+import org.apache.tomcat.util.http.fileupload.UploadContext;
+import org.apache.tomcat.util.http.fileupload.util.LimitedInputStream;
+
+/**
+ * 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 pFileUploadBase Upload instance
+     * @param pRequestContext 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, @SuppressWarnings("unused") RequestContext pRequestContext)
+            throws FileUploadException, IOException {
+        String contentType = ctx.getContentType();
+        if ((null == contentType)
+                || (!contentType.toLowerCase(Locale.ENGLISH).startsWith(FileUploadBase.MULTIPART))) {
+            throw new InvalidContentTypeException(
+                    String.format("the request doesn't contain a %s or %s stream, content type header is %s",
+                           FileUploadBase.MULTIPART_FORM_DATA, FileUploadBase.MULTIPART_MIXED, contentType));
+        }
+
+        final long requestSize = ((UploadContext) ctx).contentLength();
+
+        InputStream input; // N.B. this is eventually closed in MultipartStream processing
+        if (sizeMax >= 0) {
+            if (requestSize != -1 && requestSize > sizeMax) {
+                throw new SizeLimitExceededException(
+                    String.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(
+                    String.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(
+                    String.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/java/org/apache/tomcat/util/http/fileupload/impl/FileItemStreamImpl.java b/java/org/apache/tomcat/util/http/fileupload/impl/FileItemStreamImpl.java
new file mode 100644
index 0000000..29427e6
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/fileupload/impl/FileItemStreamImpl.java
@@ -0,0 +1,222 @@
+/*
+ * 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.tomcat.util.http.fileupload.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.tomcat.util.http.fileupload.FileItemHeaders;
+import org.apache.tomcat.util.http.fileupload.FileItemStream;
+import org.apache.tomcat.util.http.fileupload.FileUploadException;
+import org.apache.tomcat.util.http.fileupload.InvalidFileNameException;
+import org.apache.tomcat.util.http.fileupload.MultipartStream.ItemInputStream;
+import org.apache.tomcat.util.http.fileupload.util.Closeable;
+import org.apache.tomcat.util.http.fileupload.util.LimitedInputStream;
+import org.apache.tomcat.util.http.fileupload.util.Streams;
+
+
+/**
+ * Default implementation of {@link FileItemStream}.
+ */
+public class FileItemStreamImpl implements FileItemStream {
+    private final FileItemIteratorImpl fileItemIteratorImpl;
+
+    /**
+     * The file items content type.
+     */
+    private final String contentType;
+
+    /**
+     * The file items field name.
+     */
+    private final String fieldName;
+
+    /**
+     * The file items file name.
+     */
+    final String name;
+
+    /**
+     * Whether the file item is a form field.
+     */
+    private final boolean formField;
+
+    /**
+     * The file items input stream.
+     */
+    private final InputStream stream;
+
+    /**
+     * Whether the file item was already opened.
+     */
+    private boolean opened;
+
+    /**
+     * The headers, if any.
+     */
+    private FileItemHeaders headers;
+
+    /**
+     * Creates a new instance.
+     * @param pFileItemIterator Iterator for all files in this upload
+     * @param pName The items file name, or null.
+     * @param pFieldName The items field name.
+     * @param pContentType The items content type, or null.
+     * @param pFormField Whether the item is a form field.
+     * @param pContentLength The items content length, if known, or -1
+     * @throws FileUploadException If an error is encountered processing the request
+     * @throws IOException Creating the file item failed.
+     */
+    public FileItemStreamImpl(FileItemIteratorImpl pFileItemIterator, String pName, String pFieldName,
+            String pContentType, boolean pFormField,
+            long pContentLength) throws FileUploadException, IOException {
+        fileItemIteratorImpl = pFileItemIterator;
+        name = pName;
+        fieldName = pFieldName;
+        contentType = pContentType;
+        formField = pFormField;
+        final long fileSizeMax = fileItemIteratorImpl.getFileSizeMax();
+        if (fileSizeMax != -1) { // Check if limit is already exceeded
+            if (pContentLength != -1
+                    && pContentLength > fileSizeMax) {
+                FileSizeLimitExceededException e =
+                        new FileSizeLimitExceededException(
+                                String.format("The field %s exceeds its maximum permitted size of %s bytes.",
+                                        fieldName, Long.valueOf(fileSizeMax)),
+                                pContentLength, fileSizeMax);
+                e.setFileName(pName);
+                e.setFieldName(pFieldName);
+                throw new FileUploadIOException(e);
+            }
+        }
+        // OK to construct stream now
+        final ItemInputStream itemStream = fileItemIteratorImpl.getMultiPartStream().newInputStream();
+        InputStream istream = itemStream;
+        if (fileSizeMax != -1) {
+            istream = new LimitedInputStream(istream, fileSizeMax) {
+                @Override
+                protected void raiseError(long pSizeMax, long pCount)
+                        throws IOException {
+                    itemStream.close(true);
+                    FileSizeLimitExceededException e =
+                        new FileSizeLimitExceededException(
+                            String.format("The field %s exceeds its maximum permitted size of %s bytes.",
+                                   fieldName, Long.valueOf(pSizeMax)),
+                            pCount, pSizeMax);
+                    e.setFieldName(fieldName);
+                    e.setFileName(name);
+                    throw new FileUploadIOException(e);
+                }
+            };
+        }
+        stream = istream;
+    }
+
+    /**
+     * Returns the items content type, or null.
+     *
+     * @return Content type, if known, or null.
+     */
+    @Override
+    public String getContentType() {
+        return contentType;
+    }
+
+    /**
+     * Returns the items field name.
+     *
+     * @return Field name.
+     */
+    @Override
+    public String getFieldName() {
+        return fieldName;
+    }
+
+    /**
+     * Returns the items file name.
+     *
+     * @return File name, if known, or null.
+     * @throws InvalidFileNameException The file name contains a NUL character,
+     *   which might be an indicator of a security attack. If you intend to
+     *   use the file name anyways, catch the exception and use
+     *   InvalidFileNameException#getName().
+     */
+    @Override
+    public String getName() {
+        return Streams.checkFileName(name);
+    }
+
+    /**
+     * Returns, whether this is a form field.
+     *
+     * @return True, if the item is a form field,
+     *   otherwise false.
+     */
+    @Override
+    public boolean isFormField() {
+        return formField;
+    }
+
+    /**
+     * Returns an input stream, which may be used to
+     * read the items contents.
+     *
+     * @return Opened input stream.
+     * @throws IOException An I/O error occurred.
+     */
+    @Override
+    public InputStream openStream() throws IOException {
+        if (opened) {
+            throw new IllegalStateException(
+                    "The stream was already opened.");
+        }
+        if (((Closeable) stream).isClosed()) {
+            throw new FileItemStream.ItemSkippedException();
+        }
+        return stream;
+    }
+
+    /**
+     * Closes the file item.
+     *
+     * @throws IOException An I/O error occurred.
+     */
+    public void close() throws IOException {
+        stream.close();
+    }
+
+    /**
+     * Returns the file item headers.
+     *
+     * @return The items header object
+     */
+    @Override
+    public FileItemHeaders getHeaders() {
+        return headers;
+    }
+
+    /**
+     * Sets the file item headers.
+     *
+     * @param pHeaders The items header object
+     */
+    @Override
+    public void setHeaders(FileItemHeaders pHeaders) {
+        headers = pHeaders;
+    }
+
+}
\ No newline at end of file
diff --git a/java/org/apache/tomcat/util/http/fileupload/impl/FileSizeLimitExceededException.java b/java/org/apache/tomcat/util/http/fileupload/impl/FileSizeLimitExceededException.java
new file mode 100644
index 0000000..3eaa9d8
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/fileupload/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.tomcat.util.http.fileupload.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/java/org/apache/tomcat/util/http/fileupload/impl/FileUploadIOException.java b/java/org/apache/tomcat/util/http/fileupload/impl/FileUploadIOException.java
new file mode 100644
index 0000000..8a4066a
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/fileupload/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.tomcat.util.http.fileupload.impl;
+
+import java.io.IOException;
+
+import org.apache.tomcat.util.http.fileupload.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/java/org/apache/tomcat/util/http/fileupload/impl/IOFileUploadException.java b/java/org/apache/tomcat/util/http/fileupload/impl/IOFileUploadException.java
new file mode 100644
index 0000000..e1049c7
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/fileupload/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.tomcat.util.http.fileupload.impl;
+
+import java.io.IOException;
+
+import org.apache.tomcat.util.http.fileupload.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/java/org/apache/tomcat/util/http/fileupload/impl/InvalidContentTypeException.java b/java/org/apache/tomcat/util/http/fileupload/impl/InvalidContentTypeException.java
new file mode 100644
index 0000000..295597b
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/fileupload/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.tomcat.util.http.fileupload.impl;
+
+import org.apache.tomcat.util.http.fileupload.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/java/org/apache/tomcat/util/http/fileupload/impl/SizeException.java b/java/org/apache/tomcat/util/http/fileupload/impl/SizeException.java
new file mode 100644
index 0000000..4852795
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/fileupload/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.tomcat.util.http.fileupload.impl;
+
+import org.apache.tomcat.util.http.fileupload.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/java/org/apache/tomcat/util/http/fileupload/impl/SizeLimitExceededException.java b/java/org/apache/tomcat/util/http/fileupload/impl/SizeLimitExceededException.java
new file mode 100644
index 0000000..668e99b
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/fileupload/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.tomcat.util.http.fileupload.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/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 1dad7fe..ce55169 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -165,6 +165,10 @@
         Update the internal fork of Apache Commons Codec to 9637dd4 (2019-12-06,
         1.14-SNAPSHOT). Code clean-up and a fix for CODEC-265. (markt)
       </add>
+      <add>
+        Update the internal fork of Apache Commons FileUpload to 2317552
+        (2019-12-06, 2.0-SNAPSHOT). Refactoring. (markt)
+      </add>
     </changelog>
   </subsection>
 </section>


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org