I have a spring boot based server and a client app which both of them runs on embedded tomcat. I call server app via 3rd party tools(curl etc.), which then server app calls client app via RestClient. Client app returns a basic pojo as response. Also client response has Transfer-encoding : chunked
header. When client hits the keep alive timeout of tomcat while server app calls it(which will result on client returning connection: close
header), server can not handle the response. As i understand it correctly embedded tomcat decides that this response is a normal response rather than a chunked response(? also not sure of this ?). Because of that it does not include END_CHUNK_BYTES
from ChunkedOutputFilter
. This results in 3rd party tools not be able to parse server response correctly.
Sample server-client projetcs : https://github.com/nkeskin/tomcat-error-demo-projects.git
I have added demo projects to reproduce the problem. To run projects simply cd to both server-app and client-app directories then run
./mvnw spring-boot:run
command. To hit the problem early i have set the keep alive timeout to 1 second both on client and on server app :
server.tomcat.keep-alive-timeout=1s
Finally calling the server endpoint via curl : (Ideally it should hit the error case as soon as possible, but might need to call it a couple of times)
curl localhost:8080/server
curl: (56) chunk hex-length char not a hex digit: 0x7b
Is this a normal behavior or a kind of bug? Shouldnt tomcat be able to parse response which contains both connection: close and transfer-encoding: chunked headers?
Environment : Darwin Mac 24.3.0 Darwin Kernel Version 24.3.0: Thu Jan 2 20:22:58 PST 2025; root:xnu-11215.81.4~3/RELEASE_ARM64_T8132 arm64 jdk-21.0.5+11 Eclipse Temurin aarch64 Spring Boot 3.4.3
Comment From: bclozel
Hello @nkeskin
This behavior comes from the fact that your controller is copying all headers received by the client to the server response. Some headers should be written by the Servlet container itself as the application is not in a position to make that decision.
This is somewhat similar to https://github.com/spring-projects/spring-framework/issues/21523
Please filter out headers before copying them and this should work fine.