Affects: 6.1.2 on Spring Boot 3.2.0
When the ShallowEtagHeaderFilter
is enabled along with the jetty web server, static resources fail to load every second time.
If you fetch a static resource under this configuration, it will work fine for the first call. On the second request, with an ETag attached, the server responds with the following error:
<h2>HTTP ERROR 500 java.io.IOException: written 0 < 4 content-length</h2>
URI: | http://localhost:8080/
-- | --
500
java.io.IOException: written 0 < 4 content-length
java.io.IOException: written 0 < 4 content-length
<h3>Caused by:</h3><pre>java.io.IOException: written 0 < 4 content-length
at org.eclipse.jetty.server.internal.HttpChannelState$ChannelResponse.write(HttpChannelState.java:1182)
at org.eclipse.jetty.server.Response$Wrapper.write(Response.java:735)
at org.eclipse.jetty.server.handler.ContextResponse.write(ContextResponse.java:56)
at org.eclipse.jetty.ee10.servlet.HttpOutput.channelWrite(HttpOutput.java:204)
at org.eclipse.jetty.ee10.servlet.HttpOutput.complete(HttpOutput.java:435)
at org.eclipse.jetty.ee10.servlet.ServletContextResponse.completeOutput(ServletContextResponse.java:209)
at org.eclipse.jetty.ee10.servlet.ServletChannel.handle(ServletChannel.java:553)
at org.eclipse.jetty.ee10.servlet.ServletHandler.handle(ServletHandler.java:464)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:571)
at org.eclipse.jetty.ee10.servlet.SessionHandler.handle(SessionHandler.java:703)
at org.eclipse.jetty.server.handler.ContextHandler.handle(ContextHandler.java:761)
at org.eclipse.jetty.server.Server.handle(Server.java:179)
at org.eclipse.jetty.server.internal.HttpChannelState$HandlerInvoker.run(HttpChannelState.java:594)
at org.eclipse.jetty.server.internal.HttpConnection.onFillable(HttpConnection.java:424)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:322)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:99)
at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:478)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:441)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:293)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.produce(AdaptiveExecutionStrategy.java:195)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:971)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1201)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1156)
at java.base/java.lang.Thread.run(Thread.java:1583)
</pre>
It seems that the response is already marked as "Not Modified" and the body cleared by one caching component, while the other still expects it to be unchanged.
Repository with minimal reproducer: https://github.com/sebastian-steiner/spring-cacheissue
Comment From: poutsma
This might be related to https://github.com/jetty/jetty.project/issues/9953.
Comment From: poutsma
The reason for this issue is that the response has a Content-Length header that does not correspond to the amount of bytes written, an error that Jetty 12 tracks more strictly. While the ShallowEtagHeaderFilter
does filter out the content-length if set through setContentLength
, it does not do so when setHeader("Content-Length", x)
is used. I will prepare a fix.
Comment From: sebastian-steiner
Thanks for the explanation and the quick triage!