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