Accept ';' '\\"' in the filename of HTTP Content-Disposition header
Motivation: HttpPostMultipartRequestDecoder threw an ArrayIndexOutOfBoundsException when trying to decode Content-Disposition header with filename containing ';' or protected \\". See issue #3326 and #3327. Modifications: Added splitMultipartHeaderValues method which cares about quotes, and use it in splitMultipartHeader method, instead of StringUtils.split. Result: Filenames can contain semicolons and protected \\".
This commit is contained in:
parent
392fb764b6
commit
7f907e8c2a
@ -1786,7 +1786,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
|
|||||||
String svalue = sb.substring(valueStart, valueEnd);
|
String svalue = sb.substring(valueStart, valueEnd);
|
||||||
String[] values;
|
String[] values;
|
||||||
if (svalue.indexOf(';') >= 0) {
|
if (svalue.indexOf(';') >= 0) {
|
||||||
values = StringUtil.split(svalue, ';');
|
values = splitMultipartHeaderValues(svalue);
|
||||||
} else {
|
} else {
|
||||||
values = StringUtil.split(svalue, ',');
|
values = StringUtil.split(svalue, ',');
|
||||||
}
|
}
|
||||||
@ -1799,4 +1799,38 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
|
|||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split one header value in Multipart
|
||||||
|
* @return an array of String where values that were separated by ';' or ','
|
||||||
|
*/
|
||||||
|
private static String[] splitMultipartHeaderValues(String svalue) {
|
||||||
|
List<String> values = new ArrayList<String>(1);
|
||||||
|
boolean inQuote = false;
|
||||||
|
boolean escapeNext = false;
|
||||||
|
int start = 0;
|
||||||
|
for (int i = 0; i < svalue.length(); i++) {
|
||||||
|
char c = svalue.charAt(i);
|
||||||
|
if (inQuote) {
|
||||||
|
if (escapeNext) {
|
||||||
|
escapeNext = false;
|
||||||
|
} else {
|
||||||
|
if (c == '\\') {
|
||||||
|
escapeNext = true;
|
||||||
|
} else if (c == '"') {
|
||||||
|
inQuote = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (c == '"') {
|
||||||
|
inQuote = true;
|
||||||
|
} else if (c == ';') {
|
||||||
|
values.add(svalue.substring(start, i));
|
||||||
|
start = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
values.add(svalue.substring(start));
|
||||||
|
return values.toArray(new String[values.size()]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -321,4 +321,30 @@ public class HttpPostRequestDecoderTest {
|
|||||||
decoder.offer(part3);
|
decoder.offer(part3);
|
||||||
decoder.offer(part4);
|
decoder.offer(part4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See https://github.com/netty/netty/issues/3326
|
||||||
|
@Test
|
||||||
|
public void testFilenameContainingSemicolon() throws Exception {
|
||||||
|
final String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO";
|
||||||
|
final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST,
|
||||||
|
"http://localhost");
|
||||||
|
req.headers().add(HttpHeaders.Names.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary);
|
||||||
|
// Force to use memory-based data.
|
||||||
|
final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false);
|
||||||
|
final String data = "asdf";
|
||||||
|
final String filename = "tmp;0.txt";
|
||||||
|
final String body =
|
||||||
|
"--" + boundary + "\r\n" +
|
||||||
|
"Content-Disposition: form-data; name=\"file\"; filename=\"" + filename + "\"\r\n" +
|
||||||
|
"Content-Type: image/gif\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
data + "\r\n" +
|
||||||
|
"--" + boundary + "--\r\n";
|
||||||
|
|
||||||
|
req.content().writeBytes(body.getBytes(CharsetUtil.UTF_8.name()));
|
||||||
|
// Create decoder instance to test.
|
||||||
|
final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req);
|
||||||
|
assertFalse(decoder.getBodyHttpDatas().isEmpty());
|
||||||
|
decoder.destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user