Details
-
Bug
-
Resolution: Unresolved
-
Minor
-
1.2.3
-
None
-
Spring Boot 2.2.4, Tomcat 9.0.30, Logback 1.2.3
TeeFilter registered like this:
{{ @Bean public FilterRegistrationBean<TeeFilter> requestResponseFilter2() {}}
FilterRegistrationBean<TeeFilter> filterRegBean = new FilterRegistrationBean<>();
filterRegBean.setFilter(new TeeFilter());
filterRegBean.setName("Logback access-logging request response filter");
return filterRegBean;
{{ }}}Spring Boot 2.2.4, Tomcat 9.0.30, Logback 1.2.3 TeeFilter registered like this: {{ @Bean public FilterRegistrationBean<TeeFilter> requestResponseFilter2() {}} FilterRegistrationBean<TeeFilter> filterRegBean = new FilterRegistrationBean<>(); filterRegBean.setFilter( new TeeFilter()); filterRegBean.setName( "Logback access-logging request response filter" ); return filterRegBean; {{ }}}
Description
TeeFilter registered like this:
@Bean public FilterRegistrationBean<TeeFilter> requestResponseFilter2() { FilterRegistrationBean<TeeFilter> filterRegBean = new FilterRegistrationBean<>(); filterRegBean.setFilter(new TeeFilter()); filterRegBean.setName("Logback access-logging request response filter"); return filterRegBean; }
Symptom
Webapp fails to process multipart HTTP requests with the following exception:
org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request; nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: Stream closed
Troubleshooting
- Multipart requests work when TeeFilter is disabled.
- Multipart requests work when Spring Boot is downgraded to 2.1.12 (Tomcat still 9.0.30, Logback still 1.2.3)
Root cause
`TeeFilter` decorates incoming request with `TeeHttpServletRequest` that reads the input stream and closes it (see `TeeServletInputStream#duplicateInputStream`). Everything works as expected here.
However, when `HttpServletRequest#getParts` is called later, `TeeHttpServletRequest` delegates it to the original request, which has its stream closed already, which results in the exception.
Workaround
Force loading of parts before request passes through `TeeFilter`. With Tomcat this is as simple as:
private static class MultipartSafeTeeFilter extends TeeFilter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { request.getParameter("whatever"); super.doFilter(request, response, filterChain); } }