Add RFC5987 support for HTTP header field params

This commit adds support for HTTP header field parameters encoding, as
described in RFC5987.
Note that the default implementation still relies on US-ASCII encoding,
as the latest rfc7230 Section 3.2.4 says that:

> Newly defined header fields SHOULD limit their field values to
  US-ASCII octets

Issue: SPR-14547
This commit is contained in:
Brian Clozel
2016-08-25 14:21:25 +02:00
parent 41f7680e20
commit f2faf84f31
4 changed files with 88 additions and 3 deletions

View File

@@ -16,6 +16,8 @@
package org.springframework.util;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -50,6 +52,7 @@ import java.util.TimeZone;
* @author Rick Evans
* @author Arjen Poutsma
* @author Sam Brannen
* @author Brian Clozel
* @since 16 April 2001
*/
public abstract class StringUtils {
@@ -1193,4 +1196,44 @@ public abstract class StringUtils {
return arrayToDelimitedString(arr, ",");
}
/**
* Encode the given header field param as describe in the rfc5987.
* @param input the header field param
* @param charset the charset of the header field param string
* @return the encoded header field param
* @see <a href="https://tools.ietf.org/html/rfc5987">rfc5987</a>
* @since 5.0
*/
public static String encodeHttpHeaderFieldParam(String input, Charset charset) {
Assert.notNull(charset, "charset should not be null");
if(StandardCharsets.US_ASCII.equals(charset)) {
return input;
}
Assert.isTrue(StandardCharsets.UTF_8.equals(charset) || StandardCharsets.ISO_8859_1.equals(charset),
"charset should be UTF-8 or ISO-8859-1");
final byte[] source = input.getBytes(charset);
final int len = source.length;
final StringBuilder sb = new StringBuilder(len << 1);
sb.append(charset.name());
sb.append("''");
for (byte b : source) {
if (isRFC5987AttrChar(b)) {
sb.append((char) b);
}
else {
sb.append('%');
char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, 16));
char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, 16));
sb.append(hex1);
sb.append(hex2);
}
}
return sb.toString();
}
private static boolean isRFC5987AttrChar(byte c) {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|| c == '!' || c == '#' || c == '$' || c == '&' || c == '+' || c == '-'
|| c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~';
}
}