You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When using HeaderWriterFilter and asynchronous processing (e. g. a Spring MVC controller method that returns a StreamingResponseBody), headers may be set twice from different threads, potentially leading to race conditions and undefined behaviour.
The following two things happen in parallel:
The asynchronous processing writes to the response body. After data has been written, the response is committed. This causes the HeaderWriterFilter to write headers from the asynchronous task's thread.
The execution of the HeaderWriterFilter on the way back through the filter chain. This will write headers from the original request handling thread.
Both threads may successfully pass the isDisableOnResponseCommitted() check in HeaderWriterFilter.HeaderWriterResponse#writeHeaders, causing all HeaderWriters to be invoked twice. This may cause duplicate headers, because even if the HeaderWriter checks if the header is already present, this check-and-add is usually not atomic.
It's getting even worse: The implementation of HttpServletResponse is not guaranteed to be thread safe according to the Servlet Spec.
When using Apache Tomcat, adding headers from different threads causes nasty race conditions that (because of Tomcat's instance recycling) can durably break the inner workings of their org.apache.tomcat.util.http.MimeHeaders class, potentially leading to errors in completely unrelated requests. I already experienced duplicate and missing response headers. As with most race conditions, these specific issues are almost impossible to reproduce.
Because other parts of the application may also add headers, this issue cannot probably be solved inside the HeaderWriterFilter alone.
To make things even worse, Tomcat itself also manipulates response headers internally: For example, the Vary response headers created by the application are combined into one if response compression is enabled. This happens in the same thread where the response is committed, but is probably out of Spring Security's reach.
To Reproduce
Create a Spring MVC (or Spring Boot) application that uses HeaderWriterFilter and has a @Controller method that returns a StreamingResponseBody. This is already sufficient to reproduce the duplicate headers.
Adding a HeaderWriter that adds a lot of headers helps reproducing the errors caused by adding headers from different threads.
Expected behavior
HeaderWriterFilter doesn't invoke its HeaderWriters from different threads at the same time. However, this might not be sufficient, as described above.
Describe the bug
When using
HeaderWriterFilter
and asynchronous processing (e. g. a Spring MVC controller method that returns aStreamingResponseBody
), headers may be set twice from different threads, potentially leading to race conditions and undefined behaviour.The following two things happen in parallel:
HeaderWriterFilter
to write headers from the asynchronous task's thread.HeaderWriterFilter
on the way back through the filter chain. This will write headers from the original request handling thread.Both threads may successfully pass the
isDisableOnResponseCommitted()
check inHeaderWriterFilter.HeaderWriterResponse#writeHeaders
, causing allHeaderWriter
s to be invoked twice. This may cause duplicate headers, because even if theHeaderWriter
checks if the header is already present, this check-and-add is usually not atomic.It's getting even worse: The implementation of
HttpServletResponse
is not guaranteed to be thread safe according to the Servlet Spec.When using Apache Tomcat, adding headers from different threads causes nasty race conditions that (because of Tomcat's instance recycling) can durably break the inner workings of their
org.apache.tomcat.util.http.MimeHeaders
class, potentially leading to errors in completely unrelated requests. I already experienced duplicate and missing response headers. As with most race conditions, these specific issues are almost impossible to reproduce.Because other parts of the application may also add headers, this issue cannot probably be solved inside the
HeaderWriterFilter
alone.To make things even worse, Tomcat itself also manipulates response headers internally: For example, the
Vary
response headers created by the application are combined into one if response compression is enabled. This happens in the same thread where the response is committed, but is probably out of Spring Security's reach.To Reproduce
Create a Spring MVC (or Spring Boot) application that uses
HeaderWriterFilter
and has a@Controller
method that returns aStreamingResponseBody
. This is already sufficient to reproduce the duplicate headers.Adding a
HeaderWriter
that adds a lot of headers helps reproducing the errors caused by adding headers from different threads.Expected behavior
HeaderWriterFilter
doesn't invoke itsHeaderWriter
s from different threads at the same time. However, this might not be sufficient, as described above.Sample
https://github.com/chschu/spring-security-header-writer-filter-async-bug
The sample also includes a workaround: Flush the response programmatically before starting the asynchronous processing.
The text was updated successfully, but these errors were encountered: