micronaut-core icon indicating copy to clipboard operation
micronaut-core copied to clipboard

Netty HttpClient times out when sending a request body longer than 8096 bytes to an HTTP/2 endpoint

Open asw12 opened this issue 1 year ago • 1 comments

Expected Behavior

From a Netty DefaultHttpClient user's perspective, sending a request to an HTTP/2 endpoint should work for any content length so long as its under the threshold at which the server returns 413 Content Too Large.

This was working previously working for me on micronaut-core 4.6.6.

Actual Behaviour

When sending a request with more than 8096 bytes, evidently a threshold defined in Netty's HttpPostBodyUtil, to an HTTP/2 webserver, I eventually encounter either a ReadTimeout or ResponseClosedException as a result of the server unceremoniously closing the client's connection.

Stepping into the code a bit more deeply, I noticed that execution is winding up hitting this error, though it isn't really surfaced in logs:

Message must be an Http2StreamFrame: UnpooledSlicedByteBuf(ridx: 0, widx: 8096, cap: 8096/8096, unwrapped: CompositeByteBuf(ridx: 0, widx: 8096, cap: 8096, components=2))

https://github.com/netty/netty/blob/netty-4.1.114.Final/codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractHttp2StreamChannel.java#L1007

I'm not 100% sure, but I think this might be a regression from https://github.com/micronaut-projects/micronaut-core/pull/11158.

Steps To Reproduce

I created this test case reproducing the issue: https://github.com/micronaut-projects/micronaut-core/compare/4.7.x...asw12:micronaut-core:large-http2-request#diff-64c535267c745f508c54cda504f9faccdb75b71c7877019c2851de2a8dac7a2aR36

It's basically a copy of the existing JDK Http2Spec.groovy, but in the netty client module.


+    def "test http2 post long string"() {
+        when:
+        def body = Map.of("q", "a" * 8097)
+
+        def response = client.toBlocking().retrieve(HttpRequest.POST("/http2", body)
+                .contentType(MediaType.APPLICATION_FORM_URLENCODED_TYPE))
+
+        then:
+        response == "hello"
+    }
+
+
     @Controller("/http2")
     @Requires(property = "spec.name", value = "Http2Spec")
     static class SpecController {
 
         @Get
         @Produces(MediaType.TEXT_PLAIN)
         String get() {
             "hello"
         }
+
+        @Post
+        @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+        @Produces(MediaType.TEXT_PLAIN)
+        String post() {
+            "hello"
+        }
     }

Environment Information

JDK Version 17

Example Application

https://github.com/asw12/micronaut-core/tree/large-http2-request

Version

micronaut-core 4.7.0

asw12 avatar Oct 17 '24 19:10 asw12

I'll take a look at the test case. Can you sign the CLA so I can use your test case if I make a PR? https://cla-assistant.io/micronaut-projects/micronaut-core

yawkat avatar Oct 18 '24 08:10 yawkat