HttpClient: Determine empty entity in response - java

I'm wondering how to determine an empty http response.
With empty http response I mean, that the http response will only have set some headers, but contains an empty http body.
For example: I do a HTTP POST to an webserver, but the webserver will only return an status code for my HTTP POST and nothing else.
The problem is, that I have written a little http framework on top of apache HttpClient to do auto json parsing etc. So the default use case of this framework is to make a request and parse the response. However if the response does not contain data, like mentioned in the example above, I will ensure that my framework skip json parsing.
So I do something like this:
HttpResponse response = httpClient.execute(uriRequest);
HttpEntity entity = response.getEntity();
if (entity != null){
InputStream in = entity.getContent();
// json parsing
}
However entity is always != null. And also the retrieved inputstream is != null. Is there a simple way to determine if the http body is empty or not?
The only way I see is that the server response contains the Content-Length header field set to 0.
But not every server set this field.
Any suggestions?

In HttpClient, getEntity() can return null. See the latest samples.
However, there's a difference between an empty entity, and no entity. Sounds like you've got an empty entity. (Sorry to be pedantic -- it's just that HTTP is pedantic. :) With respect to detecting empty entities, have you tried reading from the entity input stream? If the response is an empty entity, you should get an immediate EOF.
Do you need to determine if the entity is empty without reading any bytes from the entity body? Based on the code above, I don't think you do. If that's the case, you can just wrap the entity InputStream with a PushbackInputStream and check:
HttpResponse response = httpClient.execute(uriRequest);
HttpEntity entity = response.getEntity();
if(entity != null) {
InputStream in = new PushbackInputStream(entity.getContent());
try {
int firstByte=in.read();
if(firstByte != -1) {
in.unread(firstByte);
// json parsing
}
else {
// empty
}
}
finally {
// Don't close so we can reuse the connection
EntityUtils.consumeQuietly(entity);
// Or, if you're sure you won't re-use the connection
in.close();
}
}
It's best not to read the entire response into memory just in case it's large. This solution will test for emptiness using constant memory (4 bytes :).
EDIT: <pedantry> In HTTP, if a request has no Content-Length header, then there should be a Transfer-Encoding: chunked header. If there is no Transfer-Encoding: chunked header either, then you should have no entity as opposed to an empty entity. </pedantry>

I would suggest to use the class EntityUtils to get the response as String. If it returns the empty string, then the response is empty.
String resp = EntityUtils.toString(client.execute(uriRequest).getEntity())
if (resp == null || "".equals(resp)) {
// no entity or empty entity
} else {
// got something
JSON.parse(resp);
}
The assumption here is that, for sake of code simplicity and manutenibility, you don't care to distinguish between empty entity and no entity, and that if there is a response, you need to read it anyway.

Related

Unable to read application/json message in Response output

I'm testing REST API and while I make GET call to retrieve resources, it's resulting into 500 Internal Server Error and in output it's returning message which has media type application/json:
[
{
"messageType": "Some error type",
"messageText": "Some message text",
"moreInfo": "Some info"
}
]
Please make note that in above output, Json is inside []
I want to read value of messageText from above output response. I tried with -
JsonObject jsonObject = response.readEntity(JsonObject.class);
but it results in following error:
java.lang.IllegalStateException: Entity input stream has already been closed.
at org.glassfish.jersey.message.internal.EntityInputStream.ensureNotClosed(EntityInputStream.java:225)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:830)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:783)
at org.glassfish.jersey.client.ClientResponse.readEntity(ClientResponse.java:326)
at org.glassfish.jersey.client.InboundJaxrsResponse$1.call(InboundJaxrsResponse.java:111)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:399)
at org.glassfish.jersey.client.InboundJaxrsResponse.readEntity(InboundJaxrsResponse.java:108)
Could you please help me how can I read the message in output? I'm using Jersy library.
According to javaDoc, a call to readEntity closes the response entity, so when you make another readEntity call you get IllegalStateException.
Unless the supplied entity type is an input stream, this method
automatically closes the an unconsumed original response entity data
stream if open.
In my case, having the expression response.readEntity(String.class)
in the Expressions pane in the Debug perspective caused this exception when I ran the code in Debug mode. The evaluation of the expression consumed the entity and caused it to close.
I solved this by first doing the readEntity to a String entity and then using the Jackson ObjectMapper to actually deserialize to the target class.
Problematic code:
Transactions transObj = response.readEntity(Transactions.class);
Solution:
String stringEntity = response.readEntity(String.class);
Transactions transObj = objectMapper.readValue(stringEntity, Transactions.class);
It seems this problem arises when the JSON string in the response entity stream is very long or complex possibly requiring multiple interactions thereon. 'Deserializing' to a string seems to only require a single bite. Once you have the string (and Jackson or GSON) de-serialization to target entity takes place without touching the response.
Actually it is a issue with how are we defining the reference of Response object .
Solution is weird
Not Working one :
Response response;
if (condition) {
response =
} else {
response =
}
String resp = response.readEntity(String.class);
if (response.getStatus() == 200) {}
Working One
Response response = null;
if (condition) {
response =
} else {
response =
}
String resp = response.readEntity(String.class);
if (response.getStatus() == 200) {}
So basically if we wont assign anything to response initially , Stream will be closed
When you get your error 500 you probably get another exception in the console/log. You should start checking that and then trying to resolve this one. If you don't find anything in the log, could you please post more code?

Jersey Client 2.19 doesn't throw exceptions on bad status code

I'm building a Rest Client using jersey-client 2.19:
public ReleaseEntity createRelease(ReleaseEntity newRelease, int workspaceId) {
Releases wrapper = new Releases();
wrapper.setData(Arrays.asList(newRelease));
WebTarget target = client.target(urlPrefix)
.path(AgmUrls.getReleasesUrl(workspaceId));
wrapper = target
.request()
.accept(MediaType.APPLICATION_JSON)
.post(Entity.entity(wrapper, MediaType.APPLICATION_JSON))
.readEntity(Releases.class);
return wrapper.getData().get(0);
}
The client is initialized in the constructor
this.client = ClientBuilder.newClient();
The problem is that, in case of bad response the post call does not throw an exception, neither explicit nor runtime.
Should I do this manually, or am I missing something?
This question is quite dated, but better prevent others to repeat the same mistake...
Instead of
result = target
.request()
.accept(MediaType.APPLICATION_JSON)
.post(Entity.entity(input, MediaType.APPLICATION_JSON))
.readEntity(Releases.class);
which has post(entity) return a Response on which readEntity is called, better use overloaded post(entity, responseType) which will throw WebApplicationException on Error-Statuscodes.
// throws runtime exception derived from WebApplicationException
// on error-statuscodes
result = target
.request()
.accept(MediaType.APPLICATION_JSON)
.post(Entity.entity(input, MediaType.APPLICATION_JSON), Releases.class);
Every http method in JAX-RS has such overloaded methods for reading either Responses or representation objects. Reading representation objects is highly advised to consume potential response bodies in any case.
// consumes response as string and guarantees to close the http call.
// A no-arg delete(); would be a potential leak!
target.request().delete(String.class);
Unfortunately, when response-headers must be read, it is still required to read Response instead of the representation objects.
The framework should not throw an exception. The user should handle the response however they see fit. This is the same with any client. The Response object will contain all the context you need to handle the response however you see fit.
So what you should do is get the Response first
Response response = target
.request()
.accept(MediaType.APPLICATION_JSON)
.post(Entity.entity(wrapper, MediaType.APPLICATION_JSON));
Then you can check the status
int status = response.getStatus();
Then handle the status
if (status == 200) {
wrapper = response.readEntity(Releases.class);
...
} else {
handleOtherStatus();
}
If you do not get the Response first, then you have no idea what the actual problem is, as readEntity(...) will fail (as there it's not the body you are expecting), and throw a different exception. With the Response at least you have some context if you want to tell the user what actual problem is.

How to receive raw data returned from http connection upon error

Most importantly I want to get the exceptionMessage that I am able to view as part of the data returned when I use SoapUI to make the request. I don't see anything in HttpURLConnection that includes this detailed information. Only responseCode and responseMessage, which are nice, but are lacking the description I'm looking for.
Also, does SoapUI parse this raw data into JSON and XML itself, or is there a simple way I can get it as JSON through java as well?
Thanks
The server returns HTTP headers and in most cases a body. To get the body in case of a error, you have to do something like that:
InputStream is;
if (conn.getResponseCode() / 100 == 2) { // HTTP status code 2xx, e.g. 200
is = conn.getInputStream();
// read input stream -> this is the content you wanted
} else {
is = conn.getErrorStream();
// read input stream -> contains a description of the error
// depending on header "Content-Type" you can also parse the stream
// as JSON or XML or HTML ...
}

HTTP Get: Only download the header? (HEAD is not supported)

In my code I use some Http Get request to download some files as a stream. I use the following code:
public String getClassName(String url) throws ClientProtocolException, IOException {
HttpResponse response = sendGetRequestJsonText(url);
Header[] all = response.getAllHeaders();
for (Header h : all) {
System.out.println(h.getName() + ": " + h.getValue());
}
Header[] headers = response.getHeaders("Content-Disposition");
InputStreamParser.convertStreamToString(response.getEntity().getContent());
String result = "";
for (Header header : headers) {
result = header.getValue();
}
return result.substring(result.indexOf("''") + "''".length(), result.length()).trim();
}
But this downloads the full content of the response. I want to retrieve only the http headers without the content. A HEAD request seems not to work because then i get the status 501, not implemented. How can I do that?
Instead of making a GET request, you might consider just making a HEAD request:
The HEAD method is identical to GET except that the server MUST NOT
return a message-body in the response. The metainformation contained
in the HTTP headers in response to a HEAD request SHOULD be identical
to the information sent in response to a GET request. This method can
be used for obtaining metainformation about the entity implied by the
request without transferring the entity-body itself. This method is
often used for testing hypertext links for validity, accessibility,
and recent modification.
You might be able to use the Range header in your request to specify a range of bytes to include in the response entity. Possibly something like:
Range: bytes=0-0
If it does work, you should receive back a 206 Partial Content with the bytes specified in your Range header present in the response entity. However, I've not tried this, and it's also not guaranteed to work:
A server MAY ignore the Range header.

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