Java writeBytes white space replaced by '+' - java

when trying to do a post request using HttpURLConnection, white spaces in the message got replaced by '+' and '=' is added at the end of the String.
I am using JDK 1.8.0_91, here is my code :
public void sendPost(String message) throws Exception {
String url = "http://localhost:8081/subscribe";
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
//add reuqest header
con.setRequestMethod("POST");
/*String urlParameters = "sn=C02G8416DRJM&cn=&locale=&caller=&num=12345";*/
// Send post request
con.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.writeBytes(message);
wr.flush();
wr.close();
}
sendPost("Say Hi") would give on server side : Say+Hi=

When you don't set the Content-Type request header (using setRequestProperty()), it defaults to application/x-www-form-urlencoded.
When a POST request has content type application/x-www-form-urlencoded, the ServletRequest.getParameter methods can be used to extract the parameters from the request body. This consumes the request body, so any subsequent call to getInputStream() or getReader() will return null.
When your Spring handler method asks for the request body with the #RequestBody annotation, and the body has already been consumed, Spring will rebuild the body from the parameters, in order to supply the method parameter (it is basically faking it). When Spring rebuilds the request body from the parsed parameters, it will URL encode them as required by the application/x-www-form-urlencoded content type. This encoding converts spaces to +.
The original request body with Say Hi was parsed as parameter "Say Hi" with value "" (parameter without = has empty value). When that is rebuilt in compliance with application/x-www-form-urlencoded, the parameter is encoded as Say+Hi=, which is what you're seeing.
This only happens if one of the ServletRequest.getParameter methods has been called, so you likely have a Filter that does that. If that filter was removed, the request body wouldn't have been consumed, and the #RequestBody parameter would have received the original request body.
Another way to prevent the problem is to send the request body as a text/plain content type, since that seems to be how you want it anyway, i.e. the request body is not actually x-www-form-urlencoded:
con.setRequestProperty("Content-Type", "text/plain; charset=ISO-8859-1");
Of course, if the request body truly is x-www-form-urlencoded content, like your commented code would suggest, then there shouldn't be any spaces, and there was no problem to begin with.
To recap: Your problem is that you're sending arbitrary text in the POST body, but you didn't set the content type, so it defaulted to application/x-www-form-urlencoded, and your content doesn't conform to the specifications of that content type, i.e. your request is malformed and behaved unpredictably.

Related

JAX-RS Jersey Read entity with Content-Type "*"

I am using Jax-RS to make a request to a server, which just returns a one word String, and read that response into a String variable. The issue is that I have no idea how to use the response, as its Content-Type is *; charset=UTF-8 (I verified this using Postman). Jax-RS has difficulty parsing this kind of header. Here is my code:
MultivaluedMap<String, String> formData = new MultivaluedHashMap<String, String>();
formData.add("username", username);
formData.add("target", "10");
Response response = target.request().accept(MediaType.APPLICATION_JSON_TYPE).post(Entity.form(formData));
String responseString = response.readEntity(String.class);
This POST request works. I get an actual Response that I can inspect. However, when I try to read this response into a String (last line of code), the following error is thrown:
org.glassfish.jersey.message.internal.HeaderValueException: Unable to parse "Content-Type" header value: "*; charset=UTF-8" ! at
org.glassfish.jersey.message.internal.InboundMessageContext.exception(InboundMessageContext.java:338) ! at
org.glassfish.jersey.message.internal.InboundMessageContext.singleHeader(InboundMessageContext.java:333) ! at
org.glassfish.jersey.message.internal.InboundMessageContext.getMediaType(InboundMessageContext.java:446) ! at
org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:869)
How do I make Jax-RS properly read this kind of Content-Type?!?
I do not think there is any way to get Jersey / Jax-RS to properly read that kind of Content-Type. A solution for any kind of Response that has a Content-Type that Jax-RS does not like is to simply remove the header and (if needed) add your own Content-Type header that is more appropriate for the Response. Do this BEFORE trying to read the Response entity. This fixed my issue:
response.getHeaders().remove("Content-Type");
response.getHeaders().add("Content-Type", "text/plain");
String responseString = response.readEntity(String.class);

Do I need to escape a string which is post parameters to pass in HTTP body?

I use HTTPUrlConnection to request a REST API.
I ser "Content-type" header as follows :
urlConnection.setRequestProperty("Content-type",
"application/x-www-form-urlencoded");
I set HTTP body as follows :
out = urlConnection.getOutputStream();
out.write(postParameters.getBytes("UTF-8"));
I don't know if I need to escape post parameters(which is a String) when I set HTTP header and body as shown above.
I just need Yes or No as answer, but would be great if the answer explains why yes or why no.
Since the data you are POSTing is to be interpreted as application/x-www-form-urlencoded, then it must have the form:
name1=value1&name2=value2&...
Therefore, the "value" parts MUST be URL-encoded, otherwise they will not be interpreted correctly.
Using POST with x-www-form-urlencoded is just moving the query-string part of the URL out of the request and into the body.

Getting RDF/XML web page using GET request with Accept header in Java

I want to send a GET requests that accept only results of type application/rdf+xml using the Accept: header. Is the following code right?
URLConnection connection = new URL(url + "?" + query).openConnection();
connection.setRequestProperty("Accept", "application/rdf+xml");
InputStream response = connection.getInputStream();
#gigadot nailed it, the Accept header is a suggestion to the server which the server is free to ignore.
If your application can only accept RDF/XML then you need to add logic to the receipt of the request to enforce this.
You can use the getContentType() method of a URLConnection to see what content type the server returned you and take an appropriate action (e.g. report an error) if it does not match your requirements.

Passing JSON data in get request as request body

Hi i have to send a get request to an url
http://onemoredemo.appspot.com/group?authToken=access_token&authMethod=oauth
with request body contains json object as shown below.
{"goupid":"some_variable"
}
Here is a section of java code for sending get request:
URL url1=new URL("http://onemoredemo.appspot.com/group?authToken="+access_token+"&authMethod=oauth");
conn=(HttpURLConnection) url1.openConnection();
conn.addRequestProperty("Content-type", "application/x-www-form-urlencoded");
conn.setRequestMethod("GET");
conn.setDoOutput(true);
JSONObject jj=new JSONObject();
HttpGet get;
get.
jj.put("groupid", "testing#iritesh.com");
conn.addRequestProperty("Content-TYpe", "application/json");
conn.getOutputStream().write(jj.toString().getBytes());
conn.connect();
InputStream is=conn.getInputStream();
I am getting an error java.io.FileNotFoundException.
I sent a request from mozilla browser to url
http://onemoredemo.appspot.com/group?authToken=ya29.AHES6ZRDl-RqiA8W0PhybU_hMluHrHRjlJBvq06Vze0izJq0Ovjc088&authMethod=oauth
It was giving me correct response but now its more than one hour so acccesstoken expire. I know its weird to send parameter as well as requestbody in get request but i have to send it.
Please help in how to send a json object in request body in get request.
Don't do it.
Read this:
http://tech.groups.yahoo.com/group/rest-discuss/message/9962
"Yes. In other words, any HTTP request message is allowed to contain a
message body, and thus must parse messages with that in mind. Server
semantics for GET, however, are restricted such that a body, if any,
has no semantic meaning to the request. The requirements on parsing
are separate from the requirements on method semantics.
So, yes, you can send a body with GET, and no, it is never useful to
do so.
This is part of the layered design of HTTP/1.1 that will become clear
again once the spec is partitioned (work in progress)."
For other interesting discussions on this check this:
https://stackoverflow.com/a/978094/550967
https://stackoverflow.com/a/978173/550967
https://stackoverflow.com/a/978519/550967
The body of a GET request is not read.
Have you tried adding it to the params:
http://onemoredemo.appspot.com/group?authToken=access_token&authMethod=oauth&goupid=some_variable

Problem reading request body in servlet

I'am writing a HTTP proxy that is part of a test/verification
system. The proxy filters all requests coming from the client device
and directs them towards various systems under test.
The proxy is implemented as a servlet where each request is forwarded
to the target system, it handles both GET and POST. Somtimes the
response from the target system is altered to fit various test
conditions, but that is not the part of the problem.
When forwarding a request, all headers are copied except for those
that is part of the actual HTTP transfer such as Content-Length and
Connection headers.
If the request is a HTTP POST, then the entity body of the request is
forwarded as well and here is where it doesnt work sometimes.
The code reading the entity body from the servlet request is the following:
URL url = new URL(targetURL);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
String method = request.getMethod();
java.util.Enumeration headers = request.getHeaderNames();
while(headers.hasMoreElements()) {
String headerName = (String)headers.nextElement();
String headerValue = request.getHeader(headerName);
if (...) { // do various adaptive stuff based on header
}
conn.setRequestProperty(headerName, headerValue);
}
// here is the part that fails
char postBody[] = new char[1024];
int len;
if(method.equals("POST")) {
logger.debug("guiProxy, handle post, read request body");
conn.setDoOutput(true);
BufferedReader br = request.getReader();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));
do {
logger.debug("Read request into buffer of size: " + postBody.length);
len = br.read(postBody, 0, postBody.length);
logger.debug("guiProxy, send request body, got " + len + " bytes from request");
if(len != -1) {
bw.write(postBody, 0, len);
}
} while(len != -1);
bw.close();
}
So what happends is that the first time a POST is received, -1
characters are read from the request reader, a wireshark trace shows
that the entity body containing URL encoded post parameters are there
and it is in one TCP segment so there is no network related
differences.
The second time, br.read successfully returns the 232 bytes in the
POST request entity body and every forthcoming request works as well.
The only difference between the first and forthcoming POST requests is
that in the first one, no cookies are present, but in the second one,
a cookie is present that maps to the JSESSION.
Can it be a side effect of entity body not being available since the
request processing in the servlet container allready has read the POST
parameters, but why does it work on forthcoming requests.
I believe that the solution is of course to ignore the entity body on
POST requests containing URL encoded data and fetch all parameters
from the servlet request instead using getParameter and reinsert them
int the outgoing request.
Allthough that is tricky since the POST request could contain GET
parameters, not in our application right now, but implementing it
correctly is some work.
So my question is basically: why do the reader from
request.getReader() return -1 when reading and an entity body is
present in the request, if the entity body is not available for
reading, then getReader should throw an illegal state exception. I
have also tried with InputStream using getInputStream() with the same
results.
All of this is tested on apache-tomcat-6.0.18.
So my question is basically: why do the reader from request.getReader() return -1 when reading.
It will return -1 when there is no body or when it has already been read. You cannot read it twice. Make sure that nothing before in the request/response chain has read it.
and an entity body is present in the request, if the entity body is not available for reading, then getReader should throw an illegal state exception.
It will only throw that when you have already called getInputStream() on the request before, not when it is not available.
I have also tried with InputStream using getInputStream() with the same results.
After all, I'd prefer streaming bytes than characters because you then don't need to take character encoding into account (which you aren't doing as far now, this may lead to future problems when you will get this all to work).
Seems, that moving
BufferedReader br = request.getReader()
before all operations, that read request (like request.getHeader() ), works for me well .

Categories