Skip to content

Commit 6ae911e

Browse files
Fix VertxHttpClient streaming request bodies hanging on Vert.x 5.x (#50836)
HttpClientRequest.reset(long, Throwable) returns boolean in Vert.x 4.5 but Future<Void> in 5.x. The method reference compiled against 4.5 threw NoSuchMethodError on 5.x when the write subscriber was created, which Vert.x swallowed, so streamed bodies were never sent. Invoke reset reflectively, as is already done for Vertx.createHttpClient. Related to #50793
1 parent 396d964 commit 6ae911e

2 files changed

Lines changed: 32 additions & 1 deletion

File tree

‎sdk/core/azure-core-http-vertx/CHANGELOG.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
### Bugs Fixed
1010

11+
- Fixed a bug where requests with a streaming body, such as `Flux<ByteBuffer>` or `InputStream`, would never be sent
12+
when running with Vert.x 5.x.
13+
1114
### Other Changes
1215

1316
## 1.2.0 (2026-09-29)

‎sdk/core/azure-core-http-vertx/src/main/java/com/azure/core/http/vertx/VertxHttpClient.java‎

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import com.azure.core.http.vertx.implementation.VertxHttpResponse;
1313
import com.azure.core.http.vertx.implementation.VertxRequestWriteSubscriber;
1414
import com.azure.core.http.vertx.implementation.VertxUtils;
15+
import com.azure.core.implementation.ReflectionUtils;
16+
import com.azure.core.implementation.ReflectiveInvoker;
1517
import com.azure.core.implementation.util.BinaryDataContent;
1618
import com.azure.core.implementation.util.BinaryDataHelper;
1719
import com.azure.core.implementation.util.ByteArrayContent;
@@ -38,6 +40,7 @@
3840

3941
import java.io.IOException;
4042
import java.io.UncheckedIOException;
43+
import java.lang.reflect.Method;
4144
import java.time.Duration;
4245
import java.util.Objects;
4346
import java.util.concurrent.atomic.AtomicBoolean;
@@ -55,6 +58,20 @@ class VertxHttpClient implements HttpClient {
5558
// other HTTP stacks. RFC 9110 requires a client not to wait indefinitely.
5659
private static final Duration EXPECT_CONTINUE_TIMEOUT = Duration.ofSeconds(1);
5760

61+
private static final ReflectiveInvoker REQUEST_RESET;
62+
static {
63+
// In order to support both Vert.x 4.5 and 5.x reflection is needed to reset an HttpClientRequest.
64+
// HttpClientRequest.reset(long, Throwable) is available in both versions, but it returns boolean in 4.5 and
65+
// Future<Void> in 5.x, so a call compiled against one version fails with NoSuchMethodError on the other.
66+
try {
67+
Method method = HttpClientRequest.class.getMethod("reset", long.class, Throwable.class);
68+
REQUEST_RESET = ReflectionUtils.getMethodInvoker(HttpClientRequest.class, method, false);
69+
} catch (Exception ex) {
70+
// Fail here as the method isn't available and this HttpClient wouldn't be usable anyway.
71+
throw LOGGER.logExceptionAsError(new IllegalStateException(ex));
72+
}
73+
}
74+
5875
final io.vertx.core.http.HttpClient client;
5976
final HttpClientOptions buildOptions;
6077
private final Duration responseTimeout;
@@ -269,11 +286,22 @@ private void writeBody(ContextView contextView, HttpRequest azureRequest, Progre
269286
// Right now both Flux<ByteBuffer> and InputStream bodies are being handled reactively.
270287
azureRequest.getBody()
271288
.subscribe(new VertxRequestWriteSubscriber(vertxRequest::exceptionHandler, vertxRequest::drainHandler,
272-
vertxRequest::write, vertxRequest::writeQueueFull, vertxRequest::reset, vertxRequest::end, promise,
289+
vertxRequest::write, vertxRequest::writeQueueFull,
290+
(code, cause) -> resetRequest(vertxRequest, code, cause), vertxRequest::end, promise,
273291
progressReporter, contextView));
274292
}
275293
}
276294

295+
private static void resetRequest(HttpClientRequest vertxRequest, long code, Throwable cause) {
296+
try {
297+
REQUEST_RESET.invokeWithArguments(vertxRequest, code, cause);
298+
} catch (Exception ex) {
299+
// The request is already being failed with 'cause', so don't let a failed reset replace that error.
300+
cause.addSuppressed(ex);
301+
LOGGER.atWarning().log("Failed to reset the Vert.x HttpClientRequest.", ex);
302+
}
303+
}
304+
277305
private static void reportProgress(long progress, ProgressReporter progressReporter) {
278306
if (progressReporter != null) {
279307
progressReporter.reportProgress(progress);

0 commit comments

Comments
 (0)