Tomcat response contains multiple transfer-encoding headers

Solution Verified - Updated

Environment

  • JBoss Web Server (JWS) 5.x
    • Tomcat 9.0.x

Issue

  • We are seeing multiple transfer-encoding headers in the response from our application on Tomcat. This is cause issues with the response on the client side or causing intermediary gateways/balancers to reject the response due to the duplicate header.
  • A duplicate header is not seen like this if the application runs on EAP 7/Undertow.

Resolution

  • Identify the source of any such header set by application code. You may use a custom response wrapper like below to block a duplicate header from being added and to identify the source of it:
public class DupeHeaderWrapper extends HttpServletResponseWrapper {
      
    public DupeHeaderWrapper(HttpServletResponse response) {
        super(response);
    }
      
    public void setHeader(String name, String value) {
        if (name.equalsIgnoreCase("Transfer-Encoding") {
            System.out.println("Ignoring application Transfer-Encoding);
            Thread.currentThread().dumpStack();
        } else {
            super.setHeader(name, value);
        }
    }
      
    public String addHeader(String name, String value) {
        if (name.equalsIgnoreCase("Transfer-Encoding") {
            System.out.println("Ignoring application Transfer-Encoding);
            Thread.currentThread().dumpStack();
        } else {
            super.addHeader(name, value);
        }
    }
}

Root Cause

  • There's no known issue in Tomcat for a dupe Transfer-Encoding header, but we should expect app code typically doesn't add its own such header because the encoding is typically determined by the server.
  • Tomcat only adds this header once Content from github.com is not included.here when preparing the response and adds it regardless of any existing value in such a way that if a Transfer-Encoding did exist already on the response from the app, then Tomcat's header is added without overwriting an existing value like from the app to produce multiple Transfer-Encoding headers. This is shown here with a simple jsp like so:
<html>
<head><title>helloworld</title></head>
<h1>helloworld<h1/>
<body>
<%
response.addHeader("Transfer-Encoding", "foobar");
%>
// big body content here to induce chunked encoding from Tomcat
</body>
</html>
  • And we can see our app's bad foobar encoding was added first to the response and still exists after tomcat adds its own in preparing the response:
$ curl -v localhost:8080/helloworld/hi.jsp
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /helloworld/hi.jsp HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.61.1
> Accept: */*
> 
< HTTP/1.1 200 
< Set-Cookie: JSESSIONID=A4B4F20AF92902AAC45C46B1EA8C87BA; Path=/helloworld; HttpOnly
< Transfer-Encoding: foobar
< Content-Type: text/html;charset=ISO-8859-1
< Transfer-Encoding: chunked
< Date: Tue, 24 May 2022 13:35:38 GMT
  • The same app code for EAP 7/undertow behaves differently because undertow adds its chunked transfer-encoding Content from github.com is not included.here in response preparation only if the transfer encoding on the response is currently null. So we can see the same app code only produces a single transfer-encoding from EAP 7/Undertow as it preserves the original foobar app value:
$ curl -v localhost:8080/helloworld/hi.jsp
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /helloworld2/hi.jsp HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.61.1
> Accept: */*
> 
< HTTP/1.1 200 OK
< Connection: close
< X-Powered-By: JSP/2.3
< Set-Cookie: JSESSIONID=2AF5EVSrvJjP6-KD2y0SyWJAJlZkc_fv5si2o6eo.jEMUyjNwTY26X4Y97Ajexw; path=/helloworld; secure; HttpOnly
< Transfer-Encoding: foobar
< Content-Type: text/html;charset=ISO-8859-1
< Date: Tue, 24 May 2022 13:49:52 GMT
Components
Category
Tags

This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.