Accept ';' in the filename of HTTP Content-Disposition header

Motivation:

HttpPostMultipartRequestDecoder threw an ArrayIndexOutOfBoundsException when
trying to decode Content-Disposition header with filename containing ';'.
See issue #3326.

Modifications:

Added splitMultipartHeaderValues method which cares about quotes, and use it in
splitMultipartHeader method, instead of StringUtils.split.

Result:

Filenames can contain semicolons.
This commit is contained in:
Taeho Kim 2015-01-14 12:22:21 +09:00 committed by Norman Maurer
parent 24ab3b2fd3
commit add0a0c9a4
2 changed files with 66 additions and 1 deletions

View File

@ -1609,7 +1609,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
String svalue = sb.substring(valueStart, valueEnd);
String[] values;
if (svalue.indexOf(';') >= 0) {
values = StringUtil.split(svalue, ';');
values = splitMultipartHeaderValues(svalue);
} else {
values = StringUtil.split(svalue, ',');
}
@ -1622,4 +1622,39 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
}
return array;
}
/**
* Split one header value in Multipart.
*
* @return an array of {@link 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()]);
}
}

View File

@ -200,4 +200,34 @@ public class HttpPostRequestDecoderTest {
decoder.offer(part4);
decoder.offer(HttpChunk.LAST_CHUNK);
}
// See https://github.com/netty/netty/issues/3326
@Test
public void testFilenameContainingSemicolon() throws Exception {
final String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO";
final DefaultHttpRequest req = new DefaultHttpRequest(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.setContent(ChannelBuffers.wrappedBuffer(body.getBytes(CharsetUtil.UTF_8.name())));
// Create decoder instance to test.
final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req);
assertFalse(decoder.getBodyHttpDatas().isEmpty());
}
}