From b98eab13f3b8f30dc8e83e65ebd817ac95ee6f42 Mon Sep 17 00:00:00 2001 From: Aayush Atharva Date: Sat, 18 Jul 2026 21:25:45 +0000 Subject: [PATCH] Document that BodyState.STOP may carry the final bytes --- .../main/java/org/asynchttpclient/request/body/Body.java | 7 ++++++- .../java/org/asynchttpclient/util/AuthenticatorUtils.java | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/client/src/main/java/org/asynchttpclient/request/body/Body.java b/client/src/main/java/org/asynchttpclient/request/body/Body.java index 6e38107fcd..dd8ffb0588 100644 --- a/client/src/main/java/org/asynchttpclient/request/body/Body.java +++ b/client/src/main/java/org/asynchttpclient/request/body/Body.java @@ -31,6 +31,10 @@ public interface Body extends Closeable { /** * Reads the next chunk of bytes from the body. + *

+ * A {@link BodyState#STOP} result may be returned by the same call that writes the body's last bytes, so + * {@code target} can still hold unread bytes on STOP. Consumers must drain {@code target} before honouring + * STOP, otherwise the final chunk is lost. * * @param target The buffer to store the chunk in, must not be {@code null}. * @return The state. @@ -51,7 +55,8 @@ enum BodyState { SUSPEND, /** - * There's nothing to read and input has to stop + * Input has to stop. This is the last chunk; {@code target} may still carry unread bytes that the + * consumer must send before stopping (see {@link Body#transferTo(ByteBuf)}). */ STOP } diff --git a/client/src/main/java/org/asynchttpclient/util/AuthenticatorUtils.java b/client/src/main/java/org/asynchttpclient/util/AuthenticatorUtils.java index 46a776296c..66d2b73b3c 100644 --- a/client/src/main/java/org/asynchttpclient/util/AuthenticatorUtils.java +++ b/client/src/main/java/org/asynchttpclient/util/AuthenticatorUtils.java @@ -464,6 +464,8 @@ private static String bufferAndHashBodyGenerator(BodyGenerator gen, String hashA try (Body body = gen.createBody()) { Body.BodyState state; + // Safe only because ByteArrayBodyGenerator returns STOP on an empty call; a body that returns + // STOP carrying its last bytes (e.g. MultipartBody) would be truncated here. See Body#transferTo. while ((state = body.transferTo(tmp)) != Body.BodyState.STOP) { if (state == Body.BodyState.SUSPEND) { continue; // nothing new yet