I'm trying to extract some information from raw HTTP Request messages (like the one below) and store them into instances of the org.apache.http.message.BasicHttpRequest (https://hc.apache.org/httpcomponents-core-ga/httpcore/apidocs/index.html) class.
I was able to employ org.apache.http.message.BasicLineParser class and its method parseHeader(String value, LineParser parser) to process (parse + store) the headers, but I don't know how to deal with the parameters passed with the form.
POST https://gist.github.com:443/gists HTTP/1.1
Host: gist.github.com
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en,fr;q=0.8,it-it;q=0.6,it;q=0.4,en-us;q=0.2
Accept-Encoding: gzip, deflate
DNT: 1
Referer: https://gist.github.com/
Connection: keep-alive
Content-Type: multipart/form-data; boundary=-------------------------10314229756379422411656878663
Content-Length: 1242
-----------------------------10314229756379422411656878663
Content-Disposition: form-data; name="parameter_name"
parameter_value
Do you know an utility which can parse the content starting after the header above (i.e. after the first empty line)? What I am interest in collecting are all the pairs <"parameter_name","parameter_value"> present in the request body.
I have already read similar answers/questions such as Parsing raw HTTP Request but I did not find anything helpful on the form-data component
Thanks in advance for your help and time
What you are seeing is MIME encoded content body. HttpClient is content agnostic and therefore does not provide a means of parsing such content. One can however use Apache Mime4J to do so.
Related
I am trying to do a $batch request in Java using OData v2.
An example request from the browser would be something like below between the double quotes.
But how can I make this request programatically? Is there a sample call somewhere? Any help is appreciated.
Request URL: https://someUrl/project/odata/project/FOLDER/$batch
Request Method: POST
Status Code: 202 Accepted
Remote Address: 1.2.3.4:1234
Referrer Policy: no-referrer-when-downgrade
content-encoding: gzip
content-length: 5256
content-type: multipart/mixed; boundary=E828EB257B134AC6F567C8D3B67E666E1
dataserviceversion: 2.0
Accept: multipart/mixed
Accept-Encoding: gzip, deflate, br
Accept-Language: en
Connection: keep-alive
Content-Length: 595
Content-Type: multipart/mixed;boundary=batch_4edb-a2cd-948d
Cookie: project-usercontext=project-language=EN&project-client=100;
--Some cookie content--
DataServiceVersion: 2.0
Host: host.myClient.com:1234
MaxDataServiceVersion: 2.0
Origin: https://host.myClient.com:1234
Referer: https://host.myClient.com:1234/project/index.html
project-cancel-on-close: true
project-contextid-accept: header
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/1.2.3.4 Safari/537.36
x-csrf-token: 8Fd53yy2vuCjnaFKrZNuLg==
--batch_4edb-a2cd-948d
Content-Type: application/http
Content-Transfer-Encoding: binary
GET MyEntityDetailsSet HTTP/1.1
project-contextid-accept: header
Accept: application/json
Accept-Language: en
DataServiceVersion: 2.0
MaxDataServiceVersion: 2.0
project-cancel-on-close: true
> --batch_4edb-a2cd-948d
Content-Type: application/http
Content-Transfer-Encoding: binary
GET MyObjectSet HTTP/1.1
project-contextid-accept: header
Accept: application/json
Accept-Language: en
DataServiceVersion: 2.0
MaxDataServiceVersion: 2.0
project-cancel-on-close: true
--batch_4edb-a2cd-948d--
You can use Olingo V2 as an OData client (although a rather ugly one in my opinion). There is a full tutorial dedicated to this usage on the official Olingo site: How to use Apache Olingo as client library.
Olingo knows to build requests and parse responses, but you need an underlying mechanism to execute the HTTP calls. My recommendation would be to not rely on manually opening HttpURLConnections like in the above example, but to use something like Apache Http Client or some other dedicated library instead (in order to reduce the amount of code you write and also to have access to more advanced concepts like connection polling).
In a nutshell, you must first read and parse the metadata of the service that you want to consume:
// content = read the metadata as an InputStream
Edm dataModel = EntityProvider.readMetadata(content, false);
You can build a batch request via a fluent-style API:
BatchQueryPart part = BatchQueryPart.method("GET")
.uri("/Employees('1')")
.build();
// here you could have a larger list of parts, not just a singleton list
InputStream payload = EntityProvider.writeBatchRequest(
Collections.singletonList(part), "batch_boundary");
Then you have to just execute it using your HTTP request execution mechanism of choice (method = "POST" and body = the payload variable). Afterwards, you can parse the obtained response using Olingo:
// body = the response body received
// contentType = the Content-Type header received
List<BatchSingleResponse> responses =
EntityProvider.parseBatchResponse(responseBody, contentType);
// you can obtain the body for each request from the response list
String partBody = responses.get(0).getBody();
InputStream partStream = new ByteArrayInputStream(partBody.getBytes());
String partType = responses.get(0).getHeader(HttpHeaders.CONTENT_TYPE);
Lastly, using the Edm from the first step you can also parse each individual body based on the type of request that you build. For example you could use the readEntry method to de-serialize a single entity read:
// first we have to find the entity set you used to make the request
EdmEntitySet entitySet = edm.getDefaultEntityContainer()
.getEntitySet("Employees");
ODataEntry entry = EntityProvider.readEntry(partType, entitySet,
partStream, EntityProviderReadProperties.init().build())
Lastly, you can use the entry methods to get e.g. the properties.
I have a GRAILS 3 controller that receive an HTTP post from a webservice (Chargify) with this format (the payload section has about 100 entries with a lot of sub-fields):
POST / HTTP/1.1
Accept: */*; q=0.5, application/xml
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
X-Chargify-Webhook-Id: 81309408
X-Chargify-Webhook-Signature: xxxxxxxxxxxxx
X-Chargify-Webhook-Signature-Hmac-Sha-256: yyyyyyyyyyyyyy
Content-Length: 48
User-Agent: Ruby
X-Newrelic-Id: xxxxxx
X-Newrelic-Transaction: aaaaaaaaaaaaaa=
Host: myhost.test.it
id=81197881&event=statement_settled&payload[site][id]=12345&payload[site][subdomain]=test-sandbox
Is there any way with GRAILS to parse the "payload" part and convert it dynamically to a POJO (or also a simple hashmap)?. Chargify use this strange format not recognized by GRAILS framework and I'm unable to parse it automatically.
Is there anyone to help me for parsing? Advance thanks for helping.
Can you try this ?
def readChargify() {
String requestData = request.reader.text
def reqMap = org.grails.web.util.WebUtils.fromQueryString(requestData)
}
This Java library to parse the body of webhooks was contributed by another Chargify customer and may be helpful:
https://github.com/prowave/chargify-webhook-java
So, I'm trying to create a simple (and I mean simple) POST request. Here is the class on the server-side.
#Stateless
#Path("cards")
public class CardsFacadeREST extends AbstractFacade<Cards> {
#POST
#Path("test")
#Consumes({"text/plain"})
public void createTestCard() {
Cards card = new Cards();
card.setName("Test Card");
super.create(card);
}
#GET
#Path("count")
#Produces("text/plain")
public String countREST() {
return String.valueOf(super.count());
}
}
The GET method works just fine, but the POST method isn't working for me. I'm using Chrome's Advanced Rest Client.
The URL is http://localhost:8080/dc-rest/webresources/cards/test
The type is POST
My header is like so: Content-Type: text/plain
And that's it.
I keep getting a "400: Bad Request. The request sent by the client was syntactically incorrect."
When I open the response in the JSON window, all it says is "Unexpected token <"
Here are the request headers if that makes any difference.
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.103 Safari/537.36
Origin: chrome-extension://hgmloofddffdnphfgcellkdfbfbjeloo
Content-Type: text/plain
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
Cookie: JSESSIONID=f4c746a32b46244d422800192f04; treeForm_tree- hi=treeForm:tree:applications
Body is empty.
And the response:
X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition 4.0 Java/Oracle Corporation/1.7)
Server: GlassFish Server Open Source Edition 4.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Allow: GET,DELETE,OPTIONS,PUT,POST
Access-Control-Allow-Headers: content-type
Content-Language:
Content-Type: text/html Date: Thu, 11 Feb 2016 20:48:12 GMT
Connection: close Content-Length: 1105
Body:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>GlassFish Server Open Source Edition 4.0 - Error report</title><style type="text/css"><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 400 - Bad Request</h1><hr/><p><b>type</b> Status report</p><p><b>message</b>Bad Request</p><p><b>description</b>The request sent by the client was syntactically incorrect.</p><hr/><h3>GlassFish Server Open Source Edition 4.0 </h3></body></html>
The 400-Bad request response is likely because the server has expressed that it expects a specific content type
#Consumes({"text/plain"}
However the client is not indicating that the post body is of this type.
To solve this, make sure that the POST request from your client contains the following HTTP header:
Content-Type: text/plain
Or, perhaps it is the case that you are not POSTing plain text and you intend to POST XML or JSON. Whatever the intended type, you just need to make sure the client and server are in agreement about it.
If an HTTP request has a body, it must have either Content-Length or Transfer-Encoding header.
With neither, the request has no body -- not even a body of length 0. If you want to send an empty body, the request should have header Content-Length: 0.
There is a semantic difference between having no body and having an empty body. The server apparently rejects the POST request with no body. (Though the request is actually syntactically valid according to RFC)
Actually, this part is not quite clear (discussion thread (don't read)). And some implementations set Content-Length: 0 for GET requests; some implementations omit Content-Length: 0 for empty POST bodies; both are wrong... Sometimes they work sometimes they don't. Welcome to the chaotic world of HTTP.
I hate it when this happens. It was something else entirely, my persistence bean is messed up. When I commented out "super.create(card)" in my POST method, everything worked (request body or no).
Still have no idea why that resulted in "400: Bad Request. The request sent by the client was syntactically incorrect."
Seems like I have another problem to figure out, but this one at least is solved.
Thank y'all for helping!
I got this Java code at my RESTFul Web Service:
Response response = Response.ok().
entity(method(paramether)).
header("Access-Control-Allow-Origin", "*").build();
I want to add some header that lets me set the charset to UTF-8 through the HTTP itself (because I'm facing some problems when trying to set only at the document). Thanks in advance.
Content-Type, e.g.:
Content-Type: text/html;charset=UTF-8
The Content-Type header can be used to specify the type of the content you return, along with it's character set.
As an example:
Content-Type: text/html; charset=utf-8
Taken from the Wikipedia article on HTTP headers https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
I'm attempting to use the Tumblr API in an Android app to authorize users and make text and photo posts. I'm using the Scribe library. So for, I can successfully obtain an access token and use it to get user info. I can also make text posts without any issues. This tells me that I'm signing requests correctly.
However, I've spent the last week and a half attempting to make photo posts without success. I continuously receive 401 errors (Not Authorized) I've read through many posts on the Tumblr support forum as well as here on Stack Overflow, but was unable to find a solution.
I'm reluctant to include the Jumblr library because I'm trying to keep my app as lean as possible. That said, I reviewed the Jumblr code and decided to mimic how photo posts are sent (https://github.com/tumblr/jumblr/blob/master/src/main/java/com/tumblr/jumblr/request/MultipartConverter.java). I'm still receiving the exact same error.
Below is an example my multipart POST request and the response I receive. I've replace the blog name, and OAuth signature, consumer key, and token variables, and have removed the binary image data for brevity sake. Everything else is untouched. I have a few questions...
Are there any other variables that should be included in the
multipart section? A Stack Overflow user stated that placing the
"oauth_" signature variables in there fixed his problem. I didn't
have success with this, but maybe there was something I was missing.
The Jumblr app doesn't appear to do any encoding of the image data,
although the Tumblr documentation states that it should be URL
encoded. Right now I'm sending it as the Jumblr app appears to (raw
binary). Is this correct?
Does anything else in my request look
incorrect?
REQUEST:
NOTE: I learned that the OAuth signature should be generated WITHOUT the multipart form. My code takes that into account when building this request!
POST http://api.tumblr.com/v2/blog/**REMOVED**.tumblr.com/post HTTP/1.1
Content-Type: multipart/form-data, boundary=cbe6b79db1b3cbe6b79e104e
Authorization: OAuth oauth_signature="**REMOVED**", oauth_version="1.0", oauth_nonce="3181201716", oauth_signature_method="HMAC-SHA1", oauth_consumer_key="**REMOVED**", oauth_timestamp="1388791537", oauth_token="**REMOVED**"
Content-Length: 1001
User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.3; SM-N900T Build/JSS15J)
Host: api.tumblr.com
Connection: Keep-Alive
Accept-Encoding: gzip
--cbe6b79db1b3cbe6b79e104e
Content-Disposition: form-data; name="type"
photo
--cbe6b79db1b3cbe6b79e104e
Content-Disposition: form-data; name="caption"
Another pic test...
--cbe6b79db1b3cbe6b79e104e
Content-Disposition: form-data; name="data[0]"; filename="postr_media_file_1388791537-1709648435.jpg"
Content-Type: image/jpeg
---- BINARY DATA REMOVED FOR BREVITY ----
RESPONSE:
HTTP/1.1 401 Not Authorized
Server: nginx
Date: Fri, 03 Jan 2014 23:25:39 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Connection: close
Set-Cookie: tmgioct=52c746f34266840643527780; expires=Mon, 01-Jan-2024 23:25:39 GMT; path=/; httponly
P3P: CP="ALL ADM DEV PSAi COM OUR OTRo STP IND ONL"
3c
{"meta":{"status":401,"msg":"Not Authorized"},"response":[]}
I posted the answer in the "Tumblr API Discussion" Google Group. This is what I did:
The key to doing it correctly is NOT just signing without the multipart form!!! Here are the steps...
Add all fields EXCEPT the data field as regular url encoded POST
body variables
Sign the request
Remove ALL off the post variables you just added from the request
Add the multipart form, including the data field this time
Some things to consider...
The Content-Type in the header should be "multipart/form-data"
The Content-Disposition of all form parts should be "form-data" and, of course, include a valid "name" attribute (ie. type, caption, etc...)
The Content-Disposition of the data part should also include a "filename" attribute
The only form part that should contain a Content-Type is data, and it should be set to the mime type of the file you are uploading (ie. "image/jpeg")
I used "data[0]" as the name of the data field. I haven't tested this with just "data", but according to everything I've read it should work that way as well. If you are creating a photo set, I believe you simple add additional parts (ie. data1. data[2], etc...). Again, I haven't tested anything except "data[0]", so do your due diligence!!!
I did NOT encode the binary image data!!! I saw people spending considerable amount of time on this in other posts when adding the image as a POST body variable. If doing this as a multipart form, you can skip the encoding and send raw binary data! ;-)
I hope this helps someone! I've spent two weeks banging my head on random solid objects trying to figure this out. The implementation is very easy to do, but there is zero documentation available on how exactly to build POST requests for photos properly. The official docs really should include that. If I had know what I just posted above I could have completed this in minutes instead of weeks!!!
The last request I posted earlier is still valid, but here it is again. Just remember what I mentioned about the signature!!!
REQUEST:
POST http://api.tumblr.com/v2/blog/REMOVED.tumblr.com/post HTTP/1.1
Content-Type: multipart/form-data, boundary=c60f7c041c02c60f7c046e9b
Authorization: OAuth oauth_signature="***REMOVED***", oauth_version="1.0", oauth_nonce="315351812", oauth_signature_method="HMAC-SHA1", oauth_consumer_key="***REMOVED***", oauth_timestamp="1388785116", oauth_token="***REMOVED***"
Content-Length: 1001
User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.3; SM-N900T Build/JSS15J)
Host: api.tumblr.com
Connection: Keep-Alive
Accept-Encoding: gzip
--c60f7c041c02c60f7c046e9b
Content-Disposition: form-data; name="type"
photo
--c60f7c041c02c60f7c046e9b
Content-Disposition: form-data; name="caption"
Another pic test...
--c60f7c041c02c60f7c046e9b
Content-Disposition: form-data; name="data[0]"; filename="postr_media_file_1388785116-1709648435.jpg"
Content-Type: image/jpeg
***** BINARY DATA REMOVED FOR BREVITY *****
--c60f7c041c02c60f7c046e9b--