vert-x3 / vertx-web

HTTP web applications for Vert.x
Apache License 2.0
1.11k stars 535 forks source link

Web Client post request may not complete if connection is closed during upload #2562

Closed tsegismont closed 8 months ago

tsegismont commented 9 months ago

The bug can be reproduced with this:

    int concurrency = 2;

    Vertx vertx = Vertx.vertx();

    WebClient webClient = WebClient.wrap(vertx.createHttpClient(new PoolOptions().setHttp1MaxSize(1)));

    String largeString = TestUtils.randomAlphaString(1024 * 1024);

    MultiMap form = MultiMap.caseInsensitiveMultiMap();
    form.add("foo", largeString);

    CountDownLatch latch = new CountDownLatch(concurrency);

    vertx.runOnContext(v -> {
      for (int i = 0; i < concurrency; i++) {
        final int iFinal = i;
        Future<HttpResponse<Buffer>> future = webClient.postAbs("http://www.google.com").sendForm(form);
        future.onComplete(ar -> {
          System.out.println("iFinal = " + iFinal);
          if (ar.succeeded()) {
            System.out.println("Success: " + ar.result().statusCode());
          } else {
            System.out.println("Failure");
            ar.cause().printStackTrace(System.out);
          }
          latch.countDown();
        });
      }
    });

    try {
      latch.await();
    } catch (Exception e) {
      e.printStackTrace(System.out);
    } finally {
      vertx.close();
    }

This program should terminate printing Success: 413 twice.

Instead, it never terminates and prints:

iFinal = 0
Success: 413
Jan 31, 2024 3:00:36 PM io.vertx.core.http.impl.HttpClientRequestImpl
SEVERE: Stream reset: 0
vietj commented 8 months ago

This happens when the client receives a response that closes the connection and has not yet finished sending the request. Since the request is not yet fully send (due to back-pressure) by the client the HttpClient will release the connection which is reused for the second request, since the initial request is not finished the second request allocation is created and not completed (pretty much like with pipelining) when the connection processed the connection close, the second stream allocation request is not failed.

https://github.com/eclipse-vertx/vert.x/pull/5151 fixes this in a way that when the request is not yet sent, the connection is not recycled until the request is ended. In addition the pipelining case has been addressed here https://github.com/eclipse-vertx/vert.x/pull/5152

tsegismont commented 8 months ago

Thank you @vietj