diff --git a/core/src/main/java/com/linecorp/armeria/client/AbstractClientOptionsBuilder.java b/core/src/main/java/com/linecorp/armeria/client/AbstractClientOptionsBuilder.java index 0c10f80b5316..0b247e8b0ea4 100644 --- a/core/src/main/java/com/linecorp/armeria/client/AbstractClientOptionsBuilder.java +++ b/core/src/main/java/com/linecorp/armeria/client/AbstractClientOptionsBuilder.java @@ -506,6 +506,17 @@ public AbstractClientOptionsBuilder contextCustomizer( return this; } + /** + * Sets the {@link ResponseTimeoutMode} which determines when a {@link #responseTimeout(Duration)}} + * will start to be scheduled. + * @see ResponseTimeoutMode + */ + @UnstableApi + public AbstractClientOptionsBuilder responseTimeoutMode(ResponseTimeoutMode responseTimeoutMode) { + return option(ClientOptions.RESPONSE_TIMEOUT_MODE, + requireNonNull(responseTimeoutMode, "responseTimeoutMode")); + } + /** * Builds {@link ClientOptions} with the given options and the * {@linkplain ClientOptions#of() default options}. diff --git a/core/src/main/java/com/linecorp/armeria/client/AbstractHttpRequestHandler.java b/core/src/main/java/com/linecorp/armeria/client/AbstractHttpRequestHandler.java index 5aa065e77068..d6ebf460fd0f 100644 --- a/core/src/main/java/com/linecorp/armeria/client/AbstractHttpRequestHandler.java +++ b/core/src/main/java/com/linecorp/armeria/client/AbstractHttpRequestHandler.java @@ -198,6 +198,9 @@ final boolean tryInitialize() { final CancellationScheduler scheduler = cancellationScheduler(); if (scheduler != null) { scheduler.updateTask(newCancellationTask()); + if (ctx.responseTimeoutMode() == ResponseTimeoutMode.REQUEST_WRITE) { + scheduler.start(); + } } if (ctx.isCancelled()) { // The previous cancellation task wraps the cause with an UnprocessedRequestException diff --git a/core/src/main/java/com/linecorp/armeria/client/ClientBuilder.java b/core/src/main/java/com/linecorp/armeria/client/ClientBuilder.java index caa4fb860c89..47db606eac5e 100644 --- a/core/src/main/java/com/linecorp/armeria/client/ClientBuilder.java +++ b/core/src/main/java/com/linecorp/armeria/client/ClientBuilder.java @@ -295,4 +295,9 @@ public ClientBuilder contextCustomizer( public ClientBuilder contextHook(Supplier contextHook) { return (ClientBuilder) super.contextHook(contextHook); } + + @Override + public ClientBuilder responseTimeoutMode(ResponseTimeoutMode responseTimeoutMode) { + return (ClientBuilder) super.responseTimeoutMode(responseTimeoutMode); + } } diff --git a/core/src/main/java/com/linecorp/armeria/client/ClientOptions.java b/core/src/main/java/com/linecorp/armeria/client/ClientOptions.java index 20fc04b9fb69..bb13f99139d9 100644 --- a/core/src/main/java/com/linecorp/armeria/client/ClientOptions.java +++ b/core/src/main/java/com/linecorp/armeria/client/ClientOptions.java @@ -156,6 +156,10 @@ public final class ClientOptions public static final ClientOption> CONTEXT_HOOK = ClientOption.define("CONTEXT_HOOK", NOOP_CONTEXT_HOOK); + @UnstableApi + public static final ClientOption RESPONSE_TIMEOUT_MODE = + ClientOption.define("RESPONSE_TIMEOUT_MODE", Flags.responseTimeoutMode()); + private static final List PROHIBITED_HEADER_NAMES = ImmutableList.of( HttpHeaderNames.HTTP2_SETTINGS, HttpHeaderNames.METHOD, @@ -395,6 +399,16 @@ public Supplier contextHook() { return (Supplier) get(CONTEXT_HOOK); } + /** + * Returns the {@link ResponseTimeoutMode} which determines when a {@link #responseTimeoutMillis()} + * will start to be scheduled. + * @see ResponseTimeoutMode + */ + @UnstableApi + public ResponseTimeoutMode responseTimeoutMode() { + return get(RESPONSE_TIMEOUT_MODE); + } + /** * Returns a new {@link ClientOptionsBuilder} created from this {@link ClientOptions}. */ diff --git a/core/src/main/java/com/linecorp/armeria/client/ClientOptionsBuilder.java b/core/src/main/java/com/linecorp/armeria/client/ClientOptionsBuilder.java index fa19ec265e37..70fa0cc947af 100644 --- a/core/src/main/java/com/linecorp/armeria/client/ClientOptionsBuilder.java +++ b/core/src/main/java/com/linecorp/armeria/client/ClientOptionsBuilder.java @@ -222,4 +222,9 @@ public ClientOptionsBuilder contextCustomizer( public ClientOptionsBuilder contextHook(Supplier contextHook) { return (ClientOptionsBuilder) super.contextHook(contextHook); } + + @Override + public ClientOptionsBuilder responseTimeoutMode(ResponseTimeoutMode responseTimeoutMode) { + return (ClientOptionsBuilder) super.responseTimeoutMode(responseTimeoutMode); + } } diff --git a/core/src/main/java/com/linecorp/armeria/client/ClientRequestContext.java b/core/src/main/java/com/linecorp/armeria/client/ClientRequestContext.java index 4aea2c21933a..6c86874054a6 100644 --- a/core/src/main/java/com/linecorp/armeria/client/ClientRequestContext.java +++ b/core/src/main/java/com/linecorp/armeria/client/ClientRequestContext.java @@ -612,6 +612,14 @@ default boolean isTimedOut() { @UnstableApi ExchangeType exchangeType(); + /** + * Returns the {@link ResponseTimeoutMode} which determines when a {@link #responseTimeoutMillis()} + * will start to be scheduled. + * @see ResponseTimeoutMode + */ + @UnstableApi + ResponseTimeoutMode responseTimeoutMode(); + @Override default ClientRequestContext unwrap() { return (ClientRequestContext) RequestContext.super.unwrap(); diff --git a/core/src/main/java/com/linecorp/armeria/client/ClientRequestContextWrapper.java b/core/src/main/java/com/linecorp/armeria/client/ClientRequestContextWrapper.java index 6e411d99fb02..f03e0a15de37 100644 --- a/core/src/main/java/com/linecorp/armeria/client/ClientRequestContextWrapper.java +++ b/core/src/main/java/com/linecorp/armeria/client/ClientRequestContextWrapper.java @@ -167,6 +167,11 @@ public ExchangeType exchangeType() { return unwrap().exchangeType(); } + @Override + public ResponseTimeoutMode responseTimeoutMode() { + return unwrap().responseTimeoutMode(); + } + @Override public void hook(Supplier contextHook) { unwrap().hook(contextHook); diff --git a/core/src/main/java/com/linecorp/armeria/client/HttpResponseWrapper.java b/core/src/main/java/com/linecorp/armeria/client/HttpResponseWrapper.java index a6a7e7a3e3d8..fac7ac9ce34b 100644 --- a/core/src/main/java/com/linecorp/armeria/client/HttpResponseWrapper.java +++ b/core/src/main/java/com/linecorp/armeria/client/HttpResponseWrapper.java @@ -294,7 +294,9 @@ void initTimeout() { final CancellationScheduler responseCancellationScheduler = ctxExtension.responseCancellationScheduler(); responseCancellationScheduler.updateTask(newCancellationTask()); - responseCancellationScheduler.start(); + if (ctx.responseTimeoutMode() == ResponseTimeoutMode.RESPONSE_READ) { + responseCancellationScheduler.start(); + } } } diff --git a/core/src/main/java/com/linecorp/armeria/client/ResponseTimeoutMode.java b/core/src/main/java/com/linecorp/armeria/client/ResponseTimeoutMode.java new file mode 100644 index 000000000000..eac6b74020f3 --- /dev/null +++ b/core/src/main/java/com/linecorp/armeria/client/ResponseTimeoutMode.java @@ -0,0 +1,61 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package com.linecorp.armeria.client; + +import com.linecorp.armeria.common.annotation.UnstableApi; + +/** + * Specifies when to start scheduling a response timeout. Note that this value does not affect calculation + * of the response timeout, but determines when the response timeout will affect the ongoing request. + * + *

For example, assume the following scenario with a responseTimeout of 2 seconds: + *

{@code
+ * |---request start---decorators(3s)---connection acquisition(2s)---request write/response read---|
+ * }
+ *
    + *
  • + * If {@link ResponseTimeoutMode#RESPONSE_READ} is used, the request will go through the decorators, + * acquire a connection, and write the request. Once the response header is read, the timeout task + * will trigger immediately since 5 seconds has passed which exceeds the responseTimeout of 2 seconds. + *
  • + *
  • + * If {@link ResponseTimeoutMode#REQUEST_START} is used, the timeout task will be scheduled + * immediately on request start. The timeout task will trigger while the request goes through + * the decorator, and the request will fail before acquiring a new connection. + *
  • + *
+ */ +@UnstableApi +public enum ResponseTimeoutMode { + + /** + * The response timeout is scheduled when the request first starts to execute. More specifically, + * the scheduling will take place when the request starts to go through the decorator chain. + */ + REQUEST_START, + + /** + * The response timeout is scheduled when the request is first written on the wire. + */ + REQUEST_WRITE, + + /** + * The response timeout is scheduled either when the response bytes are first read, or when the client + * finishes writing the request. + */ + RESPONSE_READ, +} diff --git a/core/src/main/java/com/linecorp/armeria/client/RestClientBuilder.java b/core/src/main/java/com/linecorp/armeria/client/RestClientBuilder.java index ee677a737887..c577104c969b 100644 --- a/core/src/main/java/com/linecorp/armeria/client/RestClientBuilder.java +++ b/core/src/main/java/com/linecorp/armeria/client/RestClientBuilder.java @@ -254,4 +254,9 @@ public RestClientBuilder contextCustomizer( public RestClientBuilder contextHook(Supplier contextHook) { return (RestClientBuilder) super.contextHook(contextHook); } + + @Override + public RestClientBuilder responseTimeoutMode(ResponseTimeoutMode responseTimeoutMode) { + return (RestClientBuilder) super.responseTimeoutMode(responseTimeoutMode); + } } diff --git a/core/src/main/java/com/linecorp/armeria/client/WebClientBuilder.java b/core/src/main/java/com/linecorp/armeria/client/WebClientBuilder.java index e5f279a6fd95..f6d1d3f656fa 100644 --- a/core/src/main/java/com/linecorp/armeria/client/WebClientBuilder.java +++ b/core/src/main/java/com/linecorp/armeria/client/WebClientBuilder.java @@ -250,4 +250,9 @@ public WebClientBuilder contextCustomizer( Consumer contextCustomizer) { return (WebClientBuilder) super.contextCustomizer(contextCustomizer); } + + @Override + public WebClientBuilder responseTimeoutMode(ResponseTimeoutMode responseTimeoutMode) { + return (WebClientBuilder) super.responseTimeoutMode(responseTimeoutMode); + } } diff --git a/core/src/main/java/com/linecorp/armeria/client/websocket/WebSocketClientBuilder.java b/core/src/main/java/com/linecorp/armeria/client/websocket/WebSocketClientBuilder.java index 8f8951516581..1109efe8477d 100644 --- a/core/src/main/java/com/linecorp/armeria/client/websocket/WebSocketClientBuilder.java +++ b/core/src/main/java/com/linecorp/armeria/client/websocket/WebSocketClientBuilder.java @@ -45,6 +45,7 @@ import com.linecorp.armeria.client.DecoratingRpcClientFunction; import com.linecorp.armeria.client.Endpoint; import com.linecorp.armeria.client.HttpClient; +import com.linecorp.armeria.client.ResponseTimeoutMode; import com.linecorp.armeria.client.RpcClient; import com.linecorp.armeria.client.WebClient; import com.linecorp.armeria.client.endpoint.EndpointGroup; @@ -390,4 +391,9 @@ public WebSocketClientBuilder contextCustomizer( public WebSocketClientBuilder contextHook(Supplier contextHook) { return (WebSocketClientBuilder) super.contextHook(contextHook); } + + @Override + public WebSocketClientBuilder responseTimeoutMode(ResponseTimeoutMode responseTimeoutMode) { + return (WebSocketClientBuilder) super.responseTimeoutMode(responseTimeoutMode); + } } diff --git a/core/src/main/java/com/linecorp/armeria/common/DefaultFlagsProvider.java b/core/src/main/java/com/linecorp/armeria/common/DefaultFlagsProvider.java index 2d4acd9f64f1..fde0412b3ec1 100644 --- a/core/src/main/java/com/linecorp/armeria/common/DefaultFlagsProvider.java +++ b/core/src/main/java/com/linecorp/armeria/common/DefaultFlagsProvider.java @@ -26,6 +26,7 @@ import com.google.common.base.Splitter; import com.google.common.collect.ImmutableSet; +import com.linecorp.armeria.client.ResponseTimeoutMode; import com.linecorp.armeria.common.util.Sampler; import com.linecorp.armeria.common.util.TlsEngineType; import com.linecorp.armeria.common.util.TransportType; @@ -100,6 +101,7 @@ final class DefaultFlagsProvider implements FlagsProvider { static final String DNS_CACHE_SPEC = "maximumSize=4096"; static final long DEFAULT_UNLOGGED_EXCEPTIONS_REPORT_INTERVAL_MILLIS = 10000; static final long DEFAULT_HTTP1_CONNECTION_CLOSE_DELAY_MILLIS = 3000; + static final ResponseTimeoutMode DEFAULT_RESPONSE_TIMEOUT_MODE = ResponseTimeoutMode.RESPONSE_READ; private DefaultFlagsProvider() {} @@ -511,4 +513,9 @@ public DistributionStatisticConfig distributionStatisticConfig() { public Long defaultHttp1ConnectionCloseDelayMillis() { return DEFAULT_HTTP1_CONNECTION_CLOSE_DELAY_MILLIS; } + + @Override + public ResponseTimeoutMode responseTimeoutMode() { + return DEFAULT_RESPONSE_TIMEOUT_MODE; + } } diff --git a/core/src/main/java/com/linecorp/armeria/common/Flags.java b/core/src/main/java/com/linecorp/armeria/common/Flags.java index fff4fd96d7b0..1beff0872cb5 100644 --- a/core/src/main/java/com/linecorp/armeria/common/Flags.java +++ b/core/src/main/java/com/linecorp/armeria/common/Flags.java @@ -46,6 +46,7 @@ import com.linecorp.armeria.client.ClientFactoryBuilder; import com.linecorp.armeria.client.DnsResolverGroupBuilder; import com.linecorp.armeria.client.Endpoint; +import com.linecorp.armeria.client.ResponseTimeoutMode; import com.linecorp.armeria.client.retry.Backoff; import com.linecorp.armeria.client.retry.RetryingClient; import com.linecorp.armeria.client.retry.RetryingRpcClient; @@ -439,6 +440,9 @@ private static boolean validateTransportType(TransportType transportType, String getValue(FlagsProvider::defaultHttp1ConnectionCloseDelayMillis, "defaultHttp1ConnectionCloseDelayMillis", value -> value >= 0); + private static final ResponseTimeoutMode RESPONSE_TIMEOUT_MODE = + getValue(FlagsProvider::responseTimeoutMode, "responseTimeoutMode"); + /** * Returns the specification of the {@link Sampler} that determines whether to retain the stack * trace of the exceptions that are thrown frequently by Armeria. A sampled exception will have the stack @@ -1642,6 +1646,20 @@ public static long defaultHttp1ConnectionCloseDelayMillis() { return DEFAULT_HTTP1_CONNECTION_CLOSE_DELAY_MILLIS; } + /** + * Returns the {@link ResponseTimeoutMode} which determines when a response timeout + * will start to be scheduled. + * + *

The default value of this flag is RESPONSE_READ. Specify the + * {@code -Dcom.linecorp.armeria.responseTimeoutMode=ResponseTimeoutMode} JVM option to + * override the default value. + * @see ResponseTimeoutMode + */ + @UnstableApi + public static ResponseTimeoutMode responseTimeoutMode() { + return RESPONSE_TIMEOUT_MODE; + } + @Nullable private static String nullableCaffeineSpec(Function method, String flagName) { return caffeineSpec(method, flagName, true); diff --git a/core/src/main/java/com/linecorp/armeria/common/FlagsProvider.java b/core/src/main/java/com/linecorp/armeria/common/FlagsProvider.java index 72dfb4f8d6ea..ab90454285b4 100644 --- a/core/src/main/java/com/linecorp/armeria/common/FlagsProvider.java +++ b/core/src/main/java/com/linecorp/armeria/common/FlagsProvider.java @@ -34,6 +34,7 @@ import com.linecorp.armeria.client.ClientBuilder; import com.linecorp.armeria.client.ClientFactoryBuilder; import com.linecorp.armeria.client.DnsResolverGroupBuilder; +import com.linecorp.armeria.client.ResponseTimeoutMode; import com.linecorp.armeria.client.retry.Backoff; import com.linecorp.armeria.client.retry.RetryingClient; import com.linecorp.armeria.client.retry.RetryingRpcClient; @@ -1232,4 +1233,19 @@ default DistributionStatisticConfig distributionStatisticConfig() { default Long defaultHttp1ConnectionCloseDelayMillis() { return null; } + + /** + * Returns the {@link ResponseTimeoutMode} which determines when a response timeout + * will start to be scheduled. + * + *

The default value of this flag is RESPONSE_READ. Specify the + * {@code -Dcom.linecorp.armeria.responseTimeoutMode=ResponseTimeoutMode} JVM option to + * override the default value. + * @see ResponseTimeoutMode + */ + @Nullable + @UnstableApi + default ResponseTimeoutMode responseTimeoutMode() { + return null; + } } diff --git a/core/src/main/java/com/linecorp/armeria/common/SystemPropertyFlagsProvider.java b/core/src/main/java/com/linecorp/armeria/common/SystemPropertyFlagsProvider.java index 2ea4026abdc2..4263e19773e9 100644 --- a/core/src/main/java/com/linecorp/armeria/common/SystemPropertyFlagsProvider.java +++ b/core/src/main/java/com/linecorp/armeria/common/SystemPropertyFlagsProvider.java @@ -36,6 +36,7 @@ import com.google.common.collect.Sets; import com.google.common.collect.Streams; +import com.linecorp.armeria.client.ResponseTimeoutMode; import com.linecorp.armeria.common.annotation.Nullable; import com.linecorp.armeria.common.util.InetAddressPredicates; import com.linecorp.armeria.common.util.Sampler; @@ -598,6 +599,12 @@ public Long defaultUnloggedExceptionsReportIntervalMillis() { return getLong("defaultUnloggedExceptionsReportIntervalMillis"); } + @Override + @Nullable + public ResponseTimeoutMode responseTimeoutMode() { + return getAndParse("responseTimeoutMode", ResponseTimeoutMode::valueOf); + } + @Nullable private static Long getLong(String name) { return getAndParse(name, Long::parseLong); diff --git a/core/src/main/java/com/linecorp/armeria/internal/client/DefaultClientRequestContext.java b/core/src/main/java/com/linecorp/armeria/internal/client/DefaultClientRequestContext.java index f8dc94c529b1..70066f3c873f 100644 --- a/core/src/main/java/com/linecorp/armeria/internal/client/DefaultClientRequestContext.java +++ b/core/src/main/java/com/linecorp/armeria/internal/client/DefaultClientRequestContext.java @@ -40,6 +40,7 @@ import com.linecorp.armeria.client.ClientRequestContext; import com.linecorp.armeria.client.Endpoint; import com.linecorp.armeria.client.RequestOptions; +import com.linecorp.armeria.client.ResponseTimeoutMode; import com.linecorp.armeria.client.UnprocessedRequestException; import com.linecorp.armeria.client.endpoint.EndpointGroup; import com.linecorp.armeria.common.AttributesGetters; @@ -569,8 +570,12 @@ private void initializeResponseCancellationScheduler() { log.endResponse(cause); } }; - responseCancellationScheduler.init(eventLoop().withoutContext()); - responseCancellationScheduler.updateTask(cancellationTask); + if (options.responseTimeoutMode() == ResponseTimeoutMode.REQUEST_START) { + responseCancellationScheduler.initAndStart(eventLoop().withoutContext(), cancellationTask); + } else { + responseCancellationScheduler.init(eventLoop().withoutContext()); + responseCancellationScheduler.updateTask(cancellationTask); + } } @Nullable @@ -1053,4 +1058,9 @@ public CompletableFuture initiateConnectionShutdown() { }); return completableFuture; } + + @Override + public ResponseTimeoutMode responseTimeoutMode() { + return options.responseTimeoutMode(); + } } diff --git a/core/src/test/java/com/linecorp/armeria/client/TimeoutModeTest.java b/core/src/test/java/com/linecorp/armeria/client/TimeoutModeTest.java new file mode 100644 index 000000000000..ad6c595c75b6 --- /dev/null +++ b/core/src/test/java/com/linecorp/armeria/client/TimeoutModeTest.java @@ -0,0 +1,93 @@ +/* + * Copyright 2024 LINE Corporation + * + * LINE Corporation licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package com.linecorp.armeria.client; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import com.linecorp.armeria.common.CommonPools; +import com.linecorp.armeria.common.HttpMethod; +import com.linecorp.armeria.common.HttpRequest; +import com.linecorp.armeria.common.HttpRequestWriter; +import com.linecorp.armeria.common.HttpResponse; +import com.linecorp.armeria.server.ServerBuilder; +import com.linecorp.armeria.testing.junit5.server.ServerExtension; + +class TimeoutModeTest { + + @RegisterExtension + static ServerExtension server = new ServerExtension() { + @Override + protected void configure(ServerBuilder sb) throws Exception { + sb.service("/", (ctx, req) -> HttpResponse.streaming()); + } + }; + + @Test + void timeoutMode_requestStart() { + final HttpResponse res = server + .webClient(cb -> { + cb.responseTimeoutMode(ResponseTimeoutMode.REQUEST_START); + cb.responseTimeoutMillis(50); + cb.decorator((delegate, ctx, req) -> { + final CompletableFuture f = new CompletableFuture<>(); + CommonPools.workerGroup().schedule(() -> f.complete(delegate.execute(ctx, req)), + 100, TimeUnit.MILLISECONDS); + return HttpResponse.of(f); + }); + }) + .get("/"); + assertThatThrownBy(() -> res.aggregate().join()) + .isInstanceOf(CompletionException.class) + .hasCauseInstanceOf(UnprocessedRequestException.class) + .hasRootCauseInstanceOf(ResponseTimeoutException.class); + } + + @Test + void timeoutMode_requestWrite() { + final HttpRequestWriter streaming = HttpRequest.streaming(HttpMethod.POST, "/"); + final HttpResponse res = server + .webClient(cb -> { + cb.responseTimeoutMode(ResponseTimeoutMode.REQUEST_WRITE); + cb.responseTimeout(Duration.ofMillis(50)); + }) + .execute(streaming); + assertThatThrownBy(() -> res.aggregate().join()) + .isInstanceOf(CompletionException.class) + .hasCauseInstanceOf(ResponseTimeoutException.class); + } + + @Test + void timeoutMode_responseWrite() { + final HttpResponse res = server + .webClient(cb -> { + cb.responseTimeoutMode(ResponseTimeoutMode.RESPONSE_READ); + cb.responseTimeout(Duration.ofMillis(50)); + }) + .get("/"); + assertThatThrownBy(() -> res.aggregate().join()) + .isInstanceOf(CompletionException.class) + .hasCauseInstanceOf(ResponseTimeoutException.class); + } +} diff --git a/eureka/src/main/java/com/linecorp/armeria/client/eureka/EurekaEndpointGroupBuilder.java b/eureka/src/main/java/com/linecorp/armeria/client/eureka/EurekaEndpointGroupBuilder.java index 1013f83bdd24..26857db0c016 100644 --- a/eureka/src/main/java/com/linecorp/armeria/client/eureka/EurekaEndpointGroupBuilder.java +++ b/eureka/src/main/java/com/linecorp/armeria/client/eureka/EurekaEndpointGroupBuilder.java @@ -42,6 +42,7 @@ import com.linecorp.armeria.client.DecoratingRpcClientFunction; import com.linecorp.armeria.client.Endpoint; import com.linecorp.armeria.client.HttpClient; +import com.linecorp.armeria.client.ResponseTimeoutMode; import com.linecorp.armeria.client.RpcClient; import com.linecorp.armeria.client.WebClient; import com.linecorp.armeria.client.endpoint.AbstractDynamicEndpointGroupBuilder; @@ -432,6 +433,11 @@ public EurekaEndpointGroupBuilder contextCustomizer( return (EurekaEndpointGroupBuilder) super.contextCustomizer(contextCustomizer); } + @Override + public EurekaEndpointGroupBuilder responseTimeoutMode(ResponseTimeoutMode responseTimeoutMode) { + return (EurekaEndpointGroupBuilder) super.responseTimeoutMode(responseTimeoutMode); + } + @Override public EurekaEndpointGroupBuilder allowEmptyEndpoints(boolean allowEmptyEndpoints) { dynamicEndpointGroupBuilder.allowEmptyEndpoints(allowEmptyEndpoints); diff --git a/eureka/src/main/java/com/linecorp/armeria/server/eureka/EurekaUpdatingListenerBuilder.java b/eureka/src/main/java/com/linecorp/armeria/server/eureka/EurekaUpdatingListenerBuilder.java index 5d684bdaed52..9b48c079eb76 100644 --- a/eureka/src/main/java/com/linecorp/armeria/server/eureka/EurekaUpdatingListenerBuilder.java +++ b/eureka/src/main/java/com/linecorp/armeria/server/eureka/EurekaUpdatingListenerBuilder.java @@ -41,6 +41,7 @@ import com.linecorp.armeria.client.DecoratingRpcClientFunction; import com.linecorp.armeria.client.Endpoint; import com.linecorp.armeria.client.HttpClient; +import com.linecorp.armeria.client.ResponseTimeoutMode; import com.linecorp.armeria.client.RpcClient; import com.linecorp.armeria.client.WebClient; import com.linecorp.armeria.client.endpoint.EndpointGroup; @@ -551,4 +552,9 @@ public EurekaUpdatingListenerBuilder contextCustomizer( Consumer contextCustomizer) { return (EurekaUpdatingListenerBuilder) super.contextCustomizer(contextCustomizer); } + + @Override + public EurekaUpdatingListenerBuilder responseTimeoutMode(ResponseTimeoutMode responseTimeoutMode) { + return (EurekaUpdatingListenerBuilder) super.responseTimeoutMode(responseTimeoutMode); + } } diff --git a/grpc/src/main/java/com/linecorp/armeria/client/grpc/GrpcClientBuilder.java b/grpc/src/main/java/com/linecorp/armeria/client/grpc/GrpcClientBuilder.java index 7b9c59e5baa0..428dcb16be42 100644 --- a/grpc/src/main/java/com/linecorp/armeria/client/grpc/GrpcClientBuilder.java +++ b/grpc/src/main/java/com/linecorp/armeria/client/grpc/GrpcClientBuilder.java @@ -55,6 +55,7 @@ import com.linecorp.armeria.client.DecoratingRpcClientFunction; import com.linecorp.armeria.client.Endpoint; import com.linecorp.armeria.client.HttpClient; +import com.linecorp.armeria.client.ResponseTimeoutMode; import com.linecorp.armeria.client.RpcClient; import com.linecorp.armeria.client.endpoint.EndpointGroup; import com.linecorp.armeria.client.redirect.RedirectConfig; @@ -595,6 +596,11 @@ public GrpcClientBuilder contextCustomizer( return (GrpcClientBuilder) super.contextCustomizer(contextCustomizer); } + @Override + public GrpcClientBuilder responseTimeoutMode(ResponseTimeoutMode responseTimeoutMode) { + return (GrpcClientBuilder) super.responseTimeoutMode(responseTimeoutMode); + } + /** * Sets the specified {@link GrpcExceptionHandlerFunction} that maps a {@link Throwable} * to a gRPC {@link Status}. diff --git a/retrofit2/src/main/java/com/linecorp/armeria/client/retrofit2/ArmeriaRetrofitBuilder.java b/retrofit2/src/main/java/com/linecorp/armeria/client/retrofit2/ArmeriaRetrofitBuilder.java index 2d588ecf6979..66d46a84b559 100644 --- a/retrofit2/src/main/java/com/linecorp/armeria/client/retrofit2/ArmeriaRetrofitBuilder.java +++ b/retrofit2/src/main/java/com/linecorp/armeria/client/retrofit2/ArmeriaRetrofitBuilder.java @@ -43,6 +43,7 @@ import com.linecorp.armeria.client.DecoratingRpcClientFunction; import com.linecorp.armeria.client.Endpoint; import com.linecorp.armeria.client.HttpClient; +import com.linecorp.armeria.client.ResponseTimeoutMode; import com.linecorp.armeria.client.RpcClient; import com.linecorp.armeria.client.WebClient; import com.linecorp.armeria.client.endpoint.EndpointGroup; @@ -450,4 +451,9 @@ public ArmeriaRetrofitBuilder contextCustomizer( Consumer contextCustomizer) { return (ArmeriaRetrofitBuilder) super.contextCustomizer(contextCustomizer); } + + @Override + public ArmeriaRetrofitBuilder responseTimeoutMode(ResponseTimeoutMode responseTimeoutMode) { + return (ArmeriaRetrofitBuilder) super.responseTimeoutMode(responseTimeoutMode); + } } diff --git a/thrift/thrift0.13/src/main/java/com/linecorp/armeria/client/thrift/ThriftClientBuilder.java b/thrift/thrift0.13/src/main/java/com/linecorp/armeria/client/thrift/ThriftClientBuilder.java index 2fc5adf8e0a5..7591c0d71aa6 100644 --- a/thrift/thrift0.13/src/main/java/com/linecorp/armeria/client/thrift/ThriftClientBuilder.java +++ b/thrift/thrift0.13/src/main/java/com/linecorp/armeria/client/thrift/ThriftClientBuilder.java @@ -42,6 +42,7 @@ import com.linecorp.armeria.client.DecoratingRpcClientFunction; import com.linecorp.armeria.client.Endpoint; import com.linecorp.armeria.client.HttpClient; +import com.linecorp.armeria.client.ResponseTimeoutMode; import com.linecorp.armeria.client.RpcClient; import com.linecorp.armeria.client.endpoint.EndpointGroup; import com.linecorp.armeria.client.redirect.RedirectConfig; @@ -373,4 +374,9 @@ public ThriftClientBuilder contextCustomizer( Consumer contextCustomizer) { return (ThriftClientBuilder) super.contextCustomizer(contextCustomizer); } + + @Override + public ThriftClientBuilder responseTimeoutMode(ResponseTimeoutMode responseTimeoutMode) { + return (ThriftClientBuilder) super.responseTimeoutMode(responseTimeoutMode); + } }