You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2023/06/11 21:33:15 UTC

[commons-fileupload] 03/04: Use Charset instead of String

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

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

commit c2f45e91d9b03646ba9a29a25b9d1625dac5237c
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sun Jun 11 17:20:52 2023 -0400

    Use Charset instead of String
    
    Update internal names
---
 .../commons/fileupload2/AbstractFileUpload.java    | 15 +++----
 .../commons/fileupload2/FileItemInputImpl.java     |  2 +-
 .../fileupload2/FileItemInputIteratorImpl.java     | 48 +++++++++++-----------
 .../apache/commons/fileupload2/MultipartInput.java | 47 +++++++++------------
 .../commons/fileupload2/ParameterParser.java       |  6 +--
 .../apache/commons/fileupload2/RequestContext.java | 14 +++++++
 6 files changed, 68 insertions(+), 64 deletions(-)

diff --git a/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/AbstractFileUpload.java b/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/AbstractFileUpload.java
index e0e0085..3b533a7 100644
--- a/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/AbstractFileUpload.java
+++ b/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/AbstractFileUpload.java
@@ -19,6 +19,7 @@ package org.apache.commons.fileupload2;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -118,7 +119,7 @@ public abstract class AbstractFileUpload {
     /**
      * The content encoding to use when reading part headers.
      */
-    private String headerEncoding;
+    private Charset headerCharset;
 
     /**
      * The progress listener.
@@ -160,7 +161,7 @@ public abstract class AbstractFileUpload {
      * Gets the field name, which is given by the content-disposition header.
      *
      * @param contentDisposition The content-dispositions header value.
-     * @return The field jake
+     * @return The field name.
      */
     private String getFieldName(final String contentDisposition) {
         String fieldName = null;
@@ -251,8 +252,8 @@ public abstract class AbstractFileUpload {
      *
      * @return The encoding used to read part headers.
      */
-    public String getHeaderEncoding() {
-        return headerEncoding;
+    public Charset getHeaderCharset() {
+        return headerCharset;
     }
 
     /**
@@ -488,10 +489,10 @@ 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.
      *
-     * @param encoding The encoding used to read part headers.
+     * @param headerCharset The encoding used to read part headers.
      */
-    public void setHeaderEncoding(final String encoding) {
-        headerEncoding = encoding;
+    public void setHeaderCharset(final Charset headerCharset) {
+        this.headerCharset = headerCharset;
     }
 
     /**
diff --git a/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/FileItemInputImpl.java b/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/FileItemInputImpl.java
index a559fb6..dababa3 100644
--- a/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/FileItemInputImpl.java
+++ b/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/FileItemInputImpl.java
@@ -96,7 +96,7 @@ class FileItemInputImpl implements FileItemInput {
                     contentLength, fileSizeMax, fileName, fieldName);
         }
         // OK to construct stream now
-        final ItemInputStream itemInputStream = fileItemInputIteratorImpl.getMultiPartStream().newInputStream();
+        final ItemInputStream itemInputStream = fileItemInputIteratorImpl.getMultiPartInput().newInputStream();
         InputStream istream = itemInputStream;
         if (fileSizeMax != -1) {
             istream = new BoundedInputStream(istream, fileSizeMax) {
diff --git a/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/FileItemInputIteratorImpl.java b/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/FileItemInputIteratorImpl.java
index 2b6dc7a..5156035 100644
--- a/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/FileItemInputIteratorImpl.java
+++ b/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/FileItemInputIteratorImpl.java
@@ -18,12 +18,14 @@ package org.apache.commons.fileupload2;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 import java.util.NoSuchElementException;
 import java.util.Objects;
 
+import org.apache.commons.io.Charsets;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.input.BoundedInputStream;
 
@@ -44,7 +46,7 @@ class FileItemInputIteratorImpl implements FileItemInputIterator {
      *
      * @see RequestContext
      */
-    private final RequestContext ctx;
+    private final RequestContext requestContext;
 
     /**
      * The maximum allowed size of a complete request.
@@ -59,7 +61,7 @@ class FileItemInputIteratorImpl implements FileItemInputIterator {
     /**
      * The multi part stream to process.
      */
-    private MultipartInput multiPartStream;
+    private MultipartInput multiPartInput;
 
     /**
      * The notifier, which used for triggering the {@link ProgressListener}.
@@ -108,7 +110,7 @@ class FileItemInputIteratorImpl implements FileItemInputIterator {
         this.fileUpload = fileUploadBase;
         this.sizeMax = fileUploadBase.getSizeMax();
         this.fileSizeMax = fileUploadBase.getFileSizeMax();
-        this.ctx = Objects.requireNonNull(requestContext, "requestContext");
+        this.requestContext = Objects.requireNonNull(requestContext, "requestContext");
         this.skipPreamble = true;
         findNextItem();
     }
@@ -127,7 +129,7 @@ class FileItemInputIteratorImpl implements FileItemInputIterator {
             currentItem.close();
             currentItem = null;
         }
-        final MultipartInput multi = getMultiPartStream();
+        final MultipartInput multi = getMultiPartInput();
         for (;;) {
             final boolean nextPart;
             if (skipPreamble) {
@@ -214,11 +216,11 @@ class FileItemInputIteratorImpl implements FileItemInputIterator {
         return fileSizeMax;
     }
 
-    public MultipartInput getMultiPartStream() throws FileUploadException, IOException {
-        if (multiPartStream == null) {
-            init(fileUpload, ctx);
+    public MultipartInput getMultiPartInput() throws FileUploadException, IOException {
+        if (multiPartInput == null) {
+            init(fileUpload, requestContext);
         }
-        return multiPartStream;
+        return multiPartInput;
     }
 
     @Override
@@ -244,21 +246,21 @@ class FileItemInputIteratorImpl implements FileItemInputIterator {
         return findNextItem();
     }
 
-    protected void init(final AbstractFileUpload fileUploadBase, final RequestContext requestContext) throws FileUploadException, IOException {
-        final String contentType = ctx.getContentType();
+    protected void init(final AbstractFileUpload fileUploadBase, final RequestContext initContext) throws FileUploadException, IOException {
+        final String contentType = requestContext.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();
+        final long contentLengthInt = requestContext.getContentLength();
         // @formatter:off
-        final long requestSize = RequestContext.class.isAssignableFrom(ctx.getClass())
+        final long requestSize = RequestContext.class.isAssignableFrom(requestContext.getClass())
                                  // Inline conditional is OK here CHECKSTYLE:OFF
-                                 ? ctx.getContentLength()
+                                 ? requestContext.getContentLength()
                                  : contentLengthInt;
                                  // CHECKSTYLE:ON
         // @formatter:on
-        final InputStream input; // N.B. this is eventually closed in MultipartInput processing
+        final InputStream inputStream; // N.B. this is eventually closed in MultipartInput processing
         if (sizeMax >= 0) {
             if (requestSize != -1 && requestSize > sizeMax) {
                 throw new FileUploadSizeException(
@@ -266,7 +268,7 @@ class FileItemInputIteratorImpl implements FileItemInputIterator {
                         requestSize);
             }
             // N.B. this is eventually closed in MultipartInput processing
-            input = new BoundedInputStream(ctx.getInputStream(), sizeMax) {
+            inputStream = new BoundedInputStream(requestContext.getInputStream(), sizeMax) {
                 @Override
                 protected void onMaxLength(final long maxLen, final long count) throws IOException {
                     throw new FileUploadSizeException(
@@ -274,28 +276,24 @@ class FileItemInputIteratorImpl implements FileItemInputIterator {
                 }
             };
         } else {
-            input = ctx.getInputStream();
-        }
-
-        String charEncoding = fileUploadBase.getHeaderEncoding();
-        if (charEncoding == null) {
-            charEncoding = ctx.getCharacterEncoding();
+            inputStream = requestContext.getInputStream();
         }
 
+        final Charset charset = Charsets.toCharset(fileUploadBase.getHeaderCharset(), requestContext.getCharset());
         multiPartBoundary = fileUploadBase.getBoundary(contentType);
         if (multiPartBoundary == null) {
-            IOUtils.closeQuietly(input); // avoid possible resource leak
+            IOUtils.closeQuietly(inputStream); // avoid possible resource leak
             throw new FileUploadException("the request was rejected because no multipart boundary was found");
         }
 
         progressNotifier = new MultipartInput.ProgressNotifier(fileUploadBase.getProgressListener(), requestSize);
         try {
-            multiPartStream = MultipartInput.builder().setInputStream(input).setBoundary(multiPartBoundary).setProgressNotifier(progressNotifier).get();
+            multiPartInput = MultipartInput.builder().setInputStream(inputStream).setBoundary(multiPartBoundary).setProgressNotifier(progressNotifier).get();
         } catch (final IllegalArgumentException e) {
-            IOUtils.closeQuietly(input); // avoid possible resource leak
+            IOUtils.closeQuietly(inputStream); // 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);
+        multiPartInput.setHeaderCharset(charset);
     }
 
     /**
diff --git a/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/MultipartInput.java b/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/MultipartInput.java
index 827fae3..6f79dcf 100644
--- a/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/MultipartInput.java
+++ b/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/MultipartInput.java
@@ -24,6 +24,7 @@ import java.io.UnsupportedEncodingException;
 import java.nio.charset.Charset;
 
 import org.apache.commons.fileupload2.FileItemInput.ItemSkippedException;
+import org.apache.commons.io.Charsets;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.build.AbstractOrigin;
 import org.apache.commons.io.build.AbstractStreamBuilder;
@@ -531,32 +532,32 @@ public final class MultipartInput {
     /**
      * The maximum length of {@code header-part} that will be processed (10 kilobytes = 10240 bytes.).
      */
-    public static final int HEADER_PART_SIZE_MAX = 10240;
+    public static final int HEADER_PART_SIZE_MAX = 10_240;
 
     /**
      * The default length of the buffer used for processing a request.
      */
-    protected static final int DEFAULT_BUFSIZE = 4096;
+    static final int DEFAULT_BUFSIZE = 4096;
 
     /**
      * A byte sequence that marks the end of {@code header-part} ({@code CRLFCRLF}).
      */
-    protected static final byte[] HEADER_SEPARATOR = { CR, LF, CR, LF };
+    static final byte[] HEADER_SEPARATOR = { CR, LF, CR, LF };
 
     /**
      * A byte sequence that that follows a delimiter that will be followed by an encapsulation ({@code CRLF}).
      */
-    protected static final byte[] FIELD_SEPARATOR = { CR, LF };
+    static final byte[] FIELD_SEPARATOR = { CR, LF };
 
     /**
      * A byte sequence that that follows a delimiter of the last encapsulation in the stream ({@code --}).
      */
-    protected static final byte[] STREAM_TERMINATOR = { DASH, DASH };
+    static final byte[] STREAM_TERMINATOR = { DASH, DASH };
 
     /**
      * A byte sequence that precedes a boundary ({@code CRLF--}).
      */
-    protected static final byte[] BOUNDARY_PREFIX = { CR, LF, DASH, DASH };
+    static final byte[] BOUNDARY_PREFIX = { CR, LF, DASH, DASH };
 
     /**
      * Compares {@code count} first bytes in the arrays {@code a} and {@code b}.
@@ -566,7 +567,7 @@ public final class MultipartInput {
      * @param count How many bytes should be compared.
      * @return {@code true} if {@code count} first bytes in arrays {@code a} and {@code b} are equal.
      */
-    public static boolean arrayEquals(final byte[] a, final byte[] b, final int count) {
+    static boolean arrayEquals(final byte[] a, final byte[] b, final int count) {
         for (int i = 0; i < count; i++) {
             if (a[i] != b[i]) {
                 return false;
@@ -634,7 +635,7 @@ public final class MultipartInput {
     /**
      * The content encoding to use when reading headers.
      */
-    private String headerEncoding;
+    private Charset headerCharset;
 
     /**
      * The progress notifier, if any, or null.
@@ -765,8 +766,8 @@ public final class MultipartInput {
      *
      * @return The encoding used to read part headers.
      */
-    public String getHeaderEncoding() {
-        return headerEncoding;
+    public Charset getHeaderCharset() {
+        return headerCharset;
     }
 
     /**
@@ -863,9 +864,6 @@ public final class MultipartInput {
      * <p>
      * Headers are returned verbatim to the input stream, including the trailing {@code CRLF} marker. Parsing is left to the application.
      * </p>
-     * <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.
@@ -898,19 +896,12 @@ public final class MultipartInput {
             baos.write(b);
         }
 
-        String headers;
-        if (headerEncoding != null) {
-            try {
-                headers = baos.toString(headerEncoding);
-            } catch (final UnsupportedEncodingException e) {
-                // Fall back to platform default if specified encoding is not supported.
-                headers = baos.toString();
-            }
-        } else {
-            headers = baos.toString();
+        try {
+            return baos.toString(Charsets.toCharset(headerCharset, Charset.defaultCharset()).name());
+        } catch (final UnsupportedEncodingException e) {
+            // not possible
+            throw new IllegalStateException(e);
         }
-
-        return headers;
     }
 
     /**
@@ -940,10 +931,10 @@ public final class MultipartInput {
      * 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 headerEncoding The encoding used to read part headers.
+     * @param headerCharset The encoding used to read part headers.
      */
-    public void setHeaderEncoding(final String headerEncoding) {
-        this.headerEncoding = headerEncoding;
+    public void setHeaderCharset(final Charset headerCharset) {
+        this.headerCharset = headerCharset;
     }
 
     /**
diff --git a/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/ParameterParser.java b/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/ParameterParser.java
index cd65201..94e4cdd 100644
--- a/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/ParameterParser.java
+++ b/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/ParameterParser.java
@@ -288,10 +288,10 @@ public class ParameterParser {
     /**
      * 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 lowerCaseNames {@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;
+    public void setLowerCaseNames(final boolean lowerCaseNames) {
+        this.lowerCaseNames = lowerCaseNames;
     }
 
 }
diff --git a/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/RequestContext.java b/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/RequestContext.java
index b463497..ca87751 100644
--- a/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/RequestContext.java
+++ b/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/RequestContext.java
@@ -18,6 +18,10 @@ package org.apache.commons.fileupload2;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.nio.charset.UnsupportedCharsetException;
+
+import org.apache.commons.io.Charsets;
 
 /**
  * Abstracts access to the request information needed for file uploads.
@@ -34,6 +38,16 @@ public interface RequestContext {
      */
     String getCharacterEncoding();
 
+    /**
+     * Gets the character encoding for the request.
+     *
+     * @return The character encoding for the request.
+     * @throws UnsupportedCharsetException If the named charset is unavailable (unchecked exception).
+     */
+    default Charset getCharset() throws UnsupportedCharsetException {
+        return Charsets.toCharset(getCharacterEncoding(), null);
+    }
+
     /**
      * Gets the content length of the request.
      *