Fix HeaderWriterFilter Race Condition
Closes gh-9175 Signed-off-by: Dmitrii Bocharov <bdshadow@gmail.com>
This commit is contained in:
committed by
Josh Cummings
parent
461ccd817d
commit
bdf501fe87
@@ -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() {
|
||||
|
||||
+20
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user