Question on HttpResponse object in servlets. Can the contents of a HttpResponse be only read once?
If so do I need to user a filter and some form of "javax.servlet.http.HttpServletResponseWrapper" in order to read the content of a HttpResponse object as I need to read its content to retrieve XML/JSON from the response? At the moment Im getting the below exception when I go to read the HttpResponse object.
Content has been consumed
at org.apache.http.entity.BasicHttpEntity.getContent(BasicHttpEntity.java:84)
Thanks,
John
This is not a problem in the server/servlet side. It's a problem in the client side. The servlet doesn't send HttpServletResponse object to the client or something, it just sends a byte stream only once. You just need to read it only once into a reuseable object such as a byte[] or String, depending on the actual content and and then reuse/copy exactly this object in the remnant of the code.
InputStream input = httpResponse.getEntity().getContent();
ByteArrayOutputStream output = new ByteArrayOutputStream(); // Or some file?
IOUtils.copy(input, output);
byte[] content = output.toByteArray();
// Now you can reuse content as many times as you want.
Do you want to read the content of the response or request? Usually we write the content of the response and do not read it, unless you have an special case here.
Related
Im currently writing a servlet that should allow the user to write custom rest endpoints. The endpoints produce a RestResponse object which i am trying to convert into the final HTTPServletResponse Object.
The RestResponse contains a org.json.simple.JSONObject representing the Response Body. I now need to get this Object into the Body of of the HTTP Response.
My Idea is to use the PrintWriter of the HTTPServletResponse, According to the Debugger the entire JSONObject ends Up in the CharBuffer of the PrintWriter as it should, however in the final HTTP Response Body inside my Browser there is only the first character.
rest is my RestResponse object and http is my HTTPServletResponse Object (coming unchanged since handed into the doGet Method of the servlet)
I tried to use various different methods like:
if (rest.hasBody()) {
//TODO this somehow fails to write a valid JSON String to the HTTP Body
PrintWriter writer = http.getWriter();
rest.getBody().writeJSONString(writer);
//Here the proper json string ends in the buffer of the writer
writer.flush();
writer.close();
}
if (rest.hasBody()) {
//TODO this somehow fails to write a valid JSON String to the HTTP Body
PrintWriter writer = http.getWriter();
writer.write(rest.getBody.toJSONString());
//Here the proper json string ends in the buffer of the writer
writer.flush();
writer.close();
}
if (rest.hasBody()) {
//TODO this somehow fails to write a valid JSON String to the HTTP Body
PrintWriter writer = http.getWriter();
writer.append(rest.getBody.toJSONString());
//Here the proper json string ends in the buffer of the writer
writer.flush();
writer.close();
}
and so on, everything gives the same result
I've been debugging this for several hours now and i still didn't figure out whats wrong, does anybody have an idea?
Cheers
So my issue was that somehow i set http.setContentlength(variable) where variable accidentally was 1, therefor apparently the body will only contain the first character.
First of all, I'm not sure whether this could be possible or not. I'm flushing bytes of data as PDF to the browser. Now the requirement is, I want to generate the pdf and also send one more extra object to be sent . Is it possible?
I've written something like this, but the result object is not getting as response.
YBUtil.GeneratePdf(response,documentBytes, "Bureau");
result.setStatus("SUCCESS");
return result; --> I want to pass this object as well
GeneratePdf method
public static void GeneratePdf(HttpServletResponse response, byte[] documentBytes, String fileName){
response.setHeader("Content-Disposition", "inline;filename="+fileName+".pdf");
response.setContentType("application/pdf");
response.setHeader("Expires", "0");
response.setHeader("Cache-Control", "must-revalidate, postcheck=0, pre-check=0");
response.setHeader("Pragma", "public");
response.setContentLength(documentBytes.length);
ServletOutputStream out = null;
try {
out = response.getOutputStream();
out.write(documentBytes);
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
In principle, this is more about the HTTP protocol than about Java.
HTTP is designed to send requests, with one optional request body sent along, and to receive a response in reaction, with one optional response body sent along. One. Not more than that.
When dealing with typical text stuff, you can send/respond a text-like format such as XML, JSON or web forms, that contain all the stuff you want it to contain. But when you want to receive/send a file, it's binary stuff and it must be sent as-is, alongside metadata that tell the file's type and name.
Now when you want to send/receive more than just a file, it looks like you're stuck. Well no. Look up multipart/form-data and realize you can use something similar for an HTTP response. Just like an email would.
Java can be programmed to respond with a multipart response. However, it is a bit of work to program, and I haven't really found an effective library that will success in helping me do it.
I have done this by sending a DTO object that will contain bytes(for pdf, this parsing of pdf is done on client side) and other values added to the DTO which are necessary.
I'm using the Apache HttpClient 4.2.3 in my application. We store the response of an HTTP call like so:
HttpResponse httpResponse = (DefaultHttpClient)httpClient.execute(httpRequest);
The response body is an InputStream in the 4.x API:
InputStream responseStream = httpResponse.getEntity().getContent();
My problem is I need to read the response body as a string and as a byte[] at various points in the application. But the InputStream used by Apache is an EofSensorInputStream, which means once I reach the stream EOF, it gets closed. Is there anyway I can get the string and byte[] representations multiple times and not close the stream?
I've already tried wrapping the byte array in a new ByteArrayInputStream and setting that as the request body, but it doesn't work since my response body can reach a few gigs. I've also tried this, but I noticed the original response stream still gets closed.
Any pointers would be welcome.
EDIT: On a related note, it would be also be great if I could find the length of the InputStream either without consuming the stream or by reversing the consumption.
1 . I think you have somewhat conflicting requirements:
a)
it doesn't work since my response body can reach a few gigs
b)
Is there anyway I can get the string and byte[] representations multiple times and not close the stream
If you do not have enough memory this is not possible.
Btw, another way to get the response as bytes is EntityUtils.byte[] toByteArray(final HttpEntity entity).
Do you really need N-gigs String? What are you going to do with it?
2 .
it would be also be great if I could find the length of the InputStream
httpResponse.getEntity().getContentLength()
3 . Since the response does not fit into the memory I would suggest to save it into a file (or temp file). Then set up InputStream on that file, and then read it as many times as you need.
I am trying to make a Http POST request using apache HTTP client. I am trying to copy contents of an HTTP POST request (received at my application) to another HTTP POST request (initiated from my application to another URL). Code is shown below:
httpPost = new HttpPost(inputURL);
// copy headers
for (Enumeration<String> e = request.getHeaderNames(); e.hasMoreElements();) {
String headerName = e.nextElement().toString();
httpPost.setHeader(headerName, request.getHeader(headerName));
}
BufferedInputStream clientToProxyBuf = new BufferedInputStream(request.getInputStream());
BasicHttpEntity basicHttpEntity = new BasicHttpEntity();
basicHttpEntity.setContent(clientToProxyBuf);
basicHttpEntity.setContentLength(clientToProxyBuf.available());
httpPost.setEntity(basicHttpEntity);
HttpResponse responseFromWeb = httpclient.execute(httpPost);
Basically, I am trying to implement a proxy application which will get a url as parameter, froward the request to the URL and then serve pages etc in custom look and feel.
Here request is HttpServletRequest. I am facing problem in setting content length. Through debugging I found out that clientToProxyBuf.available() is not giving me correct length of input stream and I am getting Http error 400 IE and Error 354 (net::ERR_CONTENT_LENGTH_MISMATCH): The server unexpectedly closed the connection in chrome.
Am I doing it wrong? Is there any other way to achieve it?
The available() function doesn't provide the actual length of the content of the stream, rather
Returns the number of bytes that can be read from this input stream without blocking. (From javadoc)
I would suggest you to first read the whole content from the stream, and then set that to the content, rather than passing the stream object. That way, you will also have the actual length of the content.
It was rather simple and very obvious. I just needed to get content length from header as:
basicHttpEntity.setContentLength(Integer.parseInt(request.getHeader("Content-Length")));
This is my code
URL url = new URL("http://172.16.32.160:8080/epramaan/loginotp");
URLConnection connection1 = url.openConnection();
connection1.setDoOutput(true);
ObjectOutputStream out=new ObjectOutputStream(connection1.getOutputStream());
out.writeObject(send);
out.flush();
out.close();
ObjectInputStream in = new ObjectInputStream(connection1.getInputStream());
String output=(String)in.readObject();
in.close();
//Rest of the code
Once the OutputStream writes data to the stream, will the object InputStream stop execution till the response is received?
I assume that by stop execution you mean block.
Just noticed that you are using readObject and not read. Please elaborate what kind of data you are reading/writing and why are u using object streams ?
As you mentioned you are using String, I would suggest to use method readFully(byte[] buf). This method blocks till all the bytes are read. Once you have the byte array, a String can be created from this byte array.
You can use InputStream.read(byte[]) for reading the entire byte array to memory (you can get the array length from the HTTP Content-Length header) and use URLConnection.setReadTimeout() for timing out if you are blocking for too long.
From the byte array you can construct your object, constructing your ObjectInputStream over a ByteArrayInputStream
Once the OutputStream writes data to the stream, will the object InputStream stop execution till the response is received?
Not precisely. Opening the InputStream doesn't block anything, and doesn't even cause the request headers to be sent. However, reading from the InputStream will do both of those things.
I suspect that the real cause of your problems is that you are getting an error response from the server that is something other than a serialized object; e.g. it could be a generic HTML error page from the server. Naturally, attempting to deserialize this fails.
The correct procedure is:
Create the URLConnection object.
Set any request headers you need to.
Connect it (or skip this ... it will happen implicitly).
Open and write to the OutputStream.
Close the OutputStream.
Use getResponseCode() to see if the request succeeded or failed.
If it succeeded, call getInputStream() and read and process the response.
If it failed, call getErrorStream() and process the error output.