Remove reflection from ContentDispositionTests
Also minor refactoring in decoding in order to tolerate the absence of a charset and treat as US_ASCII. See gh-23485
This commit is contained in:
parent
c97580035e
commit
d927d31e13
|
|
@ -208,7 +208,7 @@ public final class ContentDisposition {
|
|||
}
|
||||
else {
|
||||
sb.append("; filename*=");
|
||||
sb.append(encodeHeaderFieldParam(this.filename, this.charset));
|
||||
sb.append(encodeFilename(this.filename, this.charset));
|
||||
}
|
||||
}
|
||||
if (this.size != null) {
|
||||
|
|
@ -274,15 +274,23 @@ public final class ContentDisposition {
|
|||
String attribute = part.substring(0, eqIndex);
|
||||
String value = (part.startsWith("\"", eqIndex + 1) && part.endsWith("\"") ?
|
||||
part.substring(eqIndex + 2, part.length() - 1) :
|
||||
part.substring(eqIndex + 1, part.length()));
|
||||
part.substring(eqIndex + 1));
|
||||
if (attribute.equals("name") ) {
|
||||
name = value;
|
||||
}
|
||||
else if (attribute.equals("filename*") ) {
|
||||
filename = decodeHeaderFieldParam(value);
|
||||
charset = Charset.forName(value.substring(0, value.indexOf('\'')));
|
||||
Assert.isTrue(UTF_8.equals(charset) || ISO_8859_1.equals(charset),
|
||||
"Charset should be UTF-8 or ISO-8859-1");
|
||||
int idx1 = value.indexOf('\'');
|
||||
int idx2 = value.indexOf('\'', idx1 + 1);
|
||||
if (idx1 != -1 && idx2 != -1) {
|
||||
charset = Charset.forName(value.substring(0, idx1));
|
||||
Assert.isTrue(UTF_8.equals(charset) || ISO_8859_1.equals(charset),
|
||||
"Charset should be UTF-8 or ISO-8859-1");
|
||||
filename = decodeFilename(value.substring(idx2 + 1), charset);
|
||||
}
|
||||
else {
|
||||
// US ASCII
|
||||
filename = decodeFilename(value, StandardCharsets.US_ASCII);
|
||||
}
|
||||
}
|
||||
else if (attribute.equals("filename") && (filename == null)) {
|
||||
filename = value;
|
||||
|
|
@ -362,22 +370,15 @@ public final class ContentDisposition {
|
|||
/**
|
||||
* Decode the given header field param as described in RFC 5987.
|
||||
* <p>Only the US-ASCII, UTF-8 and ISO-8859-1 charsets are supported.
|
||||
* @param input the header field param
|
||||
* @param filename the filename
|
||||
* @param charset the charset for the filename
|
||||
* @return the encoded header field param
|
||||
* @see <a href="https://tools.ietf.org/html/rfc5987">RFC 5987</a>
|
||||
*/
|
||||
private static String decodeHeaderFieldParam(String input) {
|
||||
Assert.notNull(input, "Input String should not be null");
|
||||
int firstQuoteIndex = input.indexOf('\'');
|
||||
int secondQuoteIndex = input.indexOf('\'', firstQuoteIndex + 1);
|
||||
// US_ASCII
|
||||
if (firstQuoteIndex == -1 || secondQuoteIndex == -1) {
|
||||
return input;
|
||||
}
|
||||
Charset charset = Charset.forName(input.substring(0, firstQuoteIndex));
|
||||
Assert.isTrue(UTF_8.equals(charset) || ISO_8859_1.equals(charset),
|
||||
"Charset should be UTF-8 or ISO-8859-1");
|
||||
byte[] value = input.substring(secondQuoteIndex + 1, input.length()).getBytes(charset);
|
||||
private static String decodeFilename(String filename, Charset charset) {
|
||||
Assert.notNull(filename, "'input' String` should not be null");
|
||||
Assert.notNull(charset, "'charset' should not be null");
|
||||
byte[] value = filename.getBytes(charset);
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
int index = 0;
|
||||
while (index < value.length) {
|
||||
|
|
@ -417,7 +418,7 @@ public final class ContentDisposition {
|
|||
* @return the encoded header field param
|
||||
* @see <a href="https://tools.ietf.org/html/rfc5987">RFC 5987</a>
|
||||
*/
|
||||
private static String encodeHeaderFieldParam(String input, Charset charset) {
|
||||
private static String encodeFilename(String input, Charset charset) {
|
||||
Assert.notNull(input, "Input String should not be null");
|
||||
Assert.notNull(charset, "Charset should not be null");
|
||||
if (StandardCharsets.US_ASCII.equals(charset)) {
|
||||
|
|
|
|||
|
|
@ -16,16 +16,12 @@
|
|||
|
||||
package org.springframework.http;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
|
|
@ -74,6 +70,30 @@ public class ContentDispositionTests {
|
|||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseEncodedFilenameWithoutCharset() {
|
||||
assertThat(parse("form-data; name=\"name\"; filename*=test.txt"))
|
||||
.isEqualTo(ContentDisposition.builder("form-data")
|
||||
.name("name")
|
||||
.filename("test.txt")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseEncodedFilenameWithInvalidCharset() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> parse("form-data; name=\"name\"; filename*=UTF-16''test.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseEncodedFilenameWithInvalidName() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> parse("form-data; name=\"name\"; filename*=UTF-8''%A"));
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> parse("form-data; name=\"name\"; filename*=UTF-8''%A.txt"));
|
||||
}
|
||||
|
||||
@Test // gh-23077
|
||||
public void parseWithEscapedQuote() {
|
||||
assertThat(parse("form-data; name=\"file\"; filename=\"\\\"The Twilight Zone\\\".txt\"; size=123"))
|
||||
|
|
@ -147,7 +167,7 @@ public class ContentDispositionTests {
|
|||
|
||||
|
||||
@Test
|
||||
public void headerValue() {
|
||||
public void format() {
|
||||
assertThat(
|
||||
ContentDisposition.builder("form-data")
|
||||
.name("foo")
|
||||
|
|
@ -158,7 +178,7 @@ public class ContentDispositionTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void headerValueWithEncodedFilename() {
|
||||
public void formatWithEncodedFilename() {
|
||||
assertThat(
|
||||
ContentDisposition.builder("form-data")
|
||||
.name("name")
|
||||
|
|
@ -167,67 +187,25 @@ public class ContentDispositionTests {
|
|||
.isEqualTo("form-data; name=\"name\"; filename*=UTF-8''%E4%B8%AD%E6%96%87.txt");
|
||||
}
|
||||
|
||||
@Test // SPR-14547
|
||||
public void encodeHeaderFieldParam() {
|
||||
Method encode = ReflectionUtils.findMethod(ContentDisposition.class,
|
||||
"encodeHeaderFieldParam", String.class, Charset.class);
|
||||
ReflectionUtils.makeAccessible(encode);
|
||||
|
||||
String result = (String)ReflectionUtils.invokeMethod(encode, null, "test.txt",
|
||||
StandardCharsets.US_ASCII);
|
||||
assertThat(result).isEqualTo("test.txt");
|
||||
|
||||
result = (String)ReflectionUtils.invokeMethod(encode, null, "中文.txt", StandardCharsets.UTF_8);
|
||||
assertThat(result).isEqualTo("UTF-8''%E4%B8%AD%E6%96%87.txt");
|
||||
@Test
|
||||
public void formatWithEncodedFilenameUsingUsAscii() {
|
||||
assertThat(
|
||||
ContentDisposition.builder("form-data")
|
||||
.name("name")
|
||||
.filename("test.txt", StandardCharsets.US_ASCII)
|
||||
.build()
|
||||
.toString())
|
||||
.isEqualTo("form-data; name=\"name\"; filename=\"test.txt\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeHeaderFieldParamInvalidCharset() {
|
||||
Method encode = ReflectionUtils.findMethod(ContentDisposition.class,
|
||||
"encodeHeaderFieldParam", String.class, Charset.class);
|
||||
ReflectionUtils.makeAccessible(encode);
|
||||
public void formatWithEncodedFilenameUsingInvalidCharset() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
ReflectionUtils.invokeMethod(encode, null, "test", StandardCharsets.UTF_16));
|
||||
}
|
||||
|
||||
@Test // SPR-14408
|
||||
public void decodeHeaderFieldParam() {
|
||||
Method decode = ReflectionUtils.findMethod(ContentDisposition.class,
|
||||
"decodeHeaderFieldParam", String.class);
|
||||
ReflectionUtils.makeAccessible(decode);
|
||||
|
||||
String result = (String)ReflectionUtils.invokeMethod(decode, null, "test.txt");
|
||||
assertThat(result).isEqualTo("test.txt");
|
||||
|
||||
result = (String)ReflectionUtils.invokeMethod(decode, null, "UTF-8''%E4%B8%AD%E6%96%87.txt");
|
||||
assertThat(result).isEqualTo("中文.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeHeaderFieldParamInvalidCharset() {
|
||||
Method decode = ReflectionUtils.findMethod(ContentDisposition.class,
|
||||
"decodeHeaderFieldParam", String.class);
|
||||
ReflectionUtils.makeAccessible(decode);
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
ReflectionUtils.invokeMethod(decode, null, "UTF-16''test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeHeaderFieldParamShortInvalidEncodedFilename() {
|
||||
Method decode = ReflectionUtils.findMethod(ContentDisposition.class,
|
||||
"decodeHeaderFieldParam", String.class);
|
||||
ReflectionUtils.makeAccessible(decode);
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
ReflectionUtils.invokeMethod(decode, null, "UTF-8''%A"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeHeaderFieldParamLongerInvalidEncodedFilename() {
|
||||
Method decode = ReflectionUtils.findMethod(ContentDisposition.class,
|
||||
"decodeHeaderFieldParam", String.class);
|
||||
ReflectionUtils.makeAccessible(decode);
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
ReflectionUtils.invokeMethod(decode, null, "UTF-8''%A.txt"));
|
||||
ContentDisposition.builder("form-data")
|
||||
.name("name")
|
||||
.filename("test.txt", StandardCharsets.UTF_16)
|
||||
.build()
|
||||
.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue