You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by jo...@apache.org on 2020/05/25 19:28:58 UTC

[commons-fileupload] branch master updated (7d97c49 -> 4942801)

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

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


    from 7d97c49  Merge branch 'update_commons_io_to_26'
     new fb8c176  Added support for RFC 5987 aka RFC 2231
     new 1a8a103  Detect asterisk only if is at end & added tests
     new d612165  Merge branch 'rfc5987_compliance'
     new 4942801  Merge in Github PR #24.

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


Summary of changes:
 pom.xml                                            |   4 +
 src/changes/changes.xml                            |   4 +-
 .../commons/fileupload2/ParameterParser.java       |   6 +-
 .../fileupload2/util/mime/RFC2231Utility.java      | 133 +++++++++++++++++++++
 .../commons/fileupload2/ParameterParserTest.java   |  37 +++++-
 .../util/mime/RFC2231UtilityTestCase.java          |  87 ++++++++++++++
 6 files changed, 267 insertions(+), 4 deletions(-)
 create mode 100644 src/main/java/org/apache/commons/fileupload2/util/mime/RFC2231Utility.java
 create mode 100644 src/test/java/org/apache/commons/fileupload2/util/mime/RFC2231UtilityTestCase.java


[commons-fileupload] 04/04: Merge in Github PR #24.

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

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

commit 49428011a3ae8cd99132093396e344901fba8a7d
Author: Jochen Wiedmann <jo...@gmail.com>
AuthorDate: Mon May 25 21:28:55 2020 +0200

    Merge in Github PR #24.
---
 pom.xml                                               |  4 ++++
 src/changes/changes.xml                               |  4 +++-
 .../commons/fileupload2/util/mime/RFC2231Utility.java | 19 +++++++++----------
 3 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/pom.xml b/pom.xml
index a5a2949..5bb5e5b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -179,6 +179,10 @@
     <contributor>
       <name>David Georg Reichelt</name>
     </contributor>
+    <contributor>
+      <name>Merbin J Anselm</name>
+      <email>merbinjanselm@gmail.com</email>
+    </contributor>
   </contributors>
 
   <scm>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 1cd5908..e1d7edd 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -51,7 +51,9 @@ The <action> type attribute can be add,update,fix,remove.
       <action                        dev="jochen" type="add">Add the package org.apache.fileupload2.jaksrvlt,
         for compliance with Jakarta Servlet API 5.0.
       </action>
-      <action                        dev="jochen" type="add">Making FileUploadException a subclass of IOException. (Mibor API simplification.)
+      <action                        dev="jochen" type="add">Making FileUploadException a subclass of IOException. (Mibor API simplification.)</action>
+      <action issue="FILEUPLOAD-274" dev="jochen" due-to="Merbin J Anselm" due-to-email="merbinjanselm@gmail.com">
+        RFC 5987 compliance
       </action>
     </release>
     <release version="1.4" date="2018-12-23" description="1.4 Release">
diff --git a/src/main/java/org/apache/commons/fileupload2/util/mime/RFC2231Utility.java b/src/main/java/org/apache/commons/fileupload2/util/mime/RFC2231Utility.java
index 955d185..427878e 100644
--- a/src/main/java/org/apache/commons/fileupload2/util/mime/RFC2231Utility.java
+++ b/src/main/java/org/apache/commons/fileupload2/util/mime/RFC2231Utility.java
@@ -22,8 +22,8 @@ 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
  * 
- * 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>
@@ -46,7 +46,7 @@ public final class RFC2231Utility {
     /**
      * Checks if Asterisk (*) at the end of parameter name to indicate,
      * if it has charset and language information to decode the value
-     * @param paramName
+     * @param paramName The parameter, which is being checked.
      * @return {@code true}, if encoded as per RFC 2231, {@code false} otherwise
      */
     public static boolean hasEncodedValue(String paramName) {
@@ -59,7 +59,7 @@ public final class RFC2231Utility {
     /**
      * If {@code paramName} has Asterisk (*) at the end, it will be stripped off, 
      * else the passed value will be returned
-     * @param paramName
+     * @param paramName The parameter, which is being inspected.
      * @return stripped {@code paramName} of Asterisk (*), if RFC2231 encoded
      */
     public static String stripDelimiter(String paramName) {
@@ -74,19 +74,18 @@ public final class RFC2231Utility {
     /**
      * Decode a string of text obtained from a HTTP header as per RFC 2231
      * 
-     * <p/>
      * <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***}
-     * <p/>
+     *
      * <b>Eg 2.</b> {@code iso-8859-1'en'%A3%20rate}
-     * will be decoded to {@code £ rate}
-     * <p/>
+     * 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}
+     * 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
      * @return Decoded text based on charset encoding
-     * @throws UnsupportedEncodingException
+     * @throws UnsupportedEncodingException The requested character set wasn't found.
      */
     public static String decodeText(String encodedText) throws UnsupportedEncodingException {
         int langDelimitStart = encodedText.indexOf('\'');


[commons-fileupload] 03/04: Merge branch 'rfc5987_compliance'

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

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

commit d61216573bf26a6ac6461a822111d84cad9e26b0
Merge: 7d97c49 1a8a103
Author: Jochen Wiedmann <jo...@gmail.com>
AuthorDate: Mon May 25 21:15:54 2020 +0200

    Merge branch 'rfc5987_compliance'

 .../commons/fileupload2/ParameterParser.java       |   6 +-
 .../fileupload2/util/mime/RFC2231Utility.java      | 134 +++++++++++++++++++++
 .../commons/fileupload2/ParameterParserTest.java   |  37 +++++-
 .../util/mime/RFC2231UtilityTestCase.java          |  87 +++++++++++++
 4 files changed, 261 insertions(+), 3 deletions(-)


[commons-fileupload] 02/04: Detect asterisk only if is at end & added tests

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

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

commit 1a8a103eefa5616503e9947804c6a20b12669e42
Author: Merbin J Anselm <me...@sap.com>
AuthorDate: Tue Dec 10 12:00:25 2019 +0530

    Detect asterisk only if is at end & added tests
---
 .../commons/fileupload2/ParameterParser.java       |  9 ++----
 .../fileupload2/util/mime/RFC2231Utility.java      | 28 ++++++++++++++++
 .../commons/fileupload2/ParameterParserTest.java   | 22 +++++++++++++
 .../util/mime/RFC2231UtilityTestCase.java          | 37 +++++++++++++++++++---
 4 files changed, 85 insertions(+), 11 deletions(-)

diff --git a/src/main/java/org/apache/commons/fileupload2/ParameterParser.java b/src/main/java/org/apache/commons/fileupload2/ParameterParser.java
index 2735259..ef24a89 100644
--- a/src/main/java/org/apache/commons/fileupload2/ParameterParser.java
+++ b/src/main/java/org/apache/commons/fileupload2/ParameterParser.java
@@ -306,12 +306,10 @@ public class ParameterParser {
 
         String paramName = null;
         String paramValue = null;
-        boolean hasExtendedParams = false;
         while (hasChar()) {
             paramName = parseToken(new char[] {
                     '=', separator });
             paramValue = null;
-            hasExtendedParams = (paramName != null) ? paramName.contains("*") : false; //TODO: Check only if delimiter is at end
             if (hasChar() && (charArray[pos] == '=')) {
                 pos++; // skip '='
                 paramValue = parseQuotedToken(new char[] {
@@ -319,7 +317,7 @@ public class ParameterParser {
 
                 if (paramValue != null) {
                     try {
-                        paramValue = hasExtendedParams ? RFC2231Utility.decodeText(paramValue)
+                        paramValue = RFC2231Utility.hasEncodedValue(paramName) ? RFC2231Utility.decodeText(paramValue)
                                 : MimeUtility.decodeText(paramValue);
                     } catch (UnsupportedEncodingException e) {
                         // let's keep the original value in this case
@@ -330,13 +328,10 @@ public class ParameterParser {
                 pos++; // skip separator
             }
             if ((paramName != null) && (paramName.length() > 0)) {
-                if (hasExtendedParams) {
-                    paramName = paramName.replace("*", ""); //strip of the * from the name //TODO: Replace the last character alone
-                }
+                paramName = RFC2231Utility.stripDelimiter(paramName);
                 if (this.lowerCaseNames) {
                     paramName = paramName.toLowerCase(Locale.ENGLISH);
                 }
-
                 params.put(paramName, paramValue);
             }
         }
diff --git a/src/main/java/org/apache/commons/fileupload2/util/mime/RFC2231Utility.java b/src/main/java/org/apache/commons/fileupload2/util/mime/RFC2231Utility.java
index 37dce05..955d185 100644
--- a/src/main/java/org/apache/commons/fileupload2/util/mime/RFC2231Utility.java
+++ b/src/main/java/org/apache/commons/fileupload2/util/mime/RFC2231Utility.java
@@ -44,6 +44,34 @@ public final class RFC2231Utility {
     }
 
     /**
+     * Checks if Asterisk (*) at the end of parameter name to indicate,
+     * if it has charset and language information to decode the value
+     * @param paramName
+     * @return {@code true}, if encoded as per RFC 2231, {@code false} otherwise
+     */
+    public static boolean hasEncodedValue(String paramName) {
+        if (paramName != null) {
+            return paramName.lastIndexOf("*") == (paramName.length() - 1);
+        }
+        return false;
+    }
+
+    /**
+     * If {@code paramName} has Asterisk (*) at the end, it will be stripped off, 
+     * else the passed value will be returned
+     * @param paramName
+     * @return stripped {@code paramName} of Asterisk (*), if RFC2231 encoded
+     */
+    public static String stripDelimiter(String paramName) {
+        if (hasEncodedValue(paramName)) {
+            StringBuilder paramBuilder = new StringBuilder(paramName);
+            paramBuilder.deleteCharAt(paramName.lastIndexOf("*"));
+            return paramBuilder.toString();
+        }
+        return paramName;
+    }
+
+    /**
      * Decode a string of text obtained from a HTTP header as per RFC 2231
      * 
      * <p/>
diff --git a/src/test/java/org/apache/commons/fileupload2/ParameterParserTest.java b/src/test/java/org/apache/commons/fileupload2/ParameterParserTest.java
index 96988b3..70838f3 100644
--- a/src/test/java/org/apache/commons/fileupload2/ParameterParserTest.java
+++ b/src/test/java/org/apache/commons/fileupload2/ParameterParserTest.java
@@ -126,9 +126,31 @@ public class ParameterParserTest {
     @Test
     public void testFileUpload274() {
         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";
         Map<String, String> params = parser.parse(s, new char[] { ',', ';' });
         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
+
+        // 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[] { ',', ';' });
+        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[] { ',', ';' });
+        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[] { ',', ';' });
+        assertEquals("a\'b\'c", params.get("filename"));
     }
 
 }
diff --git a/src/test/java/org/apache/commons/fileupload2/util/mime/RFC2231UtilityTestCase.java b/src/test/java/org/apache/commons/fileupload2/util/mime/RFC2231UtilityTestCase.java
index 9100728..315713c 100644
--- a/src/test/java/org/apache/commons/fileupload2/util/mime/RFC2231UtilityTestCase.java
+++ b/src/test/java/org/apache/commons/fileupload2/util/mime/RFC2231UtilityTestCase.java
@@ -17,7 +17,9 @@
 package org.apache.commons.fileupload2.util.mime;
 
 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 java.io.UnsupportedEncodingException;
 
@@ -31,6 +33,33 @@ import org.junit.jupiter.api.Test;
 public final class RFC2231UtilityTestCase {
 
     @Test
+    public void testHasEncodedValue() {
+        String nameWithAsteriskAtEnd = "paramname*";
+        assertTrue(RFC2231Utility.hasEncodedValue(nameWithAsteriskAtEnd));
+
+        String nameWithAsteriskNotAtEnd = "param*name";
+        assertFalse(RFC2231Utility.hasEncodedValue(nameWithAsteriskNotAtEnd));
+
+        String nameWithoutAsterisk = "paramname";
+        assertFalse(RFC2231Utility.hasEncodedValue(nameWithoutAsterisk));
+    }
+
+    @Test
+    public void testStripDelimiter() {
+        String nameWithAsteriskAtEnd = "paramname*";
+        assertEquals("paramname", RFC2231Utility.stripDelimiter(nameWithAsteriskAtEnd));
+
+        String nameWithAsteriskNotAtEnd = "param*name";
+        assertEquals("param*name", RFC2231Utility.stripDelimiter(nameWithAsteriskNotAtEnd));
+
+        String nameWithTwoAsterisks = "param*name*";
+        assertEquals("param*name", RFC2231Utility.stripDelimiter(nameWithTwoAsterisks));
+
+        String nameWithoutAsterisk = "paramname";
+        assertEquals("paramname", RFC2231Utility.stripDelimiter(nameWithoutAsterisk));
+    }
+
+    @Test
     public void noNeedToDecode() throws Exception {
         assertEncoded("abc", "abc");
     }
@@ -45,14 +74,14 @@ public final class RFC2231UtilityTestCase {
         assertEncoded("\u00A3 rate", "iso-8859-1'en'%A3%20rate"); //"£ rate"
     }
 
-    private static void assertEncoded(String expected, String encoded) throws Exception {
-        assertEquals(expected, RFC2231Utility.decodeText(encoded));
-    }
-
     @Test
     public void decodeInvalidEncoding() throws Exception {
         assertThrows(UnsupportedEncodingException.class, () -> {
             RFC2231Utility.decodeText("abc'en'hello");
         });
     }
+
+    private static void assertEncoded(String expected, String encoded) throws Exception {
+        assertEquals(expected, RFC2231Utility.decodeText(encoded));
+    }
 }


[commons-fileupload] 01/04: Added support for RFC 5987 aka RFC 2231

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

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

commit fb8c1764ece508bb7cfc6c2e37d0f6b5132679e8
Author: Merbin J Anselm <me...@sap.com>
AuthorDate: Mon Dec 9 17:36:57 2019 +0530

    Added support for RFC 5987 aka RFC 2231
---
 .../commons/fileupload2/ParameterParser.java       |   9 +-
 .../fileupload2/util/mime/RFC2231Utility.java      | 106 +++++++++++++++++++++
 .../commons/fileupload2/ParameterParserTest.java   |  15 ++-
 .../util/mime/RFC2231UtilityTestCase.java          |  58 +++++++++++
 4 files changed, 186 insertions(+), 2 deletions(-)

diff --git a/src/main/java/org/apache/commons/fileupload2/ParameterParser.java b/src/main/java/org/apache/commons/fileupload2/ParameterParser.java
index bc3321e..2735259 100644
--- a/src/main/java/org/apache/commons/fileupload2/ParameterParser.java
+++ b/src/main/java/org/apache/commons/fileupload2/ParameterParser.java
@@ -22,6 +22,7 @@ import java.util.Locale;
 import java.util.Map;
 
 import org.apache.commons.fileupload2.util.mime.MimeUtility;
+import org.apache.commons.fileupload2.util.mime.RFC2231Utility;
 
 /**
  * A simple parser intended to parse sequences of name/value pairs.
@@ -305,10 +306,12 @@ public class ParameterParser {
 
         String paramName = null;
         String paramValue = null;
+        boolean hasExtendedParams = false;
         while (hasChar()) {
             paramName = parseToken(new char[] {
                     '=', separator });
             paramValue = null;
+            hasExtendedParams = (paramName != null) ? paramName.contains("*") : false; //TODO: Check only if delimiter is at end
             if (hasChar() && (charArray[pos] == '=')) {
                 pos++; // skip '='
                 paramValue = parseQuotedToken(new char[] {
@@ -316,7 +319,8 @@ public class ParameterParser {
 
                 if (paramValue != null) {
                     try {
-                        paramValue = MimeUtility.decodeText(paramValue);
+                        paramValue = hasExtendedParams ? RFC2231Utility.decodeText(paramValue)
+                                : MimeUtility.decodeText(paramValue);
                     } catch (UnsupportedEncodingException e) {
                         // let's keep the original value in this case
                     }
@@ -326,6 +330,9 @@ public class ParameterParser {
                 pos++; // skip separator
             }
             if ((paramName != null) && (paramName.length() > 0)) {
+                if (hasExtendedParams) {
+                    paramName = paramName.replace("*", ""); //strip of the * from the name //TODO: Replace the last character alone
+                }
                 if (this.lowerCaseNames) {
                     paramName = paramName.toLowerCase(Locale.ENGLISH);
                 }
diff --git a/src/main/java/org/apache/commons/fileupload2/util/mime/RFC2231Utility.java b/src/main/java/org/apache/commons/fileupload2/util/mime/RFC2231Utility.java
new file mode 100644
index 0000000..37dce05
--- /dev/null
+++ b/src/main/java/org/apache/commons/fileupload2/util/mime/RFC2231Utility.java
@@ -0,0 +1,106 @@
+/*
+ * 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.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
+ * 
+ * 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>
+ */
+public final class RFC2231Utility {
+
+    private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
+
+    private static final byte[] HEX_DECODE = new byte[0x80];
+
+    // create a ASCII decoded array of Hexadecimal values
+    static {
+        for (int i = 0; i < HEX_DIGITS.length; i++) {
+            HEX_DECODE[HEX_DIGITS[i]] = (byte) i;
+            HEX_DECODE[Character.toLowerCase(HEX_DIGITS[i])] = (byte) i;
+        }
+    }
+
+    /**
+     * Decode a string of text obtained from a HTTP header as per RFC 2231
+     * 
+     * <p/>
+     * <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***}
+     * <p/>
+     * <b>Eg 2.</b> {@code iso-8859-1'en'%A3%20rate}
+     * will be decoded to {@code £ rate}
+     * <p/>
+     * <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
+     * @return Decoded text based on charset encoding
+     * @throws UnsupportedEncodingException
+     */
+    public static String decodeText(String encodedText) throws UnsupportedEncodingException {
+        int langDelimitStart = encodedText.indexOf('\'');
+        if (langDelimitStart == -1) {
+            // missing charset
+            return encodedText;
+        }
+        String mimeCharset = encodedText.substring(0, langDelimitStart);
+        int langDelimitEnd = encodedText.indexOf('\'', langDelimitStart + 1);
+        if (langDelimitEnd == -1) {
+            // missing language
+            return encodedText;
+        }
+        byte[] bytes = fromHex(encodedText.substring(langDelimitEnd + 1));
+        return new String(bytes, getJavaCharset(mimeCharset));
+    }
+
+    /**
+     * Convert {@code text} to their corresponding Hex value
+     * @param text - ASCII text input
+     * @return Byte array of characters decoded from ASCII table
+     */
+    private static byte[] fromHex(String text) {
+        ByteArrayOutputStream out = new ByteArrayOutputStream(text.length());
+        for (int i = 0; i < text.length();) {
+            char c = text.charAt(i++);
+            if (c == '%') {
+                if (i > text.length() - 2) {
+                    break; // unterminated sequence
+                }
+                byte b1 = HEX_DECODE[text.charAt(i++) & 0x7f];
+                byte b2 = HEX_DECODE[text.charAt(i++) & 0x7f];
+                out.write((b1 << 4) | b2);
+            } else {
+                out.write((byte) c);
+            }
+        }
+        return out.toByteArray();
+    }
+
+    private static String getJavaCharset(String mimeCharset) {
+        // good enough for standard values
+        return mimeCharset;
+    }
+}
diff --git a/src/test/java/org/apache/commons/fileupload2/ParameterParserTest.java b/src/test/java/org/apache/commons/fileupload2/ParameterParserTest.java
index 877bf87..96988b3 100644
--- a/src/test/java/org/apache/commons/fileupload2/ParameterParserTest.java
+++ b/src/test/java/org/apache/commons/fileupload2/ParameterParserTest.java
@@ -19,6 +19,7 @@ package org.apache.commons.fileupload2;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNull;
 
+import java.io.UnsupportedEncodingException;
 import java.util.Map;
 
 import org.apache.commons.fileupload2.ParameterParser;
@@ -111,11 +112,23 @@ public class ParameterParserTest {
      * Test for <a href="https://issues.apache.org/jira/browse/FILEUPLOAD-199">FILEUPLOAD-199</a>
      */
     @Test
-    public void fileUpload199() {
+    public void testFileUpload199() {
         ParameterParser parser = new ParameterParser();
         String s = "Content-Disposition: form-data; name=\"file\"; filename=\"=?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?= =?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=\"\r\n";
         Map<String, String> params = parser.parse(s, new char[] { ',', ';' });
         assertEquals("If you can read this you understand the example.", params.get("filename"));
     }
 
+    /**
+     * Test for <a href="https://issues.apache.org/jira/browse/FILEUPLOAD-274">FILEUPLOAD-274</a>
+     * @throws UnsupportedEncodingException
+     */
+    @Test
+    public void testFileUpload274() {
+        ParameterParser parser = new ParameterParser();
+        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
+    }
+
 }
diff --git a/src/test/java/org/apache/commons/fileupload2/util/mime/RFC2231UtilityTestCase.java b/src/test/java/org/apache/commons/fileupload2/util/mime/RFC2231UtilityTestCase.java
new file mode 100644
index 0000000..9100728
--- /dev/null
+++ b/src/test/java/org/apache/commons/fileupload2/util/mime/RFC2231UtilityTestCase.java
@@ -0,0 +1,58 @@
+/*
+ * 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.util.mime;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.io.UnsupportedEncodingException;
+
+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
+ */
+public final class RFC2231UtilityTestCase {
+
+    @Test
+    public void noNeedToDecode() throws Exception {
+        assertEncoded("abc", "abc");
+    }
+
+    @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"
+    }
+
+    @Test
+    public void decodeIso88591() throws Exception {
+        assertEncoded("\u00A3 rate", "iso-8859-1'en'%A3%20rate"); //"£ rate"
+    }
+
+    private static void assertEncoded(String expected, String encoded) throws Exception {
+        assertEquals(expected, RFC2231Utility.decodeText(encoded));
+    }
+
+    @Test
+    public void decodeInvalidEncoding() throws Exception {
+        assertThrows(UnsupportedEncodingException.class, () -> {
+            RFC2231Utility.decodeText("abc'en'hello");
+        });
+    }
+}