Merge remote-tracking branch 'origin/main'

This commit is contained in:
Olga Maciaszek-Sharma
2024-11-19 15:32:58 +01:00
25 changed files with 627 additions and 284 deletions

View File

@@ -17,7 +17,7 @@ given the ability to merge pull requests.
## Code of Conduct
This project adheres to the Contributor Covenant [code of
conduct](https://github.com/spring-cloud/spring-cloud-build/blob/main/docs/src/main/asciidoc/code-of-conduct.adoc). By participating, you are expected to uphold this code. Please report
conduct](https://github.com/spring-cloud/spring-cloud-build/blob/main/docs/modules/ROOT/partials/code-of-conduct.adoc). By participating, you are expected to uphold this code. Please report
unacceptable behavior to spring-code-of-conduct@pivotal.io.
## Code Conventions and Housekeeping

View File

@@ -28,6 +28,6 @@ jobs:
cache: 'maven'
- name: Build with Maven
run: ./mvnw clean install -B -U -P sonar
- uses: codecov/codecov-action@v4
- uses: codecov/codecov-action@v5
with:
fail_ci_if_error: false

View File

@@ -98,13 +98,18 @@ NOTE: Because of a limitation in Eureka, it is not possible to support per-serve
If you want to customize the RestTemplate used by the Eureka HTTP Client you may want to create a bean of `EurekaClientHttpRequestFactorySupplier` and provide your own logic for generating a `ClientHttpRequestFactory` instance.
All default timeout-related properties for RestTemplate used by the Eureka HTTP Client are set to 3 minutes (in keeping with Apache HC5 default `RequestConfig` and `SocketConfig`). Therefore, to specify the timeout values, you must specify the value directly with the properties in `eureka.client.rest-template-timeout`. (All timeout properties are in milliseconds.)
All default timeout-related properties for RestTemplate and RestClient used by the Eureka HTTP Client are set to 3 minutes (in keeping with Apache HC5 default `RequestConfig` and `SocketConfig`). Therefore, to specify the timeout values, you must specify the value directly with the properties in `eureka.client.rest-template-timeout` or `eureka.client.restclient.timeout`. All timeout properties are in milliseconds.
.application.yml
[source,yaml]
----
eureka:
client:
restclient:
timeout:
connect-timeout: 5000
connect-request-timeout: 8000
socket-timeout: 10000
rest-template-timeout:
connect-timeout: 5000
connect-request-timeout: 8000

View File

@@ -50,6 +50,9 @@
|eureka.client.rest-template-timeout.connect-timeout | `+++180000+++` | Default values are set to 180000, in keeping with {@link RequestConfig} and {@link SocketConfig} defaults.
|eureka.client.rest-template-timeout.socket-timeout | `+++180000+++` |
|eureka.client.restclient.enabled | `+++false+++` | Enables the use of RestClient for Eureka HTTP Client.
|eureka.client.restclient.timeout.connect-request-timeout | `+++180000+++` |
|eureka.client.restclient.timeout.connect-timeout | `+++180000+++` | Default values are set to 180000, in keeping with {@link RequestConfig} and {@link SocketConfig} defaults.
|eureka.client.restclient.timeout.socket-timeout | `+++180000+++` |
|eureka.client.service-url | | Map of availability zone to list of fully qualified URLs to communicate with eureka server. Each value can be a single URL or a comma separated list of alternative locations. Typically the eureka server URLs carry protocol,host,port,context and version information if any. Example: https://ec2-256-156-243-129.compute-1.amazonaws.com:7001/eureka/ The changes are effective at runtime at the next service url refresh cycle as specified by eurekaServiceUrlPollIntervalSeconds.
|eureka.client.should-enforce-registration-at-init | `+++false+++` | Indicates whether the client should enforce registration during initialization. Defaults to false.
|eureka.client.should-unregister-on-shutdown | `+++true+++` | Indicates whether the client should explicitly unregister itself from the remote server on client shutdown.

View File

@@ -2,7 +2,7 @@
"dependencies": {
"antora": "3.2.0-alpha.6",
"@antora/atlas-extension": "1.0.0-alpha.2",
"@antora/collector-extension": "1.0.0-beta.4",
"@antora/collector-extension": "1.0.0-beta.5",
"@asciidoctor/tabs": "1.0.0-beta.6",
"@springio/antora-extensions": "1.14.2",
"@springio/asciidoctor-extensions": "1.0.0-alpha.14"

View File

@@ -22,7 +22,6 @@ import org.junit.jupiter.api.BeforeAll;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration;
import org.springframework.cloud.netflix.eureka.http.DefaultEurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClientFactories;
@@ -33,7 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat;
public class EurekaClientTests extends BaseCertTests {
private static final Log log = LogFactory.getLog(EurekaClientTests.class);
private static final Log LOG = LogFactory.getLog(EurekaClientTests.class);
static EurekaServerRunner server;
@@ -44,7 +43,7 @@ public class EurekaClientTests extends BaseCertTests {
server = startEurekaServer(EurekaClientTests.TestEurekaServer.class);
service = startService(server, EurekaClientTests.TestApp.class);
assertThat(service.discoveryClientOptionalArgs()).isInstanceOf(RestTemplateDiscoveryClientOptionalArgs.class);
log.info("Successfully asserted that Jersey will be used");
LOG.info("Successfully asserted that Jersey will be used");
waitForRegistration(() -> new EurekaClientTests().createEurekaClient());
}
@@ -59,9 +58,8 @@ public class EurekaClientTests extends BaseCertTests {
@Bean
public RestTemplateTransportClientFactories forceRestTemplateTransportClientFactories(
DiscoveryClientOptionalArgsConfiguration configuration,
RestTemplateDiscoveryClientOptionalArgs discoveryClientOptionalArgs) {
return configuration.restTemplateTransportClientFactories(discoveryClientOptionalArgs);
return new RestTemplateTransportClientFactories(discoveryClientOptionalArgs);
}
@Bean

View File

@@ -0,0 +1,106 @@
/*
* Copyright 2018-2024 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.
* 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 org.springframework.cloud.netflix.eureka;
import java.io.IOException;
import java.security.GeneralSecurityException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.configuration.TlsProperties;
import org.springframework.cloud.netflix.eureka.http.EurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.RestClientDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.http.RestClientTransportClientFactories;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestClient;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration.setupTLS;
/**
* Tests for verifying TLS setup with {@link RestClientTransportClientFactories}.
*
* @author Olga Maciaszek-Sharma
*/
public class RestClientEurekaClientTests extends BaseCertTests {
private static final Log LOG = LogFactory.getLog(RestClientEurekaClientTests.class);
private static EurekaServerRunner server;
private static EurekaClientRunner service;
@BeforeAll
public static void setupAll() {
server = startEurekaServer(TestEurekaServer.class);
service = startService(server, TestApp.class);
// Will use RestClient
assertThat(service.discoveryClientOptionalArgs()).isInstanceOf(RestClientDiscoveryClientOptionalArgs.class);
LOG.info("Successfully asserted that RestClient will be used");
waitForRegistration(() -> new RestClientEurekaClientTests().createEurekaClient());
}
@AfterAll
public static void tearDownAll() {
stopService(service);
stopEurekaServer(server);
}
@Override
EurekaClientRunner createEurekaClient() {
return new EurekaClientRunner(TestApp.class, server);
}
@SpringBootConfiguration
@EnableAutoConfiguration
public static class TestApp {
// Want to force reusing exactly the same bean as on production without excluding
// jersey from the classpath
@Bean
public RestClientDiscoveryClientOptionalArgs forceRestClientDiscoveryClientOptionalArgs(
TlsProperties tlsProperties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier)
throws GeneralSecurityException, IOException {
RestClientDiscoveryClientOptionalArgs result = new RestClientDiscoveryClientOptionalArgs(
eurekaClientHttpRequestFactorySupplier, RestClient::builder);
setupTLS(result, tlsProperties);
return result;
}
@Bean
public RestClientTransportClientFactories forceRestClientTransportClientFactories(
RestClientDiscoveryClientOptionalArgs discoveryClientOptionalArgs) {
return new RestClientTransportClientFactories(discoveryClientOptionalArgs);
}
}
@SpringBootConfiguration
@EnableAutoConfiguration
@EnableEurekaServer
public static class TestEurekaServer {
}
}

View File

@@ -24,13 +24,11 @@ import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.configuration.TlsProperties;
import org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration;
import org.springframework.cloud.netflix.eureka.http.DefaultEurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.EurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClientFactories;
@@ -38,6 +36,7 @@ import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import org.springframework.context.annotation.Bean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration.setupTLS;
public class RestTemplateEurekaClientTests extends BaseCertTests {
@@ -49,7 +48,7 @@ public class RestTemplateEurekaClientTests extends BaseCertTests {
@BeforeAll
public static void setupAll() {
server = startEurekaServer(RestTemplateEurekaClientTests.RestTemplateTestEurekaServer.class);
server = startEurekaServer(TestEurekaServer.class);
service = startService(server, RestTemplateEurekaClientTests.RestTemplateTestApp.class);
// Will use RestTemplate
assertThat(service.discoveryClientOptionalArgs()).isInstanceOf(RestTemplateDiscoveryClientOptionalArgs.class);
@@ -72,24 +71,27 @@ public class RestTemplateEurekaClientTests extends BaseCertTests {
@EnableAutoConfiguration
public static class RestTemplateTestApp {
// Want to force reusing exactly the same bean as on production without excluding
// jersey from the classpath
@Bean
public RestTemplateDiscoveryClientOptionalArgs forceRestTemplateDiscoveryClientOptionalArgs(
TlsProperties tlsProperties, DiscoveryClientOptionalArgsConfiguration configuration,
TlsProperties tlsProperties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier)
throws GeneralSecurityException, IOException {
return configuration.restTemplateDiscoveryClientOptionalArgs(tlsProperties,
eurekaClientHttpRequestFactorySupplier, new RestTemplateBuilderObjectProvider());
RestTemplateDiscoveryClientOptionalArgs result = new RestTemplateDiscoveryClientOptionalArgs(
eurekaClientHttpRequestFactorySupplier, RestTemplateBuilder::new);
setupTLS(result, tlsProperties);
return result;
}
// Want to force reusing exactly the same bean as on production without excluding
// jersey from the classpath
@Bean
public RestTemplateTransportClientFactories forceRestTemplateTransportClientFactories(
DiscoveryClientOptionalArgsConfiguration configuration,
RestTemplateDiscoveryClientOptionalArgs discoveryClientOptionalArgs) {
return configuration.restTemplateTransportClientFactories(discoveryClientOptionalArgs);
return new RestTemplateTransportClientFactories(discoveryClientOptionalArgs);
}
@Bean
EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier(
RestTemplateTimeoutProperties restTemplateTimeoutProperties) {
return new DefaultEurekaClientHttpRequestFactorySupplier(restTemplateTimeoutProperties);
}
}
@@ -97,33 +99,7 @@ public class RestTemplateEurekaClientTests extends BaseCertTests {
@SpringBootConfiguration
@EnableAutoConfiguration
@EnableEurekaServer
public static class RestTemplateTestEurekaServer {
}
private static class RestTemplateBuilderObjectProvider implements ObjectProvider<RestTemplateBuilder> {
private final RestTemplateBuilder builder = new RestTemplateBuilder();
@Override
public RestTemplateBuilder getObject(Object... args) throws BeansException {
return builder;
}
@Override
public RestTemplateBuilder getIfAvailable() throws BeansException {
return builder;
}
@Override
public RestTemplateBuilder getIfUnique() throws BeansException {
return builder;
}
@Override
public RestTemplateBuilder getObject() throws BeansException {
return builder;
}
public static class TestEurekaServer {
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2013-2024 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.
* 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 org.springframework.cloud.netflix.eureka;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.web.client.RestClient;
/**
* A {@link RestClient}-specific {@link TimeoutProperties} implementation.
*
* @author Olga Maciaszek-Sharma
* @since 4.2.0
*/
@ConfigurationProperties("eureka.client.restclient.timeout")
public class RestClientTimeoutProperties extends TimeoutProperties {
@Override
public String toString() {
return "RestClientTimeoutProperties{" + ", connectTimeout=" + connectTimeout + ", connectRequestTimeout="
+ connectRequestTimeout + ", socketTimeout=" + socketTimeout + '}';
}
}

View File

@@ -16,79 +16,23 @@
package org.springframework.cloud.netflix.eureka;
import java.util.Objects;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.core5.http.io.SocketConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.netflix.eureka.http.RestTemplateEurekaHttpClient;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
/**
* Properties for configuring timeouts used in {@link RestTemplate} required by
* {@link RestTemplateEurekaHttpClient}.
* A {@link RestTemplate}-specific {@link TimeoutProperties} implementation.
*
* @author Jiwon Jeon
* @author Mooyong Lee
* @author Olga Maciaszek-Sharma
* @since 3.1.6
* @deprecated {@link RestTemplate}-based implementation to be removed in favour of
* {@link RestClient}-based implementation.
*/
@Deprecated(forRemoval = true)
@ConfigurationProperties("eureka.client.rest-template-timeout")
public class RestTemplateTimeoutProperties {
/**
* Default values are set to 180000, in keeping with {@link RequestConfig} and
* {@link SocketConfig} defaults.
*/
private int connectTimeout = 180000; // 3 * MINUTES
private int connectRequestTimeout = 180000; // 3 * MINUTES
private int socketTimeout = 180000; // 3 * MINUTES
public int getConnectTimeout() {
return connectTimeout;
}
public int getConnectRequestTimeout() {
return connectRequestTimeout;
}
public int getSocketTimeout() {
return socketTimeout;
}
public void setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
}
public void setConnectRequestTimeout(int connectRequestTimeout) {
this.connectRequestTimeout = connectRequestTimeout;
}
public void setSocketTimeout(int socketTimeout) {
this.socketTimeout = socketTimeout;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
RestTemplateTimeoutProperties that = (RestTemplateTimeoutProperties) o;
return connectTimeout == that.connectTimeout && connectRequestTimeout == that.connectRequestTimeout
&& socketTimeout == that.socketTimeout;
}
@Override
public int hashCode() {
return Objects.hash(connectTimeout, connectRequestTimeout, socketTimeout);
}
public class RestTemplateTimeoutProperties extends TimeoutProperties {
@Override
public String toString() {

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2013-2024 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.
* 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 org.springframework.cloud.netflix.eureka;
import java.util.Objects;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.core5.http.io.SocketConfig;
import org.springframework.web.client.RestTemplate;
/**
* Properties for configuring timeouts used in {@link RestTemplate} required by
* {@link EurekaHttpClient}.
*
* @author Jiwon Jeon
* @author Mooyong Lee
* @author Olga Maciaszek-Sharma
* @since 4.2.0
*/
public abstract class TimeoutProperties {
/**
* Default values are set to 180000, in keeping with {@link RequestConfig} and
* {@link SocketConfig} defaults.
*/
protected int connectTimeout = 180000; // 3 * MINUTES
protected int connectRequestTimeout = 180000; // 3 * MINUTES
protected int socketTimeout = 180000; // 3 * MINUTES
public int getConnectTimeout() {
return connectTimeout;
}
public int getConnectRequestTimeout() {
return connectRequestTimeout;
}
public int getSocketTimeout() {
return socketTimeout;
}
public void setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
}
public void setConnectRequestTimeout(int connectRequestTimeout) {
this.connectRequestTimeout = connectRequestTimeout;
}
public void setSocketTimeout(int socketTimeout) {
this.socketTimeout = socketTimeout;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
RestTemplateTimeoutProperties that = (RestTemplateTimeoutProperties) o;
return connectTimeout == that.connectTimeout && connectRequestTimeout == that.connectRequestTimeout
&& socketTimeout == that.socketTimeout;
}
@Override
public int hashCode() {
return Objects.hash(connectTimeout, connectRequestTimeout, socketTimeout);
}
}

View File

@@ -38,6 +38,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.configuration.SSLContextFactory;
import org.springframework.cloud.configuration.TlsProperties;
import org.springframework.cloud.netflix.eureka.RestClientTimeoutProperties;
import org.springframework.cloud.netflix.eureka.RestTemplateTimeoutProperties;
import org.springframework.cloud.netflix.eureka.http.DefaultEurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.EurekaClientHttpRequestFactorySupplier;
@@ -51,6 +52,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
/**
@@ -60,7 +62,7 @@ import org.springframework.web.reactive.function.client.WebClient;
* @author Wonchul Heo
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(RestTemplateTimeoutProperties.class)
@EnableConfigurationProperties({ RestTemplateTimeoutProperties.class, RestClientTimeoutProperties.class })
public class DiscoveryClientOptionalArgsConfiguration {
protected static final Log logger = LogFactory.getLog(DiscoveryClientOptionalArgsConfiguration.class);
@@ -71,40 +73,8 @@ public class DiscoveryClientOptionalArgsConfiguration {
return new TlsProperties();
}
@Bean
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
@Conditional(RestTemplateEnabledCondition.class)
@ConditionalOnMissingBean(value = { AbstractDiscoveryClientOptionalArgs.class }, search = SearchStrategy.CURRENT)
public RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOptionalArgs(TlsProperties tlsProperties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
ObjectProvider<RestTemplateBuilder> restTemplateBuilders) throws GeneralSecurityException, IOException {
if (logger.isInfoEnabled()) {
logger.info("Eureka HTTP Client uses RestTemplate.");
}
RestTemplateDiscoveryClientOptionalArgs result = new RestTemplateDiscoveryClientOptionalArgs(
eurekaClientHttpRequestFactorySupplier, restTemplateBuilders::getIfAvailable);
setupTLS(result, tlsProperties);
return result;
}
@Bean
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
@Conditional(RestTemplateEnabledCondition.class)
@ConditionalOnMissingBean(value = { TransportClientFactories.class }, search = SearchStrategy.CURRENT)
public RestTemplateTransportClientFactories restTemplateTransportClientFactories(
RestTemplateDiscoveryClientOptionalArgs optionalArgs) {
return new RestTemplateTransportClientFactories(optionalArgs);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier(
RestTemplateTimeoutProperties restTemplateTimeoutProperties) {
return new DefaultEurekaClientHttpRequestFactorySupplier(restTemplateTimeoutProperties);
}
private static void setupTLS(AbstractDiscoveryClientOptionalArgs<?> args, TlsProperties properties)
// Visible for tests
public static void setupTLS(AbstractDiscoveryClientOptionalArgs<?> args, TlsProperties properties)
throws GeneralSecurityException, IOException {
if (properties.isEnabled()) {
SSLContextFactory factory = new SSLContextFactory(properties);
@@ -112,8 +82,49 @@ public class DiscoveryClientOptionalArgsConfiguration {
}
}
/**
* @deprecated {@link RestTemplate}-based implementation to be removed in favour of
* {@link RestClient}-based implementation.
*/
@Configuration(proxyBeanMethods = false)
@Conditional(JerseyClientPresentAndEnabledCondition.class)
@Conditional(OnRestTemplatePresentAndEnabledCondition.class)
@Deprecated
static class RestTemplateConfiguration {
@Bean
@ConditionalOnMissingBean
EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier(
RestTemplateTimeoutProperties restTemplateTimeoutProperties) {
return new DefaultEurekaClientHttpRequestFactorySupplier(restTemplateTimeoutProperties);
}
@Bean
@ConditionalOnMissingBean(value = { AbstractDiscoveryClientOptionalArgs.class },
search = SearchStrategy.CURRENT)
public RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOptionalArgs(
TlsProperties tlsProperties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
ObjectProvider<RestTemplateBuilder> restTemplateBuilders) throws GeneralSecurityException, IOException {
if (logger.isInfoEnabled()) {
logger.info("Eureka HTTP Client uses RestTemplate.");
}
RestTemplateDiscoveryClientOptionalArgs result = new RestTemplateDiscoveryClientOptionalArgs(
eurekaClientHttpRequestFactorySupplier, restTemplateBuilders::getIfAvailable);
setupTLS(result, tlsProperties);
return result;
}
@Bean
@ConditionalOnMissingBean(value = { TransportClientFactories.class }, search = SearchStrategy.CURRENT)
public RestTemplateTransportClientFactories restTemplateTransportClientFactories(
RestTemplateDiscoveryClientOptionalArgs optionalArgs) {
return new RestTemplateTransportClientFactories(optionalArgs);
}
}
@Configuration(proxyBeanMethods = false)
@Conditional(OnJerseyClientPresentAndEnabledCondition.class)
@ConditionalOnBean(value = AbstractDiscoveryClientOptionalArgs.class, search = SearchStrategy.CURRENT)
static class DiscoveryClientOptionalArgsTlsConfiguration {
@@ -127,7 +138,8 @@ public class DiscoveryClientOptionalArgsConfiguration {
}
@Conditional(JerseyClientNotPresentOrNotEnabledCondition.class)
@Configuration(proxyBeanMethods = false)
@Conditional(OnJerseyClientNotPresentOrNotEnabledCondition.class)
@ConditionalOnClass(name = "org.springframework.web.reactive.function.client.WebClient")
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", havingValue = "true")
protected static class WebClientConfiguration {
@@ -156,8 +168,8 @@ public class DiscoveryClientOptionalArgsConfiguration {
}
@Configuration
@Conditional(JerseyClientNotPresentOrNotEnabledCondition.class)
@Configuration(proxyBeanMethods = false)
@Conditional(OnJerseyClientNotPresentOrNotEnabledCondition.class)
@ConditionalOnMissingClass("org.springframework.web.reactive.function.client.WebClient")
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", havingValue = "true")
protected static class WebClientNotFoundConfiguration {
@@ -170,20 +182,30 @@ public class DiscoveryClientOptionalArgsConfiguration {
}
@ConditionalOnClass(name = "org.springframework.web.client.RestClient")
@Conditional(RestClientEnabledCondition.class)
@Configuration(proxyBeanMethods = false)
@Conditional(OnRestClientPresentAndEnabledCondition.class)
protected static class RestClientConfiguration {
@Bean
@ConditionalOnMissingBean
EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier(
RestClientTimeoutProperties restClientTimeoutProperties) {
return new DefaultEurekaClientHttpRequestFactorySupplier(restClientTimeoutProperties);
}
@Bean
@ConditionalOnMissingBean(value = { AbstractDiscoveryClientOptionalArgs.class },
search = SearchStrategy.CURRENT)
public RestClientDiscoveryClientOptionalArgs restClientDiscoveryClientOptionalArgs(TlsProperties tlsProperties,
ObjectProvider<RestClient.Builder> builder) throws GeneralSecurityException, IOException {
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
ObjectProvider<RestClient.Builder> restClientBuilderProvider)
throws GeneralSecurityException, IOException {
if (logger.isInfoEnabled()) {
logger.info("Eureka HTTP Client uses RestClient.");
}
RestClientDiscoveryClientOptionalArgs result = new RestClientDiscoveryClientOptionalArgs(
builder::getIfAvailable);
eurekaClientHttpRequestFactorySupplier,
() -> restClientBuilderProvider.getIfAvailable(RestClient::builder));
setupTLS(result, tlsProperties);
return result;
}
@@ -191,15 +213,15 @@ public class DiscoveryClientOptionalArgsConfiguration {
@Bean
@ConditionalOnMissingBean(value = TransportClientFactories.class, search = SearchStrategy.CURRENT)
public RestClientTransportClientFactories restClientTransportClientFactories(
ObjectProvider<RestClient.Builder> builder) {
return new RestClientTransportClientFactories(builder::getIfAvailable);
RestClientDiscoveryClientOptionalArgs args) {
return new RestClientTransportClientFactories(args);
}
}
static class JerseyClientPresentAndEnabledCondition extends AllNestedConditions {
static class OnJerseyClientPresentAndEnabledCondition extends AllNestedConditions {
JerseyClientPresentAndEnabledCondition() {
OnJerseyClientPresentAndEnabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@@ -215,9 +237,9 @@ public class DiscoveryClientOptionalArgsConfiguration {
}
static class JerseyClientNotPresentOrNotEnabledCondition extends AnyNestedCondition {
static class OnJerseyClientNotPresentOrNotEnabledCondition extends AnyNestedCondition {
JerseyClientNotPresentOrNotEnabledCondition() {
OnJerseyClientNotPresentOrNotEnabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@@ -233,13 +255,23 @@ public class DiscoveryClientOptionalArgsConfiguration {
}
static class RestTemplateEnabledCondition extends AllNestedConditions {
/**
* @deprecated {@link RestTemplate}-based implementation to be removed in favour of
* {@link RestClient}-based implementation.
*/
@Deprecated(forRemoval = true)
static class OnRestTemplatePresentAndEnabledCondition extends AllNestedConditions {
RestTemplateEnabledCondition() {
OnRestTemplatePresentAndEnabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@Conditional(JerseyClientNotPresentOrNotEnabledCondition.class)
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
static class OnRestTemplatePresent {
}
@Conditional(OnJerseyClientNotPresentOrNotEnabledCondition.class)
static class OnJerseyClientNotPresentOrNotEnabled {
}
@@ -257,13 +289,18 @@ public class DiscoveryClientOptionalArgsConfiguration {
}
static class RestClientEnabledCondition extends AllNestedConditions {
static class OnRestClientPresentAndEnabledCondition extends AllNestedConditions {
RestClientEnabledCondition() {
OnRestClientPresentAndEnabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@Conditional(JerseyClientNotPresentOrNotEnabledCondition.class)
@ConditionalOnClass(name = "org.springframework.web.client.RestClient")
static class OnRestClientPresent {
}
@Conditional(OnJerseyClientNotPresentOrNotEnabledCondition.class)
static class OnJerseyClientNotPresentOrNotEnabled {
}

View File

@@ -27,7 +27,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration;
import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
@@ -35,6 +34,7 @@ import org.springframework.cloud.config.client.ConfigServerInstanceProvider;
import org.springframework.cloud.config.client.ConfigServicePropertySourceLocator;
import org.springframework.cloud.configuration.TlsProperties;
import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
import org.springframework.cloud.netflix.eureka.RestClientTimeoutProperties;
import org.springframework.cloud.netflix.eureka.RestTemplateTimeoutProperties;
import org.springframework.cloud.netflix.eureka.http.DefaultEurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.EurekaClientHttpRequestFactorySupplier;
@@ -50,6 +50,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.lang.Nullable;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
/**
@@ -59,11 +60,12 @@ import org.springframework.web.reactive.function.client.WebClient;
* @author Dave Syer
* @author Armin Krezovic
* @author Wonchul Heo
* @author Olga Maciaszek-Sharma
*/
@ConditionalOnClass(ConfigServicePropertySourceLocator.class)
@Conditional(EurekaConfigServerBootstrapConfiguration.EurekaConfigServerBootstrapCondition.class)
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(RestTemplateTimeoutProperties.class)
@EnableConfigurationProperties({ RestTemplateTimeoutProperties.class, RestClientTimeoutProperties.class })
public class EurekaConfigServerBootstrapConfiguration {
@Bean
@@ -72,31 +74,70 @@ public class EurekaConfigServerBootstrapConfiguration {
return new EurekaClientConfigBean();
}
@Bean
@ConditionalOnMissingBean(EurekaHttpClient.class)
@Conditional(RestTemplateEnabledCondition.class)
public RestTemplateEurekaHttpClient configDiscoveryRestTemplateEurekaHttpClient(EurekaClientConfigBean config,
Environment env, @Nullable TlsProperties properties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
ObjectProvider<RestTemplateBuilder> restTemplateBuilders) {
return (RestTemplateEurekaHttpClient) new RestTemplateTransportClientFactory(properties,
eurekaClientHttpRequestFactorySupplier, restTemplateBuilders::getIfAvailable)
.newClient(HostnameBasedUrlRandomizer.randomEndpoint(config, env));
}
@Bean
@ConditionalOnMissingBean
EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier(
RestTemplateTimeoutProperties restTemplateTimeoutProperties) {
return new DefaultEurekaClientHttpRequestFactorySupplier(restTemplateTimeoutProperties);
}
@Bean
public ConfigServerInstanceProvider.Function eurekaConfigServerInstanceProvider(EurekaHttpClient client,
EurekaClientConfig config) {
return new EurekaConfigServerInstanceProvider(client, config)::getInstances;
}
/**
* @deprecated {@link RestTemplate}-based implementation to be removed in favour of
* {@link RestClient}-based implementation.
*/
@Configuration(proxyBeanMethods = false)
@Conditional(RestTemplateConfiguration.OnRestTemplatePresentAndEnabledCondition.class)
@Deprecated
static class RestTemplateConfiguration {
@Bean
@ConditionalOnMissingBean(EurekaHttpClient.class)
public RestTemplateEurekaHttpClient configDiscoveryRestTemplateEurekaHttpClient(EurekaClientConfigBean config,
Environment env, @Nullable TlsProperties properties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
ObjectProvider<RestTemplateBuilder> restTemplateBuilders) {
return (RestTemplateEurekaHttpClient) new RestTemplateTransportClientFactory(properties,
eurekaClientHttpRequestFactorySupplier, restTemplateBuilders::getIfAvailable)
.newClient(HostnameBasedUrlRandomizer.randomEndpoint(config, env));
}
@Bean
@ConditionalOnMissingBean
EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier(
RestTemplateTimeoutProperties restTemplateTimeoutProperties) {
return new DefaultEurekaClientHttpRequestFactorySupplier(restTemplateTimeoutProperties);
}
/**
* @deprecated {@link RestTemplate}-based implementation to be removed in favour
* of {@link RestClient}-based implementation.
*/
@Deprecated(forRemoval = true)
static class OnRestTemplatePresentAndEnabledCondition extends AllNestedConditions {
OnRestTemplatePresentAndEnabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
static class OnRestTemplatePresent {
}
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
havingValue = "false")
static class OnWebClientDisabled {
}
@ConditionalOnProperty(prefix = "eureka.client", name = "restclient.enabled", havingValue = "false")
static class OnRestClientDisabled {
}
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.springframework.web.reactive.function.client.WebClient")
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", havingValue = "true")
@@ -114,55 +155,50 @@ public class EurekaConfigServerBootstrapConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.springframework.web.client.RestClient")
@Conditional(RestClientEnabledCondition.class)
@ImportAutoConfiguration(RestClientAutoConfiguration.class)
protected static class RestClientConfiguration {
@Conditional(RestClientConfiguration.OnRestClientPresentAndEnabledCondition.class)
static class RestClientConfiguration {
@Bean
@ConditionalOnMissingBean(EurekaHttpClient.class)
public RestClientEurekaHttpClient configDiscoveryRestClientEurekaHttpClient(EurekaClientConfigBean config,
ObjectProvider<RestClient.Builder> builder, Environment env) {
return (RestClientEurekaHttpClient) new RestClientTransportClientFactory(builder::getIfAvailable)
@Nullable TlsProperties properties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
ObjectProvider<RestClient.Builder> restClientBuilderProvider, Environment env) {
return (RestClientEurekaHttpClient) new RestClientTransportClientFactory(properties,
eurekaClientHttpRequestFactorySupplier,
() -> restClientBuilderProvider.getIfAvailable(RestClient::builder))
.newClient(HostnameBasedUrlRandomizer.randomEndpoint(config, env));
}
}
static class RestTemplateEnabledCondition extends AllNestedConditions {
RestTemplateEnabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
@Bean
@ConditionalOnMissingBean
EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier(
RestClientTimeoutProperties restClientTimeoutProperties) {
return new DefaultEurekaClientHttpRequestFactorySupplier(restClientTimeoutProperties);
}
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
havingValue = "false")
static class OnWebClientDisabled {
static class OnRestClientPresentAndEnabledCondition extends AllNestedConditions {
}
OnRestClientPresentAndEnabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnProperty(prefix = "eureka.client", name = "restclient.enabled", havingValue = "false")
static class OnRestClientDisabled {
@ConditionalOnClass(name = "org.springframework.web.client.RestClient")
static class OnRestClientPresentCondition {
}
}
}
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
havingValue = "false")
static class OnWebClientDisabled {
static class RestClientEnabledCondition extends AllNestedConditions {
}
RestClientEnabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnProperty(prefix = "eureka.client", name = "restclient.enabled", matchIfMissing = true,
havingValue = "true")
static class OnRestClientEnabled {
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
havingValue = "false")
static class OnWebClientDisabled {
}
@ConditionalOnProperty(prefix = "eureka.client", name = "restclient.enabled", matchIfMissing = true,
havingValue = "true")
static class OnRestClientEnabled {
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2022 the original author or authors.
* Copyright 2013-2024 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.
@@ -30,9 +30,10 @@ import org.springframework.cloud.config.client.ConfigServerConfigDataLocationRes
import org.springframework.cloud.config.client.ConfigServerInstanceProvider;
import org.springframework.cloud.configuration.TlsProperties;
import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
import org.springframework.cloud.netflix.eureka.RestClientTimeoutProperties;
import org.springframework.cloud.netflix.eureka.http.DefaultEurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.EurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClientFactory;
import org.springframework.cloud.netflix.eureka.http.RestClientTransportClientFactory;
import org.springframework.util.ClassUtils;
public class EurekaConfigServerBootstrapper implements BootstrapRegistryInitializer {
@@ -57,10 +58,10 @@ public class EurekaConfigServerBootstrapper implements BootstrapRegistryInitiali
return (id) -> Collections.emptyList();
}
EurekaClientConfigBean config = context.get(EurekaClientConfigBean.class);
EurekaHttpClient httpClient = new RestTemplateTransportClientFactory(
EurekaHttpClient httpClient = new RestClientTransportClientFactory(
context.getOrElse(TlsProperties.class, null),
context.getOrElse(EurekaClientHttpRequestFactorySupplier.class,
new DefaultEurekaClientHttpRequestFactorySupplier()))
new DefaultEurekaClientHttpRequestFactorySupplier(new RestClientTimeoutProperties())))
.newClient(HostnameBasedUrlRandomizer.randomEndpoint(config, getPropertyResolver(context)));
return new EurekaConfigServerInstanceProvider(httpClient, config)::getInstances;
});

View File

@@ -32,6 +32,7 @@ import org.apache.hc.core5.http.io.SocketConfig;
import org.apache.hc.core5.util.Timeout;
import org.springframework.cloud.netflix.eureka.RestTemplateTimeoutProperties;
import org.springframework.cloud.netflix.eureka.TimeoutProperties;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.lang.Nullable;
@@ -47,29 +48,38 @@ import org.springframework.lang.Nullable;
*/
public class DefaultEurekaClientHttpRequestFactorySupplier implements EurekaClientHttpRequestFactorySupplier {
private final RestTemplateTimeoutProperties restTemplateTimeoutProperties;
private final TimeoutProperties timeoutProperties;
/**
* @deprecated in favour of
* {@link DefaultEurekaClientHttpRequestFactorySupplier#DefaultEurekaClientHttpRequestFactorySupplier(RestTemplateTimeoutProperties)}
* {@link DefaultEurekaClientHttpRequestFactorySupplier#DefaultEurekaClientHttpRequestFactorySupplier(TimeoutProperties)}
*/
@Deprecated(forRemoval = true)
public DefaultEurekaClientHttpRequestFactorySupplier() {
this.restTemplateTimeoutProperties = new RestTemplateTimeoutProperties();
this.timeoutProperties = new RestTemplateTimeoutProperties();
}
public DefaultEurekaClientHttpRequestFactorySupplier(RestTemplateTimeoutProperties restTemplateTimeoutProperties) {
this.restTemplateTimeoutProperties = restTemplateTimeoutProperties;
/**
* @deprecated in favour of
* {@link DefaultEurekaClientHttpRequestFactorySupplier#DefaultEurekaClientHttpRequestFactorySupplier(TimeoutProperties)}
*/
@Deprecated(forRemoval = true)
public DefaultEurekaClientHttpRequestFactorySupplier(RestTemplateTimeoutProperties timeoutProperties) {
this.timeoutProperties = timeoutProperties;
}
public DefaultEurekaClientHttpRequestFactorySupplier(TimeoutProperties timeoutProperties) {
this.timeoutProperties = timeoutProperties;
}
@Override
public ClientHttpRequestFactory get(SSLContext sslContext, @Nullable HostnameVerifier hostnameVerifier) {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
if (sslContext != null || hostnameVerifier != null || restTemplateTimeoutProperties != null) {
httpClientBuilder.setConnectionManager(
buildConnectionManager(sslContext, hostnameVerifier, restTemplateTimeoutProperties));
if (sslContext != null || hostnameVerifier != null || timeoutProperties != null) {
httpClientBuilder
.setConnectionManager(buildConnectionManager(sslContext, hostnameVerifier, timeoutProperties));
}
if (restTemplateTimeoutProperties != null) {
if (timeoutProperties != null) {
httpClientBuilder.setDefaultRequestConfig(buildRequestConfig());
}
@@ -80,7 +90,7 @@ public class DefaultEurekaClientHttpRequestFactorySupplier implements EurekaClie
}
private HttpClientConnectionManager buildConnectionManager(SSLContext sslContext, HostnameVerifier hostnameVerifier,
RestTemplateTimeoutProperties restTemplateTimeoutProperties) {
TimeoutProperties restTemplateTimeoutProperties) {
PoolingHttpClientConnectionManagerBuilder connectionManagerBuilder = PoolingHttpClientConnectionManagerBuilder
.create();
SSLConnectionSocketFactoryBuilder sslConnectionSocketFactoryBuilder = SSLConnectionSocketFactoryBuilder
@@ -102,9 +112,9 @@ public class DefaultEurekaClientHttpRequestFactorySupplier implements EurekaClie
private RequestConfig buildRequestConfig() {
return RequestConfig.custom()
.setConnectTimeout(Timeout.of(restTemplateTimeoutProperties.getConnectTimeout(), TimeUnit.MILLISECONDS))
.setConnectTimeout(Timeout.of(timeoutProperties.getConnectTimeout(), TimeUnit.MILLISECONDS))
.setConnectionRequestTimeout(
Timeout.of(restTemplateTimeoutProperties.getConnectRequestTimeout(), TimeUnit.MILLISECONDS))
Timeout.of(timeoutProperties.getConnectRequestTimeout(), TimeUnit.MILLISECONDS))
.build();
}

View File

@@ -18,6 +18,9 @@ package org.springframework.cloud.netflix.eureka.http;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Optional;
import javax.net.ssl.SSLContext;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationFeature;
@@ -36,6 +39,8 @@ import com.netflix.discovery.converters.jackson.serializer.InstanceInfoJsonBeanS
import com.netflix.discovery.shared.Applications;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import org.springframework.cloud.configuration.SSLContextFactory;
import org.springframework.cloud.configuration.TlsProperties;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.lang.Nullable;
@@ -44,6 +49,7 @@ import org.springframework.lang.Nullable;
*
* @author Daniel Lavoie
* @author Wonchul Heo
* @author Olga Maciaszek-Sharma
* @since 4.2.0
*/
final class EurekaHttpClientUtils {
@@ -123,6 +129,18 @@ final class EurekaHttpClientUtils {
return null;
}
static Optional<SSLContext> context(TlsProperties properties) {
if (properties == null || !properties.isEnabled()) {
return Optional.empty();
}
try {
return Optional.of(new SSLContextFactory(properties).createSSLContext());
}
catch (Exception e) {
throw new IllegalStateException(e);
}
}
record UserInfo(String username, String password) {
}

View File

@@ -28,11 +28,28 @@ import org.springframework.web.client.RestClient;
* supplying {@link ClientRequestFilter}.
*
* @author Wonchul Heo
* @author Olga Maciaszek-Sharma
* @since 4.2.0
*/
public class RestClientDiscoveryClientOptionalArgs extends AbstractDiscoveryClientOptionalArgs<Void> {
public RestClientDiscoveryClientOptionalArgs(Supplier<RestClient.Builder> builder) {
private final EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier;
private final Supplier<RestClient.Builder> restClientBuilderSupplier;
public RestClientDiscoveryClientOptionalArgs(
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
Supplier<RestClient.Builder> restClientBuilderSupplier) {
this.eurekaClientHttpRequestFactorySupplier = eurekaClientHttpRequestFactorySupplier;
this.restClientBuilderSupplier = restClientBuilderSupplier;
}
EurekaClientHttpRequestFactorySupplier getEurekaClientHttpRequestFactorySupplier() {
return eurekaClientHttpRequestFactorySupplier;
}
Supplier<RestClient.Builder> getRestClientBuilderSupplier() {
return restClientBuilderSupplier;
}
}

View File

@@ -18,7 +18,6 @@ package org.springframework.cloud.netflix.eureka.http;
import java.util.Collection;
import java.util.Optional;
import java.util.function.Supplier;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
@@ -28,31 +27,32 @@ import com.netflix.discovery.EurekaClientConfig;
import com.netflix.discovery.shared.transport.TransportClientFactory;
import com.netflix.discovery.shared.transport.jersey.TransportClientFactories;
import org.springframework.web.client.RestClient;
/**
* @author Wonchul Heo
* @author Olga Maciaszek-Sharma
* @since 4.2.0
*/
public class RestClientTransportClientFactories implements TransportClientFactories<Void> {
private final Supplier<RestClient.Builder> builder;
private final RestClientDiscoveryClientOptionalArgs args;
public RestClientTransportClientFactories(Supplier<RestClient.Builder> builder) {
this.builder = builder;
public RestClientTransportClientFactories(RestClientDiscoveryClientOptionalArgs args) {
this.args = args;
}
@Override
public TransportClientFactory newTransportClientFactory(EurekaClientConfig clientConfig,
Collection<Void> additionalFilters, InstanceInfo myInstanceInfo) {
return new RestClientTransportClientFactory(builder);
return new RestClientTransportClientFactory(args.getSSLContext(), args.getHostnameVerifier(),
args.getEurekaClientHttpRequestFactorySupplier(), args.getRestClientBuilderSupplier());
}
@Override
public TransportClientFactory newTransportClientFactory(final EurekaClientConfig clientConfig,
final Collection<Void> additionalFilters, final InstanceInfo myInstanceInfo,
final Optional<SSLContext> sslContext, final Optional<HostnameVerifier> hostnameVerifier) {
return new RestClientTransportClientFactory(builder);
return new RestClientTransportClientFactory(args.getSSLContext(), args.getHostnameVerifier(),
args.getEurekaClientHttpRequestFactorySupplier(), args.getRestClientBuilderSupplier());
}
}

View File

@@ -16,19 +16,26 @@
package org.springframework.cloud.netflix.eureka.http;
import java.util.Optional;
import java.util.function.Supplier;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import com.netflix.discovery.shared.resolver.EurekaEndpoint;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import com.netflix.discovery.shared.transport.TransportClientFactory;
import org.springframework.cloud.configuration.TlsProperties;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.support.BasicAuthenticationInterceptor;
import org.springframework.web.client.RestClient;
import org.springframework.web.util.UriComponentsBuilder;
import static org.springframework.cloud.netflix.eureka.http.EurekaHttpClientUtils.context;
import static org.springframework.cloud.netflix.eureka.http.EurekaHttpClientUtils.extractUserInfo;
import static org.springframework.cloud.netflix.eureka.http.EurekaHttpClientUtils.mappingJacksonHttpMessageConverter;
@@ -38,20 +45,49 @@ import static org.springframework.cloud.netflix.eureka.http.EurekaHttpClientUtil
* deserialization.
*
* @author Wonchul Heo
* @author Olga Maciaszek-Sharma
* @since 4.2.0
*/
public class RestClientTransportClientFactory implements TransportClientFactory {
private final Optional<SSLContext> sslContext;
private final Optional<HostnameVerifier> hostnameVerifier;
private final EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier;
private final Supplier<RestClient.Builder> builderSupplier;
public RestClientTransportClientFactory(Supplier<RestClient.Builder> builderSupplier) {
public RestClientTransportClientFactory(Optional<SSLContext> sslContext,
Optional<HostnameVerifier> hostnameVerifier,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
Supplier<RestClient.Builder> builderSupplier) {
this.sslContext = sslContext;
this.hostnameVerifier = hostnameVerifier;
this.eurekaClientHttpRequestFactorySupplier = eurekaClientHttpRequestFactorySupplier;
this.builderSupplier = builderSupplier;
}
public RestClientTransportClientFactory(TlsProperties tlsProperties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
Supplier<RestClient.Builder> builderSupplier) {
this(context(tlsProperties), Optional.empty(), eurekaClientHttpRequestFactorySupplier, builderSupplier);
}
public RestClientTransportClientFactory(TlsProperties tlsProperties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier) {
this(tlsProperties, eurekaClientHttpRequestFactorySupplier, RestClient::builder);
}
@Override
public EurekaHttpClient newClient(EurekaEndpoint endpoint) {
// we want a copy to modify. Don't change the original
final RestClient.Builder builder = builderSupplier.get().clone();
ClientHttpRequestFactory requestFactory = this.eurekaClientHttpRequestFactorySupplier
.get(this.sslContext.orElse(null), this.hostnameVerifier.orElse(null));
builder.requestFactory(requestFactory);
setUrl(builder, endpoint.getServiceUrl());
builder.messageConverters(converters -> converters.add(0, mappingJacksonHttpMessageConverter()));
@@ -69,6 +105,10 @@ public class RestClientTransportClientFactory implements TransportClientFactory
return new RestClientEurekaHttpClient(builder.build());
}
@Override
public void shutdown() {
}
private static void setUrl(RestClient.Builder builder, String serviceUrl) {
final String url = UriComponentsBuilder.fromUriString(serviceUrl).userInfo(null).toUriString();
@@ -79,8 +119,4 @@ public class RestClientTransportClientFactory implements TransportClientFactory
builder.baseUrl(url);
}
@Override
public void shutdown() {
}
}

View File

@@ -21,11 +21,16 @@ import java.util.function.Supplier;
import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
/**
* @author Daniel Lavoie
* @author Armin Krezovic
* @deprecated {@link RestTemplate}-based implementation to be removed in favour of
* {@link RestClient}-based implementation.
*/
@Deprecated
public class RestTemplateDiscoveryClientOptionalArgs extends AbstractDiscoveryClientOptionalArgs<Void> {
protected final EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier;

View File

@@ -38,6 +38,7 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
@@ -46,7 +47,10 @@ import static com.netflix.discovery.shared.transport.EurekaHttpResponse.anEureka
/**
* @author Daniel Lavoie
* @author Václav Plic
* @deprecated {@link RestTemplate}-based implementation to be removed in favour of
* {@link RestClient}-based implementation.
*/
@Deprecated(forRemoval = true)
public class RestTemplateEurekaHttpClient implements EurekaHttpClient {
private final RestTemplate restTemplate;

View File

@@ -27,10 +27,16 @@ import com.netflix.discovery.EurekaClientConfig;
import com.netflix.discovery.shared.transport.TransportClientFactory;
import com.netflix.discovery.shared.transport.jersey.TransportClientFactories;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
/**
* @author Daniel Lavoie
* @author Armin Krezovic
* @deprecated {@link RestTemplate}-based implementation to be removed in favour of
* {@link RestClient}-based implementation.
*/
@Deprecated(forRemoval = true)
public class RestTemplateTransportClientFactories implements TransportClientFactories<Void> {
private final RestTemplateDiscoveryClientOptionalArgs args;
@@ -42,16 +48,16 @@ public class RestTemplateTransportClientFactories implements TransportClientFact
@Override
public TransportClientFactory newTransportClientFactory(EurekaClientConfig clientConfig,
Collection<Void> additionalFilters, InstanceInfo myInstanceInfo) {
return new RestTemplateTransportClientFactory(this.args.getSSLContext(), this.args.getHostnameVerifier(),
this.args.eurekaClientHttpRequestFactorySupplier, this.args.restTemplateBuilderSupplier);
return new RestTemplateTransportClientFactory(args.getSSLContext(), args.getHostnameVerifier(),
args.eurekaClientHttpRequestFactorySupplier, args.restTemplateBuilderSupplier);
}
@Override
public TransportClientFactory newTransportClientFactory(final EurekaClientConfig clientConfig,
final Collection<Void> additionalFilters, final InstanceInfo myInstanceInfo,
final Optional<SSLContext> sslContext, final Optional<HostnameVerifier> hostnameVerifier) {
return new RestTemplateTransportClientFactory(this.args.getSSLContext(), this.args.getHostnameVerifier(),
this.args.eurekaClientHttpRequestFactorySupplier, this.args.restTemplateBuilderSupplier);
return new RestTemplateTransportClientFactory(args.getSSLContext(), args.getHostnameVerifier(),
args.eurekaClientHttpRequestFactorySupplier, args.restTemplateBuilderSupplier);
}
}

View File

@@ -27,7 +27,6 @@ import com.netflix.discovery.shared.transport.EurekaHttpClient;
import com.netflix.discovery.shared.transport.TransportClientFactory;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.configuration.SSLContextFactory;
import org.springframework.cloud.configuration.TlsProperties;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
@@ -35,9 +34,11 @@ import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.support.BasicAuthenticationInterceptor;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import static org.springframework.cloud.netflix.eureka.http.EurekaHttpClientUtils.context;
import static org.springframework.cloud.netflix.eureka.http.EurekaHttpClientUtils.extractUserInfo;
import static org.springframework.cloud.netflix.eureka.http.EurekaHttpClientUtils.mappingJacksonHttpMessageConverter;
@@ -49,7 +50,11 @@ import static org.springframework.cloud.netflix.eureka.http.EurekaHttpClientUtil
* @author Daniel Lavoie
* @author Armin Krezovic
* @author Wonchul Heo
* @author Olga Maciaszek-Sharma
* @deprecated {@link RestTemplate}-based implementation to be removed in favour of
* {@link RestClient}-based implementation.
*/
@Deprecated(forRemoval = true)
public class RestTemplateTransportClientFactory implements TransportClientFactory {
private final Optional<SSLContext> sslContext;
@@ -60,32 +65,6 @@ public class RestTemplateTransportClientFactory implements TransportClientFactor
private final Supplier<RestTemplateBuilder> restTemplateBuilderSupplier;
public RestTemplateTransportClientFactory(TlsProperties tlsProperties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
Supplier<RestTemplateBuilder> restTemplateBuilderSupplier) {
this.sslContext = context(tlsProperties);
this.hostnameVerifier = Optional.empty();
this.eurekaClientHttpRequestFactorySupplier = eurekaClientHttpRequestFactorySupplier;
this.restTemplateBuilderSupplier = restTemplateBuilderSupplier;
}
public RestTemplateTransportClientFactory(TlsProperties tlsProperties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier) {
this(tlsProperties, eurekaClientHttpRequestFactorySupplier, RestTemplateBuilder::new);
}
private Optional<SSLContext> context(TlsProperties properties) {
if (properties == null || !properties.isEnabled()) {
return Optional.empty();
}
try {
return Optional.of(new SSLContextFactory(properties).createSSLContext());
}
catch (Exception e) {
throw new IllegalStateException(e);
}
}
public RestTemplateTransportClientFactory(Optional<SSLContext> sslContext,
Optional<HostnameVerifier> hostnameVerifier,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
@@ -96,6 +75,18 @@ public class RestTemplateTransportClientFactory implements TransportClientFactor
this.restTemplateBuilderSupplier = restTemplateBuilderSupplier;
}
public RestTemplateTransportClientFactory(TlsProperties tlsProperties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
Supplier<RestTemplateBuilder> restTemplateBuilderSupplier) {
this(context(tlsProperties), Optional.empty(), eurekaClientHttpRequestFactorySupplier,
restTemplateBuilderSupplier);
}
public RestTemplateTransportClientFactory(TlsProperties tlsProperties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier) {
this(tlsProperties, eurekaClientHttpRequestFactorySupplier, RestTemplateBuilder::new);
}
public RestTemplateTransportClientFactory(Optional<SSLContext> sslContext,
Optional<HostnameVerifier> hostnameVerifier,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier) {
@@ -103,6 +94,11 @@ public class RestTemplateTransportClientFactory implements TransportClientFactor
this(sslContext, hostnameVerifier, eurekaClientHttpRequestFactorySupplier, RestTemplateBuilder::new);
}
// Visible for testing
/**
* @deprecated pass the default values while initialising object in test classes.
*/
@Deprecated(forRemoval = true)
public RestTemplateTransportClientFactory() {
this(Optional.empty(), Optional.empty(), new DefaultEurekaClientHttpRequestFactorySupplier());
}

View File

@@ -16,6 +16,8 @@
package org.springframework.cloud.netflix.eureka.http;
import java.util.Optional;
import com.netflix.appinfo.providers.EurekaConfigBasedInstanceInfoProvider;
import com.netflix.discovery.shared.resolver.DefaultEndpoint;
import org.junit.jupiter.api.BeforeEach;
@@ -26,11 +28,15 @@ 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.netflix.eureka.EurekaInstanceConfigBean;
import org.springframework.cloud.netflix.eureka.RestClientTimeoutProperties;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.web.client.RestClient;
/**
* Tests for {@link RestClientEurekaHttpClient}.
*
* @author Wonchul Heo
* @author Olga Maciaszek-Sharma
*/
@SpringBootTest(classes = EurekaServerMockApplication.class,
properties = { "debug=true", "security.basic.enabled=true", "eureka.client.fetch-registry=false",
@@ -47,7 +53,9 @@ class RestClientEurekaHttpClientTests extends AbstractEurekaHttpClientTests {
@BeforeEach
void setup() {
eurekaHttpClient = new RestClientTransportClientFactory(RestClient::builder)
eurekaHttpClient = new RestClientTransportClientFactory(Optional.empty(), Optional.empty(),
new DefaultEurekaClientHttpRequestFactorySupplier(new RestClientTimeoutProperties()),
RestClient::builder)
.newClient(new DefaultEndpoint(serviceUrl));
EurekaInstanceConfigBean config = new EurekaInstanceConfigBean(inetUtils);

View File

@@ -16,18 +16,24 @@
package org.springframework.cloud.netflix.eureka.http;
import java.util.Optional;
import com.netflix.discovery.shared.resolver.DefaultEndpoint;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.netflix.eureka.RestClientTimeoutProperties;
import org.springframework.web.client.RestClient;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link RestClientTransportClientFactory}.
*
* @author Wonchul Heo
* @author Olga Maciaszek-Sharma
*/
class RestClientTransportClientFactoryTests {
@@ -35,7 +41,9 @@ class RestClientTransportClientFactoryTests {
@BeforeEach
void setup() {
transportClientFactory = new RestClientTransportClientFactory(RestClient::builder);
transportClientFactory = new RestClientTransportClientFactory(Optional.empty(), Optional.empty(),
new DefaultEurekaClientHttpRequestFactorySupplier(new RestClientTimeoutProperties()),
RestClient::builder);
}
@Test