Skip to content

Fix race condition in HeaderWriterFilter when using asynchronous processing - #19705

Open
justin-tay wants to merge 1 commit into
spring-projects:mainfrom
justin-tay:gh-15510
Open

justin-tay wants to merge 1 commit into
spring-projects:mainfrom
justin-tay:gh-15510

Conversation

@justin-tay

Copy link
Copy Markdown
Contributor

Closes gh-15510

This fixes the issue where the headers are still being modified in the main thread by HeaderWriterFilter while the asynchronous processing might have completed and flushed the response. The AtomicBoolean added previously stops the headers being written twice but doesn't fix this issue.

On Tomcat this sometimes produces a header with an empty name which certain clients reject.

In org.apache.tomcat.util.http.MimeHeaders if the following sequence happens

public MessageBytes setValue(String name) {
  ..
  MimeHeaderField mh = createHeader(); // set in the main thread by HeaderWriterFilter
  mh.getName().setString(name); // not yet set before the asynchronous processing commits in the async thread
  ..
}

With org.apache.coyote.http11.Http11OutputBuffer

public void sendHeader(MessageBytes name, MessageBytes value) {
  write(name); // blank as no name set yet
  headerBuffer.put(Constants.COLON).put(Constants.SP);
  write(value); // blank as no value set yet
  headerBuffer.put(Constants.CR).put(Constants.LF);
}

This causes just : to appear as a header and I ended up getting this in my client.

Caused by: org.apache.http.ProtocolException: Invalid header: : 
	at org.apache.http.impl.io.AbstractMessageParser.parseHeaders(AbstractMessageParser.java:230)

HeaderWriterFilter writes headers in a finally block once the filter
chain returns, and again when the response is committed. With
asynchronous processing, such as a StreamingResponseBody, these
happen on different threads. The AtomicBoolean added previously
stops the headers being written twice, but the thread that loses
the race still commits the response immediately, so the servlet
container can write out the headers while the other thread is
modifying them. On Tomcat this produces a header with an empty name.

Synchronize writeHeaders() so that the commit waits until the
headers have been written.

Closes spring-projectsgh-15510

Signed-off-by: Justin Tay <49700559+justin-tay@users.noreply.github.com>
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Sep 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: waiting-for-triage An issue we've not yet triaged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Race condition in HeaderWriterFilter when using asynchronous processing

2 participants