Skip to content

HttpClient.send doesn't fail on ReadStream failure #5550

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
NilsRenaud opened this issue Apr 11, 2025 · 0 comments
Open

HttpClient.send doesn't fail on ReadStream failure #5550

NilsRenaud opened this issue Apr 11, 2025 · 0 comments
Labels
Milestone

Comments

@NilsRenaud
Copy link
Contributor

NilsRenaud commented Apr 11, 2025

Version

I'm using Vert.x 4.5.14.

Context

When sending an HTTP Request using a ReadStream as input of the request's body, then on ReadStream failure the httpClient.send(readStream) method ends up in success. Hiding that the request body was corrupted.

Do you have a reproducer?

Here is a Junit unit test which reproduce the issue:

import java.util.concurrent.CountDownLatch;

import org.junit.jupiter.api.Test;

import io.vertx.codegen.annotations.Nullable;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServer;
import io.vertx.core.streams.ReadStream;

class MyTest {

    @Test
    void test() throws Exception {
        HttpServer server = consumeIncomingRequestAndReturnOk();
        CountDownLatch latch = new CountDownLatch(1);
        try {
            Vertx.vertx()
                 .createHttpClient()
                 .request(HttpMethod.POST, server.actualPort(), "localhost", "/")
                 .compose(request -> request.send(new FailingReadStream()))
                 .onComplete(response -> System.out.println("Worked although it should not"),
                             Throwable::printStackTrace)
                 .onComplete(h -> latch.countDown());
            latch.await(); // As the request.send never completes, this hangs forever.
        } finally {
            server.close();
        }
    }

    private HttpServer consumeIncomingRequestAndReturnOk() throws Exception {
        CountDownLatch latch = new CountDownLatch(1);
        HttpServer server = io.vertx.core.Vertx.vertx().createHttpServer()
                                               .requestHandler(req ->
                                                                       req.bodyHandler(buff -> req.response()
                                                                                                  .setStatusCode(200)
                                                                                                  .end()));
        server.listen(0).onSuccess(startedServer -> latch.countDown());
        latch.await();
        return server;
    }

    private static final class FailingReadStream implements ReadStream<Buffer> {
        Handler<Throwable> exceptionHandler;

        @Override
        public ReadStream<Buffer> exceptionHandler(@Nullable final Handler<Throwable> handler) {
            this.exceptionHandler = handler;
            return this;
        }

        @Override
        public ReadStream<Buffer> handler(@Nullable final Handler<Buffer> handler) {
            return this;
        }

        @Override
        public ReadStream<Buffer> pause() {
            return this;
        }

        @Override
        public ReadStream<Buffer> resume() {
            exceptionHandler.handle(new IllegalStateException("Boom"));
            return this;
        }

        @Override
        public ReadStream<Buffer> fetch(final long amount) {
            exceptionHandler.handle(new IllegalStateException("Boom"));
            return this;
        }

        @Override
        public ReadStream<Buffer> endHandler(@Nullable final Handler<Void> endHandler) {
            return this;
        }
    }
}

Investigation

If I understood correctly, the issue is caused by this line that ignores the result of pipeTo future (which is a failed future):

default Future<HttpClientResponse> send(ReadStream<Buffer> body) {
    MultiMap headers = headers();
    if (headers == null || !headers.contains(HttpHeaders.CONTENT_LENGTH)) {
      setChunked(true);
    }
    body.pipeTo(this); // <--- Here
    return response();
  }
@NilsRenaud NilsRenaud added the bug label Apr 11, 2025
@vietj vietj added this to the 4.5.15 milestone Apr 11, 2025
@vietj vietj modified the milestones: 4.5.15, 4.5.16 May 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants