How to fix different multipart boundary implementations? - java

I created a file upload functionality in Jersey.
#POST
#Path("/import")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public static Response importFile(
#FormDataParam("file") InputStream fileInputStream,
#FormDataParam("file") FormDataContentDisposition fileMetaData,
When I send a file as multipart formdata from the browser (Firefox and Chrome) using Fetch API or a jQuery POST, Jersey responds with a org.jvnet.mimepull.MIMEParsingException: Missing start boundary.
This is apparently not due to the fact that e.g. Chrome uses a case-sensitive boundary, as others have had, since the issue happens in Firefox as well, which uses a numeric boundary.
It seems that in my case, the browser sends the boundary with two additional dashes before each boundary in the body and after the last one (which seems to be correct according to the multipart specification https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html), but Jersey (2.25.1) / Mimepull (1.9.14) does not accept that.
Content-Type multipart/form-data; boundary=
---------------------------23978830417520517351040096390
-----------------------------23978830417520517351040096390
Content-Disposition: form-data; name="file"; filename="testdata.csv"
Content-Type: text/csv
<snip>
-----------------------------23978830417520517351040096390--
On the contrary, when I send my request using Postman, it encodes the boundary in the body without those two additional dashes and Jersey accepts the request.
POST /myapi/import HTTP/1.1
Content-Length: 708
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="testdata.csv"
Content-Type: text/csv
(data)
----WebKitFormBoundary7MA4YWxkTrZu0gW
<snip>
----WebKitFormBoundary7MA4YWxkTrZu0gW
What is the correct behaviour in this case and how is it possible to implement the file upload in a way that different clients can use it?
Is there an alternative to using multipart implementation for file uploads?

Related

Multipart/form-data request with multiple files: request seems to be garbled due to odd byte sequence

We need to send a multipart/form-data request containing two files. Since we had some issues with the requests, we set up a request bin page to try and analyze the requests themselves, since Postman seemed to work fine.
My second file is generated by a different service as a byte stream and represents a JSON object. This byte stream seems to have a NUL (0) byte terminator at the end.
If I add this second file to the multipart/form-data request, something like the following results:
With the body looking as follows:
--9ac41cec-a0ff-4afd-b687-6b8c2f1bddd6.
Content-Disposition: form-data; name="metadata"; filename="metadata-1584981615.json".
Content-Type: application/json.
Content-Length: 581.
.
{"...":"..."}.
--9ac41cec-a0ff-4afd-b687-6b8c2f1bddd6.
Content-Disposition: form-data; name="file"; filename="file-1584981615.json".
Content-Type: application/json.
Content-Length: 12205.
.
{"...":"..."}..
--9ac41cec-a0ff-4afd-b687-6b8c2f1bddd6--.
Obviously, this is supremely invalid. It seems that the multipart request is adding the . characters at the end of every line.
I was able to reproduce this behaviour with Spring Web 5.1.5.RELEASE, okHttp 3.14.7 and Apache HttpComponents 4.5.7. The behaviour does not seem to change if I remove the NUL (0) byte terminator from the byte stream.
Here's the odd part: the receiving webserver accepts this request as a valid multipart/form-data request.
For the record, here's what a valid request (that is also accepted by the webserver) looks like from request bin's point of view (different file):
--83611e7f-ad57-4893-9d1b-5ba2c4543d2a
Content-Disposition: form-data; name="metadata"; filename="metadata-1584982345.json"
Content-Type: application/json
Content-Length: 895
{"...": "..."}
--83611e7f-ad57-4893-9d1b-5ba2c4543d2a
Content-Disposition: form-data; name="file"; filename="file-1584982345.json"
Content-Type: application/json
Content-Length: 9868
{"...": "..."}
--83611e7f-ad57-4893-9d1b-5ba2c4543d2a--
Aside from having the explicit terminator at the end (which does not seem to make a difference at all), the byte stream does not look broken. new String(bytes) gives a valid JSON object. So I'm thinking it has to be something else.
Has anyone encountered this issue before? I'm unsure how to proceed with debugging this issue, and I'm fairly certain that sending improper requests, even though they might be accepted by the remote server at this time, almost certainly guarantees that my code will break somewhere along the line...

Uploading JSON string besides File with Ionic native file-transfer plugin to Java JAX-RS Server

I am trying to upload a file to my Java JAX-RS(Jersey) web service.
My method signature on the server side if anyone interested is like this;
upload(#FormDataParam("file") InputStream uploadedStream, #FormDataParam("file") FormDataContentDisposition fileDetail, #FormDataParam("question") String req_question, #Context HttpHeaders headers)
When I try to upload a file with Postman (Client for testing) using multipart/form-data, it is working properly. When I sniff it with Fiddler I see this;
POST http://localhost:8080/application/question/post HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryT79BovKwL6ObBceu
Accept-Language: tr-TR,tr;q=0.8,en-US;q=0.6,en;q=0.4
------WebKitFormBoundaryT79BovKwL6ObBceu
Content-Disposition: form-data; name="file"; filename="PARS.png"
Content-Type: image/png
[HERE SOME STRANGE RAW BINARY DATA]
------WebKitFormBoundaryT79BovKwL6ObBceu
Content-Disposition: form-data; name="question"
{"title": "This is title!", "fk_field_id":1}
------WebKitFormBoundaryT79BovKwL6ObBceu--
As you can see it has proper differentation of types of data being sent. But when I try to achieve the same upload with Ionic/Angular my request look like this;
POST http://localhost:8080/application/question/post HTTP/1.1
Content-Type: multipart/form-data
Accept-Language: tr-TR,tr;q=0.8,en-US;q=0.6,en;q=0.4
/Exif II* [SOME STRANGE RAW BINARY DATA]
I only see binary, there is no differentiation nor sign of the params I sent. Here is my Angular code that is responded as failed.
let options: FileUploadOptions =
{
fileKey: 'file',
headers: headers,
params: {"title": "This is title!", "fk_field_id":1} // Actually I am getting this as method parameter yet to shorten code I moved it here
};
path = {MY ENDPOINT IN SERVER}
filePath = file:///storage/emulated/0/Android/data/io.ionic.starter/cache/1497313256081.jpg // Getting as method param
fileTransfer.upload(filePath, path, options).then(things => {
//Some console.log
});

Java ProxyServlet destroys filename encoding

I am using a Java Proxy Servlet that forwards any http request to a server based using Apache httpClient. It works nice, but filenames, that using special characters like "öÖäÄüÜß" get corrupted.
Browser sends
Content-Disposition: form-data; name="file"; filename="lp_fos_fr-ööÖÖääÄÄüüÜÜß_technik.pdf"
Content-Type: application/pdf
After forwarding the request by ServletProxy
HttpEntityEnclosingRequest eProxyRequest =
new BasicHttpEntityEnclosingRequest(method, proxyRequestUri);
eProxyRequest.setEntity(new InputStreamEntity(servletRequest.getInputStream(), servletRequest.getContentLength()));
The filename is corrupted:
Content-Disposition: form-data; name="file"; filename="lp_fos_fr-.........................._technik.pdf"
Content-Type: application/pdf
What could I do to preserve the filename?

MultipartEntityBuilder: Omit Content-Type and Content-Transfer

I'm trying to mimic my Browsers behaviour on a multipart/form-data POST request using org.apache.http.entity.mime.MultipartEntityBuilder
My browser only sends Content-Disposition, but no Content-Type or Content-Transfer-Encoding Headers.
I tried to use MultipartEntityBuilder.addPart() and addTextBody() but both add those Headers by default:
What I want (what my chrome browser does):
POST .../some.jsp HTTP/1.1
Host: ...
Connection: keep-alive
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary6tcnkxC7txvqE5Xl
------WebKitFormBoundary6tcnkxC7txvqE5Xl
Content-Disposition: form-data; name="merkmal"
5
What I get from MultipartEntityBuilder
POST.../some.jsp HTTP/1.1
Host: ...
Content-Type: multipart/form-data; boundary=m9Zb2QD-QaH-j-HqgGQfI8KwDkToz17ULYkZ
--m9Zb2QD-QaH-j-HqgGQfI8KwDkToz17ULYkZ
Content-Disposition: form-data; name="merkmal"
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit
5
Why?: The designated server thinks that everything after name="merkmal" is the value of merkmal (including the Headers). Other possible reason: Could the whole request somehow have a wrong encoding (especially for newline) ?
alright - intense googeling finally produced an answer.
1) There are apparently some servers that do not get on well with the "Content-Transfer-Encoding" header.
2) There is a browser compatibility mode in HttpComponents that is used like this:
MultipartEntityBuilder uploadEntityBuilder = MultipartEntityBuilder.create().setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
Using the compatibility code, both unwanted headers (Content-Transfer-Encoding and Content-Type) are not used any longer!
I hope this will some day help some poor sod like me ;)

How should I parse a POST request message's content in a Tomcat servlet

I am trying to develop a Java servlet in Tomcat to handle post requests, eventually intending to process files and return said processed files in the response to the client. Based on my searching so far, it seems that the requests I'm going to be handling are given an unusual format. I can suggest alternatives, but I do not have control over the design of the request, which will look as follows:
POST /api/0/actions/image HTTP/1.1
Host: <host>.com
Content-Length: <content-length>
Content-Type: multipart/form-data; boundary=<boundary>
Accept-Encoding: gzip, deflate, compress
Accept: */*
User-Agent: <user-agent>
--<boundary>
Content-Disposition: form-data; name="application"
{<authentication_info_as_JSON_object>}
--<boundary>
Content-Disposition: form-data; name="inputName"
<inputname>
--<boundary>
Content-Disposition: form-data; name="options"
{<options_as_JSON}
--<boundary>
Content-Disposition: form-data; name="input"; filename="<inputname>"
Content-Type: <content-type>
<file-binary>
--<boundary>--
I could parse this request's content as text using regular expressions and standard string processing methods, reconstructing the JSON objects and file(s) I need to handle manually. But that strikes me as a bug-prone and difficult to maintain approach. Are there open source tools out there that can help me process this request in a more structured or standard way?
Thanks in advance.
How is this unusual?
Use Apache Commons' FileUpload or equivalent.
If you're on a Servlet 3.0 container, this is baked in, e.g., HttpServletRequest.getParts()

Categories

Resources