javaspringspring-boothttprequestspring-filter

HttpMessageNotReadableException: Could not read document: Stream closed;


I am working with Spring boot and filtering the requests using filters. The filter is being used to have check for the user verification which is reading some data from the request body (for which i have used the HttpServletRequestWrapper implementation). From the requestWrapper i am getting the data as expected and my services are too working fine in filter. But, when on success, the filter passes the request to the main controller, i am getting a stream closed exception as follows:

   o.s.w.s.m.s.DefaultHandlerExceptionResolver - Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: Stream closed; nested exception is java.io.IOException: Stream closed

Here is the Filter class:

@WebFilter(urlPatterns = {"/getDocs" })
public class AuthenticationFilter implements Filter{

    private static Logger logger = Logger.getLogger(AuthenticationFilter.class);

    @Autowired
    private UserVerificationService userVerificationService;

    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }

    @Override
    public void doFilter(ServletRequest arg0, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        logger.info("checking token in filter");
        HttpServletRequest request = (HttpServletRequest) arg0;
        DocVerificationRequestWrapper myRequestWrapper = new DocVerificationRequestWrapper((HttpServletRequest) request);
        String body = myRequestWrapper.getBody();
        Token token = null;
        try {
            JSONObject jsonObj = new JSONObject(body);
            JSONObject tokenObj = (JSONObject) jsonObj.get("token");
            Gson gson = new Gson();
            token = gson.fromJson(tokenObj.toString(), Token.class);

            if(null != token) {
                    if(userVerificationService==null){
                    ServletContext servletContext = request.getServletContext();
                    WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
                    userVerificationService = webApplicationContext.getBean(UserVerificationService.class);
                }
                    String verStatus = userVerificationService.verifyUser(token);
                    logger.info("verStatus = "+verStatus);
                    if(verStatus != null && verStatus.equalsIgnoreCase("success")) {
                        chain.doFilter(request, response);  
                    }else
                        logger.error("Invalid token");
            }else {
                    logger.error("token missing.");
            }
        } catch (JSONException e) {
                logger.error("exception in authetication filter " + e);
        }
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        // TODO Auto-generated method stub

    }

}

and here is the HttpServletRequestWrapper implementation class:

public class DocVerificationRequestWrapper extends HttpServletRequestWrapper {
 private final String body;
 public DocVerificationRequestWrapper(HttpServletRequest request) throws IOException {
   super(request);
   StringBuilder stringBuilder = new StringBuilder();
   BufferedReader bufferedReader = null;
   try {
     InputStream inputStream = request.getInputStream();
     if (inputStream != null) {
       bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
       char[] charBuffer = new char[128];
       int bytesRead = -1;
       while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
         stringBuilder.append(charBuffer, 0, bytesRead);
       }
     } else {
       stringBuilder.append("");
     }
   } catch (IOException ex) {
       throw ex;
   } finally {
     if (bufferedReader != null) {
       try {
         bufferedReader.close();
       } catch (IOException ex) {
         throw ex;
       }
     }
   }
   body = stringBuilder.toString();
 }

 @Override
 public ServletInputStream getInputStream() throws IOException {
   final ByteArrayInputStream byteArrayInputStream = new     ByteArrayInputStream(body.getBytes());
   ServletInputStream servletInputStream = new ServletInputStream() {
     public int read() throws IOException {
       return byteArrayInputStream.read();
     }

    @Override
    public boolean isFinished() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean isReady() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void setReadListener(ReadListener listener) {
        // TODO Auto-generated method stub

    }
   };
   return servletInputStream;
 }

 @Override
 public BufferedReader getReader() throws IOException {
   return new BufferedReader(new InputStreamReader(this.getInputStream()));
 }

 public String getBody() {
   return this.body;
 }
}

Any suggestion, how to resolve this error?


Solution

  • I guess that is because instead of passing your actual wrapper to the chain, you're passing original request. Try this:

    if(verStatus != null && verStatus.equalsIgnoreCase("success")) {
        chain.doFilter(myRequestWrapper, response);  
    }
    

    Obviously, at the time when ObjectMapper trying to read request body - request input stream already closed since it has been read already by your wrapper.