HTTPCLIENT-2141: HttpClient to not retry requests if the retry interval exceeds the response timeout
This commit is contained in:
parent
bde58d6add
commit
73c1530b3f
|
@ -34,6 +34,7 @@ import org.apache.hc.client5.http.HttpRoute;
|
||||||
import org.apache.hc.client5.http.classic.ExecChain;
|
import org.apache.hc.client5.http.classic.ExecChain;
|
||||||
import org.apache.hc.client5.http.classic.ExecChain.Scope;
|
import org.apache.hc.client5.http.classic.ExecChain.Scope;
|
||||||
import org.apache.hc.client5.http.classic.ExecChainHandler;
|
import org.apache.hc.client5.http.classic.ExecChainHandler;
|
||||||
|
import org.apache.hc.client5.http.config.RequestConfig;
|
||||||
import org.apache.hc.client5.http.protocol.HttpClientContext;
|
import org.apache.hc.client5.http.protocol.HttpClientContext;
|
||||||
import org.apache.hc.core5.annotation.Contract;
|
import org.apache.hc.core5.annotation.Contract;
|
||||||
import org.apache.hc.core5.annotation.Internal;
|
import org.apache.hc.core5.annotation.Internal;
|
||||||
|
@ -46,6 +47,7 @@ import org.apache.hc.core5.http.NoHttpResponseException;
|
||||||
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
|
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
|
||||||
import org.apache.hc.core5.util.Args;
|
import org.apache.hc.core5.util.Args;
|
||||||
import org.apache.hc.core5.util.TimeValue;
|
import org.apache.hc.core5.util.TimeValue;
|
||||||
|
import org.apache.hc.core5.util.Timeout;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -133,9 +135,16 @@ public class HttpRequestRetryExec implements ExecChainHandler {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
if (retryStrategy.retryRequest(response, execCount, context)) {
|
if (retryStrategy.retryRequest(response, execCount, context)) {
|
||||||
|
final TimeValue nextInterval = retryStrategy.getRetryInterval(response, execCount, context);
|
||||||
|
// Make sure the retry interval does not exceed the response timeout
|
||||||
|
if (TimeValue.isPositive(nextInterval)) {
|
||||||
|
final RequestConfig requestConfig = context.getRequestConfig();
|
||||||
|
final Timeout responseTimeout = requestConfig.getResponseTimeout();
|
||||||
|
if (responseTimeout != null && nextInterval.compareTo(responseTimeout) > 0) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
response.close();
|
response.close();
|
||||||
final TimeValue nextInterval =
|
|
||||||
retryStrategy.getRetryInterval(response, execCount, context);
|
|
||||||
if (TimeValue.isPositive(nextInterval)) {
|
if (TimeValue.isPositive(nextInterval)) {
|
||||||
try {
|
try {
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
|
|
|
@ -36,6 +36,7 @@ import org.apache.hc.client5.http.classic.ExecChain;
|
||||||
import org.apache.hc.client5.http.classic.ExecRuntime;
|
import org.apache.hc.client5.http.classic.ExecRuntime;
|
||||||
import org.apache.hc.client5.http.classic.methods.HttpGet;
|
import org.apache.hc.client5.http.classic.methods.HttpGet;
|
||||||
import org.apache.hc.client5.http.classic.methods.HttpPost;
|
import org.apache.hc.client5.http.classic.methods.HttpPost;
|
||||||
|
import org.apache.hc.client5.http.config.RequestConfig;
|
||||||
import org.apache.hc.client5.http.entity.EntityBuilder;
|
import org.apache.hc.client5.http.entity.EntityBuilder;
|
||||||
import org.apache.hc.client5.http.protocol.HttpClientContext;
|
import org.apache.hc.client5.http.protocol.HttpClientContext;
|
||||||
import org.apache.hc.core5.http.ClassicHttpRequest;
|
import org.apache.hc.core5.http.ClassicHttpRequest;
|
||||||
|
@ -47,6 +48,7 @@ import org.apache.hc.core5.http.HttpResponse;
|
||||||
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
|
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
|
||||||
import org.apache.hc.core5.http.protocol.HttpContext;
|
import org.apache.hc.core5.http.protocol.HttpContext;
|
||||||
import org.apache.hc.core5.util.TimeValue;
|
import org.apache.hc.core5.util.TimeValue;
|
||||||
|
import org.apache.hc.core5.util.Timeout;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -106,6 +108,70 @@ public class TestHttpRequestRetryExec {
|
||||||
Mockito.verify(response, Mockito.times(1)).close();
|
Mockito.verify(response, Mockito.times(1)).close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRetryIntervalGreaterResponseTimeout() throws Exception {
|
||||||
|
final HttpRoute route = new HttpRoute(target);
|
||||||
|
final HttpGet request = new HttpGet("/test");
|
||||||
|
final HttpClientContext context = HttpClientContext.create();
|
||||||
|
context.setRequestConfig(RequestConfig.custom()
|
||||||
|
.setResponseTimeout(Timeout.ofSeconds(3))
|
||||||
|
.build());
|
||||||
|
|
||||||
|
final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
|
||||||
|
|
||||||
|
Mockito.when(chain.proceed(
|
||||||
|
Mockito.same(request),
|
||||||
|
Mockito.<ExecChain.Scope>any())).thenReturn(response);
|
||||||
|
Mockito.when(retryStrategy.retryRequest(
|
||||||
|
Mockito.<HttpResponse>any(),
|
||||||
|
Mockito.anyInt(),
|
||||||
|
Mockito.<HttpContext>any())).thenReturn(Boolean.TRUE, Boolean.FALSE);
|
||||||
|
Mockito.when(retryStrategy.getRetryInterval(
|
||||||
|
Mockito.<HttpResponse>any(),
|
||||||
|
Mockito.anyInt(),
|
||||||
|
Mockito.<HttpContext>any())).thenReturn(TimeValue.ofSeconds(5));
|
||||||
|
|
||||||
|
final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, endpoint, context);
|
||||||
|
retryExec.execute(request, scope, chain);
|
||||||
|
|
||||||
|
Mockito.verify(chain, Mockito.times(1)).proceed(
|
||||||
|
Mockito.<ClassicHttpRequest>any(),
|
||||||
|
Mockito.same(scope));
|
||||||
|
Mockito.verify(response, Mockito.times(0)).close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRetryIntervalResponseTimeoutNull() throws Exception {
|
||||||
|
final HttpRoute route = new HttpRoute(target);
|
||||||
|
final HttpGet request = new HttpGet("/test");
|
||||||
|
final HttpClientContext context = HttpClientContext.create();
|
||||||
|
context.setRequestConfig(RequestConfig.custom()
|
||||||
|
.setResponseTimeout(null)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
|
||||||
|
|
||||||
|
Mockito.when(chain.proceed(
|
||||||
|
Mockito.same(request),
|
||||||
|
Mockito.<ExecChain.Scope>any())).thenReturn(response);
|
||||||
|
Mockito.when(retryStrategy.retryRequest(
|
||||||
|
Mockito.<HttpResponse>any(),
|
||||||
|
Mockito.anyInt(),
|
||||||
|
Mockito.<HttpContext>any())).thenReturn(Boolean.TRUE, Boolean.FALSE);
|
||||||
|
Mockito.when(retryStrategy.getRetryInterval(
|
||||||
|
Mockito.<HttpResponse>any(),
|
||||||
|
Mockito.anyInt(),
|
||||||
|
Mockito.<HttpContext>any())).thenReturn(TimeValue.ofSeconds(1));
|
||||||
|
|
||||||
|
final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, endpoint, context);
|
||||||
|
retryExec.execute(request, scope, chain);
|
||||||
|
|
||||||
|
Mockito.verify(chain, Mockito.times(2)).proceed(
|
||||||
|
Mockito.<ClassicHttpRequest>any(),
|
||||||
|
Mockito.same(scope));
|
||||||
|
Mockito.verify(response, Mockito.times(1)).close();
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = RuntimeException.class)
|
@Test(expected = RuntimeException.class)
|
||||||
public void testStrategyRuntimeException() throws Exception {
|
public void testStrategyRuntimeException() throws Exception {
|
||||||
final HttpRoute route = new HttpRoute(target);
|
final HttpRoute route = new HttpRoute(target);
|
||||||
|
|
Loading…
Reference in New Issue