From af21504d330b609164a809805cc9fc108caef442 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Wed, 8 Jan 2025 12:43:26 +0100 Subject: [PATCH 1/4] Add possibility to customise HC5 RequestConfig. --- ...urekaClientHttpRequestFactorySupplier.java | 52 ++++++++++++++----- ...urekaClientHttpRequestFactorySupplier.java | 8 +++ 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/DefaultEurekaClientHttpRequestFactorySupplier.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/DefaultEurekaClientHttpRequestFactorySupplier.java index ab368a5226..98dfdaab6f 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/DefaultEurekaClientHttpRequestFactorySupplier.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/DefaultEurekaClientHttpRequestFactorySupplier.java @@ -16,11 +16,15 @@ package org.springframework.cloud.netflix.eureka.http; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.TimeUnit; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; +import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; @@ -31,6 +35,7 @@ import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.util.Timeout; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.cloud.netflix.eureka.RestTemplateTimeoutProperties; import org.springframework.cloud.netflix.eureka.TimeoutProperties; import org.springframework.http.client.ClientHttpRequestFactory; @@ -49,10 +54,12 @@ public class DefaultEurekaClientHttpRequestFactorySupplier implements EurekaClientHttpRequestFactorySupplier { private final TimeoutProperties timeoutProperties; + // TODO: switch to final after removing deprecated interfaces + private ObjectProvider> requestConfigCustomizers; /** * @deprecated in favour of - * {@link DefaultEurekaClientHttpRequestFactorySupplier#DefaultEurekaClientHttpRequestFactorySupplier(TimeoutProperties)} + * {@link DefaultEurekaClientHttpRequestFactorySupplier#DefaultEurekaClientHttpRequestFactorySupplier(TimeoutProperties, ObjectProvider)} */ @Deprecated(forRemoval = true) public DefaultEurekaClientHttpRequestFactorySupplier() { @@ -61,17 +68,28 @@ public DefaultEurekaClientHttpRequestFactorySupplier() { /** * @deprecated in favour of - * {@link DefaultEurekaClientHttpRequestFactorySupplier#DefaultEurekaClientHttpRequestFactorySupplier(TimeoutProperties)} + * {@link DefaultEurekaClientHttpRequestFactorySupplier#DefaultEurekaClientHttpRequestFactorySupplier(TimeoutProperties, ObjectProvider)} */ @Deprecated(forRemoval = true) public DefaultEurekaClientHttpRequestFactorySupplier(RestTemplateTimeoutProperties timeoutProperties) { this.timeoutProperties = timeoutProperties; } + /** + * @deprecated in favour of + * {@link DefaultEurekaClientHttpRequestFactorySupplier#DefaultEurekaClientHttpRequestFactorySupplier(TimeoutProperties, ObjectProvider)} + */ + @Deprecated(forRemoval = true) public DefaultEurekaClientHttpRequestFactorySupplier(TimeoutProperties timeoutProperties) { this.timeoutProperties = timeoutProperties; } + public DefaultEurekaClientHttpRequestFactorySupplier(TimeoutProperties timeoutProperties, + ObjectProvider> requestConfigCustomizers) { + this.timeoutProperties = timeoutProperties; + this.requestConfigCustomizers = requestConfigCustomizers; + } + @Override public ClientHttpRequestFactory get(SSLContext sslContext, @Nullable HostnameVerifier hostnameVerifier) { HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); @@ -79,9 +97,7 @@ public ClientHttpRequestFactory get(SSLContext sslContext, @Nullable HostnameVer httpClientBuilder .setConnectionManager(buildConnectionManager(sslContext, hostnameVerifier, timeoutProperties)); } - if (timeoutProperties != null) { - httpClientBuilder.setDefaultRequestConfig(buildRequestConfig()); - } + httpClientBuilder.setDefaultRequestConfig(buildRequestConfig()); CloseableHttpClient httpClient = httpClientBuilder.build(); HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); @@ -90,7 +106,7 @@ public ClientHttpRequestFactory get(SSLContext sslContext, @Nullable HostnameVer } private HttpClientConnectionManager buildConnectionManager(SSLContext sslContext, HostnameVerifier hostnameVerifier, - TimeoutProperties restTemplateTimeoutProperties) { + TimeoutProperties timeoutProperties) { PoolingHttpClientConnectionManagerBuilder connectionManagerBuilder = PoolingHttpClientConnectionManagerBuilder .create(); SSLConnectionSocketFactoryBuilder sslConnectionSocketFactoryBuilder = SSLConnectionSocketFactoryBuilder @@ -102,20 +118,30 @@ private HttpClientConnectionManager buildConnectionManager(SSLContext sslContext sslConnectionSocketFactoryBuilder.setHostnameVerifier(hostnameVerifier); } connectionManagerBuilder.setSSLSocketFactory(sslConnectionSocketFactoryBuilder.build()); - if (restTemplateTimeoutProperties != null) { + if (timeoutProperties != null) { connectionManagerBuilder.setDefaultSocketConfig(SocketConfig.custom() - .setSoTimeout(Timeout.of(restTemplateTimeoutProperties.getSocketTimeout(), TimeUnit.MILLISECONDS)) + .setSoTimeout(Timeout.of(timeoutProperties.getSocketTimeout(), TimeUnit.MILLISECONDS)) .build()); + connectionManagerBuilder.setDefaultConnectionConfig(ConnectionConfig.custom(). + setConnectTimeout(Timeout.of(timeoutProperties.getConnectTimeout(), + TimeUnit.MILLISECONDS)).build() + ); } return connectionManagerBuilder.build(); } private RequestConfig buildRequestConfig() { - return RequestConfig.custom() - .setConnectTimeout(Timeout.of(timeoutProperties.getConnectTimeout(), TimeUnit.MILLISECONDS)) - .setConnectionRequestTimeout( - Timeout.of(timeoutProperties.getConnectRequestTimeout(), TimeUnit.MILLISECONDS)) - .build(); + RequestConfig.Builder requestConfigBuilder = RequestConfig.custom(); + if (timeoutProperties != null) { + requestConfigBuilder.setConnectionRequestTimeout( + Timeout.of(timeoutProperties.getConnectRequestTimeout(), TimeUnit.MILLISECONDS)); + } + Optional.ofNullable(requestConfigCustomizers).ifPresent( + requestConfigCustomizers -> + requestConfigCustomizers + .getIfAvailable(Collections::emptySet) + .forEach(customizer -> customizer.customize(requestConfigBuilder))); + return requestConfigBuilder.build(); } } diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/EurekaClientHttpRequestFactorySupplier.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/EurekaClientHttpRequestFactorySupplier.java index b5a104c3c3..9ef5302f76 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/EurekaClientHttpRequestFactorySupplier.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/EurekaClientHttpRequestFactorySupplier.java @@ -19,6 +19,8 @@ import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; +import org.apache.hc.client5.http.config.RequestConfig; + import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.lang.Nullable; @@ -38,4 +40,10 @@ public interface EurekaClientHttpRequestFactorySupplier { */ ClientHttpRequestFactory get(SSLContext sslContext, @Nullable HostnameVerifier hostnameVerifier); + interface RequestConfigCustomizer { + + void customize(RequestConfig.Builder builder); + + } + } From 5253e7e768296d42b7d691e6e39869b6292a17b2 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Wed, 8 Jan 2025 20:33:25 +0100 Subject: [PATCH 2/4] Use customiserw while creating EurekaClientHttpRequestFactorySupplier beans. Add tests. --- ...coveryClientOptionalArgsConfiguration.java | 6 ++-- ...ekaConfigServerBootstrapConfiguration.java | 7 ++-- .../EurekaConfigServerBootstrapper.java | 2 +- .../http/EurekaServerMockApplication.java | 10 +++++- .../http/RestClientEurekaHttpClientTests.java | 35 ++++++++++++++++--- 5 files changed, 49 insertions(+), 11 deletions(-) diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java index a80c38e732..355f7888ef 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java @@ -18,6 +18,7 @@ import java.io.IOException; import java.security.GeneralSecurityException; +import java.util.Set; import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs; import com.netflix.discovery.shared.transport.jersey.TransportClientFactories; @@ -189,8 +190,9 @@ protected static class RestClientConfiguration { @Bean @ConditionalOnMissingBean EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier( - RestClientTimeoutProperties restClientTimeoutProperties) { - return new DefaultEurekaClientHttpRequestFactorySupplier(restClientTimeoutProperties); + RestClientTimeoutProperties restClientTimeoutProperties, + ObjectProvider> requestConfigCustomizers) { + return new DefaultEurekaClientHttpRequestFactorySupplier(restClientTimeoutProperties, requestConfigCustomizers); } @Bean diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java index 8d47806330..8983580a13 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java @@ -16,6 +16,8 @@ package org.springframework.cloud.netflix.eureka.config; +import java.util.Set; + import com.netflix.discovery.EurekaClientConfig; import com.netflix.discovery.shared.transport.EurekaHttpClient; @@ -174,8 +176,9 @@ public RestClientEurekaHttpClient configDiscoveryRestClientEurekaHttpClient(Eure @Bean @ConditionalOnMissingBean EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier( - RestClientTimeoutProperties restClientTimeoutProperties) { - return new DefaultEurekaClientHttpRequestFactorySupplier(restClientTimeoutProperties); + RestClientTimeoutProperties restClientTimeoutProperties, + ObjectProvider> requestConfigCustomizers) { + return new DefaultEurekaClientHttpRequestFactorySupplier(restClientTimeoutProperties, requestConfigCustomizers); } static class OnRestClientPresentAndEnabledCondition extends AllNestedConditions { diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapper.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapper.java index 7892754caa..0c2baf8609 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapper.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapper.java @@ -61,7 +61,7 @@ public void initialize(BootstrapRegistry registry) { EurekaHttpClient httpClient = new RestClientTransportClientFactory( context.getOrElse(TlsProperties.class, null), context.getOrElse(EurekaClientHttpRequestFactorySupplier.class, - new DefaultEurekaClientHttpRequestFactorySupplier(new RestClientTimeoutProperties()))) + new DefaultEurekaClientHttpRequestFactorySupplier(new RestClientTimeoutProperties(), null))) .newClient(HostnameBasedUrlRandomizer.randomEndpoint(config, getPropertyResolver(context))); return new EurekaConfigServerInstanceProvider(httpClient, config)::getInstances; }); diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/EurekaServerMockApplication.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/EurekaServerMockApplication.java index 69568aeefe..9e50186594 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/EurekaServerMockApplication.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/EurekaServerMockApplication.java @@ -25,12 +25,14 @@ import com.netflix.appinfo.MyDataCenterInfo; import com.netflix.discovery.shared.Application; import com.netflix.discovery.shared.Applications; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; @@ -45,6 +47,7 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseStatus; @@ -172,7 +175,12 @@ public Applications getApplications(@PathVariable(required = false) String addre } @GetMapping("/apps/{appName}") - public Application getApplication(@PathVariable String appName) { + public Application getApplication(@PathVariable String appName, + @RequestHeader HttpHeaders headers) { + // Used to verify that RequestConfig customizer has taken effect + if (!headers.containsKey("upgrade")) { + throw new RuntimeException("No upgrade header found"); + } return new Application(); } diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestClientEurekaHttpClientTests.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestClientEurekaHttpClientTests.java index a9fafc0cc3..e2880a7112 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestClientEurekaHttpClientTests.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestClientEurekaHttpClientTests.java @@ -16,22 +16,29 @@ package org.springframework.cloud.netflix.eureka.http; +import java.util.Collections; import java.util.Optional; +import java.util.Set; import com.netflix.appinfo.providers.EurekaConfigBasedInstanceInfoProvider; import com.netflix.discovery.shared.resolver.DefaultEndpoint; +import com.netflix.discovery.shared.transport.EurekaHttpClient; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.commons.util.InetUtils; +import org.springframework.cloud.loadbalancer.support.SimpleObjectProvider; import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean; import org.springframework.cloud.netflix.eureka.RestClientTimeoutProperties; import org.springframework.test.annotation.DirtiesContext; import org.springframework.web.client.RestClient; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; + /** * Tests for {@link RestClientEurekaHttpClient}. * @@ -53,13 +60,9 @@ class RestClientEurekaHttpClientTests extends AbstractEurekaHttpClientTests { @BeforeEach void setup() { - eurekaHttpClient = new RestClientTransportClientFactory(Optional.empty(), Optional.empty(), - new DefaultEurekaClientHttpRequestFactorySupplier(new RestClientTimeoutProperties()), - RestClient::builder) - .newClient(new DefaultEndpoint(serviceUrl)); + eurekaHttpClient = buildEurekaHttpClient(); EurekaInstanceConfigBean config = new EurekaInstanceConfigBean(inetUtils); - String appname = "customapp"; config.setIpAddress("127.0.0.1"); config.setHostname("localhost"); @@ -73,4 +76,26 @@ void setup() { info = new EurekaConfigBasedInstanceInfoProvider(config).get(); } + private EurekaHttpClient buildEurekaHttpClient() { + return buildEurekaHttpClient(Collections.emptySet()); + } + + private EurekaHttpClient buildEurekaHttpClient(Set customizers) { + return new RestClientTransportClientFactory(Optional.empty(), Optional.empty(), + new DefaultEurekaClientHttpRequestFactorySupplier(new RestClientTimeoutProperties(), + new SimpleObjectProvider<>(customizers)), + RestClient::builder) + .newClient(new DefaultEndpoint(serviceUrl)); + } + + @Test + void shouldCustomiseHttpClientRequestConfig() { + eurekaHttpClient = buildEurekaHttpClient(Set.of(builder -> + builder.setProtocolUpgradeEnabled(false))); + assertThatExceptionOfType(RuntimeException.class) + .isThrownBy(() -> eurekaHttpClient.getApplication("test")); + } + + + } From c5cc590e4f69687ca857376c065fa280a637c584 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Thu, 9 Jan 2025 12:00:38 +0100 Subject: [PATCH 3/4] Move away from ObjectProvider. Add test. --- .../netflix/eureka/EurekaClientTests.java | 6 ++-- ...coveryClientOptionalArgsConfiguration.java | 11 +++++--- ...ekaConfigServerBootstrapConfiguration.java | 13 +++++---- .../EurekaConfigServerBootstrapper.java | 3 +- ...urekaClientHttpRequestFactorySupplier.java | 28 ++++++++----------- .../http/EurekaServerMockApplication.java | 6 ++-- .../http/RestClientEurekaHttpClientTests.java | 23 ++++++--------- ...RestClientTransportClientFactoryTests.java | 4 ++- 8 files changed, 46 insertions(+), 48 deletions(-) diff --git a/spring-cloud-netflix-eureka-client-tls-tests/src/test/java/org/springframework/cloud/netflix/eureka/EurekaClientTests.java b/spring-cloud-netflix-eureka-client-tls-tests/src/test/java/org/springframework/cloud/netflix/eureka/EurekaClientTests.java index 48ba2da0c9..a762f28fed 100644 --- a/spring-cloud-netflix-eureka-client-tls-tests/src/test/java/org/springframework/cloud/netflix/eureka/EurekaClientTests.java +++ b/spring-cloud-netflix-eureka-client-tls-tests/src/test/java/org/springframework/cloud/netflix/eureka/EurekaClientTests.java @@ -16,6 +16,8 @@ package org.springframework.cloud.netflix.eureka; +import java.util.Collections; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.jupiter.api.BeforeAll; @@ -64,8 +66,8 @@ public RestTemplateTransportClientFactories forceRestTemplateTransportClientFact @Bean public RestTemplateDiscoveryClientOptionalArgs discoveryClientOptionalArgs() { - return new RestTemplateDiscoveryClientOptionalArgs( - new DefaultEurekaClientHttpRequestFactorySupplier(new RestTemplateTimeoutProperties()), null); + return new RestTemplateDiscoveryClientOptionalArgs(new DefaultEurekaClientHttpRequestFactorySupplier( + new RestTemplateTimeoutProperties(), Collections.emptySet())); } } diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java index 355f7888ef..5e86b1f0b2 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java @@ -95,8 +95,10 @@ static class RestTemplateConfiguration { @Bean @ConditionalOnMissingBean EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier( - RestTemplateTimeoutProperties restTemplateTimeoutProperties) { - return new DefaultEurekaClientHttpRequestFactorySupplier(restTemplateTimeoutProperties); + RestTemplateTimeoutProperties restTemplateTimeoutProperties, + Set requestConfigCustomizers) { + return new DefaultEurekaClientHttpRequestFactorySupplier(restTemplateTimeoutProperties, + requestConfigCustomizers); } @Bean @@ -191,8 +193,9 @@ protected static class RestClientConfiguration { @ConditionalOnMissingBean EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier( RestClientTimeoutProperties restClientTimeoutProperties, - ObjectProvider> requestConfigCustomizers) { - return new DefaultEurekaClientHttpRequestFactorySupplier(restClientTimeoutProperties, requestConfigCustomizers); + Set requestConfigCustomizers) { + return new DefaultEurekaClientHttpRequestFactorySupplier(restClientTimeoutProperties, + requestConfigCustomizers); } @Bean diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java index 8983580a13..52f7882e99 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java @@ -56,7 +56,7 @@ import org.springframework.web.reactive.function.client.WebClient; /** - * Bootstrap configuration for config client that wants to lookup the config server via + * Bootstrap configuration for config client that wants to look the config server up via * discovery. * * @author Dave Syer @@ -105,8 +105,10 @@ public RestTemplateEurekaHttpClient configDiscoveryRestTemplateEurekaHttpClient( @Bean @ConditionalOnMissingBean EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier( - RestTemplateTimeoutProperties restTemplateTimeoutProperties) { - return new DefaultEurekaClientHttpRequestFactorySupplier(restTemplateTimeoutProperties); + RestTemplateTimeoutProperties restTemplateTimeoutProperties, + Set requestConfigCustomizers) { + return new DefaultEurekaClientHttpRequestFactorySupplier(restTemplateTimeoutProperties, + requestConfigCustomizers); } /** @@ -177,8 +179,9 @@ public RestClientEurekaHttpClient configDiscoveryRestClientEurekaHttpClient(Eure @ConditionalOnMissingBean EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier( RestClientTimeoutProperties restClientTimeoutProperties, - ObjectProvider> requestConfigCustomizers) { - return new DefaultEurekaClientHttpRequestFactorySupplier(restClientTimeoutProperties, requestConfigCustomizers); + Set requestConfigCustomizers) { + return new DefaultEurekaClientHttpRequestFactorySupplier(restClientTimeoutProperties, + requestConfigCustomizers); } static class OnRestClientPresentAndEnabledCondition extends AllNestedConditions { diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapper.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapper.java index 0c2baf8609..59de877aec 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapper.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapper.java @@ -61,7 +61,8 @@ public void initialize(BootstrapRegistry registry) { EurekaHttpClient httpClient = new RestClientTransportClientFactory( context.getOrElse(TlsProperties.class, null), context.getOrElse(EurekaClientHttpRequestFactorySupplier.class, - new DefaultEurekaClientHttpRequestFactorySupplier(new RestClientTimeoutProperties(), null))) + new DefaultEurekaClientHttpRequestFactorySupplier(new RestClientTimeoutProperties(), + Collections.emptySet()))) .newClient(HostnameBasedUrlRandomizer.randomEndpoint(config, getPropertyResolver(context))); return new EurekaConfigServerInstanceProvider(httpClient, config)::getInstances; }); diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/DefaultEurekaClientHttpRequestFactorySupplier.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/DefaultEurekaClientHttpRequestFactorySupplier.java index 98dfdaab6f..912ba4cc80 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/DefaultEurekaClientHttpRequestFactorySupplier.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/DefaultEurekaClientHttpRequestFactorySupplier.java @@ -17,7 +17,6 @@ package org.springframework.cloud.netflix.eureka.http; import java.util.Collections; -import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -35,7 +34,6 @@ import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.util.Timeout; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.cloud.netflix.eureka.RestTemplateTimeoutProperties; import org.springframework.cloud.netflix.eureka.TimeoutProperties; import org.springframework.http.client.ClientHttpRequestFactory; @@ -54,12 +52,13 @@ public class DefaultEurekaClientHttpRequestFactorySupplier implements EurekaClientHttpRequestFactorySupplier { private final TimeoutProperties timeoutProperties; + // TODO: switch to final after removing deprecated interfaces - private ObjectProvider> requestConfigCustomizers; + private Set requestConfigCustomizers = Collections.emptySet(); /** * @deprecated in favour of - * {@link DefaultEurekaClientHttpRequestFactorySupplier#DefaultEurekaClientHttpRequestFactorySupplier(TimeoutProperties, ObjectProvider)} + * {@link DefaultEurekaClientHttpRequestFactorySupplier#DefaultEurekaClientHttpRequestFactorySupplier(TimeoutProperties, Set)} */ @Deprecated(forRemoval = true) public DefaultEurekaClientHttpRequestFactorySupplier() { @@ -68,7 +67,7 @@ public DefaultEurekaClientHttpRequestFactorySupplier() { /** * @deprecated in favour of - * {@link DefaultEurekaClientHttpRequestFactorySupplier#DefaultEurekaClientHttpRequestFactorySupplier(TimeoutProperties, ObjectProvider)} + * {@link DefaultEurekaClientHttpRequestFactorySupplier#DefaultEurekaClientHttpRequestFactorySupplier(TimeoutProperties, Set)} */ @Deprecated(forRemoval = true) public DefaultEurekaClientHttpRequestFactorySupplier(RestTemplateTimeoutProperties timeoutProperties) { @@ -77,7 +76,7 @@ public DefaultEurekaClientHttpRequestFactorySupplier(RestTemplateTimeoutProperti /** * @deprecated in favour of - * {@link DefaultEurekaClientHttpRequestFactorySupplier#DefaultEurekaClientHttpRequestFactorySupplier(TimeoutProperties, ObjectProvider)} + * {@link DefaultEurekaClientHttpRequestFactorySupplier#DefaultEurekaClientHttpRequestFactorySupplier(TimeoutProperties, Set)} */ @Deprecated(forRemoval = true) public DefaultEurekaClientHttpRequestFactorySupplier(TimeoutProperties timeoutProperties) { @@ -85,7 +84,7 @@ public DefaultEurekaClientHttpRequestFactorySupplier(TimeoutProperties timeoutPr } public DefaultEurekaClientHttpRequestFactorySupplier(TimeoutProperties timeoutProperties, - ObjectProvider> requestConfigCustomizers) { + Set requestConfigCustomizers) { this.timeoutProperties = timeoutProperties; this.requestConfigCustomizers = requestConfigCustomizers; } @@ -120,12 +119,11 @@ private HttpClientConnectionManager buildConnectionManager(SSLContext sslContext connectionManagerBuilder.setSSLSocketFactory(sslConnectionSocketFactoryBuilder.build()); if (timeoutProperties != null) { connectionManagerBuilder.setDefaultSocketConfig(SocketConfig.custom() - .setSoTimeout(Timeout.of(timeoutProperties.getSocketTimeout(), TimeUnit.MILLISECONDS)) + .setSoTimeout(Timeout.of(timeoutProperties.getSocketTimeout(), TimeUnit.MILLISECONDS)) + .build()); + connectionManagerBuilder.setDefaultConnectionConfig(ConnectionConfig.custom() + .setConnectTimeout(Timeout.of(timeoutProperties.getConnectTimeout(), TimeUnit.MILLISECONDS)) .build()); - connectionManagerBuilder.setDefaultConnectionConfig(ConnectionConfig.custom(). - setConnectTimeout(Timeout.of(timeoutProperties.getConnectTimeout(), - TimeUnit.MILLISECONDS)).build() - ); } return connectionManagerBuilder.build(); } @@ -136,11 +134,7 @@ private RequestConfig buildRequestConfig() { requestConfigBuilder.setConnectionRequestTimeout( Timeout.of(timeoutProperties.getConnectRequestTimeout(), TimeUnit.MILLISECONDS)); } - Optional.ofNullable(requestConfigCustomizers).ifPresent( - requestConfigCustomizers -> - requestConfigCustomizers - .getIfAvailable(Collections::emptySet) - .forEach(customizer -> customizer.customize(requestConfigBuilder))); + requestConfigCustomizers.forEach(customizer -> customizer.customize(requestConfigBuilder)); return requestConfigBuilder.build(); } diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/EurekaServerMockApplication.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/EurekaServerMockApplication.java index 9e50186594..827bfb1cdb 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/EurekaServerMockApplication.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/EurekaServerMockApplication.java @@ -25,7 +25,6 @@ import com.netflix.appinfo.MyDataCenterInfo; import com.netflix.discovery.shared.Application; import com.netflix.discovery.shared.Applications; -import jakarta.servlet.http.HttpServletRequest; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @@ -175,10 +174,9 @@ public Applications getApplications(@PathVariable(required = false) String addre } @GetMapping("/apps/{appName}") - public Application getApplication(@PathVariable String appName, - @RequestHeader HttpHeaders headers) { + public Application getApplication(@PathVariable String appName, @RequestHeader HttpHeaders headers) { // Used to verify that RequestConfig customizer has taken effect - if (!headers.containsKey("upgrade")) { + if (appName.equals("upgrade") && !headers.containsKey("upgrade")) { throw new RuntimeException("No upgrade header found"); } return new Application(); diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestClientEurekaHttpClientTests.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestClientEurekaHttpClientTests.java index e2880a7112..a4d1527b2e 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestClientEurekaHttpClientTests.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestClientEurekaHttpClientTests.java @@ -31,7 +31,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.commons.util.InetUtils; -import org.springframework.cloud.loadbalancer.support.SimpleObjectProvider; import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean; import org.springframework.cloud.netflix.eureka.RestClientTimeoutProperties; import org.springframework.test.annotation.DirtiesContext; @@ -80,22 +79,18 @@ private EurekaHttpClient buildEurekaHttpClient() { return buildEurekaHttpClient(Collections.emptySet()); } - private EurekaHttpClient buildEurekaHttpClient(Set customizers) { - return new RestClientTransportClientFactory(Optional.empty(), Optional.empty(), - new DefaultEurekaClientHttpRequestFactorySupplier(new RestClientTimeoutProperties(), - new SimpleObjectProvider<>(customizers)), - RestClient::builder) - .newClient(new DefaultEndpoint(serviceUrl)); - } - @Test void shouldCustomiseHttpClientRequestConfig() { - eurekaHttpClient = buildEurekaHttpClient(Set.of(builder -> - builder.setProtocolUpgradeEnabled(false))); - assertThatExceptionOfType(RuntimeException.class) - .isThrownBy(() -> eurekaHttpClient.getApplication("test")); + eurekaHttpClient = buildEurekaHttpClient(Set.of(builder -> builder.setProtocolUpgradeEnabled(false))); + assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> eurekaHttpClient.getApplication("upgrade")); } - + private EurekaHttpClient buildEurekaHttpClient( + Set customizers) { + return new RestClientTransportClientFactory(Optional.empty(), Optional.empty(), + new DefaultEurekaClientHttpRequestFactorySupplier(new RestClientTimeoutProperties(), customizers), + RestClient::builder) + .newClient(new DefaultEndpoint(serviceUrl)); + } } diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestClientTransportClientFactoryTests.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestClientTransportClientFactoryTests.java index 067b272c03..f396ad7e46 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestClientTransportClientFactoryTests.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestClientTransportClientFactoryTests.java @@ -16,6 +16,7 @@ package org.springframework.cloud.netflix.eureka.http; +import java.util.Collections; import java.util.Optional; import com.netflix.discovery.shared.resolver.DefaultEndpoint; @@ -42,7 +43,8 @@ class RestClientTransportClientFactoryTests { @BeforeEach void setup() { transportClientFactory = new RestClientTransportClientFactory(Optional.empty(), Optional.empty(), - new DefaultEurekaClientHttpRequestFactorySupplier(new RestClientTimeoutProperties()), + new DefaultEurekaClientHttpRequestFactorySupplier(new RestClientTimeoutProperties(), + Collections.emptySet()), RestClient::builder); } From e88988d03b5dadbe6f7fd6a638a65132bd95fdcf Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Thu, 9 Jan 2025 12:26:22 +0100 Subject: [PATCH 4/4] Add docs and javadocs. --- docs/modules/ROOT/pages/spring-cloud-netflix.adoc | 2 ++ .../cloud/netflix/eureka/EurekaClientTests.java | 2 +- .../config/DiscoveryClientOptionalArgsConfiguration.java | 2 +- .../config/EurekaConfigServerBootstrapConfiguration.java | 2 +- .../eureka/config/EurekaConfigServerBootstrapper.java | 2 +- .../DefaultEurekaClientHttpRequestFactorySupplier.java | 2 +- .../http/EurekaClientHttpRequestFactorySupplier.java | 8 +++++++- .../netflix/eureka/http/EurekaServerMockApplication.java | 3 ++- .../eureka/http/RestClientEurekaHttpClientTests.java | 2 +- .../http/RestClientTransportClientFactoryTests.java | 2 +- 10 files changed, 18 insertions(+), 9 deletions(-) diff --git a/docs/modules/ROOT/pages/spring-cloud-netflix.adoc b/docs/modules/ROOT/pages/spring-cloud-netflix.adoc index cdbdec115f..32d9d82ca0 100755 --- a/docs/modules/ROOT/pages/spring-cloud-netflix.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-netflix.adoc @@ -116,6 +116,8 @@ eureka: socket-timeout: 10000 ---- +You can also customise the `RequestConfig` for the underlying Apache HC5 client by creating a bean of type `EurekaClientHttpRequestFactorySupplier.RequestConfigCustomizer`. + === Status Page and Health Indicator The status page and health indicators for a Eureka instance default to `/info` and `/health` respectively, which are the default locations of useful endpoints in a Spring Boot Actuator application. diff --git a/spring-cloud-netflix-eureka-client-tls-tests/src/test/java/org/springframework/cloud/netflix/eureka/EurekaClientTests.java b/spring-cloud-netflix-eureka-client-tls-tests/src/test/java/org/springframework/cloud/netflix/eureka/EurekaClientTests.java index a762f28fed..8c4c5e4c03 100644 --- a/spring-cloud-netflix-eureka-client-tls-tests/src/test/java/org/springframework/cloud/netflix/eureka/EurekaClientTests.java +++ b/spring-cloud-netflix-eureka-client-tls-tests/src/test/java/org/springframework/cloud/netflix/eureka/EurekaClientTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2024 the original author or authors. + * Copyright 2018-2025 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. diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java index 5e86b1f0b2..6ceaf5a131 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 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. diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java index 52f7882e99..cc8b8a308c 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 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. diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapper.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapper.java index 59de877aec..db2c016bd5 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapper.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 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. diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/DefaultEurekaClientHttpRequestFactorySupplier.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/DefaultEurekaClientHttpRequestFactorySupplier.java index 912ba4cc80..118abb6889 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/DefaultEurekaClientHttpRequestFactorySupplier.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/DefaultEurekaClientHttpRequestFactorySupplier.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the original author or authors. + * Copyright 2013-2025 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. diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/EurekaClientHttpRequestFactorySupplier.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/EurekaClientHttpRequestFactorySupplier.java index 9ef5302f76..a6e4d4c46f 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/EurekaClientHttpRequestFactorySupplier.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/EurekaClientHttpRequestFactorySupplier.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2025 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. @@ -40,6 +40,12 @@ public interface EurekaClientHttpRequestFactorySupplier { */ ClientHttpRequestFactory get(SSLContext sslContext, @Nullable HostnameVerifier hostnameVerifier); + /** + * Allows customising the {@link RequestConfig} of the underlying Apache HC5 instance. + * + * @since 4.2.1 + */ + @FunctionalInterface interface RequestConfigCustomizer { void customize(RequestConfig.Builder builder); diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/EurekaServerMockApplication.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/EurekaServerMockApplication.java index 827bfb1cdb..6df0230335 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/EurekaServerMockApplication.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/EurekaServerMockApplication.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2025 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. @@ -61,6 +61,7 @@ * * @author Daniel Lavoie * @author Wonchul Heo + * @author Olga Maciaszek-Sharma */ @Configuration(proxyBeanMethods = false) @RestController diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestClientEurekaHttpClientTests.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestClientEurekaHttpClientTests.java index a4d1527b2e..030553c85d 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestClientEurekaHttpClientTests.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestClientEurekaHttpClientTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 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. diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestClientTransportClientFactoryTests.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestClientTransportClientFactoryTests.java index f396ad7e46..c20fdf5725 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestClientTransportClientFactoryTests.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/RestClientTransportClientFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2024 the original author or authors. + * Copyright 2017-2025 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.