1
0
mirror of synced 2026-09-12 20:25:06 +00:00

Fix HeaderWriterFilter Race Condition

Closes gh-9175

Signed-off-by: Dmitrii Bocharov <bdshadow@gmail.com>
This commit is contained in:
Dmitrii Bocharov
2026-04-18 23:18:36 +02:00
committed by Josh Cummings
parent 461ccd817d
commit bdf501fe87
2 changed files with 26 additions and 1 deletions
@@ -18,6 +18,7 @@ package org.springframework.security.web.header;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import jakarta.servlet.FilterChain;
import jakarta.servlet.RequestDispatcher;
@@ -114,6 +115,8 @@ public class HeaderWriterFilter extends OncePerRequestFilter {
private final HttpServletRequest request;
private final AtomicBoolean headersWritten = new AtomicBoolean(false);
HeaderWriterResponse(HttpServletRequest request, HttpServletResponse response) {
super(response);
this.request = request;
@@ -129,7 +132,9 @@ public class HeaderWriterFilter extends OncePerRequestFilter {
if (isDisableOnResponseCommitted()) {
return;
}
HeaderWriterFilter.this.writeHeaders(this.request, getHttpResponse());
if (this.headersWritten.compareAndSet(false, true)) {
HeaderWriterFilter.this.writeHeaders(this.request, getHttpResponse());
}
}
private HttpServletResponse getHttpResponse() {
@@ -113,6 +113,26 @@ public class HeaderWriterFilterTests {
verifyNoMoreInteractions(this.writer1);
}
// gh-9175
@Test
public void doFilterWhenWriteHeadersCalledConcurrentlyThenHeadersWrittenOnlyOnce() throws Exception {
List<HeaderWriter> headerWriters = new ArrayList<>();
headerWriters.add(this.writer1);
HeaderWriterFilter filter = new HeaderWriterFilter(headerWriters);
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
filter.doFilter(request, response, (req, resp) -> {
// Calling writeHeaders() directly simulates the race window where an
// async thread enters writeHeaders() via onResponseCommitted() but has
// not yet called disableOnResponseCommitted().
((HeaderWriterFilter.HeaderWriterResponse) resp).writeHeaders();
});
// The finally block in doHeadersAfter also calls writeHeaders().
// Without the fix, the header writers would be invoked twice.
verify(this.writer1).writeHeaders(any(HttpServletRequest.class), any(HttpServletResponse.class));
verifyNoMoreInteractions(this.writer1);
}
@Test
public void headersWrittenAtBeginningOfRequest() throws Exception {
HeaderWriterFilter filter = new HeaderWriterFilter(Collections.singletonList(this.writer1));