Skip to content

Commit

Permalink
[1.x] Enable multipart/related on FileUpload (#314)
Browse files Browse the repository at this point in the history
* added ability to use content-type: multipart/related

* reformatted a part from the new test

* added line-break on test

* set field multipartRelated to be final

* removed unnecessary condition

* added as a contributor

* small polishings due to code review

* Javadoc

---------

Co-authored-by: Gary Gregory <[email protected]>
  • Loading branch information
mufasa1976 and garydgregory authored May 20, 2024
1 parent 91a6840 commit feeece4
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 3 deletions.
4 changes: 4 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@
<name>fangwentong</name>
<email>[email protected]</email>
</contributor>
<contributor>
<name>mufasa1976</name>
<email>[email protected]</email>
</contributor>
</contributors>

<scm>
Expand Down
28 changes: 25 additions & 3 deletions src/main/java/org/apache/commons/fileupload/FileUploadBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,13 @@ public static boolean isMultipartContent(final HttpServletRequest req) {
*/
public static final String MULTIPART_MIXED = "multipart/mixed";

/**
* HTTP content type header for multiple related data.
*
* @since 1.6.0
*/
public static final String MULTIPART_RELATED = "multipart/related";

/**
* The maximum length of a single header line that will be parsed
* (1024 bytes).
Expand Down Expand Up @@ -954,6 +961,11 @@ public void setHeaders(final FileItemHeaders pHeaders) {
*/
private boolean eof;

/**
* Is this a multipart/related Request.
*/
private final boolean multipartRelated;

/**
* Creates a new instance.
*
Expand All @@ -972,10 +984,11 @@ public void setHeaders(final FileItemHeaders pHeaders) {
if (null == contentType
|| !contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART)) {
throw new InvalidContentTypeException(
format("the request doesn't contain a %s or %s stream, content type header is %s",
MULTIPART_FORM_DATA, MULTIPART_MIXED, contentType));
format("the request neither contains a %s nor a %s nor a %s stream, content type header is %s",
MULTIPART_FORM_DATA, MULTIPART_MIXED, MULTIPART_RELATED, contentType));
}

multipartRelated = contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART_RELATED);

@SuppressWarnings("deprecation") // still has to be backward compatible
final int contentLengthInt = ctx.getContentLength();
Expand Down Expand Up @@ -1068,7 +1081,16 @@ private boolean findNextItem() throws IOException {
continue;
}
final FileItemHeaders headers = getParsedHeaders(multi.readHeaders());
if (currentFieldName == null) {
if (multipartRelated) {
currentFieldName = "";
currentItem = new FileItemStreamImpl(
null, null, headers.getHeader(CONTENT_TYPE),
false, getContentLength(headers));
currentItem.setHeaders(headers);
notifier.noteItem();
itemValid = true;
return true;
} else if (currentFieldName == null) {
// We're parsing the outer multipart
final String fieldName = getFieldName(headers);
if (fieldName != null) {
Expand Down
49 changes: 49 additions & 0 deletions src/test/java/org/apache/commons/fileupload/FileUploadTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.List;

import org.apache.commons.fileupload.portlet.PortletFileUploadTest;
Expand Down Expand Up @@ -394,4 +395,52 @@ private void assertHeaders(final String[] pHeaderNames, final String[] pHeaderVa
}
}
}

/**
* Test for multipart/related without any content-disposition Header.
* This kind of Content-Type is commonly used by SOAP-Requests with Attachments (MTOM)
*/
@Test
public void testMultipartRelated() throws FileUploadException {
final String soapEnvelope =
"<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\">\r\n" +
" <soap:Header></soap:Header>\r\n" +
" <soap:Body>\r\n" +
" <ns1:Test xmlns:ns1=\"http://www.test.org/some-test-namespace\">\r\n" +
" <ns1:Attachment>\r\n" +
" <xop:Include xmlns:xop=\"http://www.w3.org/2004/08/xop/include\"" +
" href=\"ref-to-attachment%40some.domain.org\"/>\r\n" +
" </ns1:Attachment>\r\n" +
" </ns1:Test>\r\n" +
" </soap:Body>\r\n" +
"</soap:Envelope>";

final String content = "-----1234\r\n" +
"content-type: application/xop+xml; type=\"application/soap+xml\"\r\n" +
"\r\n" +
soapEnvelope + "\r\n" +
"-----1234\r\n" +
"Content-type: text/plain\r\n" +
"content-id: <[email protected]>\r\n" +
"\r\n" +
"some text/plain content\r\n" +
"-----1234--\r\n";

final List<FileItem> fileItems = Util.parseUpload(upload, content.getBytes(StandardCharsets.US_ASCII),
"multipart/related; boundary=---1234;" +
" type=\"application/xop+xml\"; start-info=\"application/soap+xml\"");
assertEquals(2, fileItems.size());

final FileItem part1 = fileItems.get(0);
assertNull(part1.getFieldName());
assertFalse(part1.isFormField());
assertEquals(soapEnvelope, part1.getString());

final FileItem part2 = fileItems.get(1);
assertNull(part2.getFieldName());
assertFalse(part2.isFormField());
assertEquals("some text/plain content", part2.getString());
assertEquals("text/plain", part2.getContentType());
assertNull(part2.getName());
}
}

0 comments on commit feeece4

Please sign in to comment.