micronaut-projects / micronaut-core

Micronaut Application Framework
http://micronaut.io
Apache License 2.0
6.07k stars 1.07k forks source link

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

Open asw12 opened 1 day ago

asw12 commented 1 day ago

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

yawkat commented 1 day ago

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