Why is HttpServletRequest inputstream empty? - java

I have this code where I read the input from a request input stream and use a JacksonMapper to convert into a POJO. Its running in a jetty 7 container with guice support.
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
RequestType requestType = mapper.readValue(req.getInputStream(), RequestType.class);
} Catch(Exception ex) {
....
}
}
However, sometimes under load the following exception is thrown. I have checked my client and I am sure its sending a valid json string. What is going wrong? Is it expected behavior for Jetty 7 under load?
java.io.EOFException: No content to map to Object due to end of input
at org.codehaus.jackson.map.ObjectMapper._initForReading(ObjectMapper.java:2433)
at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2385)
at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1637)
at com.ea.wsop.user.LoginServlet.processRequest(LoginServlet.java:69)
at com.ea.wsop.user.LoginServlet.doPost(LoginServlet.java:63)
at com.ea.wsop.user.LoginServlet$$EnhancerByGuice$$a91c2ebd.CGLIB$doPost$0(<generated>)
at com.ea.wsop.user.LoginServlet$$EnhancerByGuice$$a91c2ebd$$FastClassByGuice$$c6f479ee.invoke(<generated>)
at com.google.inject.internal.cglib.proxy.$MethodProxy.invokeSuper(MethodProxy.java:228)
at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:72)
at com.ea.monitor.MethodExecutionTimer.invoke(MethodExecutionTimer.java:130)
at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:72)
at com.google.inject.internal.InterceptorStackCallback.intercept(InterceptorStackCallback.java:52)
at com.ea.wsop.user.LoginServlet$$EnhancerByGuice$$a91c2ebd.doPost(<generated>)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
at com.ea.wsop.user.LoginServlet$$EnhancerByGuice$$a91c2ebd.CGLIB$service$8(<generated>)
at com.ea.wsop.user.LoginServlet$$EnhancerByGuice$$a91c2ebd$$FastClassByGuice$$c6f479ee.invoke(<generated>)
at com.google.inject.internal.cglib.proxy.$MethodProxy.invokeSuper(MethodProxy.java:228)
at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:72)
at com.ea.monitor.MethodExecutionTimer.invoke(MethodExecutionTimer.java:130)
at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:72)
at com.google.inject.internal.InterceptorStackCallback.intercept(InterceptorStackCallback.java:52)
at com.ea.wsop.user.LoginServlet$$EnhancerByGuice$$a91c2ebd.service(<generated>)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at com.ea.wsop.user.LoginServlet$$EnhancerByGuice$$a91c2ebd.CGLIB$service$9(<generated>)
at com.ea.wsop.user.LoginServlet$$EnhancerByGuice$$a91c2ebd$$FastClassByGuice$$c6f479ee.invoke(<generated>)
at com.google.inject.internal.cglib.proxy.$MethodProxy.invokeSuper(MethodProxy.java:228)
at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:72)
at com.ea.monitor.MethodExecutionTimer.invoke(MethodExecutionTimer.java:130)
at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:72)
at com.google.inject.internal.InterceptorStackCallback.intercept(InterceptorStackCallback.java:52)
at com.ea.wsop.user.LoginServlet$$EnhancerByGuice$$a91c2ebd.service(<generated>)
at com.google.inject.servlet.ServletDefinition.doService(ServletDefinition.java:263)

I had a similar problem running a Spring Boot application. My Spring Boot app is a simple Dispatcher servlet that reads the request body and processes it.
In my case, the client (curl) sets a content-type header of application/x-www-form-urlencoded if the curl command line uses -d {some-data} and does not set an specific content-type header via -Hcontent-type=some-other-media-type.
Inside the Apache Catalina servlet engine that Spring Boot runs, the Request class makes the following test in parseParameters()
if (!("application/x-www-form-urlencoded".equals(contentType))) {
success = true;
return;
}
For other content-type values, Request returns here, done.
However, if the content type matches application/x-www-form-urlencoded, Request continues:
try {
if (readPostBody(formData, len) != len) {
parameters.setParseFailedReason(FailReason.REQUEST_BODY_INCOMPLETE);
return;
}
} catch (....)
which will consume the body. So in my case, even though my servlet does nothing other than call request.getInputStream() and try to read() from it, it is already too late - the runtime Request already reads the input and does not buffer or unread it. The only workaround is to set a different Content-Type.
The culprit is
OrderedHiddenHttpMethodFilter(HiddenHttpMethodFilter).doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain) line 70
which is looking for the "_method" query parameter.
I was able to disable the filter by adding
#Bean
public FilterRegistrationBean registration(HiddenHttpMethodFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
(which was used to solve another problem)

It will be empty if it's already consumed beforehand. This will be implicitly done whenever you call getParameter(), getParameterValues(), getParameterMap(), getReader(), etc on the HttpServletRequest. Make sure that you don't call any of those kind of methods which by themselves need to gather information from the request body before calling getInputStream(). If your servlet isn't doing that, then start checking the servlet filters which are mapped on the same URL pattern.
Update: this seems to be GAE 1.5 specific. See also
http://code.google.com/p/googleappengine/issues/detail?id=5161
http://code.google.com/p/googleappengine/issues/detail?id=5898
I'm afraid that there's no solution/workaround until they get it fixed. You could try to check if it's available inside a Filter and if so, then copy and store it as request attribute. But this might affect further processing by some GAE servlet.

I had the problem that my request InputStream was always empty with Jetty 6.1.15, and found out that it was caused by a missing or wrong "Content-Type" header.
I generate the requests in another Java program with HttpUrlConnection. When I did not set the Content-Type header explicitly, the InputStream returned by request.getInputStream() in the receiving program was always empty. When I set the content type to "binary/octet-stream", the InputStream of the request contained the correct data.
The only method that is called on the request object before getInputStream() is getContentLength().

I was using mod_jk 1.2.39 which had a bug that caused this issue. After updating to 1.2.40 it started working.

I've had this problem with a post. I solved it by FIRST reading the inputstream and putting it in a cache, before reading the parameters. That seemed to do the trick

Systematic approach is:
Get source code for your container, or at least it's web part (can be hard to find), import in your IDE.
Make break point in your code where before HttpServletRequest->getInputStream() is called.
Step into HttpServletRequest->getInputStream() method, now you are in some ...Impl class.
Set a new break point in that getInputStream() implemmentation, or even in its read() method.
Repeat test call and see what is consuming your data.

I ended up with the problem when enabling debug logging for org.springframework in a Spring Boot 2.2.1 project, and thus using spring-webmvc 5.2.1.
This is caused by the request logging of the parameter-map, which reads the input stream if the Content-Type is application/x-www-form-urlencoded. I believe this spring issue is related to it.
See the following code which causes the problem.
private void logRequest(HttpServletRequest request) {
LogFormatUtils.traceDebug(logger, traceOn -> {
String params;
if (isEnableLoggingRequestDetails()) {
params = request.getParameterMap().entrySet().stream()
.map(entry -> entry.getKey() + ":" + Arrays.toString(entry.getValue()))
.collect(Collectors.joining(", "));
}
else {
params = (request.getParameterMap().isEmpty() ? "" : "masked");
}
...
source
I ended up reporting an issue and and changing the content-type in the request instead.

Related

Due to incorrect request with special character % and ; Spring Framework throw full stake trace and tomcat is showing full stack trace in result [duplicate]

By default, Tomcat sends some HTML content back to the client if it encounters something like an HTTP 404. I know that via web.xml an <error-page> can be configured to customize this content.
However, I'd just like for Tomcat to not send anything in terms of response content (I'd still like the status code, of course). Is there any way to easily configure this?
I'm trying to avoid A) explicitly sending empty content on the response stream from my Servlet, and B) configuring custom error pages for a whole bunch of HTTP error statuses in my web.xml.
For some background, I'm developing an HTTP API and am controlling my own response content. So for an HTTP 500, for example, I'm populating some XML content on the response containing error information. For situations like an HTTP 404, the HTTP response status is sufficient for clients, and the content tomcat is sending is unnecessary. If there's a different approach, I'm open to hearing it.
Edit:
After continued investigation, I still can't find much in the way of a solution. If someone can definitively say this is not possible, or provide a resource with evidence that it will not work, I'll accept that as an answer and try and work around it.
If you do not want tomcat to show an error page, then do not use sendError(...). Instead use setStatus(...).
e.g. if you want to give a 405 response, then you do
response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
response.getWriter().println("The method " + request.getMethod() +
" is not supported by this service.");
Also remember not to throw any Exceptions from your servlet. Instead catch the Exception and, again, set the statusCode your self.
i.e.
protected void service(HttpServletRequest request,
HttpServletResponse response) throws IOException {
try {
// servlet code here, e.g. super.service(request, response);
} catch (Exception e) {
// log the error with a timestamp, show the timestamp to the user
long now = System.currentTimeMillis();
log("Exception " + now, e);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.getWriter().println("Guru meditation: " + now);
}
}
of course, if you do not want any content, then just don't write anything to the writer, just set the status.
Although this doesn't respond exactly to the "not send anything" statement on the question, and on the wave of Clive Evans' answer, I found out that in tomcat you can make those too much verbose texts go away from error pages without creating a custom ErrorReportValve.
You can accomplish to this customizing ErrorReportValve through the 2 params "showReport" and "showServerInfo" on your "server.xml":
<Valve className="org.apache.catalina.valves.ErrorReportValve" showReport="false" showServerInfo="false" />
Link to official documentation.
Worked for me on tomcat 7.0.55, didn't work for me on tomcat 7.0.47 (I think because of something reported on the following link http://www.mail-archive.com/users#tomcat.apache.org/msg113856.html)
The quick, slightly dirty, but easy way of stopping Tomcat from sending any error body is to call setErrorReportValveClass against the tomcat host, with a custom error report valve which overrides report to do nothing. ie:
public class SecureErrorReportValve extends ErrorReportValve {
#Override
protected void report(Request request,Response response,Throwable throwable) {
}
}
and set it with:
((StandardHost) tomcat.getHost()).setErrorReportValveClass(yourErrorValveClassName);
If you want to send your message, and just think Tomcat shouldn't mess with it, you want something along the lines of:
#Override
protected void report(final Request request, final Response response, final Throwable throwable) {
String message = response.getMessage();
if (message != null) {
try {
response.getWriter().print(message);
response.finishResponse();
} catch (IOException e) {
}
}
}
Although it's Servlet spec compliant, for security reasons I don't want tomcat or any other Servlet container to send error details. I struggled with this as well a bit. After searching and trying, the solution can be summed up as:
as others mentioned, don't use sendError(), use setStatus() instead (in Jersey framework you can choose)
frameworks like e.g. Spring Security use sendError() though...
write a Filter that
a. redirects calls to sendError() to setStatus()
b. flushes the response at the end to prevent the container from further modifying the response
A little example servlet filter doing this can be found here.
As Heikki said, setting the status instead of sendError() causes the Tomcat not touch the response entity/body/payload.
If you only want to send the response headers without any entity, like in my case,
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentLength(0);
does the trick. With Content-Length: 0, the print() will have no effect even if used, like:
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentLength(0);
response.getWriter().print("this string will be ignored due to the above line");
the client receives something like:
HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=utf-8
Content-Length: 0
Date: Wed, 28 Sep 2011 08:59:49 GMT
If you want to send some error message, use the setContentLength() with message length (other than zero) or you can leave it to the server
Though this question is a bit old, I ran into this problem too. First of all, Tomcat's behavior is absolutely correct. This is per Servlet Spec. One should not alter Tomcat's behavior against the spec. As Heikki Vesalainen and mrCoder mentioned, use setStatus and setStatus only.
To whom it may concern, I have raised a ticket with Tomcat to improve the docs of sendError.
Configure <error-page> Elements in web.xml
Edit $CATALINA_HOME/conf/web.xml, add at the end the following <error-page>, save and restart tomcat
<web-app>
...
...
...
<error-page>
<error-code>404</error-code>
<location>/404.html</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/500.html</location>
</error-page>
<error-page>
<error-code>400</error-code>
<location>/400.html</location>
</error-page>
</web-app>
It works great as I expect even though I didn't actually created a valid routes for those specified location values (e.g. /400.html)
before
after
Why not just configure the <error-page> element with an empty HTML page?

How to make a PUT request with Apache Olingo?

I am trying to transfer an entity via a HTTP PUT request using following code.
public ClientEntity createEntity(URI absoluteUri,
ClientEntity ce) {
ODataEntityCreateRequest<ClientEntity> request = client
.getCUDRequestFactory().getEntityCreateRequest(absoluteUri, ce);
request.setAccept("application/json;odata.metadata=minimal");
ODataEntityCreateResponse<ClientEntity> response = request.execute();
return response.getBody();
}
The function getEntityCreateRequest, however, only creates a POST request and allows (as far as I know) no alteration of the used HttpMethod.
Unfortunately, ODataEntityUpdateRequest is also not an option, because this request only allows the HttpMethod PATCH or REPLACE.
Within the documentation I have found a function setMethod(HttpMethod method), but this method is only available for the server not the client implementation (https://olingo.apache.org/javadoc/odata4/org/apache/olingo/server/api/ODataRequest.html).
Further I discovered setUseXHTTPMethod(boolean value), which tunnels PUT, MERGE, PATCH, DELETE via POST. I checked my client's configuration to make sure, that isUseXHTTPMethod is false, which it is. (Reference to functions: https://olingo.apache.org/javadoc/odata4/org/apache/olingo/client/api/Configuration.html)
Hence I am wondering how to make a PUT request with Apache Olingo?
Thank you very much for your input.
ODataEntityUpdateRequest with UpdateType.REPLACE should be equivalent to a PUT method.
Notice the source code, line 31.
Implement the updateEntity method.

Unable to read entire POST request body content formatted as application/json

I've been having an issue with Jetty processing application/json formatted request body data. Essentially, when the request body is processed by Jetty, the request data is cut off.
I have a relatively large POST body of around 74,000 bytes. As per some advice I found online, I instantiated a new context handler with the setMaxFormContentSize property set to a sufficiently large size of 500,000 bytes.
ServletContextHandler handler = new ServletContextHandler(server, "/");
handler.setMaxFormContentSize(500000);
However, this did not seem to work correctly. I also read online that this property might only work for form encoded data, not application/json, which is a strict requirement of our application.
Is there any way to circumvent this issue? Is there some special constraint class that I can subclass to allow the processing size to increase to at least 500KB?
Edit #1: I should add that I also tried to drop the size of the limit to 5 bytes to see if it would cut off more of the data in the payload. That also didn't working, which seems to imply that's definitely ignoring the property entirely.
Edit #2: Here is where I read the information from the request stream.
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
String json = CharStreams.toString(new InputStreamReader(req.getInputStream()));
....
} catch (Exception e) {
logger.error("Exception in internal api forwarder", e);
throw e;
}
}
This seems to a standard way of reading from a request stream. I also tried using a BufferedReader from req.getReader() with the same issue.
Vivek
What is this CharStreams object?
It doesn't seem to know, or care, or honor the request character encoding. (Bad idea)
Suggest that you use the servlet request.getReader() instead of request.getInputStream() (which is really only designed for binary request body content)
Using request.getReader() will at the very least support your request character encoding properly.
Another bit of information you might want to look into is request.getContentLength() and verify that the request headers does indeed contain the size you are expecting.

Java Servlet API - How can I set the response status and reason phrase without commiting the response

I am writing a REST application using Tomcat and Spring WebMVC.
I want to signal errors to my client using HTTP status codes along with some XML payload that contains more information about what went wrong.
To catch all errors regardless of where they occur, I have written a Filter which wraps the response and overrides the sendError() method:
private static final class GenericErrorResponseWrapper
extends HttpServletResponseWrapper
{
#Override
public void sendError(int sc, String msg) throws IOException {
final HttpServletResponse wrappedResponse = (HttpServletResponse) getResponse();
wrappedResponse.setStatus(sc, msg);
wrappedResponse.setContentType("application/xml");
PrintWriter writer = wrappedResponse.getWriter();
try {
SimpleXmlWriter xmlWriter = SimpleXmlWriterWrapper.newInstance(writer);
xmlWriter.writeStartElement("ns2", "genericError")
.writeAttribute("xmlns:ns2", "http://mynamespace")
.writeCharacters(msg)
.writeEndDocument().flush();
writer.flush();
wrappedResponse.flushBuffer();
} finally {
writer.close();
}
}
}
This implementation has two problems:
It generates a deprecation warning in Eclipse, since HttpServletResponse.setStatus(sc, msg) is deprecated.
The HTTP response header generated by Tomcat is not correct, it starts with the first line "HTTP/1.1 500 OK". 500 is correct, but instead of OK the reason phrase should be "Internal Server Error".
How can I implement my filter so that it does the right thing and is free of deprecation warnings? Both alternatives named in the Javadoc are not usable for me:
sendError(sc, msg) is not usable, since it commits the response body and I can't write XML payload any more
setStatus(sc) with just the error code is theoretically usable, but it also creates the hardcoded "OK" string in the first line of the response header.
There is unfortunately no way to avoid the deprecation warning. As you already mention yourself, the two alternatives which are referred to in the API documentation do not cover the same functionality. You may of course annotate your method with #SuppressWarnings("deprecation") to indicate that the usage of the deprecated method is intended.
The other thing, that Tomcat does not use your message string, even if one is provided, is a configuration issue. For some strange reason, Tomcat will by default ignore the provided message string and use a default error message based on the passed return code. You must set the system property org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER to true to force Tomcat to use your provided error message instead. More details on this can be found in the Tomcat documentation.
As an alternative answer - you could first write the XML payload, without calling flush/flushBuffer, and only after that do sendError(int, String), which would flush the buffer.

how to write a file object on server response and without saving file on server?

I am using Spring with DWR . I want to return a file object as response , however I save the file (to be sent) at server temporary location and then send its location as href for anchor tag on client side , however I wonder if there could be a way to throw the file directly to browser on response object without saving it temporarily on server.
I expected if there could be a way to send file as a response via DWR.
public ModelAndView writeFileContentInResponse(HttpServletRequest request, HttpServletResponse response) throws IOException {
FileInputStream inputStream = new FileInputStream("FileInputStreamDemo.java"); //read the file
response.setHeader("Content-Disposition","attachment; filename=test.txt");
try {
int c;
while ((c = inputStream.read()) != -1) {
response.getWriter().write(c);
}
} finally {
if (inputStream != null)
inputStream.close();
response.getWriter().close();
}
}
It has been years since I've used Spring, and I'm unfamiliar with DWR, but the essence of your question is basic to the web.
The answer is yes, you can. In effect, you need to set the HTTP header Content-Disposition: attachment, then stream down the contents. All of this will be in the response to the original request (as opposed to sending back a link).
The actual code to achieve this will depend on your circumstances, but this should get you started.
you call the method from Java Script, right? I didn't really understand how Spring is related in this flow, but as far as I know DWR allows you to produce Java Script Stubs and call the Java methods of the exposed bean directly on server right from your java script client code.
You can read the file byte-by-byte and return it from your java method as long as it really returns a byte array.
However what would you do with this byte array on client?
I just think in this specific flow you shouldn't use the DWR but rather issue an ordinar AJAX request (if DWR can wrap it somehow for convenience - great). This request shouldn't come to DWRServlet, but rather be proceeded by a regular servlet/some web-based framework, like Spring MVC :)
Once the request comes to the servlet, use
response.setHeader("Content-Disposition","attachment; filename=test.txt");
as was already stated.
Hope this helps,
Good luck!
Mark
An example which return a excel to download from client:
//Java side:
public FileTransfer getExcel(Parametros param){
byte[] result = <here get data>;
InputStream myInputStream = new ByteArrayInputStream(result);
String excelFormat = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
FileTransfer dwrExcelFile = new FileTransfer("excel.xlsx", excelFormat, myInputStream);
return dwrExcelFile;
}
//Javascript side:
function downloadExcelFile() {
dwr.engine.setTimeout(59000);
var params = <params_to_send>;
<Java_class>.getExcel(params, {callback:function(dataFromServer) {
downloadExcelCallback(dataFromServer);
}});
}
function downloadExcelCallback(data) {
dwr.engine.openInDownload(data);
}

Categories

Resources