Skip to content

Commit

Permalink
When an HTTP client request timeout fires, the corresponding stream i…
Browse files Browse the repository at this point in the history
…s reset with a cancel code, the application cannot be aware of the cancellation because the netty stream reset will simply notify the stream to be closed.

We can keep track of the timeout in the HttpClientRequest and replace the HTTP closed exception with the timeout exception when this happens to better inform the application.
  • Loading branch information
vietj committed Aug 21, 2023
1 parent 0fc43be commit 17f6dce
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 11 deletions.
18 changes: 13 additions & 5 deletions src/main/java/io/vertx/core/http/impl/HttpClientRequestBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Promise;
import io.vertx.core.http.HttpClientRequest;
import io.vertx.core.http.HttpClientResponse;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.StreamResetException;
import io.vertx.core.http.*;
import io.vertx.core.impl.ContextInternal;
import io.vertx.core.impl.future.PromiseInternal;
import io.vertx.core.net.HostAndPort;
Expand Down Expand Up @@ -48,6 +45,7 @@ public abstract class HttpClientRequestBase implements HttpClientRequest {
private long currentTimeoutTimerId = -1;
private long currentTimeoutMs;
private long lastDataReceived;
private Throwable timeoutFired;

HttpClientRequestBase(HttpClientImpl client, HttpClientStream stream, PromiseInternal<HttpClientResponse> responsePromise, boolean ssl, HttpMethod method, SocketAddress server, String host, int port, String uri) {
this.client = client;
Expand Down Expand Up @@ -169,6 +167,13 @@ public synchronized HttpClientRequest setTimeout(long timeoutMs) {
return this;
}

protected Throwable mapException(Throwable t) {
if (t instanceof HttpClosedException && timeoutFired != null) {
t = timeoutFired;
}
return t;
}

void handleException(Throwable t) {
fail(t);
}
Expand Down Expand Up @@ -209,6 +214,7 @@ private synchronized long cancelTimeout() {
}

private void handleTimeout(long timeoutMs) {
NoStackTraceTimeoutException cause;
synchronized (this) {
currentTimeoutTimerId = -1;
currentTimeoutMs = 0;
Expand All @@ -222,8 +228,10 @@ private void handleTimeout(long timeoutMs) {
return;
}
}
cause = timeoutEx(timeoutMs, method, server, uri);
timeoutFired = cause;
}
reset(timeoutEx(timeoutMs, method, server, uri));
reset(cause);
}

static NoStackTraceTimeoutException timeoutEx(long timeoutMs, HttpMethod method, SocketAddress server, String uri) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public class HttpClientRequestImpl extends HttpClientRequestBase implements Http

@Override
void handleException(Throwable t) {
t = mapException(t);
super.handleException(t);
if (endPromise.tryFail(t)) {
Handler<Throwable> handler = exceptionHandler();
Expand Down
8 changes: 2 additions & 6 deletions src/test/java/io/vertx/core/http/HttpTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3200,9 +3200,7 @@ public void testResponseDataTimeout() {
AtomicInteger count = new AtomicInteger();
resp.exceptionHandler(t -> {
if (count.getAndIncrement() == 0) {
assertTrue(
t instanceof TimeoutException || /* HTTP/1 */
t instanceof VertxException /* HTTP/2: connection closed */);
assertTrue(t instanceof TimeoutException);
assertEquals(expected, received);
complete();
}
Expand All @@ -3222,9 +3220,7 @@ public void testResponseDataTimeout() {
AtomicInteger count = new AtomicInteger();
req.exceptionHandler(t -> {
if (count.getAndIncrement() == 0) {
assertTrue(
t instanceof TimeoutException || /* HTTP/1 */
t instanceof VertxException /* HTTP/2: connection closed */);
assertTrue(t instanceof TimeoutException);
assertEquals(expected, received);
complete();
}
Expand Down

0 comments on commit 17f6dce

Please sign in to comment.