From 1a3816bba181968db999050a91543de3a1ed1ec6 Mon Sep 17 00:00:00 2001 From: wang007 <704450642@qq.com> Date: Tue, 9 Sep 2025 10:43:24 +0800 Subject: [PATCH] Fix http client write buffer failed. Motivation: When using the vertx-http-proxy proxy for the h2 protocol, there is a bug in the handling of content-length when writing buffer. Modifications: HttpClientRequestImpl#write,HttpClientRequestImpl#requiresContentLength --- .../core/http/impl/HttpClientRequestImpl.java | 23 +++++++-- .../io/vertx/tests/http/Http2ClientTest.java | 48 +++++++++++++++++++ 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl.java b/vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl.java index d34cd032237..371b9a70b9d 100644 --- a/vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl.java +++ b/vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl.java @@ -482,18 +482,31 @@ public Future write(String chunk, String enc) { return write(BufferInternal.buffer(chunk, enc).getByteBuf(), false); } - private boolean requiresContentLength() { + private boolean requiresContentLength(boolean writeHead) { + if (writeHead) { + return !chunked && (headers == null || !headers.contains(CONTENT_LENGTH)) && !isConnect; + } + if (version() == HttpVersion.HTTP_2) { + return false; + } return !chunked && (headers == null || !headers.contains(CONTENT_LENGTH)) && !isConnect; } private Future write(ByteBuf buff, boolean end) { if (end) { - if (buff != null && requiresContentLength()) { - headers().set(CONTENT_LENGTH, HttpUtils.positiveLongToString(buff.readableBytes())); + if (buff != null && requiresContentLength(true)) { + boolean headWritten; + synchronized (this) { + headWritten = this.headWritten; + } + if (!headWritten) { + headers().set(CONTENT_LENGTH, HttpUtils.positiveLongToString(buff.readableBytes())); + } } - } else if (requiresContentLength()) { - throw new IllegalStateException("You must set the Content-Length header to be the total size of the message " + } else if (requiresContentLength(false)) { + IllegalStateException e = new IllegalStateException("You must set the Content-Length header to be the total size of the message " + "body BEFORE sending any data if you are not using HTTP chunked encoding."); + return Future.failedFuture(e); } return doWrite(buff, end, false); } diff --git a/vertx-core/src/test/java/io/vertx/tests/http/Http2ClientTest.java b/vertx-core/src/test/java/io/vertx/tests/http/Http2ClientTest.java index 31ed6a93734..505c222aab6 100644 --- a/vertx-core/src/test/java/io/vertx/tests/http/Http2ClientTest.java +++ b/vertx-core/src/test/java/io/vertx/tests/http/Http2ClientTest.java @@ -2330,4 +2330,52 @@ public void testClearTestDirectServerCloseBeforeSettingsRead() { })); await(); } + + @Test + public void testWriteChunked() throws Exception { + String p1 = "12345"; + String p2 = "56789"; + server.requestHandler(req -> { + Buffer buffer = Buffer.buffer(); + AtomicInteger handleChunkCount = new AtomicInteger(); + req.handler(buff -> { + buffer.appendBuffer(buff); + handleChunkCount.incrementAndGet(); + }); + req.endHandler(v -> { + assertEquals(handleChunkCount.get(), 2); + assertEquals(buffer.toString(), p1 + p2); + + HttpServerResponse response = req.response(); + response.setStatusCode(200); + response + .write(p2) + .flatMap(v0 -> response.end(p1)); + }); + }); + startServer(); + + client.request(requestOptions).onComplete(onSuccess(req -> { + req.write(p1) + .flatMap(v -> req.end(p2)); + + AtomicInteger handleChunkCount = new AtomicInteger(); + Buffer buffer = Buffer.buffer(); + req.response().onComplete(onSuccess(response -> { + response.handler(buf -> { + buffer.appendBuffer(buf); + handleChunkCount.incrementAndGet(); + }); + + response.endHandler(v -> { + assertEquals(handleChunkCount.get(), 2); + assertEquals(buffer.toString(), p2 + p1); + complete(); + }); + })); + })); + + await(); + } + }