fix #6066 Support optional filename in HttpPostRequestEncoder
Motivation: According to https://www.ietf.org/rfc/rfc2388.txt 4.4, filename after "content-disposition" is optional and arbitrary (does not need to match a real filename). Modifications: This change supports an extra addBodyFileUpload overload to precise the filename (default to File.getName). If empty or null this argument should be ignored during encoding. Result: - A backward-compatible addBodyFileUpload(String, File, String, boolean) to use file.getName() as filename. - A new addBodyFileUpload(String, String, File, String, boolean) overload to precise filename - Couple of tests for the empty use case
This commit is contained in:
parent
55c291ae5b
commit
ea0ddc0ea2
@ -34,6 +34,7 @@ import io.netty.handler.codec.http.HttpUtil;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
import io.netty.handler.codec.http.LastHttpContent;
|
||||
import io.netty.handler.stream.ChunkedInput;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import io.netty.util.internal.ThreadLocalRandom;
|
||||
|
||||
import java.io.File;
|
||||
@ -362,12 +363,39 @@ public class HttpPostRequestEncoder implements ChunkedInput<HttpContent> {
|
||||
*/
|
||||
public void addBodyFileUpload(String name, File file, String contentType, boolean isText)
|
||||
throws ErrorDataEncoderException {
|
||||
addBodyFileUpload(name, file.getName(), file, contentType, isText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a file as a FileUpload
|
||||
*
|
||||
* @param name
|
||||
* the name of the parameter
|
||||
* @param file
|
||||
* the file to be uploaded (if not Multipart mode, only the filename will be included)
|
||||
* @param filename
|
||||
* the filename to use for this File part, empty String will be ignored by
|
||||
* the encoder
|
||||
* @param contentType
|
||||
* the associated contentType for the File
|
||||
* @param isText
|
||||
* True if this file should be transmitted in Text format (else binary)
|
||||
* @throws NullPointerException
|
||||
* for name and file
|
||||
* @throws ErrorDataEncoderException
|
||||
* if the encoding is in error or if the finalize were already done
|
||||
*/
|
||||
public void addBodyFileUpload(String name, String filename, File file, String contentType, boolean isText)
|
||||
throws ErrorDataEncoderException {
|
||||
if (name == null) {
|
||||
throw new NullPointerException("name");
|
||||
}
|
||||
if (file == null) {
|
||||
throw new NullPointerException("file");
|
||||
}
|
||||
if (filename == null) {
|
||||
filename = StringUtil.EMPTY_STRING;
|
||||
}
|
||||
String scontentType = contentType;
|
||||
String contentTransferEncoding = null;
|
||||
if (contentType == null) {
|
||||
@ -380,7 +408,7 @@ public class HttpPostRequestEncoder implements ChunkedInput<HttpContent> {
|
||||
if (!isText) {
|
||||
contentTransferEncoding = HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value();
|
||||
}
|
||||
FileUpload fileUpload = factory.createFileUpload(request, name, file.getName(), scontentType,
|
||||
FileUpload fileUpload = factory.createFileUpload(request, name, filename, scontentType,
|
||||
contentTransferEncoding, null, file.length());
|
||||
try {
|
||||
fileUpload.setContent(file);
|
||||
@ -615,12 +643,17 @@ public class HttpPostRequestEncoder implements ChunkedInput<HttpContent> {
|
||||
|
||||
.append(HttpHeaderNames.CONTENT_DISPOSITION)
|
||||
.append(": ")
|
||||
.append(HttpHeaderValues.ATTACHMENT)
|
||||
.append("; ")
|
||||
.append(HttpHeaderValues.FILENAME)
|
||||
.append("=\"")
|
||||
.append(fileUpload.getFilename())
|
||||
.append("\"\r\n");
|
||||
.append(HttpHeaderValues.ATTACHMENT);
|
||||
|
||||
if (!fileUpload.getFilename().isEmpty()) {
|
||||
replacement.append("; ")
|
||||
.append(HttpHeaderValues.FILENAME)
|
||||
.append("=\"")
|
||||
.append(fileUpload.getFilename())
|
||||
.append('"');
|
||||
}
|
||||
|
||||
replacement.append("\r\n");
|
||||
|
||||
pastAttribute.setValue(replacement.toString(), 1);
|
||||
pastAttribute.setValue("", 2);
|
||||
@ -648,16 +681,31 @@ public class HttpPostRequestEncoder implements ChunkedInput<HttpContent> {
|
||||
// add mixedmultipart delimiter, mixedmultipart body header and
|
||||
// Data to multipart list
|
||||
internal.addValue("--" + multipartMixedBoundary + "\r\n");
|
||||
// Content-Disposition: attachment; filename="file1.txt"
|
||||
internal.addValue(HttpHeaderNames.CONTENT_DISPOSITION + ": " + HttpHeaderValues.ATTACHMENT + "; "
|
||||
+ HttpHeaderValues.FILENAME + "=\"" + fileUpload.getFilename() + "\"\r\n");
|
||||
|
||||
if (fileUpload.getFilename().isEmpty()) {
|
||||
// Content-Disposition: attachment
|
||||
internal.addValue(HttpHeaderNames.CONTENT_DISPOSITION + ": "
|
||||
+ HttpHeaderValues.ATTACHMENT + "\r\n");
|
||||
} else {
|
||||
// Content-Disposition: attachment; filename="file1.txt"
|
||||
internal.addValue(HttpHeaderNames.CONTENT_DISPOSITION + ": "
|
||||
+ HttpHeaderValues.ATTACHMENT + "; "
|
||||
+ HttpHeaderValues.FILENAME + "=\"" + fileUpload.getFilename() + "\"\r\n");
|
||||
}
|
||||
} else {
|
||||
internal.addValue("--" + multipartDataBoundary + "\r\n");
|
||||
// Content-Disposition: form-data; name="files";
|
||||
// filename="file1.txt"
|
||||
internal.addValue(HttpHeaderNames.CONTENT_DISPOSITION + ": " + HttpHeaderValues.FORM_DATA + "; "
|
||||
+ HttpHeaderValues.NAME + "=\"" + fileUpload.getName() + "\"; "
|
||||
+ HttpHeaderValues.FILENAME + "=\"" + fileUpload.getFilename() + "\"\r\n");
|
||||
|
||||
if (fileUpload.getFilename().isEmpty()) {
|
||||
// Content-Disposition: form-data; name="files";
|
||||
internal.addValue(HttpHeaderNames.CONTENT_DISPOSITION + ": " + HttpHeaderValues.FORM_DATA + "; "
|
||||
+ HttpHeaderValues.NAME + "=\"" + fileUpload.getName() + "\"\r\n");
|
||||
} else {
|
||||
// Content-Disposition: form-data; name="files";
|
||||
// filename="file1.txt"
|
||||
internal.addValue(HttpHeaderNames.CONTENT_DISPOSITION + ": " + HttpHeaderValues.FORM_DATA + "; "
|
||||
+ HttpHeaderValues.NAME + "=\"" + fileUpload.getName() + "\"; "
|
||||
+ HttpHeaderValues.FILENAME + "=\"" + fileUpload.getFilename() + "\"\r\n");
|
||||
}
|
||||
}
|
||||
// Add Content-Length: xxx
|
||||
internal.addValue(HttpHeaderNames.CONTENT_LENGTH + ": " +
|
||||
|
@ -74,6 +74,39 @@ public class HttpPostRequestEncoderTest {
|
||||
assertEquals(expected, content);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleFileUploadNoName() throws Exception {
|
||||
DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1,
|
||||
HttpMethod.POST, "http://localhost");
|
||||
|
||||
HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(request, true);
|
||||
File file1 = new File(getClass().getResource("/file-01.txt").toURI());
|
||||
encoder.addBodyAttribute("foo", "bar");
|
||||
encoder.addBodyFileUpload("quux", "", file1, "text/plain", false);
|
||||
|
||||
String multipartDataBoundary = encoder.multipartDataBoundary;
|
||||
String content = getRequestBody(encoder);
|
||||
|
||||
String expected = "--" + multipartDataBoundary + "\r\n" +
|
||||
CONTENT_DISPOSITION + ": form-data; name=\"foo\"" + "\r\n" +
|
||||
CONTENT_LENGTH + ": 3" + "\r\n" +
|
||||
CONTENT_TYPE + ": text/plain; charset=UTF-8" + "\r\n" +
|
||||
"\r\n" +
|
||||
"bar" +
|
||||
"\r\n" +
|
||||
"--" + multipartDataBoundary + "\r\n" +
|
||||
CONTENT_DISPOSITION + ": form-data; name=\"quux\"\r\n" +
|
||||
CONTENT_LENGTH + ": " + file1.length() + "\r\n" +
|
||||
CONTENT_TYPE + ": text/plain" + "\r\n" +
|
||||
CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" +
|
||||
"\r\n" +
|
||||
"File 01" + StringUtil.NEWLINE +
|
||||
"\r\n" +
|
||||
"--" + multipartDataBoundary + "--" + "\r\n";
|
||||
|
||||
assertEquals(expected, content);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiFileUploadInMixedMode() throws Exception {
|
||||
DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1,
|
||||
@ -124,6 +157,56 @@ public class HttpPostRequestEncoderTest {
|
||||
assertEquals(expected, content);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiFileUploadInMixedModeNoName() throws Exception {
|
||||
DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1,
|
||||
HttpMethod.POST, "http://localhost");
|
||||
|
||||
HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(request, true);
|
||||
File file1 = new File(getClass().getResource("/file-01.txt").toURI());
|
||||
File file2 = new File(getClass().getResource("/file-02.txt").toURI());
|
||||
encoder.addBodyAttribute("foo", "bar");
|
||||
encoder.addBodyFileUpload("quux", "", file1, "text/plain", false);
|
||||
encoder.addBodyFileUpload("quux", "", file2, "text/plain", false);
|
||||
|
||||
// We have to query the value of these two fields before finalizing
|
||||
// the request, which unsets one of them.
|
||||
String multipartDataBoundary = encoder.multipartDataBoundary;
|
||||
String multipartMixedBoundary = encoder.multipartMixedBoundary;
|
||||
String content = getRequestBody(encoder);
|
||||
|
||||
String expected = "--" + multipartDataBoundary + "\r\n" +
|
||||
CONTENT_DISPOSITION + ": form-data; name=\"foo\"" + "\r\n" +
|
||||
CONTENT_LENGTH + ": 3" + "\r\n" +
|
||||
CONTENT_TYPE + ": text/plain; charset=UTF-8" + "\r\n" +
|
||||
"\r\n" +
|
||||
"bar" + "\r\n" +
|
||||
"--" + multipartDataBoundary + "\r\n" +
|
||||
CONTENT_DISPOSITION + ": form-data; name=\"quux\"" + "\r\n" +
|
||||
CONTENT_TYPE + ": multipart/mixed; boundary=" + multipartMixedBoundary + "\r\n" +
|
||||
"\r\n" +
|
||||
"--" + multipartMixedBoundary + "\r\n" +
|
||||
CONTENT_DISPOSITION + ": attachment\r\n" +
|
||||
CONTENT_LENGTH + ": " + file1.length() + "\r\n" +
|
||||
CONTENT_TYPE + ": text/plain" + "\r\n" +
|
||||
CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" +
|
||||
"\r\n" +
|
||||
"File 01" + StringUtil.NEWLINE +
|
||||
"\r\n" +
|
||||
"--" + multipartMixedBoundary + "\r\n" +
|
||||
CONTENT_DISPOSITION + ": attachment\r\n" +
|
||||
CONTENT_LENGTH + ": " + file2.length() + "\r\n" +
|
||||
CONTENT_TYPE + ": text/plain" + "\r\n" +
|
||||
CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" +
|
||||
"\r\n" +
|
||||
"File 02" + StringUtil.NEWLINE +
|
||||
"\r\n" +
|
||||
"--" + multipartMixedBoundary + "--" + "\r\n" +
|
||||
"--" + multipartDataBoundary + "--" + "\r\n";
|
||||
|
||||
assertEquals(expected, content);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleFileUploadInHtml5Mode() throws Exception {
|
||||
DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1,
|
||||
|
Loading…
Reference in New Issue
Block a user