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.
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 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.
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)
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
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
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.