diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientAutoConfiguration.java index f6ae06c18e..586c292cf9 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientAutoConfiguration.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.elasticsearch.rest; +import java.time.Duration; + import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.Credentials; @@ -68,6 +70,14 @@ public class RestClientAutoConfiguration { builder.setHttpClientConfigCallback((httpClientBuilder) -> httpClientBuilder .setDefaultCredentialsProvider(credentialsProvider)); }); + builder.setRequestConfigCallback((requestConfigBuilder) -> { + map.from(properties::getConnectionTimeout).whenNonNull() + .as(Duration::toMillis).asInt(Math::toIntExact) + .to(requestConfigBuilder::setConnectTimeout); + map.from(properties::getReadTimeout).whenNonNull().as(Duration::toMillis) + .asInt(Math::toIntExact).to(requestConfigBuilder::setSocketTimeout); + return requestConfigBuilder; + }); builderCustomizers.orderedStream() .forEach((customizer) -> customizer.customize(builder)); return builder; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientProperties.java index fe99730146..c4884e1340 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.springframework.boot.autoconfigure.elasticsearch.rest; +import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -47,6 +48,16 @@ public class RestClientProperties { */ private String password; + /** + * Connection timeout. + */ + private Duration connectionTimeout = Duration.ofSeconds(1); + + /** + * Read timeout. + */ + private Duration readTimeout = Duration.ofSeconds(30); + public List getUris() { return this.uris; } @@ -71,4 +82,20 @@ public class RestClientProperties { this.password = password; } + public Duration getConnectionTimeout() { + return this.connectionTimeout; + } + + public void setConnectionTimeout(Duration connectionTimeout) { + this.connectionTimeout = connectionTimeout; + } + + public Duration getReadTimeout() { + return this.readTimeout; + } + + public void setReadTimeout(Duration readTimeout) { + this.readTimeout = readTimeout; + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientAutoConfigurationTests.java index 86a8d4e544..ef47b22b7d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/rest/RestClientAutoConfigurationTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.autoconfigure.elasticsearch.rest; +import java.time.Duration; import java.util.HashMap; import java.util.Map; @@ -23,6 +24,7 @@ import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestClientBuilder; import org.elasticsearch.client.RestHighLevelClient; import org.junit.ClassRule; import org.junit.Test; @@ -32,6 +34,7 @@ import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.testsupport.testcontainers.ElasticsearchContainer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -74,6 +77,40 @@ public class RestClientAutoConfigurationTests { }); } + @Test + public void configureWithNoTimeoutsApplyDefaults() { + this.contextRunner.run((context) -> { + assertThat(context).hasSingleBean(RestClient.class); + RestClient restClient = context.getBean(RestClient.class); + assertTimeouts(restClient, + Duration.ofMillis(RestClientBuilder.DEFAULT_CONNECT_TIMEOUT_MILLIS), + Duration.ofMillis(RestClientBuilder.DEFAULT_SOCKET_TIMEOUT_MILLIS)); + }); + } + + @Test + public void configureWithCustomTimeouts() { + this.contextRunner + .withPropertyValues("spring.elasticsearch.rest.connection-timeout=15s", + "spring.elasticsearch.rest.read-timeout=1m") + .run((context) -> { + assertThat(context).hasSingleBean(RestClient.class); + RestClient restClient = context.getBean(RestClient.class); + assertTimeouts(restClient, Duration.ofSeconds(15), + Duration.ofMinutes(1)); + }); + } + + private static void assertTimeouts(RestClient restClient, Duration connectTimeout, + Duration readTimeout) { + Object client = ReflectionTestUtils.getField(restClient, "client"); + Object config = ReflectionTestUtils.getField(client, "defaultConfig"); + assertThat(config).hasFieldOrPropertyWithValue("socketTimeout", + Math.toIntExact(readTimeout.toMillis())); + assertThat(config).hasFieldOrPropertyWithValue("connectTimeout", + Math.toIntExact(connectTimeout.toMillis())); + } + @Test public void restClientCanQueryElasticsearchNode() { this.contextRunner diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 9e1adc7492..c289788c68 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -4805,6 +4805,7 @@ You can further tune how `RestClient` is configured, as shown in the following e [source,properties,indent=0] ---- spring.elasticsearch.rest.uris=https://search.example.com:9200 + spring.elasticsearch.rest.read-timeout=10s spring.elasticsearch.rest.username=user spring.elasticsearch.rest.password=secret ----