Refactor into Maven modules

This commit is contained in:
Gary Gregory 2023-04-30 17:25:30 -04:00
parent ed002cb037
commit e654cc4b13
85 changed files with 3331 additions and 3399 deletions

View File

@ -1,13 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<fileset-config file-format-version="1.2.0" simple-config="false" sync-formatter="false">
<local-check-config name="maven-checkstyle-plugin validate-main" location="file:/Users/garydgregory/git/commons-fileupload/src/checkstyle/fileupload_checks.xml" type="remote" description="maven-checkstyle-plugin configuration validate-main">
<property name="checkstyle.header.file" value="/Users/garydgregory/eclipse-workspace/apache-commons/.metadata/.plugins/org.eclipse.core.resources/.projects/commons-fileupload2/com.basistech.m2e.code.quality.checkstyleConfigurator/checkstyle-header-validate-main.txt"/>
<local-check-config name="maven-checkstyle-plugin validate-main" location="file:/C:/Users/ggregory/git/a/commons-fileupload/src/checkstyle/fileupload_checks.xml" type="remote" description="maven-checkstyle-plugin configuration validate-main">
<property name="checkstyle.header.file" value="C:\Users\ggregory\OneDrive - Rocket Software, Inc\ew\apache-commons\.metadata\.plugins\org.eclipse.core.resources\.projects\commons-fileupload2\com.basistech.m2e.code.quality.checkstyleConfigurator\checkstyle-header-validate-main.txt"/>
<property name="checkstyle.cache.file" value="${project_loc}/target/checkstyle-cachefile"/>
<property name="checkstyle.suppressions.file" value="/Users/garydgregory/eclipse-workspace/apache-commons/.metadata/.plugins/org.eclipse.core.resources/.projects/commons-fileupload2/com.basistech.m2e.code.quality.checkstyleConfigurator/checkstyle-suppressions-validate-main.xml"/>
<property name="checkstyle.suppressions.file" value="C:\Users\ggregory\OneDrive - Rocket Software, Inc\ew\apache-commons\.metadata\.plugins\org.eclipse.core.resources\.projects\commons-fileupload2\com.basistech.m2e.code.quality.checkstyleConfigurator\checkstyle-suppressions-validate-main.xml"/>
</local-check-config>
<fileset name="java-sources-validate-main" enabled="true" check-config-name="maven-checkstyle-plugin validate-main" local="true">
<file-match-pattern match-pattern="^src/main/java/.*\/.*\.java" include-pattern="true"/>
<file-match-pattern match-pattern="^src/main/resources.*\.properties" include-pattern="true"/>
<file-match-pattern match-pattern="^.*\.properties" include-pattern="true"/>
<file-match-pattern match-pattern="^src/test/resources.*\.properties" include-pattern="true"/>

10
.gitignore vendored
View File

@ -1,6 +1,6 @@
/target/
/.settings/
/.classpath
/.project
site-content/
**/target/
**/.settings/
**/.classpath
**/.project
**/site-content/
/commons-fileupload2.iml

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<fileset-config file-format-version="1.2.0" simple-config="false" sync-formatter="false">
<local-check-config name="maven-checkstyle-plugin validate-main" location="file:/C:/Users/ggregory/git/a/commons-fileupload/commons-fileupload2-core/../src/checkstyle/fileupload_checks.xml" type="remote" description="maven-checkstyle-plugin configuration validate-main">
<property name="checkstyle.header.file" value="C:\Users\ggregory\OneDrive - Rocket Software, Inc\ew\apache-commons\.metadata\.plugins\org.eclipse.core.resources\.projects\commons-fileupload2-core\com.basistech.m2e.code.quality.checkstyleConfigurator\checkstyle-header-validate-main.txt"/>
<property name="checkstyle.cache.file" value="${project_loc}/target/checkstyle-cachefile"/>
<property name="checkstyle.suppressions.file" value="C:\Users\ggregory\OneDrive - Rocket Software, Inc\ew\apache-commons\.metadata\.plugins\org.eclipse.core.resources\.projects\commons-fileupload2-core\com.basistech.m2e.code.quality.checkstyleConfigurator\checkstyle-suppressions-validate-main.xml"/>
</local-check-config>
<fileset name="java-sources-validate-main" enabled="true" check-config-name="maven-checkstyle-plugin validate-main" local="true">
<file-match-pattern match-pattern="^src/main/java/.*\.java" include-pattern="true"/>
<file-match-pattern match-pattern="^src/main/resources.*\.properties" include-pattern="true"/>
<file-match-pattern match-pattern="^.*\.properties" include-pattern="true"/>
<file-match-pattern match-pattern="^src/test/resources.*\.properties" include-pattern="true"/>
</fileset>
</fileset-config>

1
commons-fileupload2-core/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target/

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.commons</groupId>
<artifactId>commons-fileupload2</artifactId>
<version>2.0.0-SNAPSHOT</version>
</parent>
<artifactId>commons-fileupload2-core</artifactId>
<name>Apache Commons FileUpload Core</name>
<description>
The Apache Commons FileUpload component provides a simple yet flexible means of adding support for multipart
file upload functionality to servlets and web applications.
</description>
<properties>
<commons.parent.dir>${basedir}/..</commons.parent.dir>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons.lang3.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.moditect</groupId>
<artifactId>moditect-maven-plugin</artifactId>
<version>${moditect-maven-plugin.version}</version>
<executions>
<execution>
<id>add-module-infos</id>
<phase>package</phase>
<goals>
<goal>add-module-info</goal>
</goals>
<configuration>
<jvmVersion>9</jvmVersion>
<outputDirectory>${project.build.directory}</outputDirectory>
<overwriteExistingFiles>true</overwriteExistingFiles>
<module>
<moduleInfo>
<name>org.apache.commons.fileupload2</name>
<exports>
!org.apache.commons.fileupload2.impl;
*;
</exports>
</moduleInfo>
</module>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -16,7 +16,6 @@
*/
package org.apache.commons.fileupload2;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -36,17 +35,12 @@ import org.apache.commons.io.IOUtils;
/**
* High level API for processing file uploads.
* <p>
* This class handles multiple files per single HTML widget, sent using
* {@code multipart/mixed} encoding type, as specified by
* <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Use {@link
* #parseRequest(RequestContext)} to acquire a list of {@link
* org.apache.commons.fileupload2.FileItem}s associated with a given HTML
* widget.
* This class handles multiple files per single HTML widget, sent using {@code multipart/mixed} encoding type, as specified by
* <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Use {@link #parseRequest(RequestContext)} to acquire a list of
* {@link org.apache.commons.fileupload2.FileItem}s associated with a given HTML widget.
* </p>
* <p>
* How the data for individual parts is stored is determined by the factory
* used to create them; a given part may be in memory, on disk, or somewhere
* else.
* How the data for individual parts is stored is determined by the factory used to create them; a given part may be in memory, on disk, or somewhere else.
* </p>
*/
public abstract class AbstractFileUpload {
@ -92,17 +86,14 @@ public abstract class AbstractFileUpload {
public static final String MULTIPART_MIXED = "multipart/mixed";
/**
* Utility method that determines whether the request contains multipart
* content.
* Utility method that determines whether the request contains multipart content.
* <p>
* <strong>NOTE:</strong> This method will be moved to the
* {@code ServletFileUpload} class after the FileUpload 1.1 release.
* Unfortunately, since this method is static, it is not possible to
* provide its replacement until this method is removed.
* <strong>NOTE:</strong> This method will be moved to the {@code ServletFileUpload} class after the FileUpload 1.1 release. Unfortunately, since this
* method is static, it is not possible to provide its replacement until this method is removed.
* </p>
*
* @param ctx The request context to be evaluated. Must be non-null.
* @return {@code true} if the request is multipart;
* {@code false} otherwise.
* @return {@code true} if the request is multipart; {@code false} otherwise.
*/
public static final boolean isMultipartContent(final RequestContext ctx) {
final String contentType = ctx.getContentType();
@ -113,20 +104,17 @@ public abstract class AbstractFileUpload {
}
/**
* The maximum size permitted for the complete request, as opposed to
* {@link #fileSizeMax}. A value of -1 indicates no maximum.
* The maximum size permitted for the complete request, as opposed to {@link #fileSizeMax}. A value of -1 indicates no maximum.
*/
private long sizeMax = -1;
/**
* The maximum size permitted for a single uploaded file, as opposed
* to {@link #sizeMax}. A value of -1 indicates no maximum.
* The maximum size permitted for a single uploaded file, as opposed to {@link #sizeMax}. A value of -1 indicates no maximum.
*/
private long fileSizeMax = -1;
/**
* The maximum permitted number of files that may be uploaded in a single
* request. A value of -1 indicates no maximum.
* The maximum permitted number of files that may be uploaded in a single request. A value of -1 indicates no maximum.
*/
private long fileCountMax = -1;
@ -143,15 +131,14 @@ public abstract class AbstractFileUpload {
/**
* Gets the boundary from the {@code Content-type} header.
*
* @param contentType The value of the content type header from which to
* extract the boundary value.
* @param contentType The value of the content type header from which to extract the boundary value.
* @return The boundary, as a byte array.
*/
public byte[] getBoundary(final String contentType) {
final ParameterParser parser = new ParameterParser();
parser.setLowerCaseNames(true);
// Parameter parser can handle null input
final Map<String, String> params = parser.parse(contentType, new char[] {';', ','});
final Map<String, String> params = parser.parse(contentType, new char[] { ';', ',' });
final String boundaryStr = params.get("boundary");
if (boundaryStr == null) {
@ -163,8 +150,7 @@ public abstract class AbstractFileUpload {
}
/**
* Gets the field name from the {@code Content-disposition}
* header.
* Gets the field name from the {@code Content-disposition} header.
*
* @param headers A {@code Map} containing the HTTP request headers.
* @return The field name for the current {@code encapsulation}.
@ -174,16 +160,14 @@ public abstract class AbstractFileUpload {
}
/**
* Gets the field name, which is given by the content-disposition
* header.
* Gets the field name, which is given by the content-disposition header.
*
* @param contentDisposition The content-dispositions header value.
* @return The field jake
*/
private String getFieldName(final String contentDisposition) {
String fieldName = null;
if (contentDisposition != null
&& contentDisposition.toLowerCase(Locale.ENGLISH).startsWith(FORM_DATA)) {
if (contentDisposition != null && contentDisposition.toLowerCase(Locale.ENGLISH).startsWith(FORM_DATA)) {
final ParameterParser parser = new ParameterParser();
parser.setLowerCaseNames(true);
// Parameter parser can handle null input
@ -213,8 +197,7 @@ public abstract class AbstractFileUpload {
public abstract FileItemFactory getFileItemFactory();
/**
* Gets the file name from the {@code Content-disposition}
* header.
* Gets the file name from the {@code Content-disposition} header.
*
* @param headers The HTTP headers object.
*
@ -226,6 +209,7 @@ public abstract class AbstractFileUpload {
/**
* Gets the given content-disposition headers file name.
*
* @param contentDisposition The content-disposition headers value.
* @return The file name
*/
@ -255,8 +239,7 @@ public abstract class AbstractFileUpload {
}
/**
* Gets the maximum allowed size of a single uploaded file,
* as opposed to {@link #getSizeMax()}.
* Gets the maximum allowed size of a single uploaded file, as opposed to {@link #getSizeMax()}.
*
* @see #setFileSizeMax(long)
* @return Maximum size of a single uploaded file.
@ -266,10 +249,8 @@ public abstract class AbstractFileUpload {
}
/**
* Gets the character encoding used when reading the headers of an
* individual part. When not specified, or {@code null}, the request
* encoding is used. If that is also not specified, or {@code null},
* the platform default encoding is used.
* Gets the character encoding used when reading the headers of an individual part. When not specified, or {@code null}, the request encoding is used. If
* that is also not specified, or {@code null}, the platform default encoding is used.
*
* @return The encoding used to read part headers.
*/
@ -278,32 +259,25 @@ public abstract class AbstractFileUpload {
}
/**
* Gets an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
* compliant {@code multipart/form-data} stream.
* Gets an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant {@code multipart/form-data} stream.
*
* @param ctx The context for the request to be parsed.
* @return An iterator to instances of {@code FileItemStream}
* parsed from the request, in the order that they were
* transmitted.
* @throws FileUploadException if there are problems reading/parsing
* the request or storing files.
* @throws IOException An I/O error occurred. This may be a network
* error while communicating with the client or a problem while
* storing the uploaded content.
* @return An iterator to instances of {@code FileItemStream} parsed from the request, in the order that they were transmitted.
* @throws FileUploadException if there are problems reading/parsing the request or storing files.
* @throws IOException An I/O error occurred. This may be a network error while communicating with the client or a problem while storing the
* uploaded content.
*/
public FileItemIterator getItemIterator(final RequestContext ctx) throws FileUploadException, IOException {
return new FileItemIteratorImpl(this, ctx);
}
/**
* Parses the {@code header-part} and returns as key/value
* pairs.
* Parses the {@code header-part} and returns as key/value pairs.
* <p>
* If there are multiple headers of the same names, the name
* will map to a comma-separated list containing the values.
* If there are multiple headers of the same names, the name will map to a comma-separated list containing the values.
* </p>
* @param headerPart The {@code header-part} of the current
* {@code encapsulation}.
*
* @param headerPart The {@code header-part} of the current {@code encapsulation}.
* @return A {@code Map} containing the parsed HTTP request headers.
*/
public FileItemHeaders getParsedHeaders(final String headerPart) {
@ -321,7 +295,7 @@ public abstract class AbstractFileUpload {
int nonWs = start;
while (nonWs < len) {
final char c = headerPart.charAt(nonWs);
if (c != ' ' && c != '\t') {
if (c != ' ' && c != '\t') {
break;
}
++nonWs;
@ -349,11 +323,9 @@ public abstract class AbstractFileUpload {
}
/**
* Gets the maximum allowed size of a complete request, as opposed
* to {@link #getFileSizeMax()}.
* Gets the maximum allowed size of a complete request, as opposed to {@link #getFileSizeMax()}.
*
* @return The maximum allowed size, in bytes. The default value of
* -1 indicates, that there is no limit.
* @return The maximum allowed size, in bytes. The default value of -1 indicates, that there is no limit.
* @see #setSizeMax(long)
*
*/
@ -363,6 +335,7 @@ public abstract class AbstractFileUpload {
/**
* Creates a new instance of {@link FileItemHeaders}.
*
* @return The new instance.
*/
protected FileItemHeadersImpl newFileItemHeaders() {
@ -373,18 +346,15 @@ public abstract class AbstractFileUpload {
* Skips bytes until the end of the current line.
*
* @param headerPart The headers, which are being parsed.
* @param end Index of the last byte, which has yet been
* processed.
* @return Index of the \r\n sequence, which indicates
* end of line.
* @param end Index of the last byte, which has yet been processed.
* @return Index of the \r\n sequence, which indicates end of line.
*/
private int parseEndOfLine(final String headerPart, final int end) {
int index = end;
for (;;) {
final int offset = headerPart.indexOf('\r', index);
if (offset == -1 || offset + 1 >= headerPart.length()) {
throw new IllegalStateException(
"Expected headers to be terminated by an empty line.");
if (offset == -1 || offset + 1 >= headerPart.length()) {
throw new IllegalStateException("Expected headers to be terminated by an empty line.");
}
if (headerPart.charAt(offset + 1) == '\n') {
return offset;
@ -397,7 +367,7 @@ public abstract class AbstractFileUpload {
* Parses the next header line.
*
* @param headers String with all headers.
* @param header Map where to store the current header.
* @param header Map where to store the current header.
*/
private void parseHeaderLine(final FileItemHeadersImpl headers, final String header) {
final int colonOffset = header.indexOf(':');
@ -406,23 +376,19 @@ public abstract class AbstractFileUpload {
return;
}
final String headerName = header.substring(0, colonOffset).trim();
final String headerValue =
header.substring(colonOffset + 1).trim();
final String headerValue = header.substring(colonOffset + 1).trim();
headers.addHeader(headerName, headerValue);
}
/**
* Parses an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
* compliant {@code multipart/form-data} stream.
* Parses an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant {@code multipart/form-data} stream.
*
* @param ctx The context for the request to be parsed.
* @return A map of {@code FileItem} instances parsed from the request.
* @throws FileUploadException if there are problems reading/parsing
* the request or storing files.
* @throws FileUploadException if there are problems reading/parsing the request or storing files.
* @since 1.3
*/
public Map<String, List<FileItem>> parseParameterMap(final RequestContext ctx)
throws FileUploadException {
public Map<String, List<FileItem>> parseParameterMap(final RequestContext ctx) throws FileUploadException {
final List<FileItem> items = parseRequest(ctx);
final Map<String, List<FileItem>> itemsMap = new HashMap<>(items.size());
@ -437,17 +403,13 @@ public abstract class AbstractFileUpload {
}
/**
* Parses an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
* compliant {@code multipart/form-data} stream.
* Parses an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant {@code multipart/form-data} stream.
*
* @param ctx The context for the request to be parsed.
* @return A list of {@code FileItem} instances parsed from the
* request, in the order that they were transmitted.
* @throws FileUploadException if there are problems reading/parsing
* the request or storing files.
* @return A list of {@code FileItem} instances parsed from the request, in the order that they were transmitted.
* @throws FileUploadException if there are problems reading/parsing the request or storing files.
*/
public List<FileItem> parseRequest(final RequestContext ctx)
throws FileUploadException {
public List<FileItem> parseRequest(final RequestContext ctx) throws FileUploadException {
final List<FileItem> items = new ArrayList<>();
boolean successful = false;
try {
@ -510,8 +472,7 @@ public abstract class AbstractFileUpload {
public abstract void setFileItemFactory(FileItemFactory factory);
/**
* Sets the maximum allowed size of a single uploaded file,
* as opposed to {@link #getSizeMax()}.
* Sets the maximum allowed size of a single uploaded file, as opposed to {@link #getSizeMax()}.
*
* @see #getFileSizeMax()
* @param fileSizeMax Maximum size of a single uploaded file.
@ -521,10 +482,8 @@ public abstract class AbstractFileUpload {
}
/**
* Specifies the character encoding to be used when reading the headers of
* individual part. When not specified, or {@code null}, the request
* encoding is used. If that is also not specified, or {@code null},
* the platform default encoding is used.
* Specifies the character encoding to be used when reading the headers of individual part. When not specified, or {@code null}, the request encoding is
* used. If that is also not specified, or {@code null}, the platform default encoding is used.
*
* @param encoding The encoding used to read part headers.
*/
@ -542,11 +501,9 @@ public abstract class AbstractFileUpload {
}
/**
* Sets the maximum allowed size of a complete request, as opposed
* to {@link #setFileSizeMax(long)}.
* Sets the maximum allowed size of a complete request, as opposed to {@link #setFileSizeMax(long)}.
*
* @param sizeMax The maximum allowed size, in bytes. The default value of
* -1 indicates, that there is no limit.
* @param sizeMax The maximum allowed size, in bytes. The default value of -1 indicates, that there is no limit.
* @see #getSizeMax()
*/
public void setSizeMax(final long sizeMax) {

View File

@ -39,7 +39,6 @@ public abstract class AbstractRequestContext implements RequestContext {
* @param contentLengthDefault How to get the content length default.
*/
protected AbstractRequestContext(final Function<String, String> contentLengthString, final LongSupplier contentLengthDefault) {
super();
this.contentLengthString = contentLengthString;
this.contentLengthDefault = contentLengthDefault;
}

View File

@ -29,7 +29,7 @@ import java.io.UnsupportedEncodingException;
* </p>
* <p>
* After retrieving an instance of this class from a {@link org.apache.commons.fileupload2.FileUpload FileUpload} instance (see
* {@link org.apache.commons.fileupload2.servlet.ServletFileUpload #parseRequest(javax.servlet.http.HttpServletRequest)}), you may either request all contents
* {@code org.apache.commons.fileupload2.servlet.ServletFileUpload #parseRequest(javax.servlet.http.HttpServletRequest)}), you may either request all contents
* of the file at once using {@link #get()} or request an {@link java.io.InputStream InputStream} with {@link #getInputStream()} and process the file without
* attempting to load it into memory, which may come handy with large files.
* </p>
@ -38,6 +38,7 @@ import java.io.UnsupportedEncodingException;
* specifically defined with the same signatures as methods in that interface. This allows an implementation of this interface to also implement
* {@code javax.activation.DataSource} with minimal additional work.
* </p>
*
* @since 1.3 additionally implements FileItemHeadersSupport
*/
public interface FileItem extends FileItemHeadersSupport {
@ -165,6 +166,7 @@ public interface FileItem extends FileItemHeadersSupport {
* This method is not guaranteed to succeed if called more than once for the same item. This allows a particular implementation to use, for example, file
* renaming, where possible, rather than copying all of the underlying data, thus gaining a significant performance benefit.
* </p>
*
* @param file The {@code File} into which the uploaded item should be stored.
* @throws IOException if an error occurs.
*/

View File

@ -33,13 +33,10 @@ public interface FileItemHeadersSupport {
FileItemHeaders getHeaders();
/**
* Sets the headers read from within an item. Implementations of
* {@link FileItem} or {@link FileItemStream} should implement this
* interface to be able to get the raw headers found within the item
* header block.
* Sets the headers read from within an item. Implementations of {@link FileItem} or {@link FileItemStream} should implement this interface to be able to
* get the raw headers found within the item header block.
*
* @param headers the instance that holds onto the headers
* for this instance.
* @param headers the instance that holds onto the headers for this instance.
*/
void setHeaders(FileItemHeaders headers);

View File

@ -0,0 +1,95 @@
/*
* 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 java.io.IOException;
import java.util.List;
import javax.naming.SizeLimitExceededException;
import org.apache.commons.fileupload2.pub.FileUploadByteCountLimitException;
/**
* An iterator, as returned by {@link AbstractFileUpload#getItemIterator(RequestContext)}.
*/
public interface FileItemIterator {
List<FileItem> getFileItems() throws FileUploadException, IOException;
/**
* Gets the maximum size of a single file. An {@link FileUploadByteCountLimitException} will be thrown, if there is an uploaded file, which is exceeding
* this value. By default, this value will be copied from the {@link AbstractFileUpload#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".
*/
long getFileSizeMax();
/**
* Gets 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 AbstractFileUpload#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 request. The value -1 indicates "unlimited".
*/
long getSizeMax();
/**
* Tests 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.
*/
boolean hasNext() throws FileUploadException, IOException;
/**
* 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.
*/
FileItemStream next() throws FileUploadException, IOException;
/**
* Sets the maximum size of a single file. An {@link FileUploadByteCountLimitException} will be thrown, if there is an uploaded file, which is exceeding
* this value. By default, this value will be copied from the {@link AbstractFileUpload#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.
* <p>
* <em>Note:</em> Changing this value doesn't affect files, that have already been uploaded.
* </p>
*
* @param fileSizeMax The maximum size of a single, uploaded file. The value -1 indicates "unlimited".
*/
void setFileSizeMax(long fileSizeMax);
/**
* Sets 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 AbstractFileUpload#getSizeMax() FileUploadBase} object, however, the user may replace the default
* value with a request specific value by invoking {@link #setSizeMax(long)} on this object.
* <p>
* <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.
* </p>
*
* @param sizeMax The maximum size of the complete HTTP request. The value -1 indicates "unlimited".
*/
void setSizeMax(long sizeMax);
}

View File

@ -20,85 +20,69 @@ import java.io.IOException;
import java.io.InputStream;
/**
* Provides access to a file or form item that was
* received within a {@code multipart/form-data} POST request.
* Provides access to a file or form item that was received within a {@code multipart/form-data} POST request.
* <p>
* The items contents are retrieved by calling {@link #openStream()}.
* </p>
* <p>Instances of this class are created by accessing the
* iterator, returned by
* {@link AbstractFileUpload#getItemIterator(RequestContext)}.
* <p>
* Instances of this class are created by accessing the iterator, returned by {@link AbstractFileUpload#getItemIterator(RequestContext)}.
* </p>
* <p><em>Note</em>: There is an interaction between the iterator and
* its associated instances of {@link FileItemStream}: By invoking
* {@link java.util.Iterator#hasNext()} on the iterator, you discard all data,
* which hasn't been read so far from the previous data.
* <p>
* <em>Note</em>: There is an interaction between the iterator and its associated instances of {@link FileItemStream}: By invoking
* {@link java.util.Iterator#hasNext()} on the iterator, you discard all data, which hasn't been read so far from the previous data.
* </p>
*/
public interface FileItemStream extends FileItemHeadersSupport {
/**
* This exception is thrown, if an attempt is made to read
* data from the {@link InputStream}, which has been returned
* by {@link FileItemStream#openStream()}, after
* {@link java.util.Iterator#hasNext()} has been invoked on the
* iterator, which created the {@link FileItemStream}.
* This exception is thrown, if an attempt is made to read data from the {@link InputStream}, which has been returned by
* {@link FileItemStream#openStream()}, after {@link java.util.Iterator#hasNext()} has been invoked on the iterator, which created the
* {@link FileItemStream}.
*/
class ItemSkippedException extends IOException {
/**
* The exceptions serial version UID, which is being used
* when serializing an exception instance.
* The exceptions serial version UID, which is being used when serializing an exception instance.
*/
private static final long serialVersionUID = 2;
}
/**
* Gets the content type passed by the browser or {@code null} if
* not defined.
* Gets the content type passed by the browser or {@code null} if not defined.
*
* @return The content type passed by the browser or {@code null} if
* not defined.
* @return The content type passed by the browser or {@code null} if not defined.
*/
String getContentType();
/**
* Gets the name of the field in the multipart form corresponding to
* this file item.
* Gets the name of the field in the multipart form corresponding to this file item.
*
* @return The name of the form field.
*/
String getFieldName();
/**
* Gets the original file name in the client's file system, as provided by
* the browser (or other client software). In most cases, this will be the
* base file name, without path information. However, some clients, such as
* the Opera browser, do include path information.
* Gets the original file name in the client's file system, as provided by the browser (or other client software). In most cases, this will be the base file
* name, without path information. However, some clients, such as the Opera browser, do include path information.
*
* @return The original file name in the client's file system.
*/
String getName();
/**
* Tests whether or not a {@code FileItem} instance represents
* a simple form field.
* Tests whether or not a {@code FileItem} instance represents a simple form field.
*
* @return {@code true} if the instance represents a simple form
* field; {@code false} if it represents an uploaded file.
* @return {@code true} if the instance represents a simple form field; {@code false} if it represents an uploaded file.
*/
boolean isFormField();
/**
* Opens an {@link InputStream}, which allows to read the
* items contents.
* Opens an {@link InputStream}, which allows to read the items contents.
*
* @return The input stream, from which the items data may
* be read.
* @throws IllegalStateException The method was already invoked on
* this item. It is not possible to recreate the data stream.
* @throws IOException An I/O error occurred.
* @return The input stream, from which the items data may be read.
* @throws IllegalStateException The method was already invoked on this item. It is not possible to recreate the data stream.
* @throws IOException An I/O error occurred.
* @see ItemSkippedException
*/
InputStream openStream() throws IOException;

View File

@ -17,18 +17,19 @@
package org.apache.commons.fileupload2;
/**
* <p>High level API for processing file uploads.</p>
* <p>
* High level API for processing file uploads.
* </p>
*
* <p>This class handles multiple files per single HTML widget, sent using
* {@code multipart/mixed} encoding type, as specified by
* <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Use {@link
* #parseRequest(RequestContext)} to acquire a list
* of {@link org.apache.commons.fileupload2.FileItem FileItems} associated
* with a given HTML widget.</p>
* <p>
* This class handles multiple files per single HTML widget, sent using {@code multipart/mixed} encoding type, as specified by
* <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Use {@link #parseRequest(RequestContext)} to acquire a list of
* {@link org.apache.commons.fileupload2.FileItem FileItems} associated with a given HTML widget.
* </p>
*
* <p>How the data for individual parts is stored is determined by the factory
* used to create them; a given part may be in memory, on disk, or somewhere
* else.</p>
* <p>
* How the data for individual parts is stored is determined by the factory used to create them; a given part may be in memory, on disk, or somewhere else.
* </p>
*/
public class FileUpload extends AbstractFileUpload {
@ -40,9 +41,7 @@ public class FileUpload extends AbstractFileUpload {
/**
* Constructs an uninitialized instance of this class.
*
* A factory must be
* configured, using {@code setFileItemFactory()}, before attempting
* to parse requests.
* A factory must be configured, using {@code setFileItemFactory()}, before attempting to parse requests.
*
* @see #FileUpload(FileItemFactory)
*/
@ -50,8 +49,7 @@ public class FileUpload extends AbstractFileUpload {
}
/**
* Constructs an instance of this class which uses the supplied factory to
* create {@code FileItem} instances.
* Constructs an instance of this class which uses the supplied factory to create {@code FileItem} instances.
*
* @see #FileUpload()
* @param fileItemFactory The factory to use for creating file items.

View File

@ -17,20 +17,14 @@
package org.apache.commons.fileupload2;
/**
* Signals an invalid file name.
* A file name is invalid, if it contains a NUL character.
* Attackers might use this to circumvent security checks:
* For example, a malicious user might upload a file with the name
* "foo.exe\0.png". This file name might pass security checks (i.e.
* checks for the extension ".png"), while, depending on the underlying
* C library, it might create a file named "foo.exe", as the NUL
* character is the string terminator in C.
* Signals an invalid file name. A file name is invalid, if it contains a NUL character. Attackers might use this to circumvent security checks: For example, a
* malicious user might upload a file with the name "foo.exe\0.png". This file name might pass security checks (i.e. checks for the extension ".png"), while,
* depending on the underlying C library, it might create a file named "foo.exe", as the NUL character is the string terminator in C.
*/
public class InvalidFileNameException extends RuntimeException {
/**
* Serial version UID, being used, if the exception
* is serialized.
* Serial version UID, being used, if the exception is serialized.
*/
private static final long serialVersionUID = 2;
@ -42,7 +36,7 @@ public class InvalidFileNameException extends RuntimeException {
/**
* Constructs a new instance.
*
* @param name The file name causing the exception.
* @param name The file name causing the exception.
* @param message A human readable error message.
*/
public InvalidFileNameException(final String name, final String message) {

View File

@ -16,7 +16,6 @@
*/
package org.apache.commons.fileupload2;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
@ -59,6 +58,7 @@ import org.apache.commons.io.output.NullOutputStream;
* <p>
* Here is an example of usage of this class:
* </p>
*
* <pre>
* try {
* MultipartStream multipartStream = new MultipartStream(input, boundary);
@ -398,8 +398,8 @@ public class MultipartStream {
/**
* Creates a new instance with the given listener and content length.
*
* @param progressListener The listener to invoke.
* @param contentLength The expected content length.
* @param progressListener The listener to invoke.
* @param contentLength The expected content length.
*/
public ProgressNotifier(final ProgressListener progressListener, final long contentLength) {
this.progressListener = progressListener;
@ -557,18 +557,17 @@ public class MultipartStream {
*/
private final ProgressNotifier notifier;
/**
* Constructs a {@code MultipartStream} with a custom size buffer.
* <p>
* Note that the buffer must be at least big enough to contain the boundary string, plus 4 characters for CR/LF and double dash, plus at least one byte of
* data. Too small a buffer size setting will degrade performance.
* </p>
* @param input The {@code InputStream} to serve as a data source.
* @param boundary The token used for dividing the stream into {@code encapsulations}.
* @param bufferSize The size of the buffer to be used, in bytes.
* @param notifier The notifier, which is used for calling the progress listener, if any.
*
* @param input The {@code InputStream} to serve as a data source.
* @param boundary The token used for dividing the stream into {@code encapsulations}.
* @param bufferSize The size of the buffer to be used, in bytes.
* @param notifier The notifier, which is used for calling the progress listener, if any.
* @throws IllegalArgumentException If the buffer size is too small.
* @since 1.3.1
*/
@ -604,8 +603,8 @@ public class MultipartStream {
/**
* Constructs a {@code MultipartStream} with a default size buffer.
*
* @param input The {@code InputStream} to serve as a data source.
* @param boundary The token used for dividing the stream into {@code encapsulations}.
* @param input The {@code InputStream} to serve as a data source.
* @param boundary The token used for dividing the stream into {@code encapsulations}.
* @param progressNotifier An object for calling the progress listener, if any.
* @see #MultipartStream(InputStream, byte[], int, ProgressNotifier)
*/
@ -642,6 +641,7 @@ public class MultipartStream {
* <p>
* Use this method to skip encapsulations you don't need or don't understand.
* </p>
*
* @return The amount of data discarded.
* @throws MalformedStreamException if the stream ends unexpectedly.
* @throws IOException if an i/o error occurs.
@ -692,8 +692,8 @@ public class MultipartStream {
}
/**
* Gets the character encoding used when reading the headers of an individual part. When not specified, or {@code null}, the platform default encoding
* is used.
* Gets the character encoding used when reading the headers of an individual part. When not specified, or {@code null}, the platform default encoding is
* used.
*
* @return The encoding used to read part headers.
*/
@ -716,6 +716,7 @@ public class MultipartStream {
* Arbitrary large amounts of data can be processed by this method using a constant size buffer. (see
* {@link #MultipartStream(InputStream,byte[],int, MultipartStream.ProgressNotifier) constructor}).
* </p>
*
* @param output The {@code Stream} to write data into. May be null, in which case this method is equivalent to {@link #discardBodyData()}.
* @return the amount of data written.
* @throws MalformedStreamException if the stream ends unexpectedly.
@ -798,6 +799,7 @@ public class MultipartStream {
* <p>
* <strong>TODO</strong> allow limiting maximum header size to protect against abuse.
* </p>
*
* @return The {@code header-part} of the current encapsulation.
* @throws FileUploadSizeException if the bytes read from the stream exceeded the size limits.
* @throws MalformedStreamException if the stream ends unexpectedly.
@ -856,6 +858,7 @@ public class MultipartStream {
* <p>
* Restoring the parent stream boundary token after processing of a nested stream is left to the application.
* </p>
*
* @param boundary The boundary to be used for parsing of the nested stream.
* @throws FileUploadBoundaryException if the {@code boundary} has a different length than the one being currently parsed.
*/
@ -868,8 +871,8 @@ public class MultipartStream {
}
/**
* Sets the character encoding to be used when reading the headers of individual parts. When not specified, or {@code null}, the platform default
* encoding is used.
* Sets the character encoding to be used when reading the headers of individual parts. When not specified, or {@code null}, the platform default encoding
* is used.
*
* @param encoding The encoding used to read part headers.
*/

View File

@ -27,9 +27,8 @@ import org.apache.commons.fileupload2.util.mime.RFC2231Utility;
/**
* A simple parser intended to parse sequences of name/value pairs.
* <p>
* Parameter values are expected to be enclosed in quotes if they
* contain unsafe characters, such as '=' characters or separators.
* Parameter values are optional and can be omitted.
* Parameter values are expected to be enclosed in quotes if they contain unsafe characters, such as '=' characters or separators. Parameter values are optional
* and can be omitted.
* </p>
* <p>
* {@code param1 = value; param2 = "anything goes; really"; param3}
@ -74,12 +73,9 @@ public class ParameterParser {
}
/**
* A helper method to process the parsed token. This method removes
* leading and trailing blanks as well as enclosing quotation marks,
* when necessary.
* A helper method to process the parsed token. This method removes leading and trailing blanks as well as enclosing quotation marks, when necessary.
*
* @param quoted {@code true} if quotation marks are expected,
* {@code false} otherwise.
* @param quoted {@code true} if quotation marks are expected, {@code false} otherwise.
* @return the token
*/
private String getToken(final boolean quoted) {
@ -92,10 +88,7 @@ public class ParameterParser {
i2--;
}
// Strip away quotation marks if necessary
if (quoted
&& ((i2 - i1) >= 2)
&& (chars[i1] == '"')
&& (chars[i2 - 1] == '"')) {
if (quoted && ((i2 - i1) >= 2) && (chars[i1] == '"') && (chars[i2 - 1] == '"')) {
i1++;
i2--;
}
@ -109,20 +102,16 @@ public class ParameterParser {
/**
* Tests if there any characters left to parse.
*
* @return {@code true} if there are unparsed characters,
* {@code false} otherwise.
* @return {@code true} if there are unparsed characters, {@code false} otherwise.
*/
private boolean hasChar() {
return this.pos < this.len;
}
/**
* Tests {@code true} if parameter names are to be converted to lower
* case when name/value pairs are parsed.
* Tests {@code true} if parameter names are to be converted to lower case when name/value pairs are parsed.
*
* @return {@code true} if parameter names are to be
* converted to lower case when name/value pairs are parsed.
* Otherwise returns {@code false}
* @return {@code true} if parameter names are to be converted to lower case when name/value pairs are parsed. Otherwise returns {@code false}
*/
public boolean isLowerCaseNames() {
return this.lowerCaseNames;
@ -131,10 +120,9 @@ public class ParameterParser {
/**
* Tests if the given character is present in the array of characters.
*
* @param ch the character to test for presence in the array of characters
* @param ch the character to test for presence in the array of characters
* @param charray the array of characters to test against
* @return {@code true} if the character is present in the array of
* characters, {@code false} otherwise.
* @return {@code true} if the character is present in the array of characters, {@code false} otherwise.
*/
private boolean isOneOf(final char ch, final char[] charray) {
boolean result = false;
@ -148,11 +136,9 @@ public class ParameterParser {
}
/**
* Extracts a map of name/value pairs from the given array of
* characters. Names are expected to be unique.
* Extracts a map of name/value pairs from the given array of characters. Names are expected to be unique.
*
* @param charArray the array of characters that contains a sequence of
* name/value pairs
* @param charArray the array of characters that contains a sequence of name/value pairs
* @param separator the name/value pairs separator
* @return a map of name/value pairs
*/
@ -164,21 +150,15 @@ public class ParameterParser {
}
/**
* Extracts a map of name/value pairs from the given array of
* characters. Names are expected to be unique.
* Extracts a map of name/value pairs from the given array of characters. Names are expected to be unique.
*
* @param charArray the array of characters that contains a sequence of
* name/value pairs
* @param offset - the initial offset.
* @param length - the length.
* @param charArray the array of characters that contains a sequence of name/value pairs
* @param offset - the initial offset.
* @param length - the length.
* @param separator the name/value pairs separator
* @return a map of name/value pairs
*/
public Map<String, String> parse(
final char[] charArray,
final int offset,
final int length,
final char separator) {
public Map<String, String> parse(final char[] charArray, final int offset, final int length, final char separator) {
if (charArray == null) {
return new HashMap<>();
@ -191,18 +171,15 @@ public class ParameterParser {
String paramName;
String paramValue;
while (hasChar()) {
paramName = parseToken(new char[] {
'=', separator });
paramName = parseToken(new char[] { '=', separator });
paramValue = null;
if (hasChar() && (charArray[pos] == '=')) {
pos++; // skip '='
paramValue = parseQuotedToken(new char[] {
separator });
paramValue = parseQuotedToken(new char[] { separator });
if (paramValue != null) {
try {
paramValue = RFC2231Utility.hasEncodedValue(paramName) ? RFC2231Utility.decodeText(paramValue)
: MimeUtility.decodeText(paramValue);
paramValue = RFC2231Utility.hasEncodedValue(paramName) ? RFC2231Utility.decodeText(paramValue) : MimeUtility.decodeText(paramValue);
} catch (final UnsupportedEncodingException e) {
// let's keep the original value in this case
}
@ -223,10 +200,9 @@ public class ParameterParser {
}
/**
* Extracts a map of name/value pairs from the given string. Names are
* expected to be unique.
* Extracts a map of name/value pairs from the given string. Names are expected to be unique.
*
* @param str the string that contains a sequence of name/value pairs
* @param str the string that contains a sequence of name/value pairs
* @param separator the name/value pairs separator
* @return a map of name/value pairs
*/
@ -238,11 +214,10 @@ public class ParameterParser {
}
/**
* Extracts a map of name/value pairs from the given string. Names are
* expected to be unique. Multiple separators may be specified and
* the earliest found in the input string is used.
* Extracts a map of name/value pairs from the given string. Names are expected to be unique. Multiple separators may be specified and the earliest found in
* the input string is used.
*
* @param str the string that contains a sequence of name/value pairs
* @param str the string that contains a sequence of name/value pairs
* @param separators the name/value pairs separators
* @return a map of name/value pairs
*/
@ -265,12 +240,9 @@ public class ParameterParser {
}
/**
* Parses out a token until any of the given terminators
* is encountered outside the quotation marks.
* Parses out a token until any of the given terminators is encountered outside the quotation marks.
*
* @param terminators the array of terminating characters. Any of these
* characters when encountered outside the quotation marks signify the end
* of the token
* @param terminators the array of terminating characters. Any of these characters when encountered outside the quotation marks signify the end of the token
* @return the token
*/
private String parseQuotedToken(final char[] terminators) {
@ -296,11 +268,9 @@ public class ParameterParser {
}
/**
* Parses out a token until any of the given terminators
* is encountered.
* Parses out a token until any of the given terminators is encountered.
*
* @param terminators the array of terminating characters. Any of these
* characters when encountered signify the end of the token
* @param terminators the array of terminating characters. Any of these characters when encountered signify the end of the token
* @return the token
*/
private String parseToken(final char[] terminators) {
@ -319,12 +289,9 @@ public class ParameterParser {
}
/**
* Sets the flag if parameter names are to be converted to lower case when
* name/value pairs are parsed.
* Sets the flag if parameter names are to be converted to lower case when name/value pairs are parsed.
*
* @param b {@code true} if parameter names are to be
* converted to lower case when name/value pairs are parsed.
* {@code false} otherwise.
* @param b {@code true} if parameter names are to be converted to lower case when name/value pairs are parsed. {@code false} otherwise.
*/
public void setLowerCaseNames(final boolean b) {
this.lowerCaseNames = b;

View File

@ -24,6 +24,7 @@ import java.io.InputStream;
* <p>
* This interface should be implemented for each type of request that may be handled by FileUpload, such as servlets and portlets.
* </p>
*
* @since 1.1
*/
public interface RequestContext {

View File

@ -1,240 +1,199 @@
/*
* 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.disk;
import java.io.File;
import org.apache.commons.fileupload2.FileItem;
import org.apache.commons.fileupload2.FileItemFactory;
import org.apache.commons.io.FileCleaningTracker;
/**
* The default {@link org.apache.commons.fileupload2.FileItemFactory}
* implementation.
* <p>
* This implementation creates
* {@link org.apache.commons.fileupload2.FileItem} instances which keep their
* content either in memory, for smaller items, or in a temporary file on disk,
* for larger items. The size threshold, above which content will be stored on
* disk, is configurable, as is the directory in which temporary files will be
* created.
* </p>
* <p>
* If not otherwise configured, the default configuration values are as
* follows:
* </p>
* <ul>
* <li>Size threshold is 10KB.</li>
* <li>Repository is the system default temp directory, as returned by
* {@code System.getProperty("java.io.tmpdir")}.</li>
* </ul>
* <p>
* <b>NOTE</b>: Files are created in the system default temp directory with
* predictable names. This means that a local attacker with write access to that
* directory can perform a TOUTOC attack to replace any uploaded file with a
* file of the attackers choice. The implications of this will depend on how the
* uploaded file is used but could be significant. When using this
* implementation in an environment with local, untrusted users,
* {@link #setRepository(File)} MUST be used to configure a repository location
* that is not publicly writable. In a Servlet container the location identified
* by the ServletContext attribute {@code javax.servlet.context.tempdir}
* may be used.
* </p>
* <p>
* Temporary files, which are created for file items, should be
* deleted later on. The best way to do this is using a
* {@link FileCleaningTracker}, which you can set on the
* {@link DiskFileItemFactory}. However, if you do use such a tracker,
* then you must consider the following: Temporary files are automatically
* deleted as soon as they are no longer needed. (More precisely, when the
* corresponding instance of {@link java.io.File} is garbage collected.)
* This is done by the so-called reaper thread, which is started and stopped
* automatically by the {@link FileCleaningTracker} when there are files to be
* tracked.
* It might make sense to terminate that thread, for example, if
* your web application ends. See the section on "Resource cleanup"
* in the users guide of commons-fileupload.
* </p>
*
* @since 1.1
*/
public class DiskFileItemFactory implements FileItemFactory {
/**
* The default threshold above which uploads will be stored on disk.
*/
public static final int DEFAULT_SIZE_THRESHOLD = 10240;
/**
* The directory in which uploaded files will be stored, if stored on disk.
*/
private File repository;
/**
* The threshold above which uploads will be stored on disk.
*/
private int sizeThreshold = DEFAULT_SIZE_THRESHOLD;
/**
* The instance of {@link FileCleaningTracker}, which is responsible
* for deleting temporary files.
* <p>
* May be null, if tracking files is not required.
* </p>
*/
private FileCleaningTracker fileCleaningTracker;
/**
* Default content charset to be used when no explicit charset
* parameter is provided by the sender.
*/
private String defaultCharset = DiskFileItem.DEFAULT_CHARSET;
/**
* Constructs an unconfigured instance of this class. The resulting factory
* may be configured by calling the appropriate setter methods.
*/
public DiskFileItemFactory() {
this(DEFAULT_SIZE_THRESHOLD, null);
}
/**
* Constructs a preconfigured instance of this class.
*
* @param sizeThreshold The threshold, in bytes, below which items will be
* retained in memory and above which they will be
* stored as a file.
* @param repository The data repository, which is the directory in
* which files will be created, should the item size
* exceed the threshold.
*/
public DiskFileItemFactory(final int sizeThreshold, final File repository) {
this.sizeThreshold = sizeThreshold;
this.repository = repository;
}
/**
* Creates a new {@link org.apache.commons.fileupload2.disk.DiskFileItem}
* instance from the supplied parameters and the local factory
* configuration.
*
* @param fieldName The name of the form field.
* @param contentType The content type of the form field.
* @param isFormField {@code true} if this is a plain form field;
* {@code false} otherwise.
* @param fileName The name of the uploaded file, if any, as supplied
* by the browser or other client.
* @return The newly created file item.
*/
@Override
public FileItem createItem(final String fieldName, final String contentType,
final boolean isFormField, final String fileName) {
final DiskFileItem result = new DiskFileItem(fieldName, contentType,
isFormField, fileName, sizeThreshold, repository);
result.setDefaultCharset(defaultCharset);
final FileCleaningTracker tracker = getFileCleaningTracker();
if (tracker != null) {
tracker.track(result.getTempFile(), result);
}
return result;
}
/**
* Gets the default charset for use when no explicit charset
* parameter is provided by the sender.
*
* @return the default charset
*/
public String getDefaultCharset() {
return defaultCharset;
}
/**
* Gets the tracker, which is responsible for deleting temporary
* files.
*
* @return An instance of {@link FileCleaningTracker}, or null
* (default), if temporary files aren't tracked.
*/
public FileCleaningTracker getFileCleaningTracker() {
return fileCleaningTracker;
}
/**
* Gets the directory used to temporarily store files that are larger
* than the configured size threshold.
*
* @return The directory in which temporary files will be located.
* @see #setRepository(java.io.File)
*/
public File getRepository() {
return repository;
}
/**
* Gets the size threshold beyond which files are written directly to
* disk. The default value is 10240 bytes.
*
* @return The size threshold, in bytes.
* @see #setSizeThreshold(int)
*/
public int getSizeThreshold() {
return sizeThreshold;
}
/**
* Sets the default charset for use when no explicit charset
* parameter is provided by the sender.
* @param charset the default charset
*/
public void setDefaultCharset(final String charset) {
defaultCharset = charset;
}
/**
* Sets the tracker, which is responsible for deleting temporary
* files.
*
* @param tracker An instance of {@link FileCleaningTracker},
* which will from now on track the created files, or null
* (default), to disable tracking.
*/
public void setFileCleaningTracker(final FileCleaningTracker tracker) {
fileCleaningTracker = tracker;
}
/**
* Sets the directory used to temporarily store files that are larger
* than the configured size threshold.
*
* @param repository The directory in which temporary files will be located.
* @see #getRepository()
*/
public void setRepository(final File repository) {
this.repository = repository;
}
/**
* Sets the size threshold beyond which files are written directly to disk.
*
* @param sizeThreshold The size threshold, in bytes.
* @see #getSizeThreshold()
*/
public void setSizeThreshold(final int sizeThreshold) {
this.sizeThreshold = sizeThreshold;
}
}
/*
* 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.disk;
import java.io.File;
import org.apache.commons.fileupload2.FileItem;
import org.apache.commons.fileupload2.FileItemFactory;
import org.apache.commons.io.FileCleaningTracker;
/**
* The default {@link org.apache.commons.fileupload2.FileItemFactory} implementation.
* <p>
* This implementation creates {@link org.apache.commons.fileupload2.FileItem} instances which keep their content either in memory, for smaller items, or in a
* temporary file on disk, for larger items. The size threshold, above which content will be stored on disk, is configurable, as is the directory in which
* temporary files will be created.
* </p>
* <p>
* If not otherwise configured, the default configuration values are as follows:
* </p>
* <ul>
* <li>Size threshold is 10KB.</li>
* <li>Repository is the system default temp directory, as returned by {@code System.getProperty("java.io.tmpdir")}.</li>
* </ul>
* <p>
* <b>NOTE</b>: Files are created in the system default temp directory with predictable names. This means that a local attacker with write access to that
* directory can perform a TOUTOC attack to replace any uploaded file with a file of the attackers choice. The implications of this will depend on how the
* uploaded file is used but could be significant. When using this implementation in an environment with local, untrusted users, {@link #setRepository(File)}
* MUST be used to configure a repository location that is not publicly writable. In a Servlet container the location identified by the ServletContext attribute
* {@code javax.servlet.context.tempdir} may be used.
* </p>
* <p>
* Temporary files, which are created for file items, should be deleted later on. The best way to do this is using a {@link FileCleaningTracker}, which you can
* set on the {@link DiskFileItemFactory}. However, if you do use such a tracker, then you must consider the following: Temporary files are automatically
* deleted as soon as they are no longer needed. (More precisely, when the corresponding instance of {@link java.io.File} is garbage collected.) This is done by
* the so-called reaper thread, which is started and stopped automatically by the {@link FileCleaningTracker} when there are files to be tracked. It might make
* sense to terminate that thread, for example, if your web application ends. See the section on "Resource cleanup" in the users guide of commons-fileupload.
* </p>
*
* @since 1.1
*/
public class DiskFileItemFactory implements FileItemFactory {
/**
* The default threshold above which uploads will be stored on disk.
*/
public static final int DEFAULT_SIZE_THRESHOLD = 10240;
/**
* The directory in which uploaded files will be stored, if stored on disk.
*/
private File repository;
/**
* The threshold above which uploads will be stored on disk.
*/
private int sizeThreshold = DEFAULT_SIZE_THRESHOLD;
/**
* The instance of {@link FileCleaningTracker}, which is responsible for deleting temporary files.
* <p>
* May be null, if tracking files is not required.
* </p>
*/
private FileCleaningTracker fileCleaningTracker;
/**
* Default content charset to be used when no explicit charset parameter is provided by the sender.
*/
private String defaultCharset = DiskFileItem.DEFAULT_CHARSET;
/**
* Constructs an unconfigured instance of this class. The resulting factory may be configured by calling the appropriate setter methods.
*/
public DiskFileItemFactory() {
this(DEFAULT_SIZE_THRESHOLD, null);
}
/**
* Constructs a preconfigured instance of this class.
*
* @param sizeThreshold The threshold, in bytes, below which items will be retained in memory and above which they will be stored as a file.
* @param repository The data repository, which is the directory in which files will be created, should the item size exceed the threshold.
*/
public DiskFileItemFactory(final int sizeThreshold, final File repository) {
this.sizeThreshold = sizeThreshold;
this.repository = repository;
}
/**
* Creates a new {@link org.apache.commons.fileupload2.disk.DiskFileItem} instance from the supplied parameters and the local factory configuration.
*
* @param fieldName The name of the form field.
* @param contentType The content type of the form field.
* @param isFormField {@code true} if this is a plain form field; {@code false} otherwise.
* @param fileName The name of the uploaded file, if any, as supplied by the browser or other client.
* @return The newly created file item.
*/
@Override
public FileItem createItem(final String fieldName, final String contentType, final boolean isFormField, final String fileName) {
final DiskFileItem result = new DiskFileItem(fieldName, contentType, isFormField, fileName, sizeThreshold, repository);
result.setDefaultCharset(defaultCharset);
final FileCleaningTracker tracker = getFileCleaningTracker();
if (tracker != null) {
tracker.track(result.getTempFile(), result);
}
return result;
}
/**
* Gets the default charset for use when no explicit charset parameter is provided by the sender.
*
* @return the default charset
*/
public String getDefaultCharset() {
return defaultCharset;
}
/**
* Gets the tracker, which is responsible for deleting temporary files.
*
* @return An instance of {@link FileCleaningTracker}, or null (default), if temporary files aren't tracked.
*/
public FileCleaningTracker getFileCleaningTracker() {
return fileCleaningTracker;
}
/**
* Gets the directory used to temporarily store files that are larger than the configured size threshold.
*
* @return The directory in which temporary files will be located.
* @see #setRepository(java.io.File)
*/
public File getRepository() {
return repository;
}
/**
* Gets the size threshold beyond which files are written directly to disk. The default value is 10240 bytes.
*
* @return The size threshold, in bytes.
* @see #setSizeThreshold(int)
*/
public int getSizeThreshold() {
return sizeThreshold;
}
/**
* Sets the default charset for use when no explicit charset parameter is provided by the sender.
*
* @param charset the default charset
*/
public void setDefaultCharset(final String charset) {
defaultCharset = charset;
}
/**
* Sets the tracker, which is responsible for deleting temporary files.
*
* @param tracker An instance of {@link FileCleaningTracker}, which will from now on track the created files, or null (default), to disable tracking.
*/
public void setFileCleaningTracker(final FileCleaningTracker tracker) {
fileCleaningTracker = tracker;
}
/**
* Sets the directory used to temporarily store files that are larger than the configured size threshold.
*
* @param repository The directory in which temporary files will be located.
* @see #getRepository()
*/
public void setRepository(final File repository) {
this.repository = repository;
}
/**
* Sets the size threshold beyond which files are written directly to disk.
*
* @param sizeThreshold The size threshold, in bytes.
* @see #getSizeThreshold()
*/
public void setSizeThreshold(final int sizeThreshold) {
this.sizeThreshold = sizeThreshold;
}
}

View File

@ -0,0 +1,47 @@
/*
* 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.
*/
/**
* <p>
* A disk-based implementation of the {@link org.apache.commons.fileupload2.FileItem FileItem} interface. This implementation retains smaller items in memory,
* while writing larger ones to disk. The threshold between these two is configurable, as is the location of files that are written to disk.
* </p>
* <p>
* In typical usage, an instance of {@link org.apache.commons.fileupload2.disk.DiskFileItemFactory DiskFileItemFactory} would be created, configured, and then
* passed to a {@link org.apache.commons.fileupload2.FileUpload FileUpload} implementation such as
* {@code org.apache.commons.fileupload2.servlet.ServletFileUpload ServletFileUpload} or
* {@code org.apache.commons.fileupload2.portlet.PortletFileUpload PortletFileUpload}.
* </p>
* <p>
* The following code fragment demonstrates this usage.
* </p>
*
* <pre>
* DiskFileItemFactory factory = new DiskFileItemFactory();
* // maximum size that will be stored in memory
* factory.setSizeThreshold(4096);
* // the location for saving data that is larger than getSizeThreshold()
* factory.setRepository(new File("/tmp"));
*
* ServletFileUpload upload = new ServletFileUpload(factory);
* </pre>
* <p>
* Please see the FileUpload <a href="https://commons.apache.org/fileupload/using.html" target="_top">User Guide</a> for further details and examples of how to
* use this package.
* </p>
*/
package org.apache.commons.fileupload2.disk;

View File

@ -1,332 +1,331 @@
/*
* 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 java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.NoSuchElementException;
import java.util.Objects;
import org.apache.commons.fileupload2.AbstractFileUpload;
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.FileUploadException;
import org.apache.commons.fileupload2.MultipartStream;
import org.apache.commons.fileupload2.ProgressListener;
import org.apache.commons.fileupload2.RequestContext;
import org.apache.commons.fileupload2.pub.FileUploadContentTypeException;
import org.apache.commons.fileupload2.pub.FileUploadSizeException;
import org.apache.commons.fileupload2.util.LimitedInputStream;
import org.apache.commons.io.IOUtils;
/**
* The iterator, which is returned by {@link AbstractFileUpload#getItemIterator(RequestContext)}.
*/
public class FileItemIteratorImpl implements FileItemIterator {
/**
* The file uploads processing utility.
*
* @see AbstractFileUpload
*/
private final AbstractFileUpload fileUploadBase;
/**
* The request context.
*
* @see RequestContext
*/
private final RequestContext ctx;
/**
* The maximum allowed size of a complete request.
*/
private long sizeMax;
/**
* The maximum allowed size of a single uploaded file.
*/
private long 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;
/**
* Constructs a new instance.
*
* @param fileUploadBase Main processor.
* @param requestContext The request context.
* @throws FileUploadException An error occurred while parsing the request.
* @throws IOException An I/O error occurred.
*/
public FileItemIteratorImpl(final AbstractFileUpload fileUploadBase, final RequestContext requestContext) throws FileUploadException, IOException {
this.fileUploadBase = fileUploadBase;
this.sizeMax = fileUploadBase.getSizeMax();
this.fileSizeMax = fileUploadBase.getFileSizeMax();
this.ctx = Objects.requireNonNull(requestContext, "requestContext");
this.skipPreamble = true;
findNextItem();
}
/**
* Finds 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 (;;) {
final 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;
}
final FileItemHeaders headers = fileUploadBase.getParsedHeaders(multi.readHeaders());
if (currentFieldName == null) {
// We're parsing the outer multipart
final String fieldName = fileUploadBase.getFieldName(headers);
if (fieldName != null) {
final String subContentType = headers.getHeader(AbstractFileUpload.CONTENT_TYPE);
if (subContentType != null && subContentType.toLowerCase(Locale.ENGLISH).startsWith(AbstractFileUpload.MULTIPART_MIXED)) {
currentFieldName = fieldName;
// Multiple files associated with this field name
final byte[] subBoundary = fileUploadBase.getBoundary(subContentType);
multi.setBoundary(subBoundary);
skipPreamble = true;
continue;
}
final String fileName = fileUploadBase.getFileName(headers);
currentItem = new FileItemStreamImpl(this, fileName, fieldName, headers.getHeader(AbstractFileUpload.CONTENT_TYPE), fileName == null,
getContentLength(headers));
currentItem.setHeaders(headers);
progressNotifier.noteItem();
itemValid = true;
return true;
}
} else {
final String fileName = fileUploadBase.getFileName(headers);
if (fileName != null) {
currentItem = new FileItemStreamImpl(this, fileName, currentFieldName, headers.getHeader(AbstractFileUpload.CONTENT_TYPE), false,
getContentLength(headers));
currentItem.setHeaders(headers);
progressNotifier.noteItem();
itemValid = true;
return true;
}
}
multi.discardBodyData();
}
}
private long getContentLength(final FileItemHeaders headers) {
try {
return Long.parseLong(headers.getHeader(AbstractFileUpload.CONTENT_LENGTH));
} catch (final Exception e) {
return -1;
}
}
@Override
public List<FileItem> getFileItems() throws FileUploadException, IOException {
final List<FileItem> items = new ArrayList<>();
while (hasNext()) {
final FileItemStream fis = next();
items.add(fileUploadBase.getFileItemFactory().createItem(fis.getFieldName(), fis.getContentType(), fis.isFormField(), fis.getName()));
}
return items;
}
@Override
public long getFileSizeMax() {
return fileSizeMax;
}
public MultipartStream getMultiPartStream() throws FileUploadException, IOException {
if (multiPartStream == null) {
init(fileUploadBase, ctx);
}
return multiPartStream;
}
@Override
public long getSizeMax() {
return sizeMax;
}
/**
* Tests 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;
}
return findNextItem();
}
protected void init(final AbstractFileUpload fileUploadBase, final RequestContext requestContext) throws FileUploadException, IOException {
final String contentType = ctx.getContentType();
if ((null == contentType) || (!contentType.toLowerCase(Locale.ENGLISH).startsWith(AbstractFileUpload.MULTIPART))) {
throw new FileUploadContentTypeException(String.format("the request doesn't contain a %s or %s stream, content type header is %s",
AbstractFileUpload.MULTIPART_FORM_DATA, AbstractFileUpload.MULTIPART_MIXED, contentType), contentType);
}
final long contentLengthInt = ctx.getContentLength();
// @formatter:off
final long requestSize = RequestContext.class.isAssignableFrom(ctx.getClass())
// Inline conditional is OK here CHECKSTYLE:OFF
? ctx.getContentLength()
: contentLengthInt;
// CHECKSTYLE:ON
// @formatter:on
final InputStream input; // N.B. this is eventually closed in MultipartStream processing
if (sizeMax >= 0) {
if (requestSize != -1 && requestSize > sizeMax) {
throw new FileUploadSizeException(
String.format("the request was rejected because its size (%s) exceeds the configured maximum (%s)", requestSize, sizeMax), sizeMax,
requestSize);
}
// N.B. this is eventually closed in MultipartStream processing
input = new LimitedInputStream(ctx.getInputStream(), sizeMax) {
@Override
protected void raiseError(final long maxLen, final long count) throws IOException {
throw new FileUploadSizeException(
String.format("The request was rejected because its size (%s) exceeds the configured maximum (%s)", count, maxLen), maxLen, count);
}
};
} 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 (final IllegalArgumentException e) {
IOUtils.closeQuietly(input); // avoid possible resource leak
throw new FileUploadContentTypeException(String.format("The boundary specified in the %s header is too long", AbstractFileUpload.CONTENT_TYPE), e);
}
multiPartStream.setHeaderEncoding(charEncoding);
}
/**
* 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 void setFileSizeMax(final long fileSizeMax) {
this.fileSizeMax = fileSizeMax;
}
@Override
public void setSizeMax(final long sizeMax) {
this.sizeMax = sizeMax;
}
}
/*
* 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 java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.NoSuchElementException;
import java.util.Objects;
import org.apache.commons.fileupload2.AbstractFileUpload;
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.FileUploadException;
import org.apache.commons.fileupload2.MultipartStream;
import org.apache.commons.fileupload2.ProgressListener;
import org.apache.commons.fileupload2.RequestContext;
import org.apache.commons.fileupload2.pub.FileUploadContentTypeException;
import org.apache.commons.fileupload2.pub.FileUploadSizeException;
import org.apache.commons.fileupload2.util.LimitedInputStream;
import org.apache.commons.io.IOUtils;
/**
* The iterator, which is returned by {@link AbstractFileUpload#getItemIterator(RequestContext)}.
*/
public class FileItemIteratorImpl implements FileItemIterator {
/**
* The file uploads processing utility.
*
* @see AbstractFileUpload
*/
private final AbstractFileUpload fileUploadBase;
/**
* The request context.
*
* @see RequestContext
*/
private final RequestContext ctx;
/**
* The maximum allowed size of a complete request.
*/
private long sizeMax;
/**
* The maximum allowed size of a single uploaded file.
*/
private long 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;
/**
* Constructs a new instance.
*
* @param fileUploadBase Main processor.
* @param requestContext The request context.
* @throws FileUploadException An error occurred while parsing the request.
* @throws IOException An I/O error occurred.
*/
public FileItemIteratorImpl(final AbstractFileUpload fileUploadBase, final RequestContext requestContext) throws FileUploadException, IOException {
this.fileUploadBase = fileUploadBase;
this.sizeMax = fileUploadBase.getSizeMax();
this.fileSizeMax = fileUploadBase.getFileSizeMax();
this.ctx = Objects.requireNonNull(requestContext, "requestContext");
this.skipPreamble = true;
findNextItem();
}
/**
* Finds 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 (;;) {
final 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;
}
final FileItemHeaders headers = fileUploadBase.getParsedHeaders(multi.readHeaders());
if (currentFieldName == null) {
// We're parsing the outer multipart
final String fieldName = fileUploadBase.getFieldName(headers);
if (fieldName != null) {
final String subContentType = headers.getHeader(AbstractFileUpload.CONTENT_TYPE);
if (subContentType != null && subContentType.toLowerCase(Locale.ENGLISH).startsWith(AbstractFileUpload.MULTIPART_MIXED)) {
currentFieldName = fieldName;
// Multiple files associated with this field name
final byte[] subBoundary = fileUploadBase.getBoundary(subContentType);
multi.setBoundary(subBoundary);
skipPreamble = true;
continue;
}
final String fileName = fileUploadBase.getFileName(headers);
currentItem = new FileItemStreamImpl(this, fileName, fieldName, headers.getHeader(AbstractFileUpload.CONTENT_TYPE), fileName == null,
getContentLength(headers));
currentItem.setHeaders(headers);
progressNotifier.noteItem();
itemValid = true;
return true;
}
} else {
final String fileName = fileUploadBase.getFileName(headers);
if (fileName != null) {
currentItem = new FileItemStreamImpl(this, fileName, currentFieldName, headers.getHeader(AbstractFileUpload.CONTENT_TYPE), false,
getContentLength(headers));
currentItem.setHeaders(headers);
progressNotifier.noteItem();
itemValid = true;
return true;
}
}
multi.discardBodyData();
}
}
private long getContentLength(final FileItemHeaders headers) {
try {
return Long.parseLong(headers.getHeader(AbstractFileUpload.CONTENT_LENGTH));
} catch (final Exception e) {
return -1;
}
}
@Override
public List<FileItem> getFileItems() throws FileUploadException, IOException {
final List<FileItem> items = new ArrayList<>();
while (hasNext()) {
final FileItemStream fis = next();
items.add(fileUploadBase.getFileItemFactory().createItem(fis.getFieldName(), fis.getContentType(), fis.isFormField(), fis.getName()));
}
return items;
}
@Override
public long getFileSizeMax() {
return fileSizeMax;
}
public MultipartStream getMultiPartStream() throws FileUploadException, IOException {
if (multiPartStream == null) {
init(fileUploadBase, ctx);
}
return multiPartStream;
}
@Override
public long getSizeMax() {
return sizeMax;
}
/**
* Tests 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;
}
return findNextItem();
}
protected void init(final AbstractFileUpload fileUploadBase, final RequestContext requestContext) throws FileUploadException, IOException {
final String contentType = ctx.getContentType();
if ((null == contentType) || (!contentType.toLowerCase(Locale.ENGLISH).startsWith(AbstractFileUpload.MULTIPART))) {
throw new FileUploadContentTypeException(String.format("the request doesn't contain a %s or %s stream, content type header is %s",
AbstractFileUpload.MULTIPART_FORM_DATA, AbstractFileUpload.MULTIPART_MIXED, contentType), contentType);
}
final long contentLengthInt = ctx.getContentLength();
// @formatter:off
final long requestSize = RequestContext.class.isAssignableFrom(ctx.getClass())
// Inline conditional is OK here CHECKSTYLE:OFF
? ctx.getContentLength()
: contentLengthInt;
// CHECKSTYLE:ON
// @formatter:on
final InputStream input; // N.B. this is eventually closed in MultipartStream processing
if (sizeMax >= 0) {
if (requestSize != -1 && requestSize > sizeMax) {
throw new FileUploadSizeException(
String.format("the request was rejected because its size (%s) exceeds the configured maximum (%s)", requestSize, sizeMax), sizeMax,
requestSize);
}
// N.B. this is eventually closed in MultipartStream processing
input = new LimitedInputStream(ctx.getInputStream(), sizeMax) {
@Override
protected void raiseError(final long maxLen, final long count) throws IOException {
throw new FileUploadSizeException(
String.format("The request was rejected because its size (%s) exceeds the configured maximum (%s)", count, maxLen), maxLen, count);
}
};
} 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 (final IllegalArgumentException e) {
IOUtils.closeQuietly(input); // avoid possible resource leak
throw new FileUploadContentTypeException(String.format("The boundary specified in the %s header is too long", AbstractFileUpload.CONTENT_TYPE), e);
}
multiPartStream.setHeaderEncoding(charEncoding);
}
/**
* 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 void setFileSizeMax(final long fileSizeMax) {
this.fileSizeMax = fileSizeMax;
}
@Override
public void setSizeMax(final long sizeMax) {
this.sizeMax = sizeMax;
}
}

View File

@ -1,206 +1,205 @@
/*
* 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 java.io.InputStream;
import org.apache.commons.fileupload2.FileItemHeaders;
import org.apache.commons.fileupload2.FileItemStream;
import org.apache.commons.fileupload2.FileUploadException;
import org.apache.commons.fileupload2.InvalidFileNameException;
import org.apache.commons.fileupload2.MultipartStream.ItemInputStream;
import org.apache.commons.fileupload2.disk.DiskFileItem;
import org.apache.commons.fileupload2.pub.FileUploadByteCountLimitException;
import org.apache.commons.fileupload2.util.LimitedInputStream;
/**
* Default implementation of {@link FileItemStream}.
*/
public class FileItemStreamImpl implements FileItemStream {
/**
* The File Item iterator implementation.
*
* @see FileItemIteratorImpl
*/
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.
*/
private final String fileName;
/**
* Whether the file item is a form field.
*/
private final boolean formField;
/**
* The file items input stream.
*/
private final InputStream inputStream;
/**
* The file items input stream closed flag.
*/
private boolean inputStreamClosed;
/**
* The headers, if any.
*/
private FileItemHeaders headers;
/**
* Creates a new instance.
*
* @param fileItemIterator The {@link FileItemIteratorImpl iterator}, which returned this file item.
* @param fileName The items file name, or null.
* @param fieldName The items field name.
* @param contentType The items content type, or null.
* @param formField Whether the item is a form field.
* @param contentLength The items content length, if known, or -1
* @throws IOException Creating the file item failed.
* @throws FileUploadException Parsing the incoming data stream failed.
*/
public FileItemStreamImpl(final FileItemIteratorImpl fileItemIterator, final String fileName, final String fieldName, final String contentType,
final boolean formField, final long contentLength) throws FileUploadException, IOException {
this.fileItemIteratorImpl = fileItemIterator;
this.fileName = fileName;
this.fieldName = fieldName;
this.contentType = contentType;
this.formField = formField;
final long fileSizeMax = fileItemIteratorImpl.getFileSizeMax();
if (fileSizeMax != -1 && contentLength != -1 && contentLength > fileSizeMax) {
throw new FileUploadByteCountLimitException(String.format("The field %s exceeds its maximum permitted size of %s bytes.", fieldName, fileSizeMax),
contentLength, fileSizeMax, fileName, fieldName);
}
// OK to construct stream now
final ItemInputStream itemInputStream = fileItemIteratorImpl.getMultiPartStream().newInputStream();
InputStream istream = itemInputStream;
if (fileSizeMax != -1) {
istream = new LimitedInputStream(istream, fileSizeMax) {
@Override
protected void raiseError(final long sizeMax, final long count) throws IOException {
itemInputStream.close(true);
throw new FileUploadByteCountLimitException(
String.format("The field %s exceeds its maximum permitted size of %s bytes.", fieldName, sizeMax), count, sizeMax, fileName,
fieldName);
}
};
}
this.inputStream = istream;
}
/**
* Closes the file item.
*
* @throws IOException An I/O error occurred.
*/
public void close() throws IOException {
inputStream.close();
inputStreamClosed = true;
}
/**
* Gets the items content type, or null.
*
* @return Content type, if known, or null.
*/
@Override
public String getContentType() {
return contentType;
}
/**
* Gets the items field name.
*
* @return Field name.
*/
@Override
public String getFieldName() {
return fieldName;
}
/**
* Gets the file item headers.
*
* @return The items header object
*/
@Override
public FileItemHeaders getHeaders() {
return headers;
}
/**
* Gets 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 DiskFileItem.checkFileName(fileName);
}
/**
* Tests whether this is a form field.
*
* @return True, if the item is a form field, otherwise false.
*/
@Override
public boolean isFormField() {
return formField;
}
/**
* Gets the 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 (inputStreamClosed) {
throw new FileItemStream.ItemSkippedException();
}
return inputStream;
}
/**
* Sets the file item headers.
*
* @param headers The items header object
*/
@Override
public void setHeaders(final FileItemHeaders headers) {
this.headers = headers;
}
}
/*
* 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 java.io.InputStream;
import org.apache.commons.fileupload2.FileItemHeaders;
import org.apache.commons.fileupload2.FileItemStream;
import org.apache.commons.fileupload2.FileUploadException;
import org.apache.commons.fileupload2.InvalidFileNameException;
import org.apache.commons.fileupload2.MultipartStream.ItemInputStream;
import org.apache.commons.fileupload2.disk.DiskFileItem;
import org.apache.commons.fileupload2.pub.FileUploadByteCountLimitException;
import org.apache.commons.fileupload2.util.LimitedInputStream;
/**
* Default implementation of {@link FileItemStream}.
*/
public class FileItemStreamImpl implements FileItemStream {
/**
* The File Item iterator implementation.
*
* @see FileItemIteratorImpl
*/
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.
*/
private final String fileName;
/**
* Whether the file item is a form field.
*/
private final boolean formField;
/**
* The file items input stream.
*/
private final InputStream inputStream;
/**
* The file items input stream closed flag.
*/
private boolean inputStreamClosed;
/**
* The headers, if any.
*/
private FileItemHeaders headers;
/**
* Creates a new instance.
*
* @param fileItemIterator The {@link FileItemIteratorImpl iterator}, which returned this file item.
* @param fileName The items file name, or null.
* @param fieldName The items field name.
* @param contentType The items content type, or null.
* @param formField Whether the item is a form field.
* @param contentLength The items content length, if known, or -1
* @throws IOException Creating the file item failed.
* @throws FileUploadException Parsing the incoming data stream failed.
*/
public FileItemStreamImpl(final FileItemIteratorImpl fileItemIterator, final String fileName, final String fieldName, final String contentType,
final boolean formField, final long contentLength) throws FileUploadException, IOException {
this.fileItemIteratorImpl = fileItemIterator;
this.fileName = fileName;
this.fieldName = fieldName;
this.contentType = contentType;
this.formField = formField;
final long fileSizeMax = fileItemIteratorImpl.getFileSizeMax();
if (fileSizeMax != -1 && contentLength != -1 && contentLength > fileSizeMax) {
throw new FileUploadByteCountLimitException(String.format("The field %s exceeds its maximum permitted size of %s bytes.", fieldName, fileSizeMax),
contentLength, fileSizeMax, fileName, fieldName);
}
// OK to construct stream now
final ItemInputStream itemInputStream = fileItemIteratorImpl.getMultiPartStream().newInputStream();
InputStream istream = itemInputStream;
if (fileSizeMax != -1) {
istream = new LimitedInputStream(istream, fileSizeMax) {
@Override
protected void raiseError(final long sizeMax, final long count) throws IOException {
itemInputStream.close(true);
throw new FileUploadByteCountLimitException(
String.format("The field %s exceeds its maximum permitted size of %s bytes.", fieldName, sizeMax), count, sizeMax, fileName,
fieldName);
}
};
}
this.inputStream = istream;
}
/**
* Closes the file item.
*
* @throws IOException An I/O error occurred.
*/
public void close() throws IOException {
inputStream.close();
inputStreamClosed = true;
}
/**
* Gets the items content type, or null.
*
* @return Content type, if known, or null.
*/
@Override
public String getContentType() {
return contentType;
}
/**
* Gets the items field name.
*
* @return Field name.
*/
@Override
public String getFieldName() {
return fieldName;
}
/**
* Gets the file item headers.
*
* @return The items header object
*/
@Override
public FileItemHeaders getHeaders() {
return headers;
}
/**
* Gets 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 DiskFileItem.checkFileName(fileName);
}
/**
* Tests whether this is a form field.
*
* @return True, if the item is a form field, otherwise false.
*/
@Override
public boolean isFormField() {
return formField;
}
/**
* Gets the 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 (inputStreamClosed) {
throw new FileItemStream.ItemSkippedException();
}
return inputStream;
}
/**
* Sets the file item headers.
*
* @param headers The items header object
*/
@Override
public void setHeaders(final FileItemHeaders headers) {
this.headers = headers;
}
}

View File

@ -17,29 +17,23 @@
/**
* <p>
* A component for handling HTML file uploads as specified by
* <a href="http://www.ietf.org/rfc/rfc1867.txt" target="_top">RFC&nbsp;1867</a>.
* This component provides support for uploads within both servlets (JSR 53)
* and portlets (JSR 168).
* A component for handling HTML file uploads as specified by <a href="http://www.ietf.org/rfc/rfc1867.txt" target="_top">RFC&nbsp;1867</a>. This component
* provides support for uploads within both servlets (JSR 53) and portlets (JSR 168).
* </p>
* <p>
* While this package provides the generic functionality for file uploads,
* these classes are not typically used directly. Instead, normal usage
* involves one of the provided extensions of
* {@link org.apache.commons.fileupload2.FileUpload FileUpload} such as
* {@link org.apache.commons.fileupload2.servlet.ServletFileUpload ServletFileUpload}
* or
* {@link org.apache.commons.fileupload2.portlet.PortletFileUpload PortletFileUpload},
* together with a factory for
* {@link org.apache.commons.fileupload2.FileItem FileItem} instances,
* such as
* {@link org.apache.commons.fileupload2.disk.DiskFileItemFactory DiskFileItemFactory}.
* While this package provides the generic functionality for file uploads, these classes are not typically used directly. Instead, normal usage involves one of
* the provided extensions of {@link org.apache.commons.fileupload2.FileUpload FileUpload} such as
* {@code org.apache.commons.fileupload2.servlet.ServletFileUpload ServletFileUpload} or
* {@code org.apache.commons.fileupload2.portlet.PortletFileUpload PortletFileUpload}, together with a factory for
* {@link org.apache.commons.fileupload2.FileItem FileItem} instances, such as {@link org.apache.commons.fileupload2.disk.DiskFileItemFactory
* DiskFileItemFactory}.
* </p>
* <p>
* The following is a brief example of typical usage in a servlet, storing
* the uploaded files on disk.
* The following is a brief example of typical usage in a servlet, storing the uploaded files on disk.
* </p>
* <pre>public void doPost(HttpServletRequest req, HttpServletResponse res) {
*
* <pre>
* public void doPost(HttpServletRequest req, HttpServletResponse res) {
* DiskFileItemFactory factory = new DiskFileItemFactory();
* // maximum size that will be stored in memory
* factory.setSizeThreshold(4096);
@ -66,20 +60,14 @@
* }
* </pre>
* <p>
* In the example above, the first file is loaded into memory as a
* {@code String}. Before calling the {@code getString} method,
* the data may have been in memory or on disk depending on its size. The
* second file we assume it will be large and therefore never explicitly
* load it into memory, though if it is less than 4096 bytes it will be
* in memory before it is written to its final location. When writing to
* the final location, if the data is larger than the threshold, an attempt
* is made to rename the temporary file to the given location. If it cannot
* be renamed, it is streamed to the new location.
* In the example above, the first file is loaded into memory as a {@code String}. Before calling the {@code getString} method, the data may have been in memory
* or on disk depending on its size. The second file we assume it will be large and therefore never explicitly load it into memory, though if it is less than
* 4096 bytes it will be in memory before it is written to its final location. When writing to the final location, if the data is larger than the threshold, an
* attempt is made to rename the temporary file to the given location. If it cannot be renamed, it is streamed to the new location.
* </p>
* <p>
* Please see the FileUpload
* <a href="https://commons.apache.org/fileupload/using.html" target="_top">User Guide</a>
* for further details and examples of how to use this package.
* Please see the FileUpload <a href="https://commons.apache.org/fileupload/using.html" target="_top">User Guide</a> for further details and examples of how to
* use this package.
* </p>
*/
package org.apache.commons.fileupload2;

View File

@ -1,72 +1,72 @@
/*
* 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.pub;
/**
* Signals that a file size exceeds the configured maximum.
*/
public class FileUploadByteCountLimitException extends FileUploadSizeException {
/**
* The exceptions UID, for serializing an instance.
*/
private static final long serialVersionUID = 2;
/**
* File name of the item, which caused the exception.
*/
private final String fileName;
/**
* Field name of the item, which caused the exception.
*/
private final String fieldName;
/**
* Constructs an instance with the specified detail message, and actual and permitted sizes.
*
* @param message The detail message (which is saved for later retrieval by the {@link #getMessage()} method)
* @param actual The actual request size.
* @param permitted The maximum permitted request size.
* @param fileName File name of the item, which caused the exception.
* @param fieldName Field name of the item, which caused the exception.
*/
public FileUploadByteCountLimitException(final String message, final long actual, final long permitted, final String fileName, final String fieldName) {
super(message, permitted, actual);
this.fileName = fileName;
this.fieldName = fieldName;
}
/**
* Gets the field name of the item, which caused the exception.
*
* @return Field name, if known, or null.
*/
public String getFieldName() {
return fieldName;
}
/**
* Gets the file name of the item, which caused the exception.
*
* @return File name, if known, or null.
*/
public String getFileName() {
return fileName;
}
}
/*
* 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.pub;
/**
* Signals that a file size exceeds the configured maximum.
*/
public class FileUploadByteCountLimitException extends FileUploadSizeException {
/**
* The exceptions UID, for serializing an instance.
*/
private static final long serialVersionUID = 2;
/**
* File name of the item, which caused the exception.
*/
private final String fileName;
/**
* Field name of the item, which caused the exception.
*/
private final String fieldName;
/**
* Constructs an instance with the specified detail message, and actual and permitted sizes.
*
* @param message The detail message (which is saved for later retrieval by the {@link #getMessage()} method)
* @param actual The actual request size.
* @param permitted The maximum permitted request size.
* @param fileName File name of the item, which caused the exception.
* @param fieldName Field name of the item, which caused the exception.
*/
public FileUploadByteCountLimitException(final String message, final long actual, final long permitted, final String fileName, final String fieldName) {
super(message, permitted, actual);
this.fileName = fileName;
this.fieldName = fieldName;
}
/**
* Gets the field name of the item, which caused the exception.
*
* @return Field name, if known, or null.
*/
public String getFieldName() {
return fieldName;
}
/**
* Gets the file name of the item, which caused the exception.
*
* @return File name, if known, or null.
*/
public String getFileName() {
return fileName;
}
}

View File

@ -1,62 +1,60 @@
/*
* 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.pub;
import org.apache.commons.fileupload2.FileUploadException;
/**
* Signals that a request is not a multipart request.
*/
public class FileUploadContentTypeException extends FileUploadException {
/**
* The exceptions UID, for serializing an instance.
*/
private static final long serialVersionUID = 2;
/**
* The guilty content type.
*/
private String contentType;
/**
* Constructs an instance with the specified detail message.
*
* @param message The detail message (which is saved for later retrieval by the {@link #getMessage()} method)
* @param contentType The guilty content type.
*/
public FileUploadContentTypeException(final String message, final String contentType) {
super(message);
this.contentType = contentType;
}
/**
* Constructs an instance with the specified detail message and cause.
*
* @param message
* The detail message (which is saved for later retrieval
* by the {@link #getMessage()} method)
* @param cause the original cause
*/
public FileUploadContentTypeException(final String message, final Throwable cause) {
super(message, cause);
}
public String getContentType() {
return contentType;
}
}
/*
* 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.pub;
import org.apache.commons.fileupload2.FileUploadException;
/**
* Signals that a request is not a multipart request.
*/
public class FileUploadContentTypeException extends FileUploadException {
/**
* The exceptions UID, for serializing an instance.
*/
private static final long serialVersionUID = 2;
/**
* The guilty content type.
*/
private String contentType;
/**
* Constructs an instance with the specified detail message.
*
* @param message The detail message (which is saved for later retrieval by the {@link #getMessage()} method)
* @param contentType The guilty content type.
*/
public FileUploadContentTypeException(final String message, final String contentType) {
super(message);
this.contentType = contentType;
}
/**
* Constructs an instance with the specified detail message and cause.
*
* @param message The detail message (which is saved for later retrieval by the {@link #getMessage()} method)
* @param cause the original cause
*/
public FileUploadContentTypeException(final String message, final Throwable cause) {
super(message, cause);
}
public String getContentType() {
return contentType;
}
}

View File

@ -1,72 +1,72 @@
/*
* 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.pub;
import org.apache.commons.fileupload2.FileUploadException;
/**
* Signals that a requests permitted size is exceeded.
*/
public class FileUploadSizeException extends FileUploadException {
/**
* Serial version UID, being used, if serialized.
*/
private static final long serialVersionUID = 2;
/**
* The actual size of the request.
*/
private final long actual;
/**
* The maximum permitted size of the request.
*/
private final long permitted;
/**
* Constructs an instance.
*
* @param message The detail message (which is saved for later retrieval by the {@link #getMessage()} method)
* @param permitted The requests size limit.
* @param actual The actual values for the request.
*/
public FileUploadSizeException(final String message, final long permitted, final long actual) {
super(message);
this.permitted = permitted;
this.actual = actual;
}
/**
* Gets the actual size of the request.
*
* @return The actual size of the request.
*/
public long getActualSize() {
return actual;
}
/**
* Gets the limit size of the request.
*
* @return The limit size of the request.
*/
public long getPermitted() {
return permitted;
}
}
/*
* 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.pub;
import org.apache.commons.fileupload2.FileUploadException;
/**
* Signals that a requests permitted size is exceeded.
*/
public class FileUploadSizeException extends FileUploadException {
/**
* Serial version UID, being used, if serialized.
*/
private static final long serialVersionUID = 2;
/**
* The actual size of the request.
*/
private final long actual;
/**
* The maximum permitted size of the request.
*/
private final long permitted;
/**
* Constructs an instance.
*
* @param message The detail message (which is saved for later retrieval by the {@link #getMessage()} method)
* @param permitted The requests size limit.
* @param actual The actual values for the request.
*/
public FileUploadSizeException(final String message, final long permitted, final long actual) {
super(message);
this.permitted = permitted;
this.actual = actual;
}
/**
* Gets the actual size of the request.
*
* @return The actual size of the request.
*/
public long getActualSize() {
return actual;
}
/**
* Gets the limit size of the request.
*
* @return The limit size of the request.
*/
public long getPermitted() {
return permitted;
}
}

View File

@ -16,7 +16,6 @@
*/
/**
* Exceptions, and other classes, that are known to be used outside
* of FileUpload.
* Exceptions, and other classes, that are known to be used outside of FileUpload.
*/
package org.apache.commons.fileupload2.pub;

View File

@ -21,8 +21,7 @@ import java.io.IOException;
import java.io.InputStream;
/**
* An input stream, which limits its data size. This stream is
* used, if the content length is unknown.
* An input stream, which limits its data size. This stream is used, if the content length is unknown.
*/
public abstract class LimitedInputStream extends FilterInputStream {
@ -40,8 +39,7 @@ public abstract class LimitedInputStream extends FilterInputStream {
* Creates a new instance.
*
* @param inputStream The input stream, which shall be limited.
* @param sizeMax The limit; no more than this number of bytes
* shall be returned by the source stream.
* @param sizeMax The limit; no more than this number of bytes shall be returned by the source stream.
*/
public LimitedInputStream(final InputStream inputStream, final long sizeMax) {
super(inputStream);
@ -49,8 +47,7 @@ public abstract class LimitedInputStream extends FilterInputStream {
}
/**
* Called to check, whether the input streams
* limit is reached.
* Called to check, whether the input streams limit is reached.
*
* @throws IOException The given limit is exceeded.
*/
@ -61,13 +58,10 @@ public abstract class LimitedInputStream extends FilterInputStream {
}
/**
* Closes this input stream and releases any system resources
* associated with the stream.
* This
* method simply performs {@code in.close()}.
* Closes this input stream and releases any system resources associated with the stream. This method simply performs {@code in.close()}.
*
* @throws IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
* @throws IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
*/
@Override
public void close() throws IOException {
@ -75,33 +69,25 @@ public abstract class LimitedInputStream extends FilterInputStream {
}
/**
* Called to indicate, that the input streams limit has
* been exceeded.
* Called to indicate, that the input streams limit has been exceeded.
*
* @param sizeMax The input streams limit, in bytes.
* @param count The actual number of bytes.
* @throws IOException The called method is expected
* to raise an IOException.
* @param count The actual number of bytes.
* @throws IOException The called method is expected to raise an IOException.
*/
protected abstract void raiseError(long sizeMax, long count)
throws IOException;
protected abstract void raiseError(long sizeMax, long count) throws IOException;
/**
* Reads the next byte of data from this input stream. The value
* byte is returned as an {@code int} in the range
* {@code 0} to {@code 255}. If no byte is available
* because the end of the stream has been reached, the value
* {@code -1} is returned. This method blocks until input data
* is available, the end of the stream is detected, or an exception
* is thrown.
* Reads the next byte of data from this input stream. The value byte is returned as an {@code int} in the range {@code 0} to {@code 255}. If no byte is
* available because the end of the stream has been reached, the value {@code -1} is returned. This method blocks until input data is available, the end of
* the stream is detected, or an exception is thrown.
* <p>
* This method
* simply performs {@code in.read()} and returns the result.
* This method simply performs {@code in.read()} and returns the result.
* </p>
* @return the next byte of data, or {@code -1} if the end of the
* stream is reached.
* @throws IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
*
* @return the next byte of data, or {@code -1} if the end of the stream is reached.
* @throws IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
*/
@Override
public int read() throws IOException {
@ -114,27 +100,20 @@ public abstract class LimitedInputStream extends FilterInputStream {
}
/**
* Reads up to {@code len} bytes of data from this input stream
* into an array of bytes. If {@code len} is not zero, the method
* blocks until some input is available; otherwise, no
* bytes are read and {@code 0} is returned.
* Reads up to {@code len} bytes of data from this input stream into an array of bytes. If {@code len} is not zero, the method blocks until some input is
* available; otherwise, no bytes are read and {@code 0} is returned.
* <p>
* This method simply performs {@code in.read(b, off, len)}
* and returns the result.
* This method simply performs {@code in.read(b, off, len)} and returns the result.
* </p>
* @param b the buffer into which the data is read.
* @param off The start offset in the destination array
* {@code b}.
* @param len the maximum number of bytes read.
* @return the total number of bytes read into the buffer, or
* {@code -1} if there is no more data because the end of
* the stream has been reached.
* @throws NullPointerException If {@code b} is {@code null}.
* @throws IndexOutOfBoundsException If {@code off} is negative,
* {@code len} is negative, or {@code len} is greater than
* {@code b.length - off}
* @throws IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
*
* @param b the buffer into which the data is read.
* @param off The start offset in the destination array {@code b}.
* @param len the maximum number of bytes read.
* @return the total number of bytes read into the buffer, or {@code -1} if there is no more data because the end of the stream has been reached.
* @throws NullPointerException If {@code b} is {@code null}.
* @throws IndexOutOfBoundsException If {@code off} is negative, {@code len} is negative, or {@code len} is greater than {@code b.length - off}
* @throws IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
*/
@Override
public int read(final byte[] b, final int off, final int len) throws IOException {

View File

@ -76,18 +76,16 @@ public final class MimeUtility {
}
/**
* Decodes a string of text obtained from a mail header into
* its proper form. The text generally will consist of a
* string of tokens, some of which may be encoded using
* base64 encoding.
* Decodes a string of text obtained from a mail header into its proper form. The text generally will consist of a string of tokens, some of which may be
* encoded using base64 encoding.
*
* @param text The text to decode.
* @param text The text to decode.
*
* @return The decoded text string.
* @throws UnsupportedEncodingException if the detected encoding in the input text is not supported.
*/
public static String decodeText(final String text) throws UnsupportedEncodingException {
// if the text contains any encoded tokens, those tokens will be marked with "=?". If the
// if the text contains any encoded tokens, those tokens will be marked with "=?". If the
// source string doesn't contain that sequent, no decoding is required.
if (!text.contains(ENCODED_TOKEN_MARKER)) {
return text;
@ -121,7 +119,7 @@ public final class MimeUtility {
offset++;
}
} else {
// we have a word token. We need to scan over the word and then try to parse it.
// we have a word token. We need to scan over the word and then try to parse it.
final int wordStart = offset;
while (offset < endOffset) {
@ -132,17 +130,17 @@ public final class MimeUtility {
}
offset++;
//NB: Trailing whitespace on these header strings will just be discarded.
// NB: Trailing whitespace on these header strings will just be discarded.
}
// pull out the word token.
final String word = text.substring(wordStart, offset);
// is the token encoded? decode the word
// is the token encoded? decode the word
if (word.startsWith(ENCODED_TOKEN_MARKER)) {
try {
// if this gives a parsing failure, treat it like a non-encoded word.
final String decodedWord = decodeWord(word);
// are any whitespace characters significant? Append 'em if we've got 'em.
// are any whitespace characters significant? Append 'em if we've got 'em.
if (!previousTokenEncoded && startWhiteSpace != -1) {
decodedText.append(text, startWhiteSpace, endWhiteSpace);
startWhiteSpace = -1;
@ -159,7 +157,7 @@ public final class MimeUtility {
// just ignore it, skip to next word
}
}
// this is a normal token, so it doesn't matter what the previous token was. Add the white space
// this is a normal token, so it doesn't matter what the previous token was. Add the white space
// if we have it.
if (startWhiteSpace != -1) {
decodedText.append(text, startWhiteSpace, endWhiteSpace);
@ -175,19 +173,18 @@ public final class MimeUtility {
}
/**
* Decodes a string using the RFC 2047 rules for an "encoded-word"
* type. This encoding has the syntax:
* Decodes a string using the RFC 2047 rules for an "encoded-word" type. This encoding has the syntax:
*
* encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
*
* @param word The possibly encoded word value.
* @param word The possibly encoded word value.
*
* @return The decoded word.
* @throws ParseException in case of a parse error of the RFC 2047
* @throws ParseException in case of a parse error of the RFC 2047
* @throws UnsupportedEncodingException Thrown when Invalid RFC 2047 encoding was found
*/
private static String decodeWord(final String word) throws ParseException, UnsupportedEncodingException {
// encoded words start with the characters "=?". If this not an encoded word, we throw a
// encoded words start with the characters "=?". If this not an encoded word, we throw a
// ParseException for the caller.
if (!word.startsWith(ENCODED_TOKEN_MARKER)) {
@ -246,8 +243,7 @@ public final class MimeUtility {
}
/**
* Translate a MIME standard character set name into the Java
* equivalent.
* Translate a MIME standard character set name into the Java equivalent.
*
* @param charset The MIME standard name.
*
@ -260,8 +256,8 @@ public final class MimeUtility {
}
final String mappedCharset = MIME2JAVA.get(charset.toLowerCase(Locale.ENGLISH));
// if there is no mapping, then the original name is used. Many of the MIME character set
// names map directly back into Java. The reverse isn't necessarily true.
// if there is no mapping, then the original name is used. Many of the MIME character set
// names map directly back into Java. The reverse isn't necessarily true.
if (mappedCharset == null) {
return charset;
}

View File

@ -25,16 +25,15 @@ import java.io.OutputStream;
final class QuotedPrintableDecoder {
/**
* The shift value required to create the upper nibble
* from the first of 2 byte values converted from ascii hex.
* The shift value required to create the upper nibble from the first of 2 byte values converted from ascii hex.
*/
private static final int UPPER_NIBBLE_SHIFT = Byte.SIZE / 2;
/**
* Decodes the encoded byte data writing it to the given output stream.
*
* @param data The array of byte data to decode.
* @param out The output stream used to return the decoded data.
* @param data The array of byte data to decode.
* @param out The output stream used to return the decoded data.
*
* @return the number of bytes produced.
* @throws IOException if an IO error occurs
@ -52,7 +51,7 @@ final class QuotedPrintableDecoder {
if (ch == '_') {
out.write(' ');
} else if (ch == '=') {
// we found an encoded character. Reduce the 3 char sequence to one.
// we found an encoded character. Reduce the 3 char sequence to one.
// but first, make sure we have two characters to work with.
if (off + 1 >= endOffset) {
throw new IOException("Invalid quoted printable encoding; truncated escape sequence");
@ -61,12 +60,12 @@ final class QuotedPrintableDecoder {
final byte b1 = data[off++];
final byte b2 = data[off++];
// we've found an encoded carriage return. The next char needs to be a newline
// we've found an encoded carriage return. The next char needs to be a newline
if (b1 == '\r') {
if (b2 != '\n') {
throw new IOException("Invalid quoted printable encoding; CR must be followed by LF");
}
// this was a soft linebreak inserted by the encoding. We just toss this away
// this was a soft linebreak inserted by the encoding. We just toss this away
// on decode.
} else {
// this is a hex pair we need to convert back to a single byte.

View File

@ -18,14 +18,15 @@ package org.apache.commons.fileupload2.util.mime;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
/**
* Utility class to decode/encode character set on HTTP Header fields based on RFC 2231.
* This implementation adheres to RFC 5987 in particular, which was defined for HTTP headers
* Utility class to decode/encode character set on HTTP Header fields based on RFC 2231. This implementation adheres to RFC 5987 in particular, which was
* defined for HTTP headers
* <p>
* RFC 5987 builds on RFC 2231, but has lesser scope like
* <a href="https://tools.ietf.org/html/rfc5987#section-3.2">mandatory charset definition</a>
* and <a href="https://tools.ietf.org/html/rfc5987#section-4">no parameter continuation</a>
* RFC 5987 builds on RFC 2231, but has lesser scope like <a href="https://tools.ietf.org/html/rfc5987#section-3.2">mandatory charset definition</a> and
* <a href="https://tools.ietf.org/html/rfc5987#section-4">no parameter continuation</a>
* </p>
*
* @see <a href="https://tools.ietf.org/html/rfc2231">RFC 2231</a>
* @see <a href="https://tools.ietf.org/html/rfc5987">RFC 5987</a>
*/
@ -59,17 +60,13 @@ public final class RFC2231Utility {
/**
* Decodes a string of text obtained from a HTTP header as per RFC 2231
*
* <b>Eg 1.</b> {@code us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A}
* will be decoded to {@code This is ***fun***}
* <b>Eg 1.</b> {@code us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A} will be decoded to {@code This is ***fun***}
*
* <b>Eg 2.</b> {@code iso-8859-1'en'%A3%20rate}
* will be decoded to {@code £ rate}.
* <b>Eg 2.</b> {@code iso-8859-1'en'%A3%20rate} will be decoded to {@code £ rate}.
*
* <b>Eg 3.</b> {@code UTF-8''%c2%a3%20and%20%e2%82%ac%20rates}
* will be decoded to {@code £ and rates}.
* <b>Eg 3.</b> {@code UTF-8''%c2%a3%20and%20%e2%82%ac%20rates} will be decoded to {@code £ and rates}.
*
* @param encodedText - Text to be decoded has a format of {@code <charset>'<language>'<encoded_value>}
* and ASCII only
* @param encodedText - Text to be decoded has a format of {@code <charset>'<language>'<encoded_value>} and ASCII only
* @return Decoded text based on charset encoding
* @throws UnsupportedEncodingException The requested character set wasn't found.
*/
@ -120,8 +117,8 @@ public final class RFC2231Utility {
}
/**
* Tests if asterisk (*) at the end of parameter name to indicate,
* if it has charset and language information to decode the value.
* Tests if asterisk (*) at the end of parameter name to indicate, if it has charset and language information to decode the value.
*
* @param paramName The parameter, which is being checked.
* @return {@code true}, if encoded as per RFC 2231, {@code false} otherwise
*/
@ -133,8 +130,8 @@ public final class RFC2231Utility {
}
/**
* If {@code paramName} has Asterisk (*) at the end, it will be stripped off,
* else the passed value will be returned.
* If {@code paramName} has Asterisk (*) at the end, it will be stripped off, else the passed value will be returned.
*
* @param paramName The parameter, which is being inspected.
* @return stripped {@code paramName} of Asterisk (*), if RFC2231 encoded
*/
@ -148,8 +145,7 @@ public final class RFC2231Utility {
}
/**
* Private constructor so that no instances can be created. This class
* contains only static utility methods.
* Private constructor so that no instances can be created. This class contains only static utility methods.
*/
private RFC2231Utility() {
}

View File

@ -16,7 +16,7 @@
*/
/**
* MIME decoder implementation, imported and retailed from
* <a href="http://svn.apache.org/repos/asf/geronimo/specs/tags/geronimo-javamail_1.4_spec-1.4/">Apache Geronimo</a>.
* MIME decoder implementation, imported and retailed from <a href="http://svn.apache.org/repos/asf/geronimo/specs/tags/geronimo-javamail_1.4_spec-1.4/">Apache
* Geronimo</a>.
*/
package org.apache.commons.fileupload2.util.mime;

View File

@ -16,8 +16,7 @@
*/
/**
* This package contains various IO related utility classes
* or methods, which are basically reusable and not necessarily
* restricted to the scope of a file upload.
* This package contains various IO related utility classes or methods, which are basically reusable and not necessarily restricted to the scope of a file
* upload.
*/
package org.apache.commons.fileupload2.util;

View File

@ -24,33 +24,22 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.stream.Stream;
import org.apache.commons.fileupload2.portlet.PortletFileUploadTest;
import org.apache.commons.fileupload2.servlet.ServletFileUploadTest;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.api.Test;
/**
* Common tests for implementations of {@link FileUpload}. This is a parameterized test.
* Tests must be valid and common to all implementations of FileUpload added as parameter
* in this class.
* Common tests for implementations of {@link FileUpload}. This is a parameterized test. Tests must be valid and common to all implementations of FileUpload
* added as parameter in this class.
*
* @see ServletFileUploadTest
* @see PortletFileUploadTest
* @since 1.4
*/
public class FileUploadTest {
public abstract class AbstractFileUploadTest extends AbstractFileUploadWrapper {
/**
* @return {@link FileUpload} classes under test.
*/
public static Stream<FileUpload> data() {
return Util.fileUploadImplementations().stream();
protected AbstractFileUploadTest(final FileUpload fileUpload) {
super(fileUpload);
}
private void assertHeaders(final String[] headerNames, final String[] headerValues,
final FileItem fileItems, final int index) {
private void assertHeaders(final String[] headerNames, final String[] headerValues, final FileItem fileItems, final int index) {
for (int i = 0; i < headerNames.length; i++) {
final String value = fileItems.getHeaders().getHeader(headerNames[i]);
if (i == index) {
@ -64,11 +53,10 @@ public class FileUploadTest {
/**
* Test for <a href="https://issues.apache.org/jira/browse/FILEUPLOAD-239">FILEUPLOAD-239</a>
*/
@ParameterizedTest
@MethodSource("data")
public void testContentTypeAttachment(final FileUpload upload)
throws IOException, FileUploadException {
final List<FileItem> fileItems = Util.parseUpload(upload,
@Test
public void testContentTypeAttachment() throws IOException, FileUploadException {
// @formatter:off
final List<FileItem> fileItems = parseUpload(upload,
"-----1234\r\n" +
"content-disposition: form-data; name=\"field1\"\r\n" +
"\r\n" +
@ -84,6 +72,7 @@ public class FileUploadTest {
"... contents of file1.txt ...\r\n" +
"-----9876--\r\n" +
"-----1234--\r\n");
// @formatter:on
assertEquals(2, fileItems.size());
final FileItem field = fileItems.get(0);
@ -102,16 +91,16 @@ public class FileUploadTest {
/**
* This is what the browser does if you submit the form without choosing a file.
*/
@ParameterizedTest
@MethodSource("data")
public void testEmptyFile(final FileUpload upload)
throws FileUploadException {
final List<FileItem> fileItems = Util.parseUpload (upload,
@Test
public void testEmptyFile() throws FileUploadException {
// @formatter:off
final List<FileItem> fileItems = parseUpload (upload,
"-----1234\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"\"\r\n" +
"\r\n" +
"\r\n" +
"-----1234--\r\n");
// @formatter:on
assertEquals(1, fileItems.size());
final FileItem file = fileItems.get(0);
@ -120,11 +109,10 @@ public class FileUploadTest {
assertEquals("", file.getName());
}
@ParameterizedTest
@MethodSource("data")
public void testFilenameCaseSensitivity(final FileUpload upload)
throws IOException, FileUploadException {
final List<FileItem> fileItems = Util.parseUpload(upload,
@Test
public void testFilenameCaseSensitivity() throws IOException, FileUploadException {
// @formatter:off
final List<FileItem> fileItems = parseUpload(upload,
"-----1234\r\n" +
"Content-Disposition: form-data; "
+ "name=\"FiLe\"; filename=\"FOO.tab\"\r\n" +
@ -133,6 +121,7 @@ public class FileUploadTest {
"This is the content of the file\n" +
"\r\n" +
"-----1234--\r\n");
// @formatter:on
assertEquals(1, fileItems.size());
final FileItem file = fileItems.get(0);
@ -140,11 +129,10 @@ public class FileUploadTest {
assertEquals("FOO.tab", file.getName());
}
@ParameterizedTest
@MethodSource("data")
public void testFileUpload(final FileUpload upload)
throws IOException, FileUploadException {
final List<FileItem> fileItems = Util.parseUpload(upload,
@Test
public void testFileUpload() throws IOException, FileUploadException {
// @formatter:off
final List<FileItem> fileItems = parseUpload(upload,
"-----1234\r\n" +
"Content-Disposition: "
+ "form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
@ -165,6 +153,7 @@ public class FileUploadTest {
"\r\n" +
"value2\r\n" +
"-----1234--\r\n");
// @formatter:on
assertEquals(4, fileItems.size());
final FileItem file = fileItems.get(0);
@ -193,17 +182,12 @@ public class FileUploadTest {
/**
* Test case for <a href="https://issues.apache.org/jira/browse/FILEUPLOAD-130">
*/
@ParameterizedTest
@MethodSource("data")
public void testFileUpload130(final FileUpload upload)
throws Exception {
final String[] headerNames = {
"SomeHeader", "OtherHeader", "YetAnotherHeader", "WhatAHeader"
};
final String[] headerValues = {
"present", "Is there", "Here", "Is That"
};
final List<FileItem> fileItems = Util.parseUpload(upload,
@Test
public void testFileUpload130() throws Exception {
final String[] headerNames = { "SomeHeader", "OtherHeader", "YetAnotherHeader", "WhatAHeader" };
final String[] headerValues = { "present", "Is there", "Here", "Is That" };
// @formatter:off
final List<FileItem> fileItems = parseUpload(upload,
"-----1234\r\n" +
"Content-Disposition: form-data; name=\"file\"; "
+ "filename=\"foo.tab\"\r\n" +
@ -230,6 +214,7 @@ public class FileUploadTest {
"\r\n" +
"value2\r\n" +
"-----1234--\r\n");
// @formatter:on
assertEquals(4, fileItems.size());
final FileItem file = fileItems.get(0);
@ -248,9 +233,9 @@ public class FileUploadTest {
/**
* Test for <a href="https://issues.apache.org/jira/browse/FILEUPLOAD-62">FILEUPLOAD-62</a>
*/
@ParameterizedTest
@MethodSource("data")
public void testFILEUPLOAD62(final FileUpload upload) throws Exception {
@Test
public void testFILEUPLOAD62() throws Exception {
// @formatter:off
final String contentType = "multipart/form-data; boundary=AaB03x";
final String request =
"--AaB03x\r\n" +
@ -274,8 +259,8 @@ public class FileUploadTest {
"...contents of file2.gif...\r\n" +
"--BbC04y--\r\n" +
"--AaB03x--";
final List<FileItem> fileItems = Util.parseUpload(upload, request.getBytes(StandardCharsets.US_ASCII),
contentType);
// @formatter:on
final List<FileItem> fileItems = parseUpload(upload, request.getBytes(StandardCharsets.US_ASCII), contentType);
assertEquals(3, fileItems.size());
final FileItem item0 = fileItems.get(0);
assertEquals("field1", item0.getFieldName());
@ -294,11 +279,10 @@ public class FileUploadTest {
/**
* Test for <a href="https://issues.apache.org/jira/browse/FILEUPLOAD-111">FILEUPLOAD-111</a>
*/
@ParameterizedTest
@MethodSource("data")
public void testFoldedHeaders(final FileUpload upload)
throws IOException, FileUploadException {
final List<FileItem> fileItems = Util.parseUpload(upload, "-----1234\r\n" +
@Test
public void testFoldedHeaders() throws IOException, FileUploadException {
// @formatter:off
final List<FileItem> fileItems = parseUpload(upload, "-----1234\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
"Content-Type: text/whatever\r\n" +
"\r\n" +
@ -319,6 +303,7 @@ public class FileUploadTest {
"\r\n" +
"value2\r\n" +
"-----1234--\r\n");
// @formatter:on
assertEquals(4, fileItems.size());
final FileItem file = fileItems.get(0);
@ -345,32 +330,31 @@ public class FileUploadTest {
}
/**
* Internet Explorer 5 for the Mac has a bug where the carriage
* return is missing on any boundary line immediately preceding
* an input with type=image. (type=submit does not have the bug.)
* Internet Explorer 5 for the Mac has a bug where the carriage return is missing on any boundary line immediately preceding an input with type=image.
* (type=submit does not have the bug.)
*/
@ParameterizedTest
@MethodSource("data")
public void testIE5MacBug(final FileUpload upload)
throws FileUploadException {
final List<FileItem> fileItems = Util.parseUpload(upload,
"-----1234\r\n" +
"Content-Disposition: form-data; name=\"field1\"\r\n" +
"\r\n" +
"fieldValue\r\n" +
"-----1234\n" + // NOTE \r missing
"Content-Disposition: form-data; name=\"submitName.x\"\r\n" +
"\r\n" +
"42\r\n" +
"-----1234\n" + // NOTE \r missing
"Content-Disposition: form-data; name=\"submitName.y\"\r\n" +
"\r\n" +
"21\r\n" +
"-----1234\r\n" +
"Content-Disposition: form-data; name=\"field2\"\r\n" +
"\r\n" +
"fieldValue2\r\n" +
"-----1234--\r\n");
@Test
public void testIE5MacBug() throws FileUploadException {
final List<FileItem> fileItems = parseUpload(upload,
// @formatter:off
"-----1234\r\n" +
"Content-Disposition: form-data; name=\"field1\"\r\n" +
"\r\n" +
"fieldValue\r\n" +
"-----1234\n" + // NOTE \r missing
"Content-Disposition: form-data; name=\"submitName.x\"\r\n" +
"\r\n" +
"42\r\n" +
"-----1234\n" + // NOTE \r missing
"Content-Disposition: form-data; name=\"submitName.y\"\r\n" +
"\r\n" +
"21\r\n" +
"-----1234\r\n" +
"Content-Disposition: form-data; name=\"field2\"\r\n" +
"\r\n" +
"fieldValue2\r\n" +
"-----1234--\r\n");
//@formatter:on
assertEquals(4, fileItems.size());

View File

@ -0,0 +1,45 @@
/*
* 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 java.nio.charset.StandardCharsets;
import java.util.List;
/**
* Common tests for implementations of {@link FileUpload}. This is a parameterized test. Tests must be valid and common to all implementations of FileUpload
* added as parameter in this class.
*/
public abstract class AbstractFileUploadWrapper {
protected final FileUpload upload;
protected AbstractFileUploadWrapper(final FileUpload fileUpload) {
this.upload = fileUpload;
}
public List<FileItem> parseUpload(final FileUpload upload, final byte[] bytes) throws FileUploadException {
return parseUpload(upload, bytes, Constants.CONTENT_TYPE);
}
public abstract List<FileItem> parseUpload(final FileUpload upload, final byte[] bytes, final String contentType) throws FileUploadException;
public List<FileItem> parseUpload(final FileUpload upload, final String content) throws FileUploadException {
final byte[] bytes = content.getBytes(StandardCharsets.US_ASCII);
return parseUpload(upload, bytes, Constants.CONTENT_TYPE);
}
}

View File

@ -28,5 +28,6 @@ public final class Constants {
*/
public static final String CONTENT_TYPE = "multipart/form-data; boundary=---1234";
private Constants() { }
private Constants() {
}
}

View File

@ -27,8 +27,7 @@ import org.apache.commons.fileupload2.util.FileItemHeadersImpl;
import org.junit.jupiter.api.Test;
/**
* Unit tests {@link FileItemHeaders} and
* {@link FileItemHeadersImpl}.
* Unit tests {@link FileItemHeaders} and {@link FileItemHeadersImpl}.
*/
public class FileItemHeadersTest {
@ -38,8 +37,7 @@ public class FileItemHeadersTest {
@Test
public void testFileItemHeaders() throws Exception {
final FileItemHeadersImpl aMutableFileItemHeaders = new FileItemHeadersImpl();
aMutableFileItemHeaders.addHeader("Content-Disposition",
"form-data; name=\"FileItem\"; filename=\"file1.txt\"");
aMutableFileItemHeaders.addHeader("Content-Disposition", "form-data; name=\"FileItem\"; filename=\"file1.txt\"");
aMutableFileItemHeaders.addHeader("Content-Type", "text/plain");
aMutableFileItemHeaders.addHeader("TestHeader", "headerValue1");
@ -53,8 +51,7 @@ public class FileItemHeadersTest {
assertEquals("testheader", headerNameEnumeration.next());
assertFalse(headerNameEnumeration.hasNext());
assertEquals(aMutableFileItemHeaders.getHeader("Content-Disposition"),
"form-data; name=\"FileItem\"; filename=\"file1.txt\"");
assertEquals(aMutableFileItemHeaders.getHeader("Content-Disposition"), "form-data; name=\"FileItem\"; filename=\"file1.txt\"");
assertEquals(aMutableFileItemHeaders.getHeader("Content-Type"), "text/plain");
assertEquals(aMutableFileItemHeaders.getHeader("content-type"), "text/plain");
assertEquals(aMutableFileItemHeaders.getHeader("TestHeader"), "headerValue1");

View File

@ -39,11 +39,7 @@ public class MultipartStreamTest {
final byte[] boundary = BOUNDARY_TEXT.getBytes();
final int iBufSize = 1;
assertThrows(IllegalArgumentException.class,
() -> new MultipartStream(
input,
boundary,
iBufSize,
new MultipartStream.ProgressNotifier(null, contents.length)));
() -> new MultipartStream(input, boundary, iBufSize, new MultipartStream.ProgressNotifier(null, contents.length)));
}
@Test
@ -52,13 +48,8 @@ public class MultipartStreamTest {
final byte[] contents = strData.getBytes();
final InputStream input = new ByteArrayInputStream(contents);
final byte[] boundary = BOUNDARY_TEXT.getBytes();
final int iBufSize =
boundary.length + MultipartStream.BOUNDARY_PREFIX.length + 1;
final MultipartStream ms = new MultipartStream(
input,
boundary,
iBufSize,
new MultipartStream.ProgressNotifier(null, contents.length));
final int iBufSize = boundary.length + MultipartStream.BOUNDARY_PREFIX.length + 1;
final MultipartStream ms = new MultipartStream(input, boundary, iBufSize, new MultipartStream.ProgressNotifier(null, contents.length));
assertNotNull(ms);
}
@ -68,10 +59,7 @@ public class MultipartStreamTest {
final byte[] contents = strData.getBytes();
final InputStream input = new ByteArrayInputStream(contents);
final byte[] boundary = BOUNDARY_TEXT.getBytes();
final MultipartStream ms = new MultipartStream(
input,
boundary,
new MultipartStream.ProgressNotifier(null, contents.length));
final MultipartStream ms = new MultipartStream(input, boundary, new MultipartStream.ProgressNotifier(null, contents.length));
assertNotNull(ms);
}

View File

@ -42,15 +42,15 @@ public class ParameterParserTest {
public void testFileUpload139() {
final ParameterParser parser = new ParameterParser();
String s = "Content-type: multipart/form-data , boundary=AaB03x";
Map<String, String> params = parser.parse(s, new char[] {',', ';' });
Map<String, String> params = parser.parse(s, new char[] { ',', ';' });
assertEquals("AaB03x", params.get("boundary"));
s = "Content-type: multipart/form-data, boundary=AaB03x";
params = parser.parse(s, new char[] {';', ',' });
params = parser.parse(s, new char[] { ';', ',' });
assertEquals("AaB03x", params.get("boundary"));
s = "Content-type: multipart/mixed, boundary=BbC04y";
params = parser.parse(s, new char[] {',', ';' });
params = parser.parse(s, new char[] { ',', ';' });
assertEquals("BbC04y", params.get("boundary"));
}
@ -74,36 +74,34 @@ public class ParameterParserTest {
final ParameterParser parser = new ParameterParser();
// Should parse a UTF-8 charset
String s = "Content-Disposition: form-data; "
+ "name=\"file\"; filename*=UTF-8''%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF\r\n";
String s = "Content-Disposition: form-data; " + "name=\"file\"; filename*=UTF-8''%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF\r\n";
Map<String, String> params = parser.parse(s, new char[] { ',', ';' });
assertEquals("\u3053\u3093\u306B\u3061\u306F", params.get("filename")); //filename = "こんにちは" in japanese
assertEquals("\u3053\u3093\u306B\u3061\u306F", params.get("filename")); // filename = "こんにちは" in japanese
// Should parse ISO-8859-1 charset
s = "Content-Disposition: form-data; name=\"file\"; filename*=UTF-8''%70%C3%A2%74%C3%A9\r\n";
params = parser.parse(s, new char[] { ',', ';' });
assertEquals("\u0070\u00e2\u0074\u00e9", params.get("filename")); //filename = "pâté" in french
assertEquals("\u0070\u00e2\u0074\u00e9", params.get("filename")); // filename = "pâté" in french
// Should not decode if '*' is not at the end of param-name
s = "Content-Disposition: form-data; name=\"file\"; file*name=UTF-8''%61%62%63\r\n";
params = parser.parse(s, new char[] {',', ';' });
params = parser.parse(s, new char[] { ',', ';' });
assertEquals("UTF-8''%61%62%63", params.get("file*name"));
// Should not decode if param-value does not follow <charset>'<lang>'<encoded>
s = "Content-Disposition: form-data; name=\"file\"; filename*=a'bc\r\n";
params = parser.parse(s, new char[] {',', ';' });
params = parser.parse(s, new char[] { ',', ';' });
assertEquals("a'bc", params.get("filename"));
// Should not decode if param-name doesn't have '*' at end
s = "Content-Disposition: form-data; name=\"file\"; filename=a'b'c\r\n";
params = parser.parse(s, new char[] {',', ';' });
params = parser.parse(s, new char[] { ',', ';' });
assertEquals("a'b'c", params.get("filename"));
}
@Test
public void testParsing() {
String s =
"test; test1 = stuff ; test2 = \"stuff; stuff\"; test3=\"stuff";
String s = "test; test1 = stuff ; test2 = \"stuff; stuff\"; test3=\"stuff";
final ParameterParser parser = new ParameterParser();
Map<String, String> params = parser.parse(s, ';');
assertNull(params.get("test"));
@ -111,7 +109,7 @@ public class ParameterParserTest {
assertEquals("stuff; stuff", params.get("test2"));
assertEquals("\"stuff", params.get("test3"));
params = parser.parse(s, new char[] {',', ';' });
params = parser.parse(s, new char[] { ',', ';' });
assertNull(params.get("test"));
assertEquals("stuff", params.get("test1"));
assertEquals("stuff; stuff", params.get("test2"));

View File

@ -24,8 +24,7 @@ import java.io.UnsupportedEncodingException;
import org.junit.jupiter.api.Test;
/**
* Use the online <a href="http://dogmamix.com/MimeHeadersDecoder/">MimeHeadersDecoder</a>
* to validate expected values.
* Use the online <a href="http://dogmamix.com/MimeHeadersDecoder/">MimeHeadersDecoder</a> to validate expected values.
*
* @since 1.3
*/
@ -43,15 +42,13 @@ public final class MimeUtilityTestCase {
@Test
public void decodeIso88591Base64Encoded() throws Exception {
assertEncoded("If you can read this you understand the example.",
"=?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?= "
+ "=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=\"\r\n");
"=?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?= " + "=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=\"\r\n");
}
@Test
public void decodeIso88591Base64EncodedWithWhiteSpace() throws Exception {
assertEncoded("If you can read this you understand the example.",
"=?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=\t \r\n =?ISO-8859-"
+ "2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=\"\r\n");
"=?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=\t \r\n =?ISO-8859-" + "2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=\"\r\n");
}
@Test

View File

@ -100,9 +100,8 @@ public final class QuotedPrintableDecoderTestCase {
*/
@Test
public void softLineBreakDecode() throws Exception {
assertEncoded("If you believe that truth=beauty, then surely mathematics is the most "
+ "beautiful branch of philosophy.", "If you believe that truth=3Dbeauty, then "
+ "surely=20=\r\nmathematics is the most beautiful branch of philosophy.");
assertEncoded("If you believe that truth=beauty, then surely mathematics is the most " + "beautiful branch of philosophy.",
"If you believe that truth=3Dbeauty, then " + "surely=20=\r\nmathematics is the most beautiful branch of philosophy.");
}
@Test

View File

@ -28,8 +28,7 @@ import org.junit.jupiter.api.Test;
/**
* The expected characters are encoded in UTF16, while the actual characters may be encoded in UTF-8/ISO-8859-1
*
* RFC 5987 recommends to support both UTF-8 & ISO 8859-1. Test values are taken
* from https://tools.ietf.org/html/rfc5987#section-3.2.2
* RFC 5987 recommends to support both UTF-8 & ISO 8859-1. Test values are taken from https://tools.ietf.org/html/rfc5987#section-3.2.2
*/
public final class RFC2231UtilityTestCase {
@ -44,13 +43,12 @@ public final class RFC2231UtilityTestCase {
@Test
public void decodeIso88591() throws Exception {
assertEncoded("\u00A3 rate", "iso-8859-1'en'%A3%20rate"); //"£ rate"
assertEncoded("\u00A3 rate", "iso-8859-1'en'%A3%20rate"); // "£ rate"
}
@Test
public void decodeUtf8() throws Exception {
assertEncoded("\u00a3 \u0061\u006e\u0064 \u20ac \u0072\u0061\u0074\u0065\u0073",
"UTF-8''%c2%a3%20and%20%e2%82%ac%20rates"); //"£ and € rates"
assertEncoded("\u00a3 \u0061\u006e\u0064 \u20ac \u0072\u0061\u0074\u0065\u0073", "UTF-8''%c2%a3%20and%20%e2%82%ac%20rates"); // "£ and € rates"
}
@Test

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<fileset-config file-format-version="1.2.0" simple-config="false" sync-formatter="false">
<local-check-config name="maven-checkstyle-plugin validate-main" location="file:/C:/Users/ggregory/git/a/commons-fileupload/commons-fileupload2-jakarta/../src/checkstyle/fileupload_checks.xml" type="remote" description="maven-checkstyle-plugin configuration validate-main">
<property name="checkstyle.header.file" value="C:\Users\ggregory\OneDrive - Rocket Software, Inc\ew\apache-commons\.metadata\.plugins\org.eclipse.core.resources\.projects\commons-fileupload2-jakarta\com.basistech.m2e.code.quality.checkstyleConfigurator\checkstyle-header-validate-main.txt"/>
<property name="checkstyle.cache.file" value="${project_loc}/target/checkstyle-cachefile"/>
<property name="checkstyle.suppressions.file" value="C:\Users\ggregory\OneDrive - Rocket Software, Inc\ew\apache-commons\.metadata\.plugins\org.eclipse.core.resources\.projects\commons-fileupload2-jakarta\com.basistech.m2e.code.quality.checkstyleConfigurator\checkstyle-suppressions-validate-main.xml"/>
</local-check-config>
<fileset name="java-sources-validate-main" enabled="true" check-config-name="maven-checkstyle-plugin validate-main" local="true">
<file-match-pattern match-pattern="^src/main/java/.*\.java" include-pattern="true"/>
<file-match-pattern match-pattern="^src/main/resources.*\.properties" include-pattern="true"/>
<file-match-pattern match-pattern="^.*\.properties" include-pattern="true"/>
<file-match-pattern match-pattern="^src/test/resources.*\.properties" include-pattern="true"/>
</fileset>
</fileset-config>

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.commons</groupId>
<artifactId>commons-fileupload2</artifactId>
<version>2.0.0-SNAPSHOT</version>
</parent>
<artifactId>commons-fileupload2-jakarta</artifactId>
<name>Apache Commons FileUpload Jakarta Servlet</name>
<description>
The Apache Commons FileUpload component provides a simple yet flexible means of adding support for multipart
file upload functionality to servlets and web applications.
</description>
<properties>
<commons.parent.dir>${basedir}/..</commons.parent.dir>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-fileupload2-core</artifactId>
<version>2.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-fileupload2-core</artifactId>
<version>2.0.0-SNAPSHOT</version>
<classifier>tests</classifier>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<!-- Version 6.0.0 requires Java 11 -->
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons.lang3.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.fileupload2.jaksrvlt;
package org.apache.commons.fileupload2.jakarta;
import org.apache.commons.io.FileCleaningTracker;
@ -23,50 +23,39 @@ import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
/**
* A servlet context listener, which ensures that the
* {@link FileCleaningTracker}'s reaper thread is terminated,
* when the web application is destroyed.
* A servlet context listener, which ensures that the {@link FileCleaningTracker}'s reaper thread is terminated, when the web application is destroyed.
*/
public class JakSrvltFileCleaner implements ServletContextListener {
public class JakartaServletFileCleaner implements ServletContextListener {
/**
* Attribute name, which is used for storing an instance of
* {@link FileCleaningTracker} in the web application.
* Attribute name, which is used for storing an instance of {@link FileCleaningTracker} in the web application.
*/
public static final String FILE_CLEANING_TRACKER_ATTRIBUTE
= JakSrvltFileCleaner.class.getName() + ".FileCleaningTracker";
public static final String FILE_CLEANING_TRACKER_ATTRIBUTE = JakartaServletFileCleaner.class.getName() + ".FileCleaningTracker";
/**
* Gets the instance of {@link FileCleaningTracker}, which is
* associated with the given {@link ServletContext}.
* Gets the instance of {@link FileCleaningTracker}, which is associated with the given {@link ServletContext}.
*
* @param servletContext The servlet context to query
* @return The contexts tracker
*/
public static FileCleaningTracker
getFileCleaningTracker(final ServletContext servletContext) {
return (FileCleaningTracker)
servletContext.getAttribute(FILE_CLEANING_TRACKER_ATTRIBUTE);
public static FileCleaningTracker getFileCleaningTracker(final ServletContext servletContext) {
return (FileCleaningTracker) servletContext.getAttribute(FILE_CLEANING_TRACKER_ATTRIBUTE);
}
/**
* Sets the instance of {@link FileCleaningTracker}, which is
* associated with the given {@link ServletContext}.
* Sets the instance of {@link FileCleaningTracker}, which is associated with the given {@link ServletContext}.
*
* @param servletContext The servlet context to modify
* @param tracker The tracker to set
* @param tracker The tracker to set
*/
public static void setFileCleaningTracker(final ServletContext servletContext,
final FileCleaningTracker tracker) {
public static void setFileCleaningTracker(final ServletContext servletContext, final FileCleaningTracker tracker) {
servletContext.setAttribute(FILE_CLEANING_TRACKER_ATTRIBUTE, tracker);
}
/**
* Called when the web application is being destroyed.
* Calls {@link FileCleaningTracker#exitWhenFinished()}.
* Called when the web application is being destroyed. Calls {@link FileCleaningTracker#exitWhenFinished()}.
*
* @param sce The servlet context, used for calling
* {@link #getFileCleaningTracker(ServletContext)}.
* @param sce The servlet context, used for calling {@link #getFileCleaningTracker(ServletContext)}.
*/
@Override
public void contextDestroyed(final ServletContextEvent sce) {
@ -74,15 +63,12 @@ public class JakSrvltFileCleaner implements ServletContextListener {
}
/**
* Called when the web application is initialized. Does
* nothing.
* Called when the web application is initialized. Does nothing.
*
* @param sce The servlet context, used for calling
* {@link #setFileCleaningTracker(ServletContext, FileCleaningTracker)}.
* @param sce The servlet context, used for calling {@link #setFileCleaningTracker(ServletContext, FileCleaningTracker)}.
*/
@Override
public void contextInitialized(final ServletContextEvent sce) {
setFileCleaningTracker(sce.getServletContext(),
new FileCleaningTracker());
setFileCleaningTracker(sce.getServletContext(), new FileCleaningTracker());
}
}

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.fileupload2.jaksrvlt;
package org.apache.commons.fileupload2.jakarta;
import java.io.IOException;
import java.util.List;
@ -32,20 +32,15 @@ import jakarta.servlet.http.HttpServletRequest;
/**
* High level API for processing file uploads.
* <p>
* This class handles multiple files per single HTML widget, sent using
* {@code multipart/mixed} encoding type, as specified by
* <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Use {@link
* #parseRequest(HttpServletRequest)} to acquire a list of {@link
* org.apache.commons.fileupload2.FileItem}s associated with a given HTML
* widget.
* This class handles multiple files per single HTML widget, sent using {@code multipart/mixed} encoding type, as specified by
* <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Use {@link #parseRequest(HttpServletRequest)} to acquire a list of
* {@link org.apache.commons.fileupload2.FileItem}s associated with a given HTML widget.
* </p>
* <p>
* How the data for individual parts is stored is determined by the factory
* used to create them; a given part may be in memory, on disk, or somewhere
* else.
* How the data for individual parts is stored is determined by the factory used to create them; a given part may be in memory, on disk, or somewhere else.
* </p>
*/
public class JakSrvltFileUpload extends FileUpload {
public class JakartaServletFileUpload extends FileUpload {
/**
* Constant for HTTP POST method.
@ -53,86 +48,70 @@ public class JakSrvltFileUpload extends FileUpload {
private static final String POST_METHOD = "POST";
/**
* Utility method that determines whether the request contains multipart
* content.
* Utility method that determines whether the request contains multipart content.
*
* @param request The servlet request to be evaluated. Must be non-null.
*
* @return {@code true} if the request is multipart;
* {@code false} otherwise.
* @return {@code true} if the request is multipart; {@code false} otherwise.
*/
public static final boolean isMultipartContent(final HttpServletRequest request) {
return POST_METHOD.equalsIgnoreCase(request.getMethod()) && AbstractFileUpload.isMultipartContent(new JakSrvltRequestContext(request));
return POST_METHOD.equalsIgnoreCase(request.getMethod()) && AbstractFileUpload.isMultipartContent(new JakartaServletRequestContext(request));
}
/**
* Constructs an uninitialized instance of this class. A factory must be
* configured, using {@code setFileItemFactory()}, before attempting
* to parse requests.
* Constructs an uninitialized instance of this class. A factory must be configured, using {@code setFileItemFactory()}, before attempting to parse
* requests.
*
* @see FileUpload#FileUpload(FileItemFactory)
*/
public JakSrvltFileUpload() {
public JakartaServletFileUpload() {
}
/**
* Constructs an instance of this class which uses the supplied factory to
* create {@code FileItem} instances.
* Constructs an instance of this class which uses the supplied factory to create {@code FileItem} instances.
*
* @see FileUpload#FileUpload()
* @param fileItemFactory The factory to use for creating file items.
*/
public JakSrvltFileUpload(final FileItemFactory fileItemFactory) {
public JakartaServletFileUpload(final FileItemFactory fileItemFactory) {
super(fileItemFactory);
}
/**
* Gets an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
* compliant {@code multipart/form-data} file item iterator.
* Gets an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant {@code multipart/form-data} file item iterator.
*
* @param request The servlet request to be parsed.
* @return An iterator to instances of {@code FileItemStream}
* parsed from the request, in the order that they were
* transmitted.
* @throws FileUploadException if there are problems reading/parsing
* the request or storing files.
* @throws IOException An I/O error occurred. This may be a network
* error while communicating with the client or a problem while
* storing the uploaded content.
* @return An iterator to instances of {@code FileItemStream} parsed from the request, in the order that they were transmitted.
* @throws FileUploadException if there are problems reading/parsing the request or storing files.
* @throws IOException An I/O error occurred. This may be a network error while communicating with the client or a problem while storing the
* uploaded content.
*/
public FileItemIterator getItemIterator(final HttpServletRequest request)
throws FileUploadException, IOException {
return super.getItemIterator(new JakSrvltRequestContext(request));
public FileItemIterator getItemIterator(final HttpServletRequest request) throws FileUploadException, IOException {
return super.getItemIterator(new JakartaServletRequestContext(request));
}
/**
* Parses an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
* compliant {@code multipart/form-data} stream.
* Parses an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant {@code multipart/form-data} stream.
*
* @param request The servlet request to be parsed.
* @return A map of {@code FileItem} instances parsed from the request.
* @throws FileUploadException if there are problems reading/parsing
* the request or storing files.
* @throws FileUploadException if there are problems reading/parsing the request or storing files.
*
* @since 1.3
*/
public Map<String, List<FileItem>> parseParameterMap(final HttpServletRequest request)
throws FileUploadException {
return parseParameterMap(new JakSrvltRequestContext(request));
public Map<String, List<FileItem>> parseParameterMap(final HttpServletRequest request) throws FileUploadException {
return parseParameterMap(new JakartaServletRequestContext(request));
}
/**
* Parses an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
* compliant {@code multipart/form-data} stream.
* Parses an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant {@code multipart/form-data} stream.
*
* @param request The servlet request to be parsed.
* @return A list of {@code FileItem} instances parsed from the
* request, in the order that they were transmitted.
* @throws FileUploadException if there are problems reading/parsing
* the request or storing files.
* @return A list of {@code FileItem} instances parsed from the request, in the order that they were transmitted.
* @throws FileUploadException if there are problems reading/parsing the request or storing files.
*/
public List<FileItem> parseRequest(final HttpServletRequest request) throws FileUploadException {
return parseRequest(new JakSrvltRequestContext(request));
return parseRequest(new JakartaServletRequestContext(request));
}
}

View File

@ -1,81 +1,80 @@
/*
* 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.jaksrvlt;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.fileupload2.AbstractRequestContext;
import jakarta.servlet.http.HttpServletRequest;
/**
* Provides access to the request information needed for a request made to
* an HTTP servlet.
*
* @since 1.1
*/
public class JakSrvltRequestContext extends AbstractRequestContext {
/**
* The request for which the context is being provided.
*/
private final HttpServletRequest request;
/**
* Construct a context for this request.
*
* @param request The request to which this context applies.
*/
public JakSrvltRequestContext(final HttpServletRequest request) {
super(request::getHeader, request::getContentLength);
this.request = request;
}
/**
* Gets the character encoding for the request.
*
* @return The character encoding for the request.
*/
@Override
public String getCharacterEncoding() {
return request.getCharacterEncoding();
}
/**
* Gets the content type of the request.
*
* @return The content type of the request.
*/
@Override
public String getContentType() {
return request.getContentType();
}
/**
* Gets the input stream for the request.
*
* @return The input stream for the request.
*
* @throws IOException if a problem occurs.
*/
@Override
public InputStream getInputStream() throws IOException {
return request.getInputStream();
}
}
/*
* 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.jakarta;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.fileupload2.AbstractRequestContext;
import jakarta.servlet.http.HttpServletRequest;
/**
* Provides access to the request information needed for a request made to an HTTP servlet.
*
* @since 1.1
*/
public class JakartaServletRequestContext extends AbstractRequestContext {
/**
* The request for which the context is being provided.
*/
private final HttpServletRequest request;
/**
* Construct a context for this request.
*
* @param request The request to which this context applies.
*/
public JakartaServletRequestContext(final HttpServletRequest request) {
super(request::getHeader, request::getContentLength);
this.request = request;
}
/**
* Gets the character encoding for the request.
*
* @return The character encoding for the request.
*/
@Override
public String getCharacterEncoding() {
return request.getCharacterEncoding();
}
/**
* Gets the content type of the request.
*
* @return The content type of the request.
*/
@Override
public String getContentType() {
return request.getContentType();
}
/**
* Gets the input stream for the request.
*
* @return The input stream for the request.
*
* @throws IOException if a problem occurs.
*/
@Override
public InputStream getInputStream() throws IOException {
return request.getInputStream();
}
}

View File

@ -0,0 +1,39 @@
/*
* 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.
*/
/**
* <p>
* An implementation of {@link org.apache.commons.fileupload2.FileUpload FileUpload} for use in servlets conforming to the namespace {@code jakarta.servlet}.
*
* </p>
* <p>
* The following code fragment demonstrates typical usage.
* </p>
*
* <pre>
* DiskFileItemFactory factory = new DiskFileItemFactory();
* // Configure the factory here, if desired.
* JakSrvltFileUpload upload = new JakSrvltFileUpload(factory);
* // Configure the uploader here, if desired.
* List fileItems = upload.parseRequest(request);
* </pre>
* <p>
* Please see the FileUpload <a href="https://commons.apache.org/fileupload/using.html" target="_top">User Guide</a> for further details and examples of how to
* use this package.
* </p>
*/
package org.apache.commons.fileupload2.jakarta;

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.fileupload2.jaksrvlt;
package org.apache.commons.fileupload2.jakarta;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -23,9 +23,9 @@ import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import org.apache.commons.fileupload2.AbstractFileUploadTest;
import org.apache.commons.fileupload2.Constants;
import org.apache.commons.fileupload2.FileItem;
import org.apache.commons.fileupload2.FileUploadTest;
import org.apache.commons.fileupload2.disk.DiskFileItemFactory;
import org.junit.jupiter.api.Test;
@ -34,40 +34,40 @@ import jakarta.servlet.http.HttpServletRequest;
/**
* Test for {@link org.apache.commons.fileupload2.servlet.ServletFileUpload}.
*
* @see FileUploadTest
* @see AbstractFileUploadTest
* @since 1.4
*/
public class JakSrvltFileUploadTest {
public class JakartaServletFileUploadTest {
@Test
public void parseImpliedUtf8()
throws Exception {
public void parseImpliedUtf8() throws Exception {
// utf8 encoded form-data without explicit content-type encoding
// @formatter:off
final String text = "-----1234\r\n" +
"Content-Disposition: form-data; name=\"utf8Html\"\r\n" +
"\r\n" +
"Thís ís the coñteñt of the fíle\n" +
"\r\n" +
"-----1234--\r\n";
// @formatter:on
final byte[] bytes = text.getBytes(StandardCharsets.UTF_8);
final HttpServletRequest request = new MockJakSrvltHttpRequest(bytes, Constants.CONTENT_TYPE);
final HttpServletRequest request = new MockJakartaServletHttpRequest(bytes, Constants.CONTENT_TYPE);
final DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();
fileItemFactory.setDefaultCharset("UTF-8");
final JakSrvltFileUpload upload = new JakSrvltFileUpload(fileItemFactory);
final JakartaServletFileUpload upload = new JakartaServletFileUpload(fileItemFactory);
final List<FileItem> fileItems = upload.parseRequest(request);
final FileItem fileItem = fileItems.get(0);
assertTrue(fileItem.getString().contains("coñteñt"), fileItem.getString());
}
/**
* Test case for <a href="https://issues.apache.org/jira/browse/FILEUPLOAD-210">
*/
@Test
public void parseParameterMap()
throws Exception {
public void parseParameterMap() throws Exception {
// @formatter:off
final String text = "-----1234\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
"Content-Type: text/whatever\r\n" +
@ -87,10 +87,11 @@ public class JakSrvltFileUploadTest {
"\r\n" +
"value2\r\n" +
"-----1234--\r\n";
// @formatter:on
final byte[] bytes = text.getBytes(StandardCharsets.US_ASCII);
final HttpServletRequest request = new MockJakSrvltHttpRequest(bytes, Constants.CONTENT_TYPE);
final HttpServletRequest request = new MockJakartaServletHttpRequest(bytes, Constants.CONTENT_TYPE);
final JakSrvltFileUpload upload = new JakSrvltFileUpload(new DiskFileItemFactory());
final JakartaServletFileUpload upload = new JakartaServletFileUpload(new DiskFileItemFactory());
final Map<String, List<FileItem>> mappedParameters = upload.parseParameterMap(request);
assertTrue(mappedParameters.containsKey("file"));
assertEquals(1, mappedParameters.get("file").size());

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.fileupload2.jaksrvlt;
package org.apache.commons.fileupload2.jakarta;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
@ -45,17 +45,15 @@ import jakarta.servlet.http.HttpSession;
import jakarta.servlet.http.HttpUpgradeHandler;
import jakarta.servlet.http.Part;
public class MockJakSrvltHttpRequest implements HttpServletRequest {
public class MockJakartaServletHttpRequest implements HttpServletRequest {
private static class MyServletInputStream
extends jakarta.servlet.ServletInputStream {
private static class MyServletInputStream extends jakarta.servlet.ServletInputStream {
private final InputStream inputStream;
private final int readLimit;
/**
* Creates a new instance, which returns the given
* streams data.
* Creates a new instance, which returns the given streams data.
*/
public MyServletInputStream(final InputStream inputStream, final int readLimit) {
this.inputStream = inputStream;
@ -103,24 +101,16 @@ public class MockJakSrvltHttpRequest implements HttpServletRequest {
private final Map<String, String> mHeaders = new java.util.HashMap<>();
/**
* Creates a new instance with the given request data
* and content type.
* Creates a new instance with the given request data and content type.
*/
public MockJakSrvltHttpRequest(
final byte[] requestData,
final String strContentType) {
this(new ByteArrayInputStream(requestData),
requestData.length, strContentType);
public MockJakartaServletHttpRequest(final byte[] requestData, final String strContentType) {
this(new ByteArrayInputStream(requestData), requestData.length, strContentType);
}
/**
* Creates a new instance with the given request data
* and content type.
* Creates a new instance with the given request data and content type.
*/
public MockJakSrvltHttpRequest(
final InputStream requestData,
final long requestLength,
final String strContentType) {
public MockJakartaServletHttpRequest(final InputStream requestData, final long requestLength, final String strContentType) {
mRequestData = requestData;
length = requestLength;
mStrContentType = strContentType;
@ -393,6 +383,10 @@ public class MockJakSrvltHttpRequest implements HttpServletRequest {
return null;
}
public String getProtocolRequestId() {
throw new IllegalStateException("Not implemented 6.0.0");
}
/**
* @see javax.servlet.http.HttpServletRequest#getQueryString()
*/
@ -463,6 +457,10 @@ public class MockJakSrvltHttpRequest implements HttpServletRequest {
return null;
}
public String getRequestId() {
throw new IllegalStateException("Not implemented 6.0.0");
}
/**
* @see javax.servlet.http.HttpServletRequest#getRequestURI()
*/
@ -509,7 +507,7 @@ public class MockJakSrvltHttpRequest implements HttpServletRequest {
if (session == null) {
return null;
}
return session.getServletContext();
return session.getServletContext();
}
/**
@ -632,8 +630,7 @@ public class MockJakSrvltHttpRequest implements HttpServletRequest {
* @see javax.servlet.ServletRequest#setCharacterEncoding(String)
*/
@Override
public void setCharacterEncoding(final String arg0)
throws UnsupportedEncodingException {
public void setCharacterEncoding(final String arg0) throws UnsupportedEncodingException {
}
/**
@ -658,8 +655,7 @@ public class MockJakSrvltHttpRequest implements HttpServletRequest {
}
@Override
public AsyncContext startAsync(final ServletRequest servletRequest, final ServletResponse servletResponse)
throws IllegalStateException {
public AsyncContext startAsync(final ServletRequest servletRequest, final ServletResponse servletResponse) throws IllegalStateException {
throw new IllegalStateException("Not implemented");
}
@ -667,4 +663,9 @@ public class MockJakSrvltHttpRequest implements HttpServletRequest {
public <T extends HttpUpgradeHandler> T upgrade(final Class<T> handlerClass) throws IOException, ServletException {
throw new IllegalStateException("Not implemented");
}
// @Override
// public ServletConnection getServletConnection() {
// throw new IllegalStateException("Not implemented 6.0.0");
// }
}

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<fileset-config file-format-version="1.2.0" simple-config="false" sync-formatter="false">
<local-check-config name="maven-checkstyle-plugin validate-main" location="file:/C:/Users/ggregory/git/a/commons-fileupload/commons-fileupload2-javax/../src/checkstyle/fileupload_checks.xml" type="remote" description="maven-checkstyle-plugin configuration validate-main">
<property name="checkstyle.header.file" value="C:\Users\ggregory\OneDrive - Rocket Software, Inc\ew\apache-commons\.metadata\.plugins\org.eclipse.core.resources\.projects\commons-fileupload2-javax\com.basistech.m2e.code.quality.checkstyleConfigurator\checkstyle-header-validate-main.txt"/>
<property name="checkstyle.cache.file" value="${project_loc}/target/checkstyle-cachefile"/>
<property name="checkstyle.suppressions.file" value="C:\Users\ggregory\OneDrive - Rocket Software, Inc\ew\apache-commons\.metadata\.plugins\org.eclipse.core.resources\.projects\commons-fileupload2-javax\com.basistech.m2e.code.quality.checkstyleConfigurator\checkstyle-suppressions-validate-main.xml"/>
</local-check-config>
<fileset name="java-sources-validate-main" enabled="true" check-config-name="maven-checkstyle-plugin validate-main" local="true">
<file-match-pattern match-pattern="^src/main/java/.*\.java" include-pattern="true"/>
<file-match-pattern match-pattern="^src/main/resources.*\.properties" include-pattern="true"/>
<file-match-pattern match-pattern="^.*\.properties" include-pattern="true"/>
<file-match-pattern match-pattern="^src/test/resources.*\.properties" include-pattern="true"/>
</fileset>
</fileset-config>

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.commons</groupId>
<artifactId>commons-fileupload2</artifactId>
<version>2.0.0-SNAPSHOT</version>
</parent>
<artifactId>commons-fileupload2-javax</artifactId>
<name>Apache Commons FileUpload Javax Servlet</name>
<description>
The Apache Commons FileUpload component provides a simple yet flexible means of adding support for multipart
file upload functionality to servlets and web applications.
</description>
<properties>
<commons.parent.dir>${basedir}/..</commons.parent.dir>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-fileupload2-core</artifactId>
<version>2.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-fileupload2-core</artifactId>
<version>2.0.0-SNAPSHOT</version>
<classifier>tests</classifier>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>${commons.servlet-api.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.fileupload2.servlet;
package org.apache.commons.fileupload2.javax;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
@ -23,50 +23,39 @@ import javax.servlet.ServletContextListener;
import org.apache.commons.io.FileCleaningTracker;
/**
* A servlet context listener, which ensures that the
* {@link FileCleaningTracker}'s reaper thread is terminated,
* when the web application is destroyed.
* A servlet context listener, which ensures that the {@link FileCleaningTracker}'s reaper thread is terminated, when the web application is destroyed.
*/
public class FileCleanerCleanup implements ServletContextListener {
/**
* Attribute name, which is used for storing an instance of
* {@link FileCleaningTracker} in the web application.
* Attribute name, which is used for storing an instance of {@link FileCleaningTracker} in the web application.
*/
public static final String FILE_CLEANING_TRACKER_ATTRIBUTE
= FileCleanerCleanup.class.getName() + ".FileCleaningTracker";
public static final String FILE_CLEANING_TRACKER_ATTRIBUTE = FileCleanerCleanup.class.getName() + ".FileCleaningTracker";
/**
* Gets the instance of {@link FileCleaningTracker}, which is
* associated with the given {@link ServletContext}.
* Gets the instance of {@link FileCleaningTracker}, which is associated with the given {@link ServletContext}.
*
* @param servletContext The servlet context to query
* @return The contexts tracker
*/
public static FileCleaningTracker
getFileCleaningTracker(final ServletContext servletContext) {
return (FileCleaningTracker)
servletContext.getAttribute(FILE_CLEANING_TRACKER_ATTRIBUTE);
public static FileCleaningTracker getFileCleaningTracker(final ServletContext servletContext) {
return (FileCleaningTracker) servletContext.getAttribute(FILE_CLEANING_TRACKER_ATTRIBUTE);
}
/**
* Sets the instance of {@link FileCleaningTracker}, which is
* associated with the given {@link ServletContext}.
* Sets the instance of {@link FileCleaningTracker}, which is associated with the given {@link ServletContext}.
*
* @param servletContext The servlet context to modify
* @param tracker The tracker to set
* @param tracker The tracker to set
*/
public static void setFileCleaningTracker(final ServletContext servletContext,
final FileCleaningTracker tracker) {
public static void setFileCleaningTracker(final ServletContext servletContext, final FileCleaningTracker tracker) {
servletContext.setAttribute(FILE_CLEANING_TRACKER_ATTRIBUTE, tracker);
}
/**
* Called when the web application is being destroyed.
* Calls {@link FileCleaningTracker#exitWhenFinished()}.
* Called when the web application is being destroyed. Calls {@link FileCleaningTracker#exitWhenFinished()}.
*
* @param sce The servlet context, used for calling
* {@link #getFileCleaningTracker(ServletContext)}.
* @param sce The servlet context, used for calling {@link #getFileCleaningTracker(ServletContext)}.
*/
@Override
public void contextDestroyed(final ServletContextEvent sce) {
@ -74,16 +63,13 @@ public class FileCleanerCleanup implements ServletContextListener {
}
/**
* Called when the web application is initialized. Does
* nothing.
* Called when the web application is initialized. Does nothing.
*
* @param sce The servlet context, used for calling
* {@link #setFileCleaningTracker(ServletContext, FileCleaningTracker)}.
* @param sce The servlet context, used for calling {@link #setFileCleaningTracker(ServletContext, FileCleaningTracker)}.
*/
@Override
public void contextInitialized(final ServletContextEvent sce) {
setFileCleaningTracker(sce.getServletContext(),
new FileCleaningTracker());
setFileCleaningTracker(sce.getServletContext(), new FileCleaningTracker());
}
}

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.fileupload2.servlet;
package org.apache.commons.fileupload2.javax;
import java.io.IOException;
import java.util.List;
@ -32,17 +32,12 @@ import org.apache.commons.fileupload2.FileUploadException;
/**
* High level API for processing file uploads.
* <p>
* This class handles multiple files per single HTML widget, sent using
* {@code multipart/mixed} encoding type, as specified by
* <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Use {@link
* #parseRequest(HttpServletRequest)} to acquire a list of {@link
* org.apache.commons.fileupload2.FileItem}s associated with a given HTML
* widget.
* This class handles multiple files per single HTML widget, sent using {@code multipart/mixed} encoding type, as specified by
* <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Use {@link #parseRequest(HttpServletRequest)} to acquire a list of
* {@link org.apache.commons.fileupload2.FileItem}s associated with a given HTML widget.
* </p>
* <p>
* How the data for individual parts is stored is determined by the factory
* used to create them; a given part may be in memory, on disk, or somewhere
* else.
* How the data for individual parts is stored is determined by the factory used to create them; a given part may be in memory, on disk, or somewhere else.
* </p>
*/
public class ServletFileUpload extends FileUpload {
@ -53,22 +48,19 @@ public class ServletFileUpload extends FileUpload {
private static final String POST_METHOD = "POST";
/**
* Tests whether the request contains multipart
* content.
* Tests whether the request contains multipart content.
*
* @param request The servlet request to be evaluated. Must be non-null.
*
* @return {@code true} if the request is multipart;
* {@code false} otherwise.
* @return {@code true} if the request is multipart; {@code false} otherwise.
*/
public static final boolean isMultipartContent(final HttpServletRequest request) {
return POST_METHOD.equalsIgnoreCase(request.getMethod()) && AbstractFileUpload.isMultipartContent(new ServletRequestContext(request));
}
/**
* Constructs an uninitialized instance of this class. A factory must be
* configured, using {@code setFileItemFactory()}, before attempting
* to parse requests.
* Constructs an uninitialized instance of this class. A factory must be configured, using {@code setFileItemFactory()}, before attempting to parse
* requests.
*
* @see FileUpload#FileUpload(FileItemFactory)
*/
@ -76,8 +68,7 @@ public class ServletFileUpload extends FileUpload {
}
/**
* Constructs an instance of this class which uses the supplied factory to
* create {@code FileItem} instances.
* Constructs an instance of this class which uses the supplied factory to create {@code FileItem} instances.
*
* @see FileUpload#FileUpload()
* @param fileItemFactory The factory to use for creating file items.
@ -87,31 +78,24 @@ public class ServletFileUpload extends FileUpload {
}
/**
* Gets an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
* compliant {@code multipart/form-data} file item iterator.
* Gets an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant {@code multipart/form-data} file item iterator.
*
* @param request The servlet request to be parsed.
* @return An iterator to instances of {@code FileItemStream}
* parsed from the request, in the order that they were
* transmitted.
* @throws FileUploadException if there are problems reading/parsing
* the request or storing files.
* @throws IOException An I/O error occurred. This may be a network
* error while communicating with the client or a problem while
* storing the uploaded content.
* @return An iterator to instances of {@code FileItemStream} parsed from the request, in the order that they were transmitted.
* @throws FileUploadException if there are problems reading/parsing the request or storing files.
* @throws IOException An I/O error occurred. This may be a network error while communicating with the client or a problem while storing the
* uploaded content.
*/
public FileItemIterator getItemIterator(final HttpServletRequest request) throws FileUploadException, IOException {
return super.getItemIterator(new ServletRequestContext(request));
}
/**
* Parses an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
* compliant {@code multipart/form-data} stream.
* Parses an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant {@code multipart/form-data} stream.
*
* @param request The servlet request to be parsed.
* @return A map of {@code FileItem} instances parsed from the request.
* @throws FileUploadException if there are problems reading/parsing
* the request or storing files.
* @throws FileUploadException if there are problems reading/parsing the request or storing files.
* @since 1.3
*/
public Map<String, List<FileItem>> parseParameterMap(final HttpServletRequest request) throws FileUploadException {
@ -119,14 +103,11 @@ public class ServletFileUpload extends FileUpload {
}
/**
* Parses an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
* compliant {@code multipart/form-data} stream.
* Parses an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant {@code multipart/form-data} stream.
*
* @param request The servlet request to be parsed.
* @return A list of {@code FileItem} instances parsed from the
* request, in the order that they were transmitted.
* @throws FileUploadException if there are problems reading/parsing
* the request or storing files.
* @return A list of {@code FileItem} instances parsed from the request, in the order that they were transmitted.
* @throws FileUploadException if there are problems reading/parsing the request or storing files.
*/
public List<FileItem> parseRequest(final HttpServletRequest request) throws FileUploadException {
return parseRequest(new ServletRequestContext(request));

View File

@ -1,81 +1,80 @@
/*
* 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.servlet;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload2.AbstractRequestContext;
/**
* Provides access to the request information needed for a request made to
* an HTTP servlet.
*
* @since 1.1
*/
public class ServletRequestContext extends AbstractRequestContext {
/**
* The request for which the context is being provided.
*/
private final HttpServletRequest request;
/**
* Constructs a context for this request.
*
* @param request The request to which this context applies.
*/
public ServletRequestContext(final HttpServletRequest request) {
super(request::getHeader, request::getContentLength);
this.request = request;
}
/**
* Gets the character encoding for the request.
*
* @return The character encoding for the request.
*/
@Override
public String getCharacterEncoding() {
return request.getCharacterEncoding();
}
/**
* Gets the content type of the request.
*
* @return The content type of the request.
*/
@Override
public String getContentType() {
return request.getContentType();
}
/**
* Gets the input stream for the request.
*
* @return The input stream for the request.
*
* @throws IOException if a problem occurs.
*/
@Override
public InputStream getInputStream() throws IOException {
return request.getInputStream();
}
}
/*
* 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.javax;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload2.AbstractRequestContext;
/**
* Provides access to the request information needed for a request made to an HTTP servlet.
*
* @since 1.1
*/
public class ServletRequestContext extends AbstractRequestContext {
/**
* The request for which the context is being provided.
*/
private final HttpServletRequest request;
/**
* Constructs a context for this request.
*
* @param request The request to which this context applies.
*/
public ServletRequestContext(final HttpServletRequest request) {
super(request::getHeader, request::getContentLength);
this.request = request;
}
/**
* Gets the character encoding for the request.
*
* @return The character encoding for the request.
*/
@Override
public String getCharacterEncoding() {
return request.getCharacterEncoding();
}
/**
* Gets the content type of the request.
*
* @return The content type of the request.
*/
@Override
public String getContentType() {
return request.getContentType();
}
/**
* Gets the input stream for the request.
*
* @return The input stream for the request.
*
* @throws IOException if a problem occurs.
*/
@Override
public InputStream getInputStream() throws IOException {
return request.getInputStream();
}
}

View File

@ -0,0 +1,40 @@
/*
* 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.
*/
/**
* <p>
* An implementation of {@link org.apache.commons.fileupload2.FileUpload FileUpload} for use in servlets conforming to JSR 53. This implementation requires only
* access to the servlet's current {@code HttpServletRequest} instance, and a suitable {@link org.apache.commons.fileupload2.FileItemFactory FileItemFactory}
* implementation, such as {@link org.apache.commons.fileupload2.disk.DiskFileItemFactory DiskFileItemFactory}.
* </p>
* <p>
* The following code fragment demonstrates typical usage.
* </p>
*
* <pre>
* DiskFileItemFactory factory = new DiskFileItemFactory();
* // Configure the factory here, if desired.
* ServletFileUpload upload = new ServletFileUpload(factory);
* // Configure the uploader here, if desired.
* List fileItems = upload.parseRequest(request);
* </pre>
* <p>
* Please see the FileUpload <a href="https://commons.apache.org/fileupload/using.html" target="_top">User Guide</a> for further details and examples of how to
* use this package.
* </p>
*/
package org.apache.commons.fileupload2.javax;

View File

@ -14,29 +14,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.fileupload2;
package org.apache.commons.fileupload2.javax;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload2.AbstractFileUpload;
final class HttpServletRequestFactory {
public static HttpServletRequest createHttpServletRequestWithNullContentType() {
final byte[] requestData = "foobar".getBytes();
return new MockHttpServletRequest(
requestData,
null);
return new MockHttpServletRequest(requestData, null);
}
static public HttpServletRequest createInvalidHttpServletRequest() {
final byte[] requestData = "foobar".getBytes();
return new MockHttpServletRequest(
requestData,
AbstractFileUpload.MULTIPART_FORM_DATA);
return new MockHttpServletRequest(requestData, AbstractFileUpload.MULTIPART_FORM_DATA);
}
public static HttpServletRequest createValidHttpServletRequest(
final String[] strFileNames) {
// todo - provide a real implementation
public static HttpServletRequest createValidHttpServletRequest(final String[] strFileNames) {
// TODO Provide a real implementation.
final StringBuilder sbRequestData = new StringBuilder();
@ -46,9 +43,7 @@ final class HttpServletRequestFactory {
final byte[] requestData = sbRequestData.toString().getBytes();
return new MockHttpServletRequest(
requestData,
AbstractFileUpload.MULTIPART_FORM_DATA);
return new MockHttpServletRequest(requestData, AbstractFileUpload.MULTIPART_FORM_DATA);
}
}

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.fileupload2;
package org.apache.commons.fileupload2.javax;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
@ -32,17 +32,17 @@ import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload2.AbstractFileUpload;
public class MockHttpServletRequest implements HttpServletRequest {
private static class MyServletInputStream
extends javax.servlet.ServletInputStream {
private static class MyServletInputStream extends javax.servlet.ServletInputStream {
private final InputStream inputStream;
private final int readLimit;
/**
* Creates a new instance, which returns the given
* streams data.
* Creates a new instance, which returns the given streams data.
*/
public MyServletInputStream(final InputStream inputStream, final int readLimit) {
this.inputStream = inputStream;
@ -75,24 +75,16 @@ public class MockHttpServletRequest implements HttpServletRequest {
private final Map<String, String> mHeaders = new java.util.HashMap<>();
/**
* Creates a new instance with the given request data
* and content type.
* Creates a new instance with the given request data and content type.
*/
public MockHttpServletRequest(
final byte[] requestData,
final String strContentType) {
this(new ByteArrayInputStream(requestData),
requestData.length, strContentType);
public MockHttpServletRequest(final byte[] requestData, final String strContentType) {
this(new ByteArrayInputStream(requestData), requestData.length, strContentType);
}
/**
* Creates a new instance with the given request data
* and content type.
* Creates a new instance with the given request data and content type.
*/
public MockHttpServletRequest(
final InputStream requestData,
final long requestLength,
final String strContentType) {
public MockHttpServletRequest(final InputStream requestData, final long requestLength, final String strContentType) {
mmRequestData = requestData;
length = requestLength;
mStrContentType = strContentType;
@ -545,8 +537,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
* @see javax.servlet.ServletRequest#setCharacterEncoding(String)
*/
@Override
public void setCharacterEncoding(final String arg0)
throws UnsupportedEncodingException {
public void setCharacterEncoding(final String arg0) throws UnsupportedEncodingException {
}
/**

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.fileupload2;
package org.apache.commons.fileupload2.javax;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -25,7 +25,11 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import org.apache.commons.fileupload2.servlet.ServletFileUpload;
import org.apache.commons.fileupload2.Constants;
import org.apache.commons.fileupload2.FileItemIterator;
import org.apache.commons.fileupload2.FileItemStream;
import org.apache.commons.fileupload2.FileUploadException;
import org.apache.commons.fileupload2.ProgressListener;
import org.junit.jupiter.api.Test;
/**
@ -55,13 +59,13 @@ public class ProgressListenerTest {
@Override
public void update(final long actualBytesRead, final long actualContentLength, final int actualItems) {
assertTrue(actualBytesRead >= 0 && actualBytesRead <= expectedContentLength);
assertTrue(actualContentLength == -1 || actualContentLength == expectedContentLength);
assertTrue(actualItems >= 0 && actualItems <= expectedItems);
assertTrue(actualBytesRead >= 0 && actualBytesRead <= expectedContentLength);
assertTrue(actualContentLength == -1 || actualContentLength == expectedContentLength);
assertTrue(actualItems >= 0 && actualItems <= expectedItems);
assertTrue(bytesRead == null || actualBytesRead >= bytesRead.longValue());
assertTrue(bytesRead == null || actualBytesRead >= bytesRead.longValue());
bytesRead = Long.valueOf(actualBytesRead);
assertTrue(items == null || actualItems >= items.intValue());
assertTrue(items == null || actualItems >= items.intValue());
items = Integer.valueOf(actualItems);
}
@ -100,12 +104,10 @@ public class ProgressListenerTest {
public void testProgressListener() throws Exception {
final int NUM_ITEMS = 512;
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int i = 0; i < NUM_ITEMS; i++) {
final String header = "-----1234\r\n"
+ "Content-Disposition: form-data; name=\"field" + (i + 1) + "\"\r\n"
+ "\r\n";
for (int i = 0; i < NUM_ITEMS; i++) {
final String header = "-----1234\r\n" + "Content-Disposition: form-data; name=\"field" + (i + 1) + "\"\r\n" + "\r\n";
baos.write(header.getBytes(StandardCharsets.US_ASCII));
for (int j = 0; j < 16384 + i; j++) {
for (int j = 0; j < 16384 + i; j++) {
baos.write((byte) j);
}
baos.write("\r\n".getBytes(StandardCharsets.US_ASCII));

View File

@ -14,42 +14,46 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.fileupload2.servlet;
package org.apache.commons.fileupload2.javax;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload2.AbstractFileUploadTest;
import org.apache.commons.fileupload2.Constants;
import org.apache.commons.fileupload2.FileItem;
import org.apache.commons.fileupload2.FileUploadTest;
import org.apache.commons.fileupload2.MockHttpServletRequest;
import org.apache.commons.fileupload2.FileUpload;
import org.apache.commons.fileupload2.FileUploadException;
import org.apache.commons.fileupload2.disk.DiskFileItemFactory;
import org.junit.jupiter.api.Test;
/**
* Test for {@link ServletFileUpload}.
*
* @see FileUploadTest
* @since 1.4
*/
public class ServletFileUploadTest {
public class ServletFileUploadTest extends AbstractFileUploadTest {
public ServletFileUploadTest() {
super(new ServletFileUpload(new DiskFileItemFactory()));
}
@Test
public void parseImpliedUtf8()
throws Exception {
public void parseImpliedUtf8() throws Exception {
// utf8 encoded form-data without explicit content-type encoding
// @formatter:off
final String text = "-----1234\r\n" +
"Content-Disposition: form-data; name=\"utf8Html\"\r\n" +
"\r\n" +
"Thís ís the coñteñt of the fíle\n" +
"\r\n" +
"-----1234--\r\n";
// @formatter:on
final byte[] bytes = text.getBytes(StandardCharsets.UTF_8);
final HttpServletRequest request = new MockHttpServletRequest(bytes, Constants.CONTENT_TYPE);
@ -62,13 +66,12 @@ public class ServletFileUploadTest {
assertTrue(fileItem.getString().contains("coñteñt"), fileItem.getString());
}
/**
* Test case for <a href="https://issues.apache.org/jira/browse/FILEUPLOAD-210">
*/
@Test
public void parseParameterMap()
throws Exception {
public void parseParameterMap() throws Exception {
// @formatter:off
final String text = "-----1234\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
"Content-Type: text/whatever\r\n" +
@ -88,6 +91,7 @@ public class ServletFileUploadTest {
"\r\n" +
"value2\r\n" +
"-----1234--\r\n";
// @formatter:on
final byte[] bytes = text.getBytes(StandardCharsets.US_ASCII);
final HttpServletRequest request = new MockHttpServletRequest(bytes, Constants.CONTENT_TYPE);
@ -102,4 +106,52 @@ public class ServletFileUploadTest {
assertTrue(mappedParameters.containsKey("multi"));
assertEquals(2, mappedParameters.get("multi").size());
}
@Override
public List<FileItem> parseUpload(final FileUpload upload, final byte[] bytes, final String contentType) throws FileUploadException {
final HttpServletRequest request = new MockHttpServletRequest(bytes, contentType);
return upload.parseRequest(new ServletRequestContext(request));
}
/**
* Runs a test with varying file sizes.
*/
@Override
@Test
public void testFileUpload() throws IOException, FileUploadException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
int add = 16;
int num = 0;
for (int i = 0; i < 16384; i += add) {
if (++add == 32) {
add = 16;
}
final String header = "-----1234\r\n" + "Content-Disposition: form-data; name=\"field" + (num++) + "\"\r\n" + "\r\n";
baos.write(header.getBytes(StandardCharsets.US_ASCII));
for (int j = 0; j < i; j++) {
baos.write((byte) j);
}
baos.write("\r\n".getBytes(StandardCharsets.US_ASCII));
}
baos.write("-----1234--\r\n".getBytes(StandardCharsets.US_ASCII));
final List<FileItem> fileItems = parseUpload(new ServletFileUpload(new DiskFileItemFactory()), baos.toByteArray());
final Iterator<FileItem> fileIter = fileItems.iterator();
add = 16;
num = 0;
for (int i = 0; i < 16384; i += add) {
if (++add == 32) {
add = 16;
}
final FileItem item = fileIter.next();
assertEquals("field" + (num++), item.getFieldName());
final byte[] bytes = item.get();
assertEquals(i, bytes.length);
for (int j = 0; j < i; j++) {
assertEquals((byte) j, bytes[j]);
}
}
assertTrue(!fileIter.hasNext());
}
}

View File

@ -1,282 +1,244 @@
/*
* 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.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload2.disk.DiskFileItemFactory;
import org.apache.commons.fileupload2.pub.FileUploadByteCountLimitException;
import org.apache.commons.fileupload2.pub.FileUploadSizeException;
import org.apache.commons.fileupload2.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test;
/**
* Unit test for items with varying sizes.
*/
public class SizesTest {
/** 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(StandardCharsets.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(StandardCharsets.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(StandardCharsets.US_ASCII), Constants.CONTENT_TYPE);
try {
upload.parseRequest(req);
fail("Expected exception.");
} catch (final FileUploadByteCountLimitException e) {
assertEquals(30, e.getPermitted());
}
}
/** 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(StandardCharsets.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(StandardCharsets.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(StandardCharsets.US_ASCII), Constants.CONTENT_TYPE);
try {
upload.parseRequest(req);
fail("Expected exception.");
} catch (final FileUploadByteCountLimitException e) {
assertEquals(5, e.getPermitted());
}
// 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(StandardCharsets.US_ASCII), Constants.CONTENT_TYPE);
try {
upload.parseRequest(req);
fail("Expected exception.");
} catch (final FileUploadByteCountLimitException e) {
assertEquals(15, e.getPermitted());
}
}
/**
* Runs a test with varying file sizes.
*/
@Test
public void testFileUpload()
throws IOException, FileUploadException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
int add = 16;
int num = 0;
for (int i = 0; i < 16384; i += add) {
if (++add == 32) {
add = 16;
}
final String header = "-----1234\r\n"
+ "Content-Disposition: form-data; name=\"field" + (num++) + "\"\r\n"
+ "\r\n";
baos.write(header.getBytes(StandardCharsets.US_ASCII));
for (int j = 0; j < i; j++) {
baos.write((byte) j);
}
baos.write("\r\n".getBytes(StandardCharsets.US_ASCII));
}
baos.write("-----1234--\r\n".getBytes(StandardCharsets.US_ASCII));
final List<FileItem> fileItems =
Util.parseUpload(new ServletFileUpload(new DiskFileItemFactory()), baos.toByteArray());
final Iterator<FileItem> fileIter = fileItems.iterator();
add = 16;
num = 0;
for (int i = 0; i < 16384; i += add) {
if (++add == 32) {
add = 16;
}
final FileItem item = fileIter.next();
assertEquals("field" + (num++), item.getFieldName());
final 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 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";
final ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
upload.setFileSizeMax(-1);
upload.setSizeMax(200);
final MockHttpServletRequest req = new MockHttpServletRequest(
request.getBytes(StandardCharsets.US_ASCII), Constants.CONTENT_TYPE);
try {
upload.parseRequest(req);
fail("Expected exception.");
} catch (final FileUploadSizeException e) {
assertEquals(200, e.getPermitted());
}
}
@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";
final 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
final MockHttpServletRequest req = new MockHttpServletRequest(
request.getBytes(StandardCharsets.US_ASCII), Constants.CONTENT_TYPE);
req.setContentLength(-1);
req.setReadLimit(10);
final FileItemIterator it = upload.getItemIterator(req);
assertTrue(it.hasNext());
final FileItemStream item = it.next();
assertFalse(item.isFormField());
assertEquals("file1", item.getFieldName());
assertEquals("foo1.tab", item.getName());
{
try (final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final InputStream stream = item.openStream()) {
IOUtils.copy(stream, baos);
}
}
// the second item is over the size max, thus we expect an error
// the header is still within size max -> this shall still succeed
assertTrue(it.hasNext());
assertThrows(FileUploadException.class, () -> {
final FileItemStream item2 = it.next();
try (final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final InputStream stream = item2.openStream()) {
IOUtils.copy(stream, baos);
}
});
}
}
/*
* 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.javax;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload2.Constants;
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.pub.FileUploadByteCountLimitException;
import org.apache.commons.fileupload2.pub.FileUploadSizeException;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test;
/**
* Unit test for items with varying sizes.
*/
public class SizesTest {
/**
* Checks, whether limiting the file size works.
*/
@Test
public void testFileSizeLimit() throws IOException, FileUploadException {
// @formatter:off
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";
// @formatter:on
ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
upload.setFileSizeMax(-1);
HttpServletRequest req = new MockHttpServletRequest(request.getBytes(StandardCharsets.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(StandardCharsets.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(StandardCharsets.US_ASCII), Constants.CONTENT_TYPE);
try {
upload.parseRequest(req);
fail("Expected exception.");
} catch (final FileUploadByteCountLimitException e) {
assertEquals(30, e.getPermitted());
}
}
/**
* Checks, whether a faked Content-Length header is detected.
*/
@Test
public void testFileSizeLimitWithFakedContentLength() throws IOException, FileUploadException {
// @formatter:off
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";
// @formatter:on
ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
upload.setFileSizeMax(-1);
HttpServletRequest req = new MockHttpServletRequest(request.getBytes(StandardCharsets.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(StandardCharsets.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(StandardCharsets.US_ASCII), Constants.CONTENT_TYPE);
try {
upload.parseRequest(req);
fail("Expected exception.");
} catch (final FileUploadByteCountLimitException e) {
assertEquals(5, e.getPermitted());
}
// 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(StandardCharsets.US_ASCII), Constants.CONTENT_TYPE);
try {
upload.parseRequest(req);
fail("Expected exception.");
} catch (final FileUploadByteCountLimitException e) {
assertEquals(15, e.getPermitted());
}
}
/**
* Checks whether maxSize works.
*/
@Test
public void testMaxSizeLimit() throws IOException, FileUploadException {
// @formatter:off
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";
// @formatter:on
final ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
upload.setFileSizeMax(-1);
upload.setSizeMax(200);
final MockHttpServletRequest req = new MockHttpServletRequest(request.getBytes(StandardCharsets.US_ASCII), Constants.CONTENT_TYPE);
try {
upload.parseRequest(req);
fail("Expected exception.");
} catch (final FileUploadSizeException e) {
assertEquals(200, e.getPermitted());
}
}
@Test
public void testMaxSizeLimitUnknownContentLength() throws IOException, FileUploadException {
// @formatter:off
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";
// @formatter:on
final 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
final MockHttpServletRequest req = new MockHttpServletRequest(request.getBytes(StandardCharsets.US_ASCII), Constants.CONTENT_TYPE);
req.setContentLength(-1);
req.setReadLimit(10);
final FileItemIterator it = upload.getItemIterator(req);
assertTrue(it.hasNext());
final FileItemStream item = it.next();
assertFalse(item.isFormField());
assertEquals("file1", item.getFieldName());
assertEquals("foo1.tab", item.getName());
{
try (final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final InputStream stream = item.openStream()) {
IOUtils.copy(stream, baos);
}
}
// the second item is over the size max, thus we expect an error
// the header is still within size max -> this shall still succeed
assertTrue(it.hasNext());
assertThrows(FileUploadException.class, () -> {
final FileItemStream item2 = it.next();
try (final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final InputStream stream = item2.openStream()) {
IOUtils.copy(stream, baos);
}
});
}
}

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.fileupload2;
package org.apache.commons.fileupload2.javax;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -32,9 +32,14 @@ import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload2.AbstractFileUpload;
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.InvalidFileNameException;
import org.apache.commons.fileupload2.MultipartStream;
import org.apache.commons.fileupload2.disk.DiskFileItemFactory;
import org.apache.commons.fileupload2.servlet.ServletFileUpload;
import org.apache.commons.fileupload2.servlet.ServletRequestContext;
import org.junit.jupiter.api.Test;
/**
@ -47,10 +52,11 @@ public class StreamingTest {
}
private String getHeader(final String value) {
// @formatter:off
return "-----1234\r\n"
+ "Content-Disposition: form-data; name=\"" + value + "\"\r\n"
+ "\r\n";
// @formatter:on
}
private byte[] newRequest() throws IOException {
@ -89,26 +95,22 @@ public class StreamingTest {
return parseUpload(new ByteArrayInputStream(bytes), bytes.length);
}
private List<FileItem> parseUpload(final InputStream inputStream, final int length)
throws FileUploadException {
private List<FileItem> parseUpload(final InputStream inputStream, final int length) throws FileUploadException {
final String contentType = "multipart/form-data; boundary=---1234";
final AbstractFileUpload upload = new ServletFileUpload();
upload.setFileItemFactory(new DiskFileItemFactory());
final HttpServletRequest request = new MockHttpServletRequest(inputStream,
length, contentType);
final HttpServletRequest request = new MockHttpServletRequest(inputStream, length, contentType);
return upload.parseRequest(new ServletRequestContext(request));
}
private FileItemIterator parseUpload(final int length, final InputStream inputStream)
throws FileUploadException, IOException {
private FileItemIterator parseUpload(final int length, final InputStream inputStream) throws FileUploadException, IOException {
final String contentType = "multipart/form-data; boundary=---1234";
final AbstractFileUpload upload = new ServletFileUpload();
upload.setFileItemFactory(new DiskFileItemFactory());
final HttpServletRequest request = new MockHttpServletRequest(inputStream,
length, contentType);
final HttpServletRequest request = new MockHttpServletRequest(inputStream, length, contentType);
return upload.getItemIterator(new ServletRequestContext(request));
}
@ -117,14 +119,13 @@ public class StreamingTest {
* Tests a file upload with varying file sizes.
*/
@Test
public void testFileUpload()
throws IOException, FileUploadException {
public void testFileUpload() throws IOException, FileUploadException {
final byte[] request = newRequest();
final List<FileItem> fileItems = parseUpload(request);
final Iterator<FileItem> fileIter = fileItems.iterator();
int add = 16;
int num = 0;
for (int i = 0; i < 16384; i += add) {
for (int i = 0; i < 16384; i += add) {
if (++add == 32) {
add = 16;
}
@ -132,7 +133,7 @@ public class StreamingTest {
assertEquals("field" + (num++), item.getFieldName());
final byte[] bytes = item.get();
assertEquals(i, bytes.length);
for (int j = 0; j < i; j++) {
for (int j = 0; j < i; j++) {
assertEquals((byte) j, bytes[j]);
}
}
@ -143,20 +144,17 @@ public class StreamingTest {
* Test for FILEUPLOAD-135
*/
@Test
public void testFILEUPLOAD135()
throws IOException, FileUploadException {
public void testFILEUPLOAD135() throws IOException, FileUploadException {
final byte[] request = newShortRequest();
final ByteArrayInputStream bais = new ByteArrayInputStream(request);
final List<FileItem> fileItems = parseUpload(new InputStream() {
@Override
public int read()
throws IOException
{
public int read() throws IOException {
return bais.read();
}
@Override
public int read(final byte[] b, final int off, final int len) throws IOException
{
public int read(final byte[] b, final int off, final int len) throws IOException {
return bais.read(b, off, Math.min(len, 3));
}
@ -167,19 +165,17 @@ public class StreamingTest {
assertEquals("field", item.getFieldName());
final byte[] bytes = item.get();
assertEquals(3, bytes.length);
assertEquals((byte)'1', bytes[0]);
assertEquals((byte)'2', bytes[1]);
assertEquals((byte)'3', bytes[2]);
assertEquals((byte) '1', bytes[0]);
assertEquals((byte) '2', bytes[1]);
assertEquals((byte) '3', bytes[2]);
assertTrue(!fileIter.hasNext());
}
/**
* Tests, whether an invalid request throws a proper
* exception.
* Tests, whether an invalid request throws a proper exception.
*/
@Test
public void testFileUploadException()
throws IOException, FileUploadException {
public void testFileUploadException() throws IOException, FileUploadException {
final byte[] request = newRequest();
final byte[] invalidRequest = new byte[request.length - 11];
System.arraycopy(request, 0, invalidRequest, 0, request.length - 11);
@ -197,6 +193,7 @@ public class StreamingTest {
@Test
public void testInvalidFileNameException() throws Exception {
final String fileName = "foo.exe\u0000.png";
// @formatter:off
final String request =
"-----1234\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"" + fileName + "\"\r\n" +
@ -217,6 +214,7 @@ public class StreamingTest {
"\r\n" +
"value2\r\n" +
"-----1234--\r\n";
// @formatter:on
final byte[] reqBytes = request.getBytes(StandardCharsets.US_ASCII);
final FileItemIterator fileItemIter = parseUpload(reqBytes.length, new ByteArrayInputStream(reqBytes));
@ -244,11 +242,11 @@ public class StreamingTest {
* Tests, whether an IOException is properly delegated.
*/
@Test
public void testIOException()
throws IOException {
public void testIOException() throws IOException {
final byte[] request = newRequest();
final InputStream stream = new FilterInputStream(new ByteArrayInputStream(request)) {
private int num;
@Override
public int read() throws IOException {
if (++num > 123) {
@ -256,10 +254,10 @@ public class StreamingTest {
}
return super.read();
}
@Override
public int read(final byte[] buffer, final int offset, final int length)
throws IOException {
for (int i = 0; i < length; i++) {
public int read(final byte[] buffer, final int offset, final int length) throws IOException {
for (int i = 0; i < length; i++) {
final int res = read();
if (res == -1) {
return i == 0 ? -1 : i;

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<fileset-config file-format-version="1.2.0" simple-config="false" sync-formatter="false">
<local-check-config name="maven-checkstyle-plugin validate-main" location="file:/C:/Users/ggregory/git/a/commons-fileupload/commons-fileupload2-portlet/../src/checkstyle/fileupload_checks.xml" type="remote" description="maven-checkstyle-plugin configuration validate-main">
<property name="checkstyle.header.file" value="C:\Users\ggregory\OneDrive - Rocket Software, Inc\ew\apache-commons\.metadata\.plugins\org.eclipse.core.resources\.projects\commons-fileupload2-portlet\com.basistech.m2e.code.quality.checkstyleConfigurator\checkstyle-header-validate-main.txt"/>
<property name="checkstyle.cache.file" value="${project_loc}/target/checkstyle-cachefile"/>
<property name="checkstyle.suppressions.file" value="C:\Users\ggregory\OneDrive - Rocket Software, Inc\ew\apache-commons\.metadata\.plugins\org.eclipse.core.resources\.projects\commons-fileupload2-portlet\com.basistech.m2e.code.quality.checkstyleConfigurator\checkstyle-suppressions-validate-main.xml"/>
</local-check-config>
<fileset name="java-sources-validate-main" enabled="true" check-config-name="maven-checkstyle-plugin validate-main" local="true">
<file-match-pattern match-pattern="^src/main/java/.*\.java" include-pattern="true"/>
<file-match-pattern match-pattern="^src/main/resources.*\.properties" include-pattern="true"/>
<file-match-pattern match-pattern="^.*\.properties" include-pattern="true"/>
<file-match-pattern match-pattern="^src/test/resources.*\.properties" include-pattern="true"/>
</fileset>
</fileset-config>

View File

@ -0,0 +1 @@
/target/

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.commons</groupId>
<artifactId>commons-fileupload2</artifactId>
<version>2.0.0-SNAPSHOT</version>
</parent>
<artifactId>commons-fileupload2-portlet</artifactId>
<name>Apache Commons FileUpload Portlet</name>
<description>
The Apache Commons FileUpload component provides a simple yet flexible means of adding support for multipart
file upload functionality to servlets and web applications.
</description>
<properties>
<commons.parent.dir>${basedir}/..</commons.parent.dir>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-fileupload2-core</artifactId>
<version>2.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-fileupload2-core</artifactId>
<version>2.0.0-SNAPSHOT</version>
<classifier>tests</classifier>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-fileupload2-javax</artifactId>
<version>2.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-fileupload2-javax</artifactId>
<version>2.0.0-SNAPSHOT</version>
<classifier>tests</classifier>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>${commons.servlet-api.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>portlet-api</groupId>
<artifactId>portlet-api</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -32,40 +32,32 @@ import org.apache.commons.fileupload2.FileUploadException;
/**
* High level API for processing file uploads.
* <p>
* This class handles multiple files per single HTML widget, sent using
* {@code multipart/mixed} encoding type, as specified by
* <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Use
* {@link org.apache.commons.fileupload2.servlet.ServletFileUpload
* #parseRequest(javax.servlet.http.HttpServletRequest)} to acquire a list
* of {@link org.apache.commons.fileupload2.FileItem FileItems} associated
* with a given HTML widget.
* This class handles multiple files per single HTML widget, sent using {@code multipart/mixed} encoding type, as specified by
* <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Use {@link org.apache.commons.fileupload2.javax.ServletFileUpload
* #parseRequest(javax.servlet.http.HttpServletRequest)} to acquire a list of {@link org.apache.commons.fileupload2.FileItem FileItems} associated with a given
* HTML widget.
* </p>
* <p>
* How the data for individual parts is stored is determined by the factory
* used to create them; a given part may be in memory, on disk, or somewhere
* else.
* How the data for individual parts is stored is determined by the factory used to create them; a given part may be in memory, on disk, or somewhere else.
* </p>
*
* @since 1.1
*/
public class PortletFileUpload extends FileUpload {
/**
* Tests whether the request contains multipart
* content.
* Tests whether the request contains multipart content.
*
* @param request The portlet request to be evaluated. Must be non-null.
* @return {@code true} if the request is multipart;
* {@code false} otherwise.
* @return {@code true} if the request is multipart; {@code false} otherwise.
*/
public static final boolean isMultipartContent(final ActionRequest request) {
return AbstractFileUpload.isMultipartContent(
new PortletRequestContext(request));
return AbstractFileUpload.isMultipartContent(new PortletRequestContext(request));
}
/**
* Constructs an uninitialized instance of this class. A factory must be
* configured, using {@code setFileItemFactory()}, before attempting
* to parse requests.
* Constructs an uninitialized instance of this class. A factory must be configured, using {@code setFileItemFactory()}, before attempting to parse
* requests.
*
* @see FileUpload#FileUpload(FileItemFactory)
*/
@ -73,8 +65,7 @@ public class PortletFileUpload extends FileUpload {
}
/**
* Constructs an instance of this class which uses the supplied factory to
* create {@code FileItem} instances.
* Constructs an instance of this class which uses the supplied factory to create {@code FileItem} instances.
*
* @see FileUpload#FileUpload()
* @param fileItemFactory The factory to use for creating file items.
@ -84,51 +75,38 @@ public class PortletFileUpload extends FileUpload {
}
/**
* Gets an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
* compliant {@code multipart/form-data} file item iterator.
* Gets an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant {@code multipart/form-data} file item iterator.
*
* @param request The portlet request to be parsed.
* @return An iterator to instances of {@code FileItemStream}
* parsed from the request, in the order that they were
* transmitted.
* @throws FileUploadException if there are problems reading/parsing
* the request or storing files.
* @throws IOException An I/O error occurred. This may be a network
* error while communicating with the client or a problem while
* storing the uploaded content.
* @return An iterator to instances of {@code FileItemStream} parsed from the request, in the order that they were transmitted.
* @throws FileUploadException if there are problems reading/parsing the request or storing files.
* @throws IOException An I/O error occurred. This may be a network error while communicating with the client or a problem while storing the
* uploaded content.
*/
public FileItemIterator getItemIterator(final ActionRequest request)
throws FileUploadException, IOException {
public FileItemIterator getItemIterator(final ActionRequest request) throws FileUploadException, IOException {
return super.getItemIterator(new PortletRequestContext(request));
}
/**
* Parses an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
* compliant {@code multipart/form-data} stream.
* Parses an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant {@code multipart/form-data} stream.
*
* @param request The portlet request to be parsed.
* @return A map of {@code FileItem} instances parsed from the request.
* @throws FileUploadException if there are problems reading/parsing
* the request or storing files.
* @throws FileUploadException if there are problems reading/parsing the request or storing files.
* @since 1.3
*/
public Map<String, List<FileItem>> parseParameterMap(final ActionRequest request)
throws FileUploadException {
public Map<String, List<FileItem>> parseParameterMap(final ActionRequest request) throws FileUploadException {
return parseParameterMap(new PortletRequestContext(request));
}
/**
* Parses an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
* compliant {@code multipart/form-data} stream.
* Parses an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant {@code multipart/form-data} stream.
*
* @param request The portlet request to be parsed.
* @return A list of {@code FileItem} instances parsed from the
* request, in the order that they were transmitted.
* @throws FileUploadException if there are problems reading/parsing
* the request or storing files.
* @return A list of {@code FileItem} instances parsed from the request, in the order that they were transmitted.
* @throws FileUploadException if there are problems reading/parsing the request or storing files.
*/
public List<FileItem> parseRequest(final ActionRequest request)
throws FileUploadException {
public List<FileItem> parseRequest(final ActionRequest request) throws FileUploadException {
return parseRequest(new PortletRequestContext(request));
}

View File

@ -1,79 +1,79 @@
/*
* 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.portlet;
import java.io.IOException;
import java.io.InputStream;
import javax.portlet.ActionRequest;
import org.apache.commons.fileupload2.AbstractRequestContext;
/**
* Provides access to the request information needed for a request made to a portlet.
*
* @since 1.1
*/
public class PortletRequestContext extends AbstractRequestContext {
/**
* The request for which the context is being provided.
*/
private final ActionRequest request;
/**
* Construct a context for this request.
*
* @param request The request to which this context applies.
*/
public PortletRequestContext(final ActionRequest request) {
super(request::getProperty, request::getContentLength);
this.request = request;
}
/**
* Gets the character encoding for the request.
*
* @return The character encoding for the request.
*/
@Override
public String getCharacterEncoding() {
return request.getCharacterEncoding();
}
/**
* Gets the content type of the request.
*
* @return The content type of the request.
*/
@Override
public String getContentType() {
return request.getContentType();
}
/**
* Gets the input stream for the request.
*
* @return The input stream for the request.
* @throws IOException if a problem occurs.
*/
@Override
public InputStream getInputStream() throws IOException {
return request.getPortletInputStream();
}
}
/*
* 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.portlet;
import java.io.IOException;
import java.io.InputStream;
import javax.portlet.ActionRequest;
import org.apache.commons.fileupload2.AbstractRequestContext;
/**
* Provides access to the request information needed for a request made to a portlet.
*
* @since 1.1
*/
public class PortletRequestContext extends AbstractRequestContext {
/**
* The request for which the context is being provided.
*/
private final ActionRequest request;
/**
* Construct a context for this request.
*
* @param request The request to which this context applies.
*/
public PortletRequestContext(final ActionRequest request) {
super(request::getProperty, request::getContentLength);
this.request = request;
}
/**
* Gets the character encoding for the request.
*
* @return The character encoding for the request.
*/
@Override
public String getCharacterEncoding() {
return request.getCharacterEncoding();
}
/**
* Gets the content type of the request.
*
* @return The content type of the request.
*/
@Override
public String getContentType() {
return request.getContentType();
}
/**
* Gets the input stream for the request.
*
* @return The input stream for the request.
* @throws IOException if a problem occurs.
*/
@Override
public InputStream getInputStream() throws IOException {
return request.getPortletInputStream();
}
}

View File

@ -0,0 +1,40 @@
/*
* 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.
*/
/**
* <p>
* An implementation of {@link org.apache.commons.fileupload2.FileUpload FileUpload} for use in portlets conforming to JSR 168. This implementation requires
* only access to the portlet's current {@code ActionRequest} instance, and a suitable {@link org.apache.commons.fileupload2.FileItemFactory FileItemFactory}
* implementation, such as {@link org.apache.commons.fileupload2.disk.DiskFileItemFactory DiskFileItemFactory}.
* </p>
* <p>
* The following code fragment demonstrates typical usage.
* </p>
*
* <pre>
* DiskFileItemFactory factory = new DiskFileItemFactory();
* // Configure the factory here, if desired.
* PortletFileUpload upload = new PortletFileUpload(factory);
* // Configure the uploader here, if desired.
* List fileItems = upload.parseRequest(request);
* </pre>
* <p>
* Please see the FileUpload <a href="https://commons.apache.org/fileupload/using.html" target="_top">User Guide</a> for further details and examples of how to
* use this package.
* </p>
*/
package org.apache.commons.fileupload2.portlet;

View File

@ -61,8 +61,7 @@ public class MockPortletActionRequest implements ActionRequest {
this(new ByteArrayInputStream(requestData), requestData.length, contentType);
}
public MockPortletActionRequest(final ByteArrayInputStream byteArrayInputStream,
final int requestLength, final String contentType) {
public MockPortletActionRequest(final ByteArrayInputStream byteArrayInputStream, final int requestLength, final String contentType) {
this.requestData = byteArrayInputStream;
length = requestLength;
this.contentType = contentType;

View File

@ -24,27 +24,33 @@ import java.util.List;
import java.util.Map;
import javax.portlet.ActionRequest;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload2.AbstractFileUploadTest;
import org.apache.commons.fileupload2.Constants;
import org.apache.commons.fileupload2.FileItem;
import org.apache.commons.fileupload2.FileUploadTest;
import org.apache.commons.fileupload2.FileUpload;
import org.apache.commons.fileupload2.FileUploadException;
import org.apache.commons.fileupload2.disk.DiskFileItemFactory;
import org.junit.jupiter.api.BeforeEach;
import org.apache.commons.fileupload2.javax.MockHttpServletRequest;
import org.apache.commons.fileupload2.javax.ServletRequestContext;
import org.junit.jupiter.api.Test;
/**
* Test for {@link PortletFileUpload}.
*
* @see FileUploadTest
* @see AbstractFileUploadTest
* @since 1.4
*/
public class PortletFileUploadTest {
public class PortletFileUploadTest extends AbstractFileUploadTest {
private PortletFileUpload upload;
public PortletFileUploadTest() {
super(new PortletFileUpload(new DiskFileItemFactory()));
}
@Test
public void parseParameterMap()
throws Exception {
public void parseParameterMap() throws Exception {
// @formatter:off
final String text = "-----1234\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
"Content-Type: text/whatever\r\n" +
@ -64,10 +70,11 @@ public class PortletFileUploadTest {
"\r\n" +
"value2\r\n" +
"-----1234--\r\n";
// @formatter:on
final byte[] bytes = text.getBytes(StandardCharsets.US_ASCII);
final ActionRequest request = new MockPortletActionRequest(bytes, Constants.CONTENT_TYPE);
final Map<String, List<FileItem>> mappedParameters = upload.parseParameterMap(request);
final Map<String, List<FileItem>> mappedParameters = ((PortletFileUpload) upload).parseParameterMap(request);
assertTrue(mappedParameters.containsKey("file"));
assertEquals(1, mappedParameters.get("file").size());
@ -78,9 +85,10 @@ public class PortletFileUploadTest {
assertEquals(2, mappedParameters.get("multi").size());
}
@BeforeEach
public void setUp() {
upload = new PortletFileUpload(new DiskFileItemFactory());
@Override
public List<FileItem> parseUpload(final FileUpload upload, final byte[] bytes, final String contentType) throws FileUploadException {
final HttpServletRequest request = new MockHttpServletRequest(bytes, contentType);
return upload.parseRequest(new ServletRequestContext(request));
}
}

84
pom.xml
View File

@ -25,9 +25,10 @@
</parent>
<artifactId>commons-fileupload2</artifactId>
<version>2.0-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Apache Commons FileUpload</name>
<name>Apache Commons FileUpload Parent</name>
<description>
The Apache Commons FileUpload component provides a simple yet flexible means of adding support for multipart
file upload functionality to servlets and web applications.
@ -223,6 +224,7 @@
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<commons.parent.dir>${basedir}</commons.parent.dir>
<commons.componentid>fileupload</commons.componentid>
<commons.module.name>org.apache.commons.fileupload</commons.module.name>
<commons.release.version>2.0</commons.release.version>
@ -235,6 +237,9 @@
<commons.osgi.export>!org.apache.commons.fileupload.util.mime,org.apache.commons.*;version=${project.version};-noimport:=true</commons.osgi.export>
<commons.osgi.import>!javax.portlet,*</commons.osgi.import>
<commons.osgi.dynamicImport>javax.portlet</commons.osgi.dynamicImport>
<commons.servlet-api.version>2.5</commons.servlet-api.version>
<commons.io.version>2.11.0</commons.io.version>
<commons.lang3.version>3.12.0</commons.lang3.version>
<japicmp.skip>true</japicmp.skip>
<clirr.skip>true</clirr.skip>
<moditect-maven-plugin.version>1.0.0.RC2</moditect-maven-plugin.version>
@ -249,43 +254,6 @@
<commons.releaseManagerKey>B6E73D84EA4FCC47166087253FAAD2CD5ECBB314</commons.releaseManagerKey>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>portlet-api</groupId>
<artifactId>portlet-api</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
@ -296,8 +264,8 @@
<id>validate-main</id>
<phase>validate</phase>
<configuration>
<configLocation>${basedir}/src/checkstyle/fileupload_checks.xml</configLocation>
<suppressionsLocation>${basedir}/src/checkstyle/checkstyle-suppressions.xml</suppressionsLocation>
<configLocation>${commons.parent.dir}/src/checkstyle/fileupload_checks.xml</configLocation>
<suppressionsLocation>${commons.parent.dir}/src/checkstyle/checkstyle-suppressions.xml</suppressionsLocation>
<includeTestSourceDirectory>false</includeTestSourceDirectory>
<enableRulesSummary>false</enableRulesSummary>
<consoleOutput>true</consoleOutput>
@ -314,8 +282,8 @@
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>${basedir}/src/main/assembly/bin.xml</descriptor>
<descriptor>${basedir}/src/main/assembly/src.xml</descriptor>
<descriptor>${commons.parent.dir}/src/main/assembly/bin.xml</descriptor>
<descriptor>${commons.parent.dir}/src/main/assembly/src.xml</descriptor>
</descriptors>
<tarLongFileMode>gnu</tarLongFileMode>
</configuration>
@ -429,12 +397,19 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<configuration>
<configLocation>${basedir}/src/checkstyle/fileupload_checks.xml</configLocation>
<suppressionsLocation>${basedir}/src/checkstyle/checkstyle-suppressions.xml</suppressionsLocation>
<configLocation>${commons.parent.dir}/src/checkstyle/fileupload_checks.xml</configLocation>
<suppressionsLocation>${commons.parent.dir}/src/checkstyle/checkstyle-suppressions.xml</suppressionsLocation>
<includeTestSourceDirectory>false</includeTestSourceDirectory>
<enableRulesSummary>false</enableRulesSummary>
</configuration>
</plugin>
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<configuration>
<excludeFilterFile>${commons.parent.dir}/spotbugs-exclude-filter.xml</excludeFilterFile>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<defaultGoal>clean verify apache-rat:check checkstyle:check javadoc:javadoc spotbugs:check</defaultGoal>
@ -462,8 +437,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<configuration>
<configLocation>${basedir}/src/checkstyle/fileupload_checks.xml</configLocation>
<suppressionsLocation>${basedir}/src/checkstyle/checkstyle-suppressions.xml</suppressionsLocation>
<configLocation>${commons.parent.dir}/src/checkstyle/fileupload_checks.xml</configLocation>
<suppressionsLocation>${commons.parent.dir}/src/checkstyle/checkstyle-suppressions.xml</suppressionsLocation>
<includeTestSourceDirectory>false</includeTestSourceDirectory>
<enableRulesSummary>false</enableRulesSummary>
</configuration>
@ -474,7 +449,7 @@
<configuration>
<targetJdk>${maven.compiler.target}</targetJdk>
<rulesets>
<ruleset>${basedir}/src/checkstyle/fileupload_basic.xml</ruleset>
<ruleset>${commons.parent.dir}/src/checkstyle/fileupload_basic.xml</ruleset>
</rulesets>
</configuration>
</plugin>
@ -567,13 +542,6 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<configuration>
<excludeFilterFile>${basedir}/spotbugs-exclude-filter.xml</excludeFilterFile>
</configuration>
</plugin>
</plugins>
</build>
</profile>
@ -595,4 +563,10 @@
</properties>
</profile>
</profiles>
<modules>
<module>commons-fileupload2-core</module>
<module>commons-fileupload2-jakarta</module>
<module>commons-fileupload2-javax</module>
<module>commons-fileupload2-portlet</module>
</modules>
</project>

View File

@ -1,107 +0,0 @@
/*
* 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 java.io.IOException;
import java.util.List;
import javax.naming.SizeLimitExceededException;
import org.apache.commons.fileupload2.pub.FileUploadByteCountLimitException;
/**
* An iterator, as returned by
* {@link AbstractFileUpload#getItemIterator(RequestContext)}.
*/
public interface FileItemIterator {
List<FileItem> getFileItems() throws FileUploadException, IOException;
/**
* Gets the maximum size of a single file. An {@link FileUploadByteCountLimitException}
* will be thrown, if there is an uploaded file, which is exceeding this value.
* By default, this value will be copied from the {@link AbstractFileUpload#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".
*/
long getFileSizeMax();
/**
* Gets 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 AbstractFileUpload#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 request. The value -1 indicates "unlimited".
*/
long getSizeMax();
/**
* Tests 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.
*/
boolean hasNext() throws FileUploadException, IOException;
/**
* 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.
*/
FileItemStream next() throws FileUploadException, IOException;
/**
* Sets the maximum size of a single file. An {@link FileUploadByteCountLimitException}
* will be thrown, if there is an uploaded file, which is exceeding this value.
* By default, this value will be copied from the {@link AbstractFileUpload#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.
* <p>
* <em>Note:</em> Changing this value doesn't affect files, that have already been uploaded.
* </p>
* @param fileSizeMax The maximum size of a single, uploaded file. The value -1 indicates "unlimited".
*/
void setFileSizeMax(long fileSizeMax);
/**
* Sets 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 AbstractFileUpload#getSizeMax()
* FileUploadBase} object, however, the user may replace the default value with a
* request specific value by invoking {@link #setSizeMax(long)} on this object.
* <p>
* <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.
* </p>
* @param sizeMax The maximum size of the complete HTTP request. The value -1 indicates "unlimited".
*/
void setSizeMax(long sizeMax);
}

View File

@ -1,54 +0,0 @@
/*
* 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.
*/
/**
* <p>
* A disk-based implementation of the
* {@link org.apache.commons.fileupload2.FileItem FileItem}
* interface. This implementation retains smaller items in memory, while
* writing larger ones to disk. The threshold between these two is
* configurable, as is the location of files that are written to disk.
* </p>
* <p>
* In typical usage, an instance of
* {@link org.apache.commons.fileupload2.disk.DiskFileItemFactory DiskFileItemFactory}
* would be created, configured, and then passed to a
* {@link org.apache.commons.fileupload2.FileUpload FileUpload}
* implementation such as
* {@link org.apache.commons.fileupload2.servlet.ServletFileUpload ServletFileUpload}
* or
* {@link org.apache.commons.fileupload2.portlet.PortletFileUpload PortletFileUpload}.
* </p>
* <p>
* The following code fragment demonstrates this usage.
* </p>
* <pre>
* DiskFileItemFactory factory = new DiskFileItemFactory();
* // maximum size that will be stored in memory
* factory.setSizeThreshold(4096);
* // the location for saving data that is larger than getSizeThreshold()
* factory.setRepository(new File("/tmp"));
*
* ServletFileUpload upload = new ServletFileUpload(factory);
* </pre>
* <p>
* Please see the FileUpload
* <a href="https://commons.apache.org/fileupload/using.html" target="_top">User Guide</a>
* for further details and examples of how to use this package.
* </p>
*/
package org.apache.commons.fileupload2.disk;

View File

@ -1,41 +0,0 @@
/*
* 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.
*/
/**
* <p>
* An implementation of
* {@link org.apache.commons.fileupload2.FileUpload FileUpload}
* for use in servlets conforming to the namespace {@code jakarta.servlet}.
*
* </p>
* <p>
* The following code fragment demonstrates typical usage.
* </p>
* <pre>
* DiskFileItemFactory factory = new DiskFileItemFactory();
* // Configure the factory here, if desired.
* JakSrvltFileUpload upload = new JakSrvltFileUpload(factory);
* // Configure the uploader here, if desired.
* List fileItems = upload.parseRequest(request);
* </pre>
* <p>
* Please see the FileUpload
* <a href="https://commons.apache.org/fileupload/using.html" target="_top">User Guide</a>
* for further details and examples of how to use this package.
* </p>
*/
package org.apache.commons.fileupload2.jaksrvlt;

View File

@ -1,45 +0,0 @@
/*
* 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.
*/
/**
* <p>
* An implementation of
* {@link org.apache.commons.fileupload2.FileUpload FileUpload}
* for use in portlets conforming to JSR 168. This implementation requires
* only access to the portlet's current {@code ActionRequest} instance,
* and a suitable
* {@link org.apache.commons.fileupload2.FileItemFactory FileItemFactory}
* implementation, such as
* {@link org.apache.commons.fileupload2.disk.DiskFileItemFactory DiskFileItemFactory}.
* </p>
* <p>
* The following code fragment demonstrates typical usage.
* </p>
* <pre>
* DiskFileItemFactory factory = new DiskFileItemFactory();
* // Configure the factory here, if desired.
* PortletFileUpload upload = new PortletFileUpload(factory);
* // Configure the uploader here, if desired.
* List fileItems = upload.parseRequest(request);
* </pre>
* <p>
* Please see the FileUpload
* <a href="https://commons.apache.org/fileupload/using.html" target="_top">User Guide</a>
* for further details and examples of how to use this package.
* </p>
*/
package org.apache.commons.fileupload2.portlet;

View File

@ -1,45 +0,0 @@
/*
* 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.
*/
/**
* <p>
* An implementation of
* {@link org.apache.commons.fileupload2.FileUpload FileUpload}
* for use in servlets conforming to JSR 53. This implementation requires
* only access to the servlet's current {@code HttpServletRequest}
* instance, and a suitable
* {@link org.apache.commons.fileupload2.FileItemFactory FileItemFactory}
* implementation, such as
* {@link org.apache.commons.fileupload2.disk.DiskFileItemFactory DiskFileItemFactory}.
* </p>
* <p>
* The following code fragment demonstrates typical usage.
* </p>
* <pre>
* DiskFileItemFactory factory = new DiskFileItemFactory();
* // Configure the factory here, if desired.
* ServletFileUpload upload = new ServletFileUpload(factory);
* // Configure the uploader here, if desired.
* List fileItems = upload.parseRequest(request);
* </pre>
* <p>
* Please see the FileUpload
* <a href="https://commons.apache.org/fileupload/using.html" target="_top">User Guide</a>
* for further details and examples of how to use this package.
* </p>
*/
package org.apache.commons.fileupload2.servlet;

View File

@ -1,62 +0,0 @@
/*
* 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 java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload2.disk.DiskFileItemFactory;
import org.apache.commons.fileupload2.portlet.PortletFileUpload;
import org.apache.commons.fileupload2.servlet.ServletFileUpload;
import org.apache.commons.fileupload2.servlet.ServletRequestContext;
/**
* Test utility methods.
*
* @since 1.4
*/
public class Util {
/**
* Return a list of {@link FileUpload} implementations for parameterized tests.
* @return a list of {@link FileUpload} implementations
*/
public static List<FileUpload> fileUploadImplementations() {
return Arrays.asList(
new ServletFileUpload(new DiskFileItemFactory()),
new PortletFileUpload(new DiskFileItemFactory()));
}
public static List<FileItem> parseUpload(final FileUpload upload, final byte[] bytes) throws FileUploadException {
return parseUpload(upload, bytes, Constants.CONTENT_TYPE);
}
public static List<FileItem> parseUpload(final FileUpload upload, final byte[] bytes, final String contentType)
throws FileUploadException {
final HttpServletRequest request = new MockHttpServletRequest(bytes, contentType);
return upload.parseRequest(new ServletRequestContext(request));
}
public static List<FileItem> parseUpload(final FileUpload upload, final String content)
throws FileUploadException {
final byte[] bytes = content.getBytes(StandardCharsets.US_ASCII);
return parseUpload(upload, bytes, Constants.CONTENT_TYPE);
}
}