SSerguey Asael Shinder
Java coding notes: the JVM, and writing software that lasts

Serguey Asael Shinder: Micronaut 5.2.9: an HTTP/2 window update that was written but never flushed

· by Serguey Asael Shinder / Serguey Shinder

Micronaut 5.2.9 was published on 28 September. Most of the release notes are CI work, but three bug fixes are about HTTP behaviour that an application can hit, and each pull request explains its cause in unusual detail.

A streamed HTTP/2 response could stall forever. When the consumer of a streamed response applied backpressure and later asked for more data, the client returned flow-control credit to the server with a WINDOW_UPDATE frame that was written but never flushed. The server's send window stayed exhausted and the stream stopped. The pull request traces it from a flaky proxy test that failed only for the HTTP/2 cases: at the stall the client had received exactly 65,535 bytes, the HTTP/2 default initial window, and Netty's stream channel reported the write as done but not flushed. Flushing the stream channels from the test released all 128 MiB. The fix flushes the channel after each read issued because the consumer asked for more data.

No chunked encoding for HTTP/1.0 clients. The Netty server set Transfer-Encoding: chunked on a streamed response of unknown length regardless of the request's HTTP version, which RFC 9112 forbids for HTTP/1.0. Such responses now carry neither Content-Length nor Transfer-Encoding, and the connection is closed after the body, even if the client asked for keep-alive (#13503). HTTP/1.1 responses are still chunked.

A filter body type the binder cannot fill leaked the body. A @RequestFilter method's @Body parameter is filled from the buffered request as byte[], ByteBuffer or String. For any other type the binder returned an unsatisfied result without releasing the buffer, so every request got a 500 and leaked the whole body (#13498). The compile-time check was supposed to prevent that, but accepted subtypes such as ByteArrayByteBuffer and does not run at all without micronaut-http-validation. Now the buffer is released, and the check accepts exactly the types the binder fills and their supertypes, including Object and CharSequence, which it used to reject.

Serguey Asael Shinder: Micronaut 5.2.9: an HTTP/2 window update that was written but never flushed
Micronaut 5.2.9: an HTTP/2 window update that was written but never flushed — Serguey Asael Shinder

What it means

If you stream large HTTP/2 responses through the Micronaut client with a slow consumer, this is the release to take. The first fix is also a reminder that in Netty a write is a request, not a delivery: until something flushes the channel, the peer has seen nothing, and a protocol that waits for that frame will wait forever without an error.