merge changes from upstream

This commit is contained in:
JiaLin
2020-08-19 00:20:44 +08:00
45 changed files with 364 additions and 112 deletions

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-netflix-docs</artifactId>
<packaging>pom</packaging>

View File

@@ -773,6 +773,10 @@ Then visit `/hystrix` and point the dashboard to an individual instance's `/hyst
NOTE: When connecting to a `/hystrix.stream` endpoint that uses HTTPS, the certificate used by the server must be trusted by the JVM.
If the certificate is not trusted, you must import the certificate into the JVM in order for the Hystrix Dashboard to make a successful connection to the stream endpoint.
NOTE: In order to use the `/proxy.stream` endpoint you must configure a list of hosts to allow connections to. To set the
list of allowed hosts use `hystrix.dashboard.proxyStreamAllowList`. You can use an Ant-style pattern in the host name to
match against a wider range of host names.
=== Turbine
Looking at an individual instance's Hystrix data is not very useful in terms of the overall health of the system. https://github.com/Netflix/Turbine[Turbine] is an application that aggregates all of the relevant `/hystrix.stream` endpoints into a combined `/turbine.stream` for use in the Hystrix Dashboard.

10
pom.xml
View File

@@ -3,14 +3,14 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Spring Cloud Netflix</name>
<description>Spring Cloud Netflix</description>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>2.3.1.BUILD-SNAPSHOT</version>
<version>2.3.2.BUILD-SNAPSHOT</version>
<relativePath />
</parent>
<scm>
@@ -21,9 +21,9 @@
</scm>
<properties>
<bintray.package>netflix</bintray.package>
<spring-cloud-commons.version>2.2.4.BUILD-SNAPSHOT</spring-cloud-commons.version>
<spring-cloud-config.version>2.2.4.BUILD-SNAPSHOT</spring-cloud-config.version>
<spring-cloud-stream.version>Horsham.SR5</spring-cloud-stream.version>
<spring-cloud-commons.version>2.2.5.BUILD-SNAPSHOT</spring-cloud-commons.version>
<spring-cloud-config.version>2.2.5.BUILD-SNAPSHOT</spring-cloud-config.version>
<spring-cloud-stream.version>Horsham.SR7</spring-cloud-stream.version>
<!-- Has to be a stable version (not one that depends on this version of netflix): -->
<donotreplacespring-cloud-contract.version>2.2.1.RELEASE</donotreplacespring-cloud-contract.version>

View File

@@ -6,7 +6,7 @@
<parent>
<artifactId>spring-cloud-netflix</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-netflix-concurrency-limits</artifactId>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-netflix-core</artifactId>

View File

@@ -5,18 +5,18 @@
<parent>
<artifactId>spring-cloud-dependencies-parent</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>2.3.1.BUILD-SNAPSHOT</version>
<version>2.3.2.BUILD-SNAPSHOT</version>
<relativePath/>
</parent>
<artifactId>spring-cloud-netflix-dependencies</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
<packaging>pom</packaging>
<name>spring-cloud-netflix-dependencies</name>
<description>Spring Cloud Netflix Dependencies</description>
<properties>
<archaius.version>0.7.6</archaius.version>
<concurrency-limits.version>0.1.12</concurrency-limits.version>
<eureka.version>1.9.21</eureka.version>
<eureka.version>1.9.25</eureka.version>
<eventbus.version>0.3.0</eventbus.version>
<hystrix.version>1.5.18</hystrix.version>
<ribbon.version>2.3.0</ribbon.version>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-netflix-eureka-client-tls-tests</artifactId>

View File

@@ -23,7 +23,7 @@ import java.security.KeyStore;
import org.junit.BeforeClass;
public class BaseCertTest {
public abstract class BaseCertTest {
protected static final String KEY_STORE_PASSWORD = "test-key-store-password";
@@ -41,6 +41,9 @@ public class BaseCertTest {
protected static File wrongClientCert;
protected BaseCertTest() {
}
@BeforeClass
public static void createCertificates() throws Exception {
KeyTool tool = new KeyTool();

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-netflix-eureka-client</artifactId>

View File

@@ -23,6 +23,7 @@ import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -35,6 +36,7 @@ import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClient
import org.springframework.cloud.netflix.eureka.http.WebClientDiscoveryClientOptionalArgs;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
/**
* @author Daniel Lavoie
@@ -46,7 +48,8 @@ public class DiscoveryClientOptionalArgsConfiguration {
@Autowired
private TlsProperties tls;
protected final Log logger = LogFactory.getLog(getClass());
protected static final Log logger = LogFactory
.getLog(DiscoveryClientOptionalArgsConfiguration.class);
@Bean
@ConditionalOnMissingClass("com.sun.jersey.api.client.filter.ClientFilter")
@@ -62,24 +65,6 @@ public class DiscoveryClientOptionalArgsConfiguration {
return result;
}
@Bean
@ConditionalOnMissingClass("com.sun.jersey.api.client.filter.ClientFilter")
@ConditionalOnClass(
name = "org.springframework.web.reactive.function.client.WebClient")
@ConditionalOnMissingBean(
value = { AbstractDiscoveryClientOptionalArgs.class,
RestTemplateDiscoveryClientOptionalArgs.class },
search = SearchStrategy.CURRENT)
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled",
havingValue = "true")
public WebClientDiscoveryClientOptionalArgs webClientDiscoveryClientOptionalArgs()
throws GeneralSecurityException, IOException {
logger.info("Eureka HTTP Client uses WebClient.");
WebClientDiscoveryClientOptionalArgs result = new WebClientDiscoveryClientOptionalArgs();
setupTLS(result);
return result;
}
@Bean
@ConditionalOnClass(name = "com.sun.jersey.api.client.filter.ClientFilter")
@ConditionalOnMissingBean(value = AbstractDiscoveryClientOptionalArgs.class,
@@ -99,6 +84,40 @@ public class DiscoveryClientOptionalArgsConfiguration {
}
}
@ConditionalOnMissingClass("com.sun.jersey.api.client.filter.ClientFilter")
@ConditionalOnClass(
name = "org.springframework.web.reactive.function.client.WebClient")
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled",
havingValue = "true")
protected static class WebClientConfiguration {
@Autowired
private TlsProperties tls;
@Bean
@ConditionalOnMissingBean(
value = { AbstractDiscoveryClientOptionalArgs.class,
RestTemplateDiscoveryClientOptionalArgs.class },
search = SearchStrategy.CURRENT)
public WebClientDiscoveryClientOptionalArgs webClientDiscoveryClientOptionalArgs(
ObjectProvider<WebClient.Builder> builder)
throws GeneralSecurityException, IOException {
logger.info("Eureka HTTP Client uses WebClient.");
WebClientDiscoveryClientOptionalArgs result = new WebClientDiscoveryClientOptionalArgs(
builder::getIfAvailable);
setupTLS(result);
return result;
}
private void setupTLS(AbstractDiscoveryClientOptionalArgs<?> args)
throws GeneralSecurityException, IOException {
if (tls.isEnabled()) {
args.setSSLContext(tls.createSSLContext());
}
}
}
@Configuration
@ConditionalOnMissingClass({ "com.sun.jersey.api.client.filter.ClientFilter",
"org.springframework.web.reactive.function.client.WebClient" })

View File

@@ -29,10 +29,14 @@ import com.netflix.discovery.shared.transport.EurekaHttpResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
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.reactive.function.client.WebClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.config.client.ConfigServerInstanceProvider;
@@ -48,6 +52,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Bootstrap configuration for config client that wants to lookup the config server via
@@ -72,18 +77,6 @@ public class EurekaConfigServerBootstrapConfiguration {
return new EurekaClientConfigBean();
}
@Bean
@ConditionalOnMissingBean(EurekaHttpClient.class)
@ConditionalOnClass(
name = "org.springframework.web.reactive.function.client.WebClient")
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled",
havingValue = "true")
public WebClientEurekaHttpClient configDiscoveryWebClientEurekaHttpClient(
EurekaClientConfigBean config, Environment env) {
return (WebClientEurekaHttpClient) new WebClientTransportClientFactory()
.newClient(new DefaultEndpoint(getEurekaUrl(config, env)));
}
@Bean
@ConditionalOnMissingBean(EurekaHttpClient.class)
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled",
@@ -94,7 +87,7 @@ public class EurekaConfigServerBootstrapConfiguration {
.newClient(new DefaultEndpoint(getEurekaUrl(config, env)));
}
private String getEurekaUrl(EurekaClientConfigBean config, Environment env) {
private static String getEurekaUrl(EurekaClientConfigBean config, Environment env) {
List<String> urls = EndpointUtils.getDiscoveryServiceUrls(config,
EurekaClientConfigBean.DEFAULT_ZONE, new HostnameBasedUrlRandomizer(
env.getProperty("eureka.instance.hostname")));
@@ -170,4 +163,25 @@ public class EurekaConfigServerBootstrapConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(
name = "org.springframework.web.reactive.function.client.WebClient")
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled",
havingValue = "true")
@ImportAutoConfiguration({ CodecsAutoConfiguration.class,
WebClientAutoConfiguration.class })
protected static class WebClientConfiguration {
@Bean
@ConditionalOnMissingBean(EurekaHttpClient.class)
public WebClientEurekaHttpClient configDiscoveryWebClientEurekaHttpClient(
EurekaClientConfigBean config, ObjectProvider<WebClient.Builder> builder,
Environment env) {
return (WebClientEurekaHttpClient) new WebClientTransportClientFactory(
builder::getIfAvailable)
.newClient(new DefaultEndpoint(getEurekaUrl(config, env)));
}
}
}

View File

@@ -16,8 +16,12 @@
package org.springframework.cloud.netflix.eureka.http;
import java.util.function.Supplier;
import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
import org.springframework.web.reactive.function.client.WebClient;
/**
* @author Daniel Lavoie
* @author Haytham Mohamed
@@ -25,8 +29,13 @@ import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
public class WebClientDiscoveryClientOptionalArgs
extends AbstractDiscoveryClientOptionalArgs<Void> {
@Deprecated
public WebClientDiscoveryClientOptionalArgs() {
setTransportClientFactories(new WebClientTransportClientFactories());
this(WebClient::builder);
}
public WebClientDiscoveryClientOptionalArgs(Supplier<WebClient.Builder> builder) {
setTransportClientFactories(new WebClientTransportClientFactories(builder));
}
}

View File

@@ -209,6 +209,10 @@ public class WebClientEurekaHttpClient implements EurekaHttpClient {
// Nothing to do
}
public WebClient getWebClient() {
return this.webClient;
}
private static Map<String, String> headersOf(ClientResponse response) {
ClientResponse.Headers httpHeaders = response.headers();
if (httpHeaders == null) {

View File

@@ -18,6 +18,7 @@ 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,12 +29,20 @@ import com.netflix.discovery.shared.transport.TransportClientFactory;
import com.netflix.discovery.shared.transport.jersey.EurekaJerseyClient;
import com.netflix.discovery.shared.transport.jersey.TransportClientFactories;
import org.springframework.web.reactive.function.client.WebClient;
/**
* @author Daniel Lavoie
* @author Haytham Mohamed
*/
public class WebClientTransportClientFactories implements TransportClientFactories<Void> {
private final Supplier<WebClient.Builder> builder;
public WebClientTransportClientFactories(Supplier<WebClient.Builder> builder) {
this.builder = builder;
}
@Override
public TransportClientFactory newTransportClientFactory(
Collection<Void> additionalFilters, EurekaJerseyClient providedJerseyClient) {
@@ -44,7 +53,7 @@ public class WebClientTransportClientFactories implements TransportClientFactori
public TransportClientFactory newTransportClientFactory(
EurekaClientConfig clientConfig, Collection<Void> additionalFilters,
InstanceInfo myInstanceInfo) {
return new WebClientTransportClientFactory();
return new WebClientTransportClientFactory(builder);
}
@Override
@@ -53,7 +62,7 @@ public class WebClientTransportClientFactories implements TransportClientFactori
final Collection<Void> additionalFilters, final InstanceInfo myInstanceInfo,
final Optional<SSLContext> sslContext,
final Optional<HostnameVerifier> hostnameVerifier) {
return new WebClientTransportClientFactory();
return new WebClientTransportClientFactory(builder);
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.cloud.netflix.eureka.http;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.function.Supplier;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationFeature;
@@ -41,13 +42,13 @@ import reactor.core.publisher.Mono;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ClientCodecConfigurer;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.ExchangeFilterFunctions;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.reactive.function.client.WebClient;
/**
@@ -60,17 +61,29 @@ import org.springframework.web.reactive.function.client.WebClient;
*/
public class WebClientTransportClientFactory implements TransportClientFactory {
private final Supplier<WebClient.Builder> builderSupplier;
@Deprecated
public WebClientTransportClientFactory() {
this(WebClient::builder);
}
public WebClientTransportClientFactory(Supplier<WebClient.Builder> builderSupplier) {
this.builderSupplier = builderSupplier;
}
@Override
public EurekaHttpClient newClient(EurekaEndpoint serviceUrl) {
WebClient.Builder builder = of(serviceUrl.getServiceUrl());
this.setExchangeStrategies(builder);
this.skipHttp400Error(builder);
public EurekaHttpClient newClient(EurekaEndpoint endpoint) {
// we want a copy to modify. Don't change the original
WebClient.Builder builder = this.builderSupplier.get().clone();
setUrl(builder, endpoint.getServiceUrl());
setCodecs(builder);
builder.filter(http4XxErrorExchangeFilterFunction());
return new WebClientEurekaHttpClient(builder.build());
}
private WebClient.Builder of(String serviceUrl) {
private WebClient.Builder setUrl(WebClient.Builder builder, String serviceUrl) {
String url = serviceUrl;
WebClient.Builder builder = WebClient.builder();
try {
URI serviceURI = new URI(serviceUrl);
if (serviceURI.getUserInfo() != null) {
@@ -88,23 +101,17 @@ public class WebClientTransportClientFactory implements TransportClientFactory {
return builder.baseUrl(url);
}
private void setExchangeStrategies(WebClient.Builder builder) {
private void setCodecs(WebClient.Builder builder) {
ObjectMapper objectMapper = objectMapper();
ExchangeStrategies strategies = ExchangeStrategies.builder()
.codecs(clientDefaultCodecsConfigurer -> {
clientDefaultCodecsConfigurer.defaultCodecs()
.jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper,
MediaType.APPLICATION_JSON));
clientDefaultCodecsConfigurer.defaultCodecs()
.jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper,
MediaType.APPLICATION_JSON));
builder.codecs(configurer -> {
ClientCodecConfigurer.ClientDefaultCodecs defaults = configurer
.defaultCodecs();
defaults.jackson2JsonEncoder(
new Jackson2JsonEncoder(objectMapper, MediaType.APPLICATION_JSON));
defaults.jackson2JsonDecoder(
new Jackson2JsonDecoder(objectMapper, MediaType.APPLICATION_JSON));
}).build();
builder.exchangeStrategies(strategies);
}
private void skipHttp400Error(WebClient.Builder builder) {
builder.filter(http4XxErrorExchangeFilterFunction());
});
}
// Skip over 4xx http errors
@@ -114,9 +121,8 @@ public class WebClientTransportClientFactory implements TransportClientFactory {
if (clientResponse.statusCode().value() == 400) {
ClientResponse newResponse = ClientResponse.from(clientResponse)
.statusCode(HttpStatus.OK).build();
newResponse.body((clientHttpResponse, context) -> {
return clientHttpResponse.getBody();
});
newResponse.body(
(clientHttpResponse, context) -> clientHttpResponse.getBody());
return Mono.just(newResponse);
}
return Mono.just(clientResponse);

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2013-2020 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.config;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.cloud.netflix.eureka.http.WebClientEurekaHttpClient;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/**
* @author Spencer Gibb
*/
@SpringBootTest(properties = { "spring.cloud.config.discovery.enabled=true",
"eureka.client.webclient.enabled=true",
"spring.codec.max-in-memory-size=310000" }, webEnvironment = RANDOM_PORT)
public class EurekaConfigServerBootstrapConfigurationWebClientIntegrationTests {
@LocalServerPort
private int port;
@Autowired
private WebClientEurekaHttpClient eurekaHttpClient;
@Test
public void webClientRespectsCodecProperties() {
WebClient webClient = eurekaHttpClient.getWebClient();
ClientResponse response = webClient.get().uri("http://localhost:" + port)
.exchange().block();
assertThat(response).isNotNull();
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.bodyToMono(String.class).block()).startsWith("....")
.hasSize(300000);
}
@SpringBootConfiguration
@EnableAutoConfiguration
@RestController
static class WebClientController extends WebSecurityConfigurerAdapter {
@GetMapping
public String hello() {
StringBuilder s = new StringBuilder();
for (int i = 0; i < 300000; i++) {
s.append(".");
}
return s.toString();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().permitAll().and().csrf().disable();
}
}
}

View File

@@ -29,6 +29,7 @@ import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.reactive.function.client.WebClient;
/**
* @author Daniel Lavoie
@@ -49,7 +50,7 @@ public class WebClientEurekaHttpClientTest extends AbstractEurekaHttpClientTest
@Before
public void setup() {
eurekaHttpClient = new WebClientTransportClientFactory()
eurekaHttpClient = new WebClientTransportClientFactory(WebClient::builder)
.newClient(new DefaultEndpoint(serviceUrl));
EurekaInstanceConfigBean config = new EurekaInstanceConfigBean(inetUtils);

View File

@@ -18,6 +18,8 @@ package org.springframework.cloud.netflix.eureka.http;
import org.junit.Test;
import org.springframework.web.reactive.function.client.WebClient;
/**
* @author Daniel Lavoie
*/
@@ -25,7 +27,8 @@ public class WebClientTransportClientFactoriesTest {
@Test(expected = UnsupportedOperationException.class)
public void testJerseyIsUnsuported() {
new WebClientTransportClientFactories().newTransportClientFactory(null, null);
new WebClientTransportClientFactories(WebClient::builder)
.newTransportClientFactory(null, null);
}
}

View File

@@ -21,6 +21,8 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.web.reactive.function.client.WebClient;
/**
* @author Daniel Lavoie
*/
@@ -30,7 +32,7 @@ public class WebClientTransportClientFactoryTest {
@Before
public void setup() {
transportClientFatory = new WebClientTransportClientFactory();
transportClientFatory = new WebClientTransportClientFactory(WebClient::builder);
}
@Test

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-netflix-eureka-server</artifactId>

View File

@@ -5,11 +5,11 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>2.3.0.RELEASE</version>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-netflix-hystrix-contract</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-cloud-netflix-hystrix-contract</name>
<description>Spring Cloud Netflix Hystrix Contract</description>

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<dependencies>

View File

@@ -19,7 +19,10 @@ package org.springframework.cloud.netflix.hystrix.dashboard;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import java.util.Optional;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
@@ -47,6 +50,8 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.ui.freemarker.SpringTemplateLoader;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
/**
@@ -88,8 +93,9 @@ public class HystrixDashboardConfiguration {
}
@Bean
public ServletRegistrationBean proxyStreamServlet() {
final ProxyStreamServlet proxyStreamServlet = new ProxyStreamServlet();
public ServletRegistrationBean proxyStreamServlet(
HystrixDashboardProperties properties) {
final ProxyStreamServlet proxyStreamServlet = new ProxyStreamServlet(properties);
proxyStreamServlet.setEnableIgnoreConnectionCloseHeader(
this.dashboardProperties.isEnableIgnoreConnectionCloseHeader());
final ServletRegistrationBean registration = new ServletRegistrationBean(
@@ -118,6 +124,8 @@ public class HystrixDashboardConfiguration {
private boolean enableIgnoreConnectionCloseHeader = false;
private HystrixDashboardProperties properties;
public void setEnableIgnoreConnectionCloseHeader(
boolean enableIgnoreConnectionCloseHeader) {
this.enableIgnoreConnectionCloseHeader = enableIgnoreConnectionCloseHeader;
@@ -125,6 +133,11 @@ public class HystrixDashboardConfiguration {
public ProxyStreamServlet() {
super();
this.properties = new HystrixDashboardProperties();
}
public ProxyStreamServlet(HystrixDashboardProperties properties) {
this.properties = properties;
}
/**
@@ -169,10 +182,18 @@ public class HystrixDashboardConfiguration {
url.append(key).append("=").append(value);
}
}
String proxyUrl = url.toString();
log.info("\n\nProxy opening connection to: " + proxyUrl + "\n\n");
String proxyUrlString = url.toString();
if (!isAllowedToProxy(proxyUrlString)) {
log.warn("Origin parameter: " + origin
+ " is not in the allowed list of proxy host names. If it "
+ "should be allowed add it to hystrix.dashboard.proxyStreamAllowList.");
return;
}
log.info("\n\nProxy opening connection to: " + proxyUrlString + "\n\n");
try {
httpget = new HttpGet(proxyUrl);
httpget = new HttpGet(proxyUrlString);
HttpClient client = ProxyConnectionManager.httpClient;
HttpResponse httpResponse = client.execute(httpget);
int statusCode = httpResponse.getStatusLine().getStatusCode();
@@ -218,7 +239,7 @@ public class HystrixDashboardConfiguration {
}
}
else {
log.warn("Failed opening connection to " + proxyUrl + " : "
log.warn("Failed opening connection to " + proxyUrlString + " : "
+ statusCode + " : " + httpResponse.getStatusLine());
}
}
@@ -250,6 +271,22 @@ public class HystrixDashboardConfiguration {
}
private boolean isAllowedToProxy(String proxyUrlString)
throws MalformedURLException {
URL proxyUrl = new URL(proxyUrlString);
String host = proxyUrl.getHost();
PathMatcher pathMatcher = new AntPathMatcher(".");
Optional<String> optionalPattern = properties.getProxyStreamAllowList()
.stream().filter(pattern -> pathMatcher.match(pattern, host))
.findFirst();
if (optionalPattern.isPresent()) {
return true;
}
return false;
}
private void copyHeadersToServletResponse(Header[] headers,
HttpServletResponse response) {
for (Header header : headers) {

View File

@@ -16,7 +16,10 @@
package org.springframework.cloud.netflix.hystrix.dashboard;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -41,6 +44,20 @@ public class HystrixDashboardProperties {
*/
private Map<String, String> initParameters = new HashMap<>();
private List<String> proxyStreamAllowList = new ArrayList<>();
public List<String> getProxyStreamAllowList() {
return proxyStreamAllowList;
}
public void setProxyStreamAllowList(List<String> proxyStreamAllowList) {
this.proxyStreamAllowList = proxyStreamAllowList;
}
public void setProxyStreamAllowList(String... regex) {
this.proxyStreamAllowList = Arrays.asList(regex);
}
public boolean isEnableIgnoreConnectionCloseHeader() {
return enableIgnoreConnectionCloseHeader;
}

View File

@@ -107,8 +107,8 @@
}
}
console.log("Command Stream: " + commandStream)
$(window).load(function() { // within load with a setTimeout to prevent the infinite spinner
$(window).on("load", function () { // within load with a setTimeout to prevent the infinite spinner
setTimeout(function() {
if(commandStream == undefined) {
console.log("commandStream is undefined")
@@ -146,7 +146,7 @@
// thread pool
var dependencyThreadPoolMonitor = new HystrixThreadPoolMonitor('dependencyThreadPools');
$(window).load(function() { // within load with a setTimeout to prevent the infinite spinner
$(window).on("load", function () { // within load with a setTimeout to prevent the infinite spinner
setTimeout(function() {
if(poolStream == undefined) {
console.log("poolStream is undefined")

View File

@@ -41,7 +41,8 @@ public class HystrixDashboardConfigurationTests {
MockHttpServletResponse response = new MockHttpServletResponse();
Header[] headers = new Header[1];
headers[0] = new BasicHeader("Content-Type", "text/proxy.stream");
HystrixDashboardConfiguration.ProxyStreamServlet proxyStreamServlet = new HystrixDashboardConfiguration.ProxyStreamServlet();
HystrixDashboardConfiguration.ProxyStreamServlet proxyStreamServlet = new HystrixDashboardConfiguration.ProxyStreamServlet(
new HystrixDashboardProperties());
ReflectionTestUtils.invokeMethod(proxyStreamServlet,
"copyHeadersToServletResponse", headers, response);
assertThat(response.getHeaderNames().size()).isEqualTo(1);
@@ -54,7 +55,8 @@ public class HystrixDashboardConfigurationTests {
Header[] headers = new Header[2];
headers[0] = new BasicHeader("Content-Type", "text/proxy.stream");
headers[1] = new BasicHeader("Connection", "close");
HystrixDashboardConfiguration.ProxyStreamServlet proxyStreamServlet = new HystrixDashboardConfiguration.ProxyStreamServlet();
HystrixDashboardConfiguration.ProxyStreamServlet proxyStreamServlet = new HystrixDashboardConfiguration.ProxyStreamServlet(
new HystrixDashboardProperties());
ReflectionTestUtils.invokeMethod(proxyStreamServlet,
"copyHeadersToServletResponse", headers, response);
assertThat(response.getHeaderNames().size()).isEqualTo(2);
@@ -68,7 +70,8 @@ public class HystrixDashboardConfigurationTests {
Header[] headers = new Header[2];
headers[0] = new BasicHeader("Content-Type", "text/proxy.stream");
headers[1] = new BasicHeader("Connection", "close");
HystrixDashboardConfiguration.ProxyStreamServlet proxyStreamServlet = new HystrixDashboardConfiguration.ProxyStreamServlet();
HystrixDashboardConfiguration.ProxyStreamServlet proxyStreamServlet = new HystrixDashboardConfiguration.ProxyStreamServlet(
new HystrixDashboardProperties());
proxyStreamServlet.setEnableIgnoreConnectionCloseHeader(true);
ReflectionTestUtils.invokeMethod(proxyStreamServlet,
"copyHeadersToServletResponse", headers, response);
@@ -83,7 +86,8 @@ public class HystrixDashboardConfigurationTests {
Header[] headers = new Header[2];
headers[0] = new BasicHeader("Content-Type", "text/proxy.stream");
headers[1] = new BasicHeader("Connection", "close");
HystrixDashboardConfiguration.ProxyStreamServlet proxyStreamServlet = new HystrixDashboardConfiguration.ProxyStreamServlet();
HystrixDashboardConfiguration.ProxyStreamServlet proxyStreamServlet = new HystrixDashboardConfiguration.ProxyStreamServlet(
new HystrixDashboardProperties());
proxyStreamServlet.setEnableIgnoreConnectionCloseHeader(false);
ReflectionTestUtils.invokeMethod(proxyStreamServlet,
"copyHeadersToServletResponse", headers, response);
@@ -111,4 +115,39 @@ public class HystrixDashboardConfigurationTests {
});
}
@Test
public void allowedHostsTest() {
HystrixDashboardProperties properties = new HystrixDashboardProperties();
HystrixDashboardConfiguration.ProxyStreamServlet proxyStreamServlet = new HystrixDashboardConfiguration.ProxyStreamServlet(
properties);
boolean allowed = ReflectionTestUtils.invokeMethod(proxyStreamServlet,
"isAllowedToProxy", "http://foo.com");
assertThat(allowed).isFalse();
properties.setProxyStreamAllowList("foo.com", "bar.*", "*world.com");
allowed = ReflectionTestUtils.invokeMethod(proxyStreamServlet, "isAllowedToProxy",
"http://user:password@foo.com");
assertThat(allowed).isTrue();
allowed = ReflectionTestUtils.invokeMethod(proxyStreamServlet, "isAllowedToProxy",
"http://bar.com");
assertThat(allowed).isTrue();
allowed = ReflectionTestUtils.invokeMethod(proxyStreamServlet, "isAllowedToProxy",
"http://bar.org");
assertThat(allowed).isTrue();
allowed = ReflectionTestUtils.invokeMethod(proxyStreamServlet, "isAllowedToProxy",
"http://helloworld.com");
assertThat(allowed).isTrue();
allowed = ReflectionTestUtils.invokeMethod(proxyStreamServlet, "isAllowedToProxy",
"http://world.com");
assertThat(allowed).isTrue();
allowed = ReflectionTestUtils.invokeMethod(proxyStreamServlet, "isAllowedToProxy",
"http://world.org");
assertThat(allowed).isFalse();
}
}

View File

@@ -67,6 +67,7 @@ public class HystrixDashboardHomePageTests {
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
"http://localhost:" + this.port + "/hystrix/monitor", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("on(\"load\"");
}
@Configuration(proxyBeanMethods = false)

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-netflix-hystrix-stream</artifactId>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-netflix-hystrix</artifactId>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>spring-cloud-netflix</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-netflix-sidecar</artifactId>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-netflix-turbine-stream</artifactId>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-netflix-turbine</artifactId>

View File

@@ -6,7 +6,7 @@
<parent>
<artifactId>spring-cloud-netflix</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>spring-cloud-starter-netflix</artifactId>

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-starter-netflix-archaius</artifactId>
<name>Spring Cloud Starter Netflix Archaius</name>

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<name>Spring Cloud Starter Netflix Eureka Client</name>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<name>Spring Cloud Starter Netflix Eureka Server</name>

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<name>Spring Cloud Starter Netflix Hystrix Dashboard</name>

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<name>Spring Cloud Starter Netflix Hystrix</name>

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<name>Spring Cloud Starter Netflix Ribbon</name>

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-starter-netflix-turbine-stream</artifactId>
<name>Spring Cloud Starter Netflix Turbine Stream</name>

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
<name>Spring Cloud Starter Netflix Turbine</name>

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix</artifactId>
<version>2.2.4.BUILD-SNAPSHOT</version>
<version>2.2.5.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<name>Spring Cloud Starter Netflix Zuul</name>