IOResources#toRetainableByteBuffer data loss when using resources without path (original) (raw)
Jetty version(s)
12.1.9
Jetty Environment
core
HTTP version
not-relevant
Java version/vendor (use: java -version)
17
OS type/version
any
Description
I'm working on https://github.com/ops4j/org.ops4j.pax.web project which implements OSGi CMPN web-related specifications consistently for Jetty, Tomcat and Undertow servers.
We had a weird issues visible at this stack trace:
- OSGi - 13:47:40.526 [qtp1791035325-54] WARN (Response.java:652) org.eclipse.jetty.server.Response - writeError: status=500, message=java.io.IOException: written 872337 < 872817 content-length, response=oejsi.HttpChannelState$ErrorResponse@6d8fd6b7{500,GET@6a85c2db http://127.0.0.1:8181/documentation/ HTTP/1.1}
java.io.IOException: written 872337 < 872817 content-length
at org.eclipse.jetty.server.internal.HttpChannelState$ChannelResponse.write(HttpChannelState.java:1385)
at org.eclipse.jetty.server.Response$Wrapper.write(Response.java:841)
at org.eclipse.jetty.server.handler.ContextResponse.write(ContextResponse.java:56)
at org.eclipse.jetty.ee8.nested.HttpChannel.send(HttpChannel.java:851)
at org.eclipse.jetty.ee8.nested.HttpChannel.sendResponse(HttpChannel.java:831)
at org.eclipse.jetty.ee8.nested.HttpChannel.write(HttpChannel.java:905)
at org.eclipse.jetty.ee8.nested.HttpOutput.channelWrite(HttpOutput.java:298)
at org.eclipse.jetty.ee8.nested.HttpOutput.lambda$sendContent$1(HttpOutput.java:1140)
at org.eclipse.jetty.http.content.CachingHttpContentFactory$CachedHttpContent.writeTo(CachingHttpContentFactory.java:391)
at org.eclipse.jetty.ee8.nested.HttpOutput.sendContent(HttpOutput.java:1142)
at org.eclipse.jetty.ee8.nested.HttpOutput.sendContent(HttpOutput.java:1033)
at org.eclipse.jetty.ee8.nested.ResourceService.sendData(ResourceService.java:612)
at org.eclipse.jetty.ee8.nested.ResourceService.doGet(ResourceService.java:281)
at org.ops4j.pax.web.service.jetty.internal.web.DefaultServlet.doGet(DefaultServlet.java:478)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:497)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:584)
...
Pax Web configures Jetty Default/Resource servlet to serve static resources, which may come from OSGi bundles (so Jetty URL Resources with bundle: scheme are used).
I've finally reproduced the issue without any OSGi code and narrowed down the problem to org.eclipse.jetty.io.RetainableByteBuffer.DynamicCapacity#append() method - it doesn't mark the passed org.eclipse.jetty.io.RetainableByteBuffer as retained, so the buffer is reused in the loop inside org.eclipse.jetty.io.IOResources#toRetainableByteBuffer().
The problem is here:
| int read = inputStream.read(buffer.getByteBuffer().array()); |
|---|
java.nio.ByteBuffer#array() is passed to read operation, however the aggregate buffer may already have non-zero java.nio.Buffer#position(), so even if some data is read, java.nio.Buffer#remaining() returns zero (as the position may exceed the limit).
How to reproduce?
See the updated org.eclipse.jetty.io.IOResourcesTest#testToRetainableByteBuffer() in this PR: