Hi, <snip/>
I've debugged further with Wireshark: 1) Golang: https://pasteboard.co/Jsnvx1z.png (SETTINGS, HEADERS+DATA) 2) Nodejs: https://pasteboard.co/JsnwRNV.png (SETTINGS, HEADERS+PARTIAL DATA, DATA) 3) Rust: https://pasteboard.co/JsnxvKGU.png (SETTINGS, HEADERS+PARTIAL DATA+DATA) 4) Netty: https://pasteboard.co/JsnxYJp.png (SETTINGS+HEADERS+DATA) 5) Tomcat: https://pasteboard.co/JsnyjpA.png (SETTINGS, HEADERS, DATA) >From the images above you can see that only Tomcat sends two separate packets for HEADERS and DATA frames. All others put several frames into one packet. Netty puts the SETTINGS, the HEADERS and the DATA into one TCP packet. I guess this is the reason for the high throughput Node.js writes the HEADERS and PARTIAL DATA in one packet and the actual DATA in a separate one Rust also uses PARTIAL DATA but puts the actual one in the same TCP packet Tomcat's way is spec compliant but most probably is also a reason for the lower throughput. More findings: I've debugged Netty code and it never receives RST_STREAM with CANCEL error at https://github.com/martin-g/http2-server-perf-tests/blob/2f628c407e2ea29be99e756a61e555d88addaa01/java/netty-http2-test/src/main/java/com/example/netty/http2/Http2ServerResponseHandler.java#L30 I've looked at Golang net code and found that it will send RST_STREAM with CANCEL if there is a problem reading the body: https://github.com/tsenart/vegeta/issues/540#issuecomment-696757695 It is not clear to me whether this is about the request body or response body. But since the unit test uses Transport.RoundTrip() I think it is about the response body. So, it looks like Tomcat sends a DATA frame which is not liked by Golang net package code and Golang sends this CANCEL error that causes the problems. The DATA frames in Wireshark look good to me, but I've made them by using Firefox as a client to be able to decrypt TLS in Wireshark (SSLKEYLOGFILE) Martin