From 38029df194169597677715a0c5424c6b0e1df1e7 Mon Sep 17 00:00:00 2001 From: Icebob Date: Wed, 3 Jun 2026 10:23:56 +0200 Subject: [PATCH] Keep connection alive for body-less responses (204) instead of closing it NettyWebResponse.end() closed the channel whenever no Content-Length header was present. This is the case for body-less responses such as "204 No Content" (or an action returning null). The close was performed without a "Connection: close" header, so HTTP clients that use keep-alive connection pools (browsers, Node's http.Agent / axios, Java HttpClient, ...) returned the socket to the pool and hit "ECONNRESET" / "socket hang up" on the next request that reused it. For a body-less response the message length is well-defined (zero), so emit an explicit "Content-Length: 0" before the headers are flushed. The existing logic then sees a Content-Length, keeps the connection alive, and the response is correctly framed. Responses that write a body are unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../moleculer/web/netty/NettyWebResponse.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/services/moleculer/web/netty/NettyWebResponse.java b/src/main/java/services/moleculer/web/netty/NettyWebResponse.java index e403b01..d58e6ca 100644 --- a/src/main/java/services/moleculer/web/netty/NettyWebResponse.java +++ b/src/main/java/services/moleculer/web/netty/NettyWebResponse.java @@ -159,6 +159,20 @@ public void send(byte[] bytes) throws IOException { */ @Override public boolean end() { + + // If no response body has been written (e.g. "204 No Content", or an + // action that returned null), the response is complete with zero + // length. Emit an explicit "Content-Length: 0" so the message is + // properly framed and the keep-alive connection can be reused. Without + // it, the block below closes the channel (no Content-Length is present) + // but no "Connection: close" header is sent, so pooled HTTP clients + // (browsers, Node http.Agent/axios, ...) reuse the socket and fail the + // next request with "ECONNRESET" / "socket hang up". + if (first.get() && (req == null || req.parser == null) + && (headers == null || headers.get(CONTENT_LENGTH) == null)) { + setHeader(CONTENT_LENGTH, "0"); + } + sendHeaders(); if (req != null && req.parser != null) { try {