Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tomcat Filter adversely affecting Request Input Stream (input stream empty)

I've added a filter to my application which simply logs certain things about a request. Some of my servlets read from ServletRequest#getInputStream. Since adding this filter, those servlets that read from ServletRequest#getInputStream no longer work as the input stream is empty. Disabling the filter by simply commenting it out from my web.xml resolves the issue.

Why is this happening and is there a way to use a filter without it messing up the ServletRequest#getInputStream?

The filter is actually Tomcat's RequestDumperFilter included in one of its example web apps. I'll only include the doFilter method as that is the important part. If you want to see the entire thing, I've put it up on PasteBin.

/**
 * Time the processing that is performed by all subsequent filters in the
 * current filter stack, including the ultimately invoked servlet.
 *
 * @param request The servlet request we are processing
 * @param result The servlet response we are creating
 * @param chain The filter chain we are processing
 *
 * @exception IOException if an input/output error occurs
 * @exception ServletException if a servlet error occurs
 */
public void doFilter(ServletRequest request, ServletResponse response,
                     FilterChain chain)
throws IOException, ServletException {

    if (filterConfig == null)
    return;

// Render the generic servlet request properties
StringWriter sw = new StringWriter();
PrintWriter writer = new PrintWriter(sw);
writer.println("Request Received at " +
           (new Timestamp(System.currentTimeMillis())));
writer.println(" characterEncoding=" + request.getCharacterEncoding());
writer.println("     contentLength=" + request.getContentLength());
writer.println("       contentType=" + request.getContentType());
writer.println("            locale=" + request.getLocale());
writer.print("           locales=");
Enumeration locales = request.getLocales();
boolean first = true;
while (locales.hasMoreElements()) {
    Locale locale = (Locale) locales.nextElement();
    if (first)
        first = false;
    else
        writer.print(", ");
    writer.print(locale.toString());
}
writer.println();
Enumeration names = request.getParameterNames();
while (names.hasMoreElements()) {
    String name = (String) names.nextElement();
    writer.print("         parameter=" + name + "=");
    String values[] = request.getParameterValues(name);
    for (int i = 0; i < values.length; i++) {
        if (i > 0)
        writer.print(", ");
    writer.print(values[i]);
    }
    writer.println();
}
writer.println("          protocol=" + request.getProtocol());
writer.println("        remoteAddr=" + request.getRemoteAddr());
writer.println("        remoteHost=" + request.getRemoteHost());
writer.println("            scheme=" + request.getScheme());
writer.println("        serverName=" + request.getServerName());
writer.println("        serverPort=" + request.getServerPort());
writer.println("          isSecure=" + request.isSecure());

// Render the HTTP servlet request properties
if (request instanceof HttpServletRequest) {
    writer.println("---------------------------------------------");
    HttpServletRequest hrequest = (HttpServletRequest) request;
    writer.println("       contextPath=" + hrequest.getContextPath());
    Cookie cookies[] = hrequest.getCookies();
        if (cookies == null)
            cookies = new Cookie[0];
    for (int i = 0; i < cookies.length; i++) {
        writer.println("            cookie=" + cookies[i].getName() +
               "=" + cookies[i].getValue());
    }
    names = hrequest.getHeaderNames();
    while (names.hasMoreElements()) {
        String name = (String) names.nextElement();
    String value = hrequest.getHeader(name);
        writer.println("            header=" + name + "=" + value);
    }
    writer.println("            method=" + hrequest.getMethod());
    writer.println("          pathInfo=" + hrequest.getPathInfo());
    writer.println("       queryString=" + hrequest.getQueryString());
    writer.println("        remoteUser=" + hrequest.getRemoteUser());
    writer.println("requestedSessionId=" +
           hrequest.getRequestedSessionId());
    writer.println("        requestURI=" + hrequest.getRequestURI());
    writer.println("       servletPath=" + hrequest.getServletPath());
}
writer.println("=============================================");

// Log the resulting string
writer.flush();
filterConfig.getServletContext().log(sw.getBuffer().toString());

// Pass control on to the next filter
    chain.doFilter(request, response);

}

Conclusion

From what I've read by Googling, any of the following methods will render getInputStream empty if called first:

  • getParameter
  • getParameterNames
  • getParameterValues
  • getParameterMap

Thanks SimoneGianni for pointing me in the right direction:

Here are some sources

  • http://www.xyzws.com/Servletfaq/what-is-the-getinputstream-of-servletrequest-for/23
  • http://davidhuo.blogspot.com/2006/05/be-careful-with-httpservletrequestgeti.html
  • https://issues.apache.org/bugzilla/show_bug.cgi?id=47410

This person actually had a similar issue and created his own wrapper class as a work-around.

  • Modify HttpServletRequest body
like image 290
John Avatar asked Jul 21 '11 18:07

John


1 Answers

If you call getParameters, getParameterNames and similar methods, you COULD interfere with getInputStream or getReader. This isn't stated clearly enough in servlet documentation, but there are some warnings about the opposite (getInputStream interfering with getParameter) in the official servlet javadocs (since 1.3, see http://download.oracle.com/javaee/1.3/api/javax/servlet/ServletRequest.html#getParameter(java.lang.String) )

Are you seeing this problem on POSTs? Since POST requests encode parameters as request body, to read the parameters you actually have to consume (the container does this for you) the input stream.

like image 146
Simone Gianni Avatar answered Nov 15 '22 08:11

Simone Gianni