File Upload using HTTP protocol through java socket - java

I am trying to understand how HTTP protocol works, So I tried to add headers manually to java Socket to send a request to httpbin.org as shown below:
BufferedWriter wr = new BufferedWriter(/*socket Outputstream*/)
wr.write("POST post HTTP/1.1\r\n");
wr.write("Host: httpbin.org\r\n");
wr.write("Accept: */*\r\n");
wr.write("Content-Length: "+data.length()+"\r\n");
wr.write("Content-Type: multipart/form-data; boundary=---WebKitFormBoundary67\r\n");
wr.write("\r\n");
wr.write(data);
wr.flush();
In above code data is the payload of HTTP request that looks exactly as below:
---WebKitFormBoundary67
Content-Disposition: form-data; name="field1"
value1
---WebKitFormBoundary67
Content-Disposition: form-data; name="field2"; filename="example.txt"
Java is better when it run long
---WebKitFormBoundary67--
But the server httpbin.org is not identifying any files attached, am I missing anything?

multipart/form-data is a multipart MIME message as defined in RFC 2046. The basic structure of a multipart MIME message in an example of a multipart/form-data message looks like this:
Content-type: multipart/form-data; boundary=foo
--foo
Content-Disposition: form-data; name=key1
abcde
--foo
Content-Disposition: form-data; name=key2; filename=foo.txt
01234
--foo--
As you can see, the boundary foo is defined in the boundary attribute, is used as delimiter between the parts with --foo and is used as the final boundary as --foo--.
Your code instead defines the boundary not as foo but as --foo and then tries to still use only --foo as a separator between the parts. To correct your code you would either need to set the boundary to only -WebKitFormBoundary67 instead of ---WebKitFormBoundary67, or use -----WebKitFormBoundary67 as separator instead of only ---WebKitFormBoundary67.

Related

Browsers and Postman doesn't decode UTF-8 filename in Content-Disposition header

so I have the situation when I need to return CSV/excel file on a POST request. The problem is to return the filename in different languages, such as Chinese, German, etc. As far as I know, there're 2 ways to do that:
to return Content-Disposition: attachment; filename="привет.txt"
to return Content-Disposition: attachment; filename*=UTF-8''%some%encoded%name.txt".
Both of them do not work with Postman/Browser, but the second one works fine with Swagger. The first way returns something like this Content-Disposition: attachment; filename="???.txt". The second one returns absolutely the same text of filename quotes (encoded UTF-8, for instance: UTF-8''%some%encoded%name.txt. I'm using Java Spring stack.
So the answer was to process that Content-Disposition header on the UI part by encoding it after UTF-8'' symbols.

Cisco ThreatGrid Submit Sample API... How do I send a Sample File?

I am not able to successfully call the ThreatGrid Submit Sample API using Java. I've used Java to call APIs in the past, so I have experience setting up these calls.
I should be POSTing to https://panacea.threatgrid.com/api/v2/samples and provide parameters in the body of my request.
I also need to write the sample file (the file being evaluated) into the body of the request.
I understand that I'll need to set the 'Content-Type' to 'multipart/form-data;' and provide a Boundary string to separate the parts of the request.
Upon calling the submit API, I am receiving an HTTP 400 Bad Request with the following error return:
{"api_version":2,"id":7162013,"error":{"message":"The parameter sample is required. ","code":400,"errors":[{"code":400,"message":"The parameter sample is required. ","help":"/doc/main/index.html","report":"support#threatgrid.com"}]}}
This is saying that I am not providing the 'sample' parameter. Sample is the file being submitted for threat evaluation. Note that my second part (section of data) that I am sending in the request body was given the name 'sample'.
Here's how I am setting the request headers in my connection:
connection.addRequestProperty("Content-Type", "multipart/form-data; boundary=BOUNDARY");
connection.addRequestProperty("cache-control", "no-cache");
connection.addRequestProperty("accept", "*/*");
connection.addRequestProperty("Content-Length", "164784" );
connection.addRequestProperty("Host", "panacea.threatgrid.com");
Here's an example of what I believe I am writing to the connection's output stream:
--BOUNDARY
Content-Disposition: form-data; name="application/json"
{"private":"true","vm":"win7-x64","email_notification":false}
--BOUNDARY
Content-Disposition: form-data; name="sample"; filename="GracePeriod.pdf"
Content-Type: application/pdf
[Bytes of the Sample File being submitted to ThreatGrid api]
--BOUNDARY--
Code that builds the body of my request:
String boundaryString = "BOUNDARY";
String LINE_FEED = "\r\n";
File sampleFileToUpload = new File(fileUrl);
outputStream.writeBytes(LINE_FEED);
outputStream.writeBytes(LINE_FEED);
outputStream.writeBytes("--" + boundaryString);
outputStream.writeBytes(LINE_FEED);
outputStream.writeBytes(LINE_FEED);
outputStream.writeBytes("Content-Disposition: form-data; name=\"application/json\"");
outputStream.writeBytes(LINE_FEED);
// Build the parameters that get placed into the Header
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("private", "true");
headers.put("vm", "win7-x64");
headers.put("email_notification", false);
Gson gson = new Gson();
String body = gson.toJson(headers);
outputStream.writeBytes( body );
outputStream.writeBytes(LINE_FEED);
outputStream.writeBytes(LINE_FEED);
outputStream.writeBytes("--" + boundaryString);
outputStream.writeBytes(LINE_FEED);
outputStream.writeBytes(LINE_FEED);
outputStream.writeBytes("Content-Disposition: form-data; name='sample'; filename='"+sampleFileToUpload.getName()+"'");
outputStream.writeBytes(LINE_FEED);
outputStream.writeBytes("Content-Type: application/pdf");
outputStream.writeBytes(LINE_FEED);
outputStream.writeBytes(LINE_FEED);
// Write the contents of the file being submitted...
FileInputStream inputStream = new FileInputStream(sampleFileToUpload);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] dataArray = new byte[16384];
while ((nRead = inputStream.read(dataArray, 0, dataArray.length)) != -1) {
buffer.write(dataArray, 0, nRead);
}
buffer.flush();
byte[] bytes = buffer.toByteArray();
inputStream.close();
outputStream.write(bytes);
outputStream.writeBytes(LINE_FEED);
outputStream.writeBytes(LINE_FEED);
outputStream.writeBytes("--" + boundaryString + "--");
outputStream.writeBytes(LINE_FEED);
outputStream.writeBytes(LINE_FEED);
outputStream.flush();
outputStream.close();
I should be getting the HTTP 200 message and the response message that contains details about my submission.
Hopefully, someone has done this before and can show me the error in my ways.
Thank you!
EDIT: I forgot to mention that I can use the Postman app to set up and call this API successfully. I set the 'private', 'vm', 'email_notification' and 'sample' items in the body of the request as form-data. Postman allows you to set these items as either text or file (there is a dropdown). In the case of 'sample', I set it to file and Postman allows me to 'attach' the file. I used the Postman console to look at what is being sent in the request and I tried to emulate that in my Java code as best as possible. There must be other detail that I need that Postman doesn't show me in the console.
I was finally able (head slightly bloodied) to get the API to respond successfully (HTTP 200 Message). I'll provide the details that got it to work if this can help anyone in the future.
Upon looking at the definition of the API, it states that "The request parameters are to encoded as 'multipart/form-data'". I was sending some of the parameters as JSON data. I decided that I needed to send each parameter as a separate form variable, each separated by a Boundary marker (I tried this once earlier, but I came back to that same idea).
After doing that I started paying attention to the detail of the spaces (CRLFs) after each item in the Request body. The API is very sensitive to how the data is formatted in the body. I found that it requires a CRLF before the actual value of the form data that you are sending.
Here's an example of the request body as I am sending it:
--BOUNDARY
Content-Disposition: form-data; name="private"
[CRLF (a space)]
true
--BOUNDARY
Content-Disposition: form-data; name="vm"
[CRLF (a space)]
win7-x64
--BOUNDARY
Content-Disposition: form-data; name="email_notification"
[CRLF (a space)]
false
--BOUNDARY
Content-Disposition: form-data; name="sample";
filename="CourseCompletionCertificate.pdf"
Content-Type: application/pdf
[CRLF (a space)]
[data stream of the Sample file in a byte array...]
--BOUNDARY--
I found examples of multipart/form-data and I noticed the use of CRLFs in the data and I did my best to copy how that data was being sent. It was after that detail that the API responded with success.

Setting the Content-Transfer-Encoding of upload file in multi-part HTTP request part with Apache HttpClient

I am using apache http library to build and send a multi-part http request that has a file part in the body. Here is a little sample of my request
Request POST HTTPS://hostname:9443/di/resources/upload?logonId=user1 HTTP/1.1:
Headers: Content-Type: multipart/form-data Set-Cookie: Path=/; HttpOnly TrustToken: -1000%2CCaKOjiTFmje3%2Fw0GGcw5%2BDwgxXHjHdQShQgW1QGiHYk%3D
Body: --ncFZGuKp50zCWWImlBFZjxbanSSoJt
Content-Disposition: form-data; name="File 1"; filename="SampleData_en.csv"
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
Identifier,title,,,,,,,,,,,,,,
Plan: Entries (1) or ent2 (2) ,2,"Set to 1 to specify plan is for Catalog Entries, set to 2 etc .....
--ncFZGuKp50zCWWImlBFZjxbanSSoJt--
The project is that the service is based on apache wink that has a problem decoding the headers in the body which gives this kind of error
Caused by: java.lang.StringIndexOutOfBoundsException
at java.lang.String.substring(String.java:1240)
at org.apache.wink.common.internal.providers.multipart.MultiPartParser.parseHeaders(MultiPartParser.java:264)
at org.apache.wink.common.internal.providers.multipart.MultiPartParser.nextPart(MultiPartParser.java:109)
at org.apache.wink.common.model.multipart.InMultiPart.hasNext(InMultiPart.java:83)
I believe the fix would be to remove the Content-Transfer-Encoding from the body? or change it to maybe a different encoding, maybe base64.
The only problem is that I dont know how to do this using the apache library and haven't been able to find anything examples. Here is the code I am using apache to create the entity portion of my HttpPost request:
MultipartEntityBuilder reqEntity = MultipartEntityBuilder.create();
File file1 = RequestUtils.getFileBody(filePath);
FileBody fileBodyFile1 = new FileBody(file1, org.apache.http.entity.ContentType.create("application/octet-stream"),
file1 .getName());
reqEntity.addPart("File 1", fileBodyFile1);

Java set Content-Desposition to HTTP Body

I am using DefaultHttpClient and MultipartEntityBuilder for sending an image but the server requires a Content-Disposition Content-Disposition: form-data; name="name" filename="image.jpg". How can I set this to the body of the HttpPostRequest? I searched for an answer for about 3 hours and couldn't make it work.
This has nothing to do with the "body" of the message, as #ElliotFrisch points out.
You've got an HttpMessage, so just call addHeader(...):
request.addHeader("Content-Disposition",
"form-data; name=\"name\" filename=\"image.jpg\"");
Before you send it via your DefaultHttpClient.

REST - HTTP Post Multipart with JSON

I need to receive an HTTP Post Multipart which contains only 2 parameters:
A JSON string
A binary file
Which is the correct way to set the body?
I'm going to test the HTTP call using Chrome REST console, so I'm wondering if the correct solution is to set a "label" key for the JSON parameter and the binary file.
On the server side I'm using Resteasy 2.x, and I'm going to read the Multipart body like this:
#POST
#Consumes("multipart/form-data")
public String postWithPhoto(MultipartFormDataInput multiPart) {
Map <String, List<InputPart>> params = multiPart.getFormDataMap();
String myJson = params.get("myJsonName").get(0).getBodyAsString();
InputPart imagePart = params.get("photo").get(0);
//do whatever I need to do with my json and my photo
}
Is this the way to go?
Is it correct to retrieve my JSON string using the key "myJsonName" that identify that particular content-disposition?
Are there any other way to receive these 2 content in one HTTP multipart request?
If I understand you correctly, you want to compose a multipart request manually from an HTTP/REST console. The multipart format is simple; a brief introduction can be found in the HTML 4.01 spec. You need to come up with a boundary, which is a string not found in the content, let’s say HereGoes. You set request header Content-Type: multipart/form-data; boundary=HereGoes. Then this should be a valid request body:
--HereGoes
Content-Disposition: form-data; name="myJsonString"
Content-Type: application/json
{"foo": "bar"}
--HereGoes
Content-Disposition: form-data; name="photo"
Content-Type: image/jpeg
Content-Transfer-Encoding: base64
<...JPEG content in base64...>
--HereGoes--

Categories