Skip to content

Commit

Permalink
#7265 HttpPostMultipartRequestDecoder should decode header field para…
Browse files Browse the repository at this point in the history
…meters
  • Loading branch information
dminkovsky committed Oct 24, 2017
1 parent 47f016b commit c485b39
Show file tree
Hide file tree
Showing 2 changed files with 169 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http.multipart.HttpPostBodyUtil.SeekAheadOptimize;
import io.netty.handler.codec.http.multipart.HttpPostBodyUtil.TransferEncodingMechanism;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException;
Expand Down Expand Up @@ -691,18 +692,7 @@ private InterfaceHttpData findMultipartDisposition() {
String[] values = contents[i].split("=", 2);
Attribute attribute;
try {
String name = cleanString(values[0]);
String value = values[1];

// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html
if (HttpHeaderValues.FILENAME.contentEquals(name)) {
// filename value is quoted string so strip them
value = value.substring(1, value.length() - 1);
} else {
// otherwise we need to clean the value
value = cleanString(value);
}
attribute = factory.createAttribute(request, name, value);
attribute = getContentDispositionAttribute(values);
} catch (NullPointerException e) {
throw new ErrorDataDecoderException(e);
} catch (IllegalArgumentException e) {
Expand Down Expand Up @@ -805,6 +795,38 @@ private InterfaceHttpData findMultipartDisposition() {
}
}

private static final String FILENAME_ENCODED = HttpHeaderValues.FILENAME.toString() + '*';

private Attribute getContentDispositionAttribute(String... values) {
String name = cleanString(values[0]);
String value = values[1];

// Filename can be token, quoted or encoded. See https://tools.ietf.org/html/rfc5987
if (HttpHeaderValues.FILENAME.contentEquals(name)) {
// Value is quoted or token. Strip if quoted:
int last = value.length() - 1;
if (last > 0 &&
value.charAt(0) == HttpConstants.DOUBLE_QUOTE &&
value.charAt(last) == HttpConstants.DOUBLE_QUOTE) {
value = value.substring(1, last);
}
} else if (FILENAME_ENCODED.equals(name)) {
try {
name = HttpHeaderValues.FILENAME.toString();
String[] split = value.split("'", 3);
value = QueryStringDecoder.decodeComponent(split[2], Charset.forName(split[0]));
} catch (ArrayIndexOutOfBoundsException e) {
throw new ErrorDataDecoderException(e);
} catch (UnsupportedCharsetException e) {
throw new ErrorDataDecoderException(e);
}
} else {
// otherwise we need to clean the value
value = cleanString(value);
}
return factory.createAttribute(request, name, value);
}

/**
* Get the FileUpload (new one or current one)
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import io.netty.util.CharsetUtil;
import org.junit.Test;

import java.net.URLEncoder;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Arrays;

Expand Down Expand Up @@ -491,4 +492,138 @@ public void testFormEncodeIncorrect() throws Exception {
content.release();
}
}

// https://github.com/netty/netty/pull/7265
@Test
public void testDecodeContentDispositionFieldParameters() throws Exception {

final String boundary = "74e78d11b0214bdcbc2f86491eeb4902";

String encoding = "utf-8";
String filename = "attached_файл.txt";
String filenameEncoded = URLEncoder.encode(filename, encoding);

final String body = "--" + boundary + "\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename*=" + encoding + "''" + filenameEncoded + "\r\n" +
"\r\n" +
"foo\r\n" +
"\r\n" +
"--" + boundary + "--";

final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1,
HttpMethod.POST,
"http://localhost",
Unpooled.wrappedBuffer(body.getBytes()));

req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary);
final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false);
final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req);
assertFalse(decoder.getBodyHttpDatas().isEmpty());
InterfaceHttpData part1 = decoder.getBodyHttpDatas().get(0);
assertTrue("the item should be a FileUpload", part1 instanceof FileUpload);
FileUpload fileUpload = (FileUpload) part1;
assertEquals("the filename should be decoded", filename, fileUpload.getFilename());
decoder.destroy();
req.release();
}

// https://github.com/netty/netty/pull/7265
@Test
public void testDecodeWithLanguageContentDispositionFieldParameters() throws Exception {

final String boundary = "74e78d11b0214bdcbc2f86491eeb4902";

String encoding = "utf-8";
String filename = "attached_файл.txt";
String language = "anything";
String filenameEncoded = URLEncoder.encode(filename, encoding);

final String body = "--" + boundary + "\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename*=" +
encoding + "'" + language + "'" + filenameEncoded + "\r\n" +
"\r\n" +
"foo\r\n" +
"\r\n" +
"--" + boundary + "--";

final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1,
HttpMethod.POST,
"http://localhost",
Unpooled.wrappedBuffer(body.getBytes()));

req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary);
final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false);
final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req);
assertFalse(decoder.getBodyHttpDatas().isEmpty());
InterfaceHttpData part1 = decoder.getBodyHttpDatas().get(0);
assertTrue("the item should be a FileUpload", part1 instanceof FileUpload);
FileUpload fileUpload = (FileUpload) part1;
assertEquals("the filename should be decoded", filename, fileUpload.getFilename());
decoder.destroy();
req.release();
}

// https://github.com/netty/netty/pull/7265
@Test
public void testDecodeMalformedNotEncodedContentDispositionFieldParameters() throws Exception {

final String boundary = "74e78d11b0214bdcbc2f86491eeb4902";

final String body = "--" + boundary + "\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename*=not-encoded\r\n" +
"\r\n" +
"foo\r\n" +
"\r\n" +
"--" + boundary + "--";

final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1,
HttpMethod.POST,
"http://localhost",
Unpooled.wrappedBuffer(body.getBytes()));

req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary);

final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false);

try {
new HttpPostRequestDecoder(inMemoryFactory, req);
fail("Was expecting an ErrorDataDecoderException");
} catch (HttpPostRequestDecoder.ErrorDataDecoderException e) {
assertTrue(e.getCause() instanceof ArrayIndexOutOfBoundsException);
} finally {
req.release();
}
}

// https://github.com/netty/netty/pull/7265
@Test
public void testDecodeMalformedBadCharsetContentDispositionFieldParameters() throws Exception {

final String boundary = "74e78d11b0214bdcbc2f86491eeb4902";

final String body = "--" + boundary + "\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename*=not-a-charset''filename\r\n" +
"\r\n" +
"foo\r\n" +
"\r\n" +
"--" + boundary + "--";

final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1,
HttpMethod.POST,
"http://localhost",
Unpooled.wrappedBuffer(body.getBytes()));

req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary);

final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false);

try {
new HttpPostRequestDecoder(inMemoryFactory, req);
fail("Was expecting an ErrorDataDecoderException");
} catch (HttpPostRequestDecoder.ErrorDataDecoderException e) {
assertTrue(e.getCause() instanceof UnsupportedCharsetException);
} finally {
req.release();
}
}
}

0 comments on commit c485b39

Please sign in to comment.