mirror of
https://github.com/spring-cloud/spring-cloud-netflix.git
synced 2026-09-20 00:59:01 +00:00
Compare commits
55
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74f769d8dd | ||
|
|
b3e4ced142 | ||
|
|
66a67bb353 | ||
|
|
e219783d40 | ||
|
|
ed56c5c1f2 | ||
|
|
55884acd85 | ||
|
|
3659a9206a | ||
|
|
91ce95babe | ||
|
|
153bf0ea63 | ||
|
|
4ab9897b3b | ||
|
|
2fffb2606e | ||
|
|
0bab3451b5 | ||
|
|
d25103ebdb | ||
|
|
af99c1b403 | ||
|
|
5eb8ceadf2 | ||
|
|
7bca5019f6 | ||
|
|
129e4618ea | ||
|
|
89ed8219cb | ||
|
|
703b4ce577 | ||
|
|
74eee18701 | ||
|
|
25aca6b548 | ||
|
|
0ad43e1018 | ||
|
|
ec92f48c76 | ||
|
|
cfdaf345e3 | ||
|
|
b0e4250a4b | ||
|
|
3892a3a711 | ||
|
|
438f7360c7 | ||
|
|
a1f7bab91a | ||
|
|
60bb89293c | ||
|
|
3ad07748b2 | ||
|
|
1540329624 | ||
|
|
151fad898b | ||
|
|
ffc2dc804c | ||
|
|
aa757592b6 | ||
|
|
78adf15eea | ||
|
|
5264f3fa26 | ||
|
|
5edbf114a6 | ||
|
|
63d07cc5fd | ||
|
|
a09a55eea7 | ||
|
|
9f5f246209 | ||
|
|
90290757cb | ||
|
|
dfdc6c39e9 | ||
|
|
00587bae01 | ||
|
|
504ff8e7d4 | ||
|
|
1510258e2b | ||
|
|
59a4e430a0 | ||
|
|
70008f7717 | ||
|
|
1897c958d1 | ||
|
|
0c3fbc34f6 | ||
|
|
01ac98df8f | ||
|
|
3a62567ec8 | ||
|
|
3f0a43f913 | ||
|
|
53e7464a6e | ||
|
|
9eb5e64cd0 | ||
|
|
edd24bc51b |
@@ -39,4 +39,4 @@ jobs:
|
||||
destination: artifacts
|
||||
- store_test_results:
|
||||
path: ~/junit/
|
||||
destination: testartifacts
|
||||
destination: testartifacts
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-docs</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
Regular → Executable
+100
-4
@@ -335,6 +335,16 @@ This section describes how to set up a Eureka server.
|
||||
To include Eureka Server in your project, use the starter with a group ID of `org.springframework.cloud` and an artifact ID of `spring-cloud-starter-netflix-eureka-server`.
|
||||
See the http://projects.spring.io/spring-cloud/[Spring Cloud Project page] for details on setting up your build system with the current Spring Cloud Release Train.
|
||||
|
||||
NOTE: If your project already uses Thymeleaf as its template engine, the Freemarker templates of the Eureka server may not be loaded correctly. In this case it is necessary to configure the template loader manually:
|
||||
|
||||
.application.yml
|
||||
----
|
||||
spring:
|
||||
freemarker:
|
||||
template-loader-path: classpath:/templates/
|
||||
prefer-file-system-access: false
|
||||
----
|
||||
|
||||
[[spring-cloud-running-eureka-server]]
|
||||
=== How to Run a Eureka Server
|
||||
|
||||
@@ -524,6 +534,30 @@ For more information on CSRF see the https://docs.spring.io/spring-security/site
|
||||
|
||||
A demo Eureka Server can be found in the Spring Cloud Samples https://github.com/spring-cloud-samples/eureka/tree/Eureka-With-Security[repo].
|
||||
|
||||
=== JDK 11 Support
|
||||
|
||||
The JAXB modules which the Eureka server depends upon were removed in JDK 11. If you intend to use JDK 11
|
||||
when running a Eureka server you must include these dependencies in your POM or Gradle file.
|
||||
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.xml.bind</groupId>
|
||||
<artifactId>jaxb-core</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.xml.bind</groupId>
|
||||
<artifactId>jaxb-impl</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
== Circuit Breaker: Hystrix Clients
|
||||
|
||||
Netflix has created a library called https://github.com/Netflix/Hystrix[Hystrix] that implements the http://martinfowler.com/bliki/CircuitBreaker.html[circuit breaker pattern].
|
||||
@@ -736,7 +770,7 @@ turbine:
|
||||
clusterNameExpression: "'default'"
|
||||
----
|
||||
|
||||
Spring Cloud provides a `spring-cloud-starter-netflix-turbine` that has all the dependencies you need to get a Turbine server running. To ad Turnbine, create a Spring Boot application and annotate it with `@EnableTurbine`.
|
||||
Spring Cloud provides a `spring-cloud-starter-netflix-turbine` that has all the dependencies you need to get a Turbine server running. To add Turbine, create a Spring Boot application and annotate it with `@EnableTurbine`.
|
||||
|
||||
NOTE: By default, Spring Cloud lets Turbine use the host and port to allow multiple processes per host, per cluster.
|
||||
If you want the native Netflix behavior built into Turbine to _not_ allow multiple processes per host, per cluster (the key to the instance ID is the hostname), set `turbine.combineHostPort=false`.
|
||||
@@ -1481,6 +1515,22 @@ NOTE: This special flag works only with `SimpleHostRoutingFilter`. Also, you loo
|
||||
query parameters with `RequestContext.getCurrentContext().setRequestQueryParams(someOverriddenParameters)`, because
|
||||
the query string is now fetched directly on the original `HttpServletRequest`.
|
||||
|
||||
=== Request URI Encoding
|
||||
When processing the incoming request, request URI is decoded before matching them to routes.
|
||||
The request URI is then re-encoded when the back end request is rebuilt in the route filters.
|
||||
This can cause some unexpected behavior if your URI includes the encoded "/" character.
|
||||
|
||||
To use the original request URI, it is possible to pass a special flag to 'ZuulProperties' so that the URI will be taken as is with the `HttpServletRequest::getRequestURI` method, as shown in the following example:
|
||||
|
||||
.application.yml
|
||||
[source,yaml]
|
||||
----
|
||||
zuul:
|
||||
decodeUrl: false
|
||||
----
|
||||
|
||||
NOTE: If you are overriding request URI using `requestURI` RequestContext attribute and this flag is set to false, then the URL set in the request context will not be encoded. It will be your responsibility to make sure the URL is already encoded.
|
||||
|
||||
=== Plain Embedded Zuul
|
||||
|
||||
If you use `@EnableZuulServer` (instead of `@EnableZuulProxy`), you can also run a Zuul server without proxying or selectively switch on parts of the proxying platform.
|
||||
@@ -1661,6 +1711,26 @@ public class ZuulConfig {
|
||||
|
||||
CAUTION: Use this filter carefully. The filter acts on the `Location` header of ALL `3XX` response codes, which may not be appropriate in all scenarios, such as when redirecting the user to an external URL.
|
||||
|
||||
=== Enabling Cross Origin Requests
|
||||
|
||||
By default Zuul routes all Cross Origin requests (CORS) to the services. If you want instead Zuul to handle these requests it can be done by providing custom `WebMvcConfigurer` bean:
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public WebMvcConfigurer corsConfigurer() {
|
||||
return new WebMvcConfigurer() {
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/path-1/**")
|
||||
.allowedOrigins("http://allowed-origin.com")
|
||||
.allowedMethods("GET", "POST");
|
||||
}
|
||||
};
|
||||
}
|
||||
----
|
||||
In the example above, we allow `GET` and `POST` methods from `http://allowed-origin.com` to send cross-origin requests to the endpoints starting with `path-1`.
|
||||
You can apply CORS configuration to a specific path pattern or globally for the whole application, using `/**` mapping.
|
||||
You can customize properties: `allowedOrigins`,`allowedMethods`,`allowedHeaders`,`exposedHeaders`,`allowCredentials` and `maxAge` via this configuration.
|
||||
|
||||
=== Metrics
|
||||
|
||||
Zuul will provide metrics under the Actuator metrics endpoint for any failures that might occur when routing requests.
|
||||
@@ -2021,6 +2091,8 @@ info:
|
||||
url: https://github.com/spring-cloud-samples
|
||||
----
|
||||
|
||||
To enable the health check request to accept all certificates when using HTTPs set `sidecar.accept-all-ssl-certificates` to `true.
|
||||
|
||||
[[retrying-failed-requests]]
|
||||
== Retrying Failed Requests
|
||||
|
||||
@@ -2033,15 +2105,15 @@ When Spring Retry is present, load-balanced `RestTemplates`, Feign, and Zuul aut
|
||||
|
||||
=== BackOff Policies
|
||||
By default, no backoff policy is used when retrying requests.
|
||||
If you would like to configure a backoff policy, you need to create a bean of type `LoadBalancedBackOffPolicyFactory`, which is used to create a `BackOffPolicy` for a given service, as shown in the following example:
|
||||
If you would like to configure a backoff policy, you need to create a bean of type `LoadBalancedRetryFactory` and override the `createBackOffPolicy` method for a given service, as shown in the following example:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@Configuration
|
||||
public class MyConfiguration {
|
||||
@Bean
|
||||
LoadBalancedBackOffPolicyFactory backOffPolicyFactory() {
|
||||
return new LoadBalancedBackOffPolicyFactory() {
|
||||
LoadBalancedRetryFactory retryFactory() {
|
||||
return new LoadBalancedRetryFactory() {
|
||||
@Override
|
||||
public BackOffPolicy createBackOffPolicy(String service) {
|
||||
return new ExponentialBackOffPolicy();
|
||||
@@ -2089,3 +2161,27 @@ are using the Apache Http Cient or `OkHttpClient` if you are using OK HTTP.
|
||||
NOTE: When you create your own HTTP client, you are also responsible for implementing the correct connection management strategies for these clients.
|
||||
Doing so improperly can result in resource management issues.
|
||||
// TODO Can we provide a link to how to do so?
|
||||
|
||||
== Modules In Maintenance Mode
|
||||
|
||||
Placing a module in maintenance mode means that the Spring Cloud team will no longer be adding new features to the module.
|
||||
We will fix blocker bugs and security issues, and we will also consider and review small pull requests from the community.
|
||||
|
||||
We intend to continue to support these modules for a period of at least a year from the general availability
|
||||
of the Greenwich release train.
|
||||
|
||||
The following Spring Cloud Netflix modules and corresponding starters will be placed into maintenance mode:
|
||||
|
||||
* spring-cloud-netflix-archaius
|
||||
* spring-cloud-netflix-hystrix-contract
|
||||
* spring-cloud-netflix-hystrix-dashboard
|
||||
* spring-cloud-netflix-hystrix-stream
|
||||
* spring-cloud-netflix-hystrix
|
||||
* spring-cloud-netflix-ribbon
|
||||
* spring-cloud-netflix-turbine-stream
|
||||
* spring-cloud-netflix-turbine
|
||||
* spring-cloud-netflix-zuul
|
||||
|
||||
NOTE: This does not include the Eureka or concurrency-limits modules.
|
||||
|
||||
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</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.1.0.RC2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
<scm>
|
||||
@@ -21,9 +21,9 @@
|
||||
</scm>
|
||||
<properties>
|
||||
<bintray.package>netflix</bintray.package>
|
||||
<spring-cloud-commons.version>2.1.0.M2</spring-cloud-commons.version>
|
||||
<spring-cloud-config.version>2.1.0.M2</spring-cloud-config.version>
|
||||
<spring-cloud-stream.version>Fishtown.RC1</spring-cloud-stream.version>
|
||||
<spring-cloud-commons.version>2.1.0.RC2</spring-cloud-commons.version>
|
||||
<spring-cloud-config.version>2.1.0.RC3</spring-cloud-config.version>
|
||||
<spring-cloud-stream.version>Fishtown.RC4</spring-cloud-stream.version>
|
||||
<!-- Has to be a stable version (not one that depends on this version of netflix): -->
|
||||
<donotreplacespring-cloud-contract.version>2.0.1.RELEASE</donotreplacespring-cloud-contract.version>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-concurrency-limits</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-core</artifactId>
|
||||
|
||||
@@ -5,19 +5,19 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-dependencies-parent</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>2.1.0.RC2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-dependencies</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</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.6</concurrency-limits.version>
|
||||
<eureka.version>1.9.7</eureka.version>
|
||||
<hystrix.version>1.5.12</hystrix.version>
|
||||
<eureka.version>1.9.8</eureka.version>
|
||||
<hystrix.version>1.5.18</hystrix.version>
|
||||
<ribbon.version>2.3.0</ribbon.version>
|
||||
<servo.version>0.12.21</servo.version>
|
||||
<zuul.version>1.3.1</zuul.version>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
|
||||
|
||||
+19
-5
@@ -23,6 +23,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
|
||||
@@ -53,6 +54,7 @@ import org.springframework.cloud.netflix.eureka.metadata.ManagementMetadataProvi
|
||||
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaAutoServiceRegistration;
|
||||
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration;
|
||||
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaServiceRegistry;
|
||||
import org.springframework.cloud.util.ProxyUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
@@ -203,8 +205,8 @@ public class EurekaClientAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DiscoveryClient discoveryClient(EurekaInstanceConfig config, EurekaClient client) {
|
||||
return new EurekaDiscoveryClient(config, client);
|
||||
public DiscoveryClient discoveryClient(EurekaClient client, EurekaClientConfig clientConfig) {
|
||||
return new EurekaDiscoveryClient(client, clientConfig);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -285,10 +287,22 @@ public class EurekaClientAutoConfiguration {
|
||||
@ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
|
||||
@org.springframework.cloud.context.config.annotation.RefreshScope
|
||||
@Lazy
|
||||
public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config, EurekaInstanceConfig instance) {
|
||||
manager.getInfo(); // force initialization
|
||||
return new CloudEurekaClient(manager, config, this.optionalArgs,
|
||||
public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config, EurekaInstanceConfig instance,
|
||||
@Autowired(required = false) HealthCheckHandler healthCheckHandler) {
|
||||
//If we use the proxy of the ApplicationInfoManager we could run into a problem
|
||||
//when shutdown is called on the CloudEurekaClient where the ApplicationInfoManager bean is
|
||||
//requested but wont be allowed because we are shutting down. To avoid this we use the
|
||||
//object directly.
|
||||
ApplicationInfoManager appManager;
|
||||
if(AopUtils.isAopProxy(manager)) {
|
||||
appManager = ProxyUtils.getTargetObject(manager);
|
||||
} else {
|
||||
appManager = manager;
|
||||
}
|
||||
CloudEurekaClient cloudEurekaClient = new CloudEurekaClient(appManager, config, this.optionalArgs,
|
||||
this.context);
|
||||
cloudEurekaClient.registerHealthCheck(healthCheckHandler);
|
||||
return cloudEurekaClient;
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
+13
-4
@@ -39,17 +39,22 @@ import static com.netflix.appinfo.InstanceInfo.PortType.SECURE;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Tim Ysewyn
|
||||
*/
|
||||
public class EurekaDiscoveryClient implements DiscoveryClient {
|
||||
|
||||
public static final String DESCRIPTION = "Spring Cloud Eureka Discovery Client";
|
||||
|
||||
private final EurekaInstanceConfig config;
|
||||
|
||||
private final EurekaClient eurekaClient;
|
||||
private final EurekaClientConfig clientConfig;
|
||||
|
||||
@Deprecated
|
||||
public EurekaDiscoveryClient(EurekaInstanceConfig config, EurekaClient eurekaClient) {
|
||||
this.config = config;
|
||||
this(eurekaClient, eurekaClient.getEurekaClientConfig());
|
||||
}
|
||||
|
||||
public EurekaDiscoveryClient(EurekaClient eurekaClient, EurekaClientConfig clientConfig) {
|
||||
this.clientConfig = clientConfig;
|
||||
this.eurekaClient = eurekaClient;
|
||||
}
|
||||
|
||||
@@ -81,6 +86,11 @@ public class EurekaDiscoveryClient implements DiscoveryClient {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInstanceId() {
|
||||
return this.instance.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceId() {
|
||||
return this.instance.getAppName();
|
||||
@@ -136,7 +146,6 @@ public class EurekaDiscoveryClient implements DiscoveryClient {
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
EurekaClientConfig clientConfig = eurekaClient.getEurekaClientConfig();
|
||||
return clientConfig instanceof Ordered ? ((Ordered) clientConfig).getOrder() : DiscoveryClient.DEFAULT_ORDER;
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -17,8 +17,6 @@
|
||||
|
||||
package org.springframework.cloud.netflix.eureka.serviceregistry;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
@@ -44,6 +42,7 @@ import com.netflix.discovery.EurekaClientConfig;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Tim Ysewyn
|
||||
*/
|
||||
public class EurekaRegistration implements Registration {
|
||||
private static final Log log = LogFactory.getLog(EurekaRegistration.class);
|
||||
@@ -117,6 +116,11 @@ public class EurekaRegistration implements Registration {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInstanceId() {
|
||||
return this.instanceConfig.getInstanceId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceId() {
|
||||
return this.instanceConfig.getAppname();
|
||||
|
||||
+4
-4
@@ -39,7 +39,7 @@ public class EurekaServiceRegistry implements ServiceRegistry<EurekaRegistration
|
||||
maybeInitializeClient(reg);
|
||||
|
||||
if (log.isInfoEnabled()) {
|
||||
log.info("Registering application " + reg.getInstanceConfig().getAppname()
|
||||
log.info("Registering application " + reg.getApplicationInfoManager().getInfo().getAppName()
|
||||
+ " with eureka with status "
|
||||
+ reg.getInstanceConfig().getInitialStatus());
|
||||
}
|
||||
@@ -62,7 +62,7 @@ public class EurekaServiceRegistry implements ServiceRegistry<EurekaRegistration
|
||||
if (reg.getApplicationInfoManager().getInfo() != null) {
|
||||
|
||||
if (log.isInfoEnabled()) {
|
||||
log.info("Unregistering application " + reg.getInstanceConfig().getAppname()
|
||||
log.info("Unregistering application " + reg.getApplicationInfoManager().getInfo().getAppName()
|
||||
+ " with eureka with status DOWN");
|
||||
}
|
||||
|
||||
@@ -91,8 +91,8 @@ public class EurekaServiceRegistry implements ServiceRegistry<EurekaRegistration
|
||||
|
||||
@Override
|
||||
public Object getStatus(EurekaRegistration registration) {
|
||||
String appname = registration.getInstanceConfig().getAppname();
|
||||
String instanceId = registration.getInstanceConfig().getInstanceId();
|
||||
String appname = registration.getApplicationInfoManager().getInfo().getAppName();
|
||||
String instanceId = registration.getApplicationInfoManager().getInfo().getId();
|
||||
InstanceInfo info = registration.getEurekaClient().getInstanceInfo(appname, instanceId);
|
||||
|
||||
HashMap<String, Object> status = new HashMap<>();
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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
|
||||
*
|
||||
* http://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 com.netflix.appinfo.ApplicationInfoManager;
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
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.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||
public class EurekaClientAutoConfigurationRandomPortTests {
|
||||
|
||||
@Autowired
|
||||
private ApplicationInfoManager infoManager;
|
||||
|
||||
@Test
|
||||
public void instanceInfoPortShouldNotBeZero() {
|
||||
InstanceInfo info = infoManager.getInfo();
|
||||
assertThat(info.getPort()).isGreaterThan(0);
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
@EnableAutoConfiguration
|
||||
public static class TestConfig {
|
||||
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -142,7 +142,7 @@ public class EurekaHealthCheckHandlerTests {
|
||||
public static class EurekaDownHealthConfiguration {
|
||||
@Bean
|
||||
public DiscoveryHealthIndicator discoveryHealthIndicator() {
|
||||
return new DiscoveryClientHealthIndicator(null) {
|
||||
return new DiscoveryClientHealthIndicator(null, null) {
|
||||
@Override
|
||||
public Health health() {
|
||||
return Health.up().build();
|
||||
|
||||
+21
@@ -42,6 +42,7 @@ import static org.junit.Assert.assertTrue;
|
||||
* @author Dave Syer
|
||||
* @author Spencer Gibb
|
||||
* @author Ryan Baxter
|
||||
* @author Tim Ysewyn
|
||||
*/
|
||||
public class EurekaInstanceConfigBeanTests {
|
||||
|
||||
@@ -207,6 +208,26 @@ public class EurekaInstanceConfigBeanTests {
|
||||
assertEquals("default secure virtual hostname is wrong", "unknown", getInstanceConfig().getSecureVirtualHostName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomInstanceId() throws Exception {
|
||||
TestPropertyValues.of("eureka.instance.instanceId=myinstance").applyTo(this.context);
|
||||
setupContext();
|
||||
assertEquals("instance id is wrong", "myinstance", getInstanceConfig().getInstanceId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomInstanceIdWithMetadata() throws Exception {
|
||||
TestPropertyValues.of("eureka.instance.metadataMap.instanceId=myinstance").applyTo(this.context);
|
||||
setupContext();
|
||||
assertEquals("instance id is wrong", "myinstance", getInstanceConfig().getInstanceId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultInstanceId() throws Exception {
|
||||
setupContext();
|
||||
assertEquals("default instance id is wrong", null, getInstanceConfig().getInstanceId());
|
||||
}
|
||||
|
||||
private void setupContext() {
|
||||
this.context.register(PropertyPlaceholderAutoConfiguration.class,
|
||||
TestConfiguration.class);
|
||||
|
||||
+27
-9
@@ -31,6 +31,7 @@ import com.netflix.appinfo.ApplicationInfoManager;
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
|
||||
import static com.netflix.appinfo.InstanceInfo.InstanceStatus.DOWN;
|
||||
import static com.netflix.appinfo.InstanceInfo.InstanceStatus.OUT_OF_SERVICE;
|
||||
import static com.netflix.appinfo.InstanceInfo.InstanceStatus.UNKNOWN;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -39,6 +40,7 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Tim Ysewyn
|
||||
*/
|
||||
public class EurekaServiceRegistryTests {
|
||||
|
||||
@@ -70,32 +72,43 @@ public class EurekaServiceRegistryTests {
|
||||
config.setAppname("myapp");
|
||||
config.setInstanceId("1234");
|
||||
|
||||
CloudEurekaClient eurekaClient = mock(CloudEurekaClient.class);
|
||||
|
||||
InstanceInfo instanceInfo = InstanceInfo.Builder.newBuilder()
|
||||
InstanceInfo local = InstanceInfo.Builder.newBuilder()
|
||||
.setAppName("myapp")
|
||||
.setInstanceId("1234")
|
||||
.setStatus(DOWN)
|
||||
.setOverriddenStatus(UNKNOWN)
|
||||
.build();
|
||||
when(eurekaClient.getInstanceInfo("myapp", "1234"))
|
||||
.thenReturn(instanceInfo);
|
||||
|
||||
InstanceInfo remote = InstanceInfo.Builder.newBuilder()
|
||||
.setAppName("myapp")
|
||||
.setInstanceId("1234")
|
||||
.setStatus(DOWN)
|
||||
.setOverriddenStatus(OUT_OF_SERVICE)
|
||||
.build();
|
||||
|
||||
CloudEurekaClient eurekaClient = mock(CloudEurekaClient.class);
|
||||
when(eurekaClient.getInstanceInfo(local.getAppName(), local.getId()))
|
||||
.thenReturn(remote);
|
||||
|
||||
ApplicationInfoManager applicationInfoManager = mock(ApplicationInfoManager.class);
|
||||
when(applicationInfoManager.getInfo()).thenReturn(local);
|
||||
|
||||
EurekaRegistration registration = EurekaRegistration.builder(config)
|
||||
.with(eurekaClient)
|
||||
.with(mock(ApplicationInfoManager.class))
|
||||
.with(applicationInfoManager)
|
||||
.with(new EurekaClientConfigBean(), mock(ApplicationEventPublisher.class))
|
||||
.build();
|
||||
|
||||
Object status = registry.getStatus(registration);
|
||||
|
||||
assertThat(registration.getInstanceId()).isEqualTo("1234");
|
||||
|
||||
assertThat(status).isInstanceOf(Map.class);
|
||||
|
||||
Map<Object, Object> map = (Map<Object, Object>) status;
|
||||
|
||||
assertThat(map).hasSize(2)
|
||||
.containsEntry("status", DOWN.toString())
|
||||
.containsEntry("overriddenStatus", UNKNOWN.toString());
|
||||
.containsEntry("overriddenStatus", OUT_OF_SERVICE.toString());
|
||||
}
|
||||
|
||||
|
||||
@@ -112,14 +125,19 @@ public class EurekaServiceRegistryTests {
|
||||
when(eurekaClient.getInstanceInfo("myapp", "1234"))
|
||||
.thenReturn(null);
|
||||
|
||||
ApplicationInfoManager applicationInfoManager = mock(ApplicationInfoManager.class);
|
||||
when(applicationInfoManager.getInfo()).thenReturn(mock(InstanceInfo.class));
|
||||
|
||||
EurekaRegistration registration = EurekaRegistration.builder(config)
|
||||
.with(eurekaClient)
|
||||
.with(mock(ApplicationInfoManager.class))
|
||||
.with(applicationInfoManager)
|
||||
.with(new EurekaClientConfigBean(), mock(ApplicationEventPublisher.class))
|
||||
.build();
|
||||
|
||||
Object status = registry.getStatus(registration);
|
||||
|
||||
assertThat(registration.getInstanceId()).isEqualTo("1234");
|
||||
|
||||
assertThat(status).isInstanceOf(Map.class);
|
||||
|
||||
Map<Object, Object> map = (Map<Object, Object>) status;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-eureka-server</artifactId>
|
||||
|
||||
+16
-5
@@ -19,6 +19,7 @@ package org.springframework.cloud.netflix.eureka.server;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
|
||||
|
||||
import static org.springframework.cloud.netflix.eureka.server.InstanceRegistryProperties.PREFIX;
|
||||
|
||||
@@ -36,9 +37,14 @@ public class InstanceRegistryProperties {
|
||||
* zero, even a successful registration won't reset the rate threshold in
|
||||
* InstanceRegistry.register()).
|
||||
*/
|
||||
@Value("${eureka.server.expectedNumberOfClientsSendingRenews:1}") // for backwards compatibility
|
||||
@Value("${eureka.server.expectedNumberOfRenewsPerMin:1}") // for backwards compatibility
|
||||
private int expectedNumberOfClientsSendingRenews = 1;
|
||||
|
||||
/** Value used in determining when leases are cancelled, default to 1 for standalone.
|
||||
* Should be set to 0 for peer replicated eurekas */
|
||||
@Value("${eureka.server.defaultOpenForTrafficCount:1}") // for backwards compatibility
|
||||
private int defaultOpenForTrafficCount = 1;
|
||||
|
||||
public int getExpectedNumberOfClientsSendingRenews() {
|
||||
return expectedNumberOfClientsSendingRenews;
|
||||
}
|
||||
@@ -47,11 +53,16 @@ public class InstanceRegistryProperties {
|
||||
this.expectedNumberOfClientsSendingRenews = expectedNumberOfClientsSendingRenews;
|
||||
}
|
||||
|
||||
/** Value used in determining when leases are cancelled, default to 1 for standalone.
|
||||
* Should be set to 0 for peer replicated eurekas */
|
||||
@Value("${eureka.server.defaultOpenForTrafficCount:1}") // for backwards compatibility
|
||||
private int defaultOpenForTrafficCount = 1;
|
||||
@DeprecatedConfigurationProperty(replacement = PREFIX+".expected-number-of-clients-sending-renews")
|
||||
@Deprecated
|
||||
public int getExpectedNumberOfRenewsPerMin() {
|
||||
return getExpectedNumberOfClientsSendingRenews();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setExpectedNumberOfRenewsPerMin(int expectedNumberOfRenewsPerMin) {
|
||||
setExpectedNumberOfClientsSendingRenews(expectedNumberOfRenewsPerMin);
|
||||
}
|
||||
|
||||
public int getDefaultOpenForTrafficCount() {
|
||||
return defaultOpenForTrafficCount;
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build</artifactId>
|
||||
<version>2.1.0.RC2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-hystrix-contract</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>spring-cloud-netflix-hystrix-contract</name>
|
||||
<description>Spring Cloud Netflix Hystrix Contract</description>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<dependencies>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-hystrix-stream</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-hystrix</artifactId>
|
||||
|
||||
-110
@@ -49,116 +49,6 @@ public class HystrixCircuitBreakerConfiguration {
|
||||
return HasFeatures.namedFeatures(new NamedFeature("Hystrix", HystrixCommandAspect.class));
|
||||
}
|
||||
|
||||
//FIXME: 2.0.0
|
||||
/*@Configuration
|
||||
@ConditionalOnProperty(value = "hystrix.metrics.enabled", matchIfMissing = true)
|
||||
@ConditionalOnClass({ HystrixMetricsPoller.class, GaugeService.class })
|
||||
@EnableConfigurationProperties(HystrixMetricsProperties.class)
|
||||
protected static class HystrixMetricsPollerConfiguration implements SmartLifecycle {
|
||||
|
||||
private static Log logger = LogFactory
|
||||
.getLog(HystrixMetricsPollerConfiguration.class);
|
||||
|
||||
@Autowired(required = false)
|
||||
private GaugeService gauges;
|
||||
|
||||
@Autowired
|
||||
private HystrixMetricsProperties metricsProperties;
|
||||
|
||||
private ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
private HystrixMetricsPoller poller;
|
||||
|
||||
private Set<String> reserved = new HashSet<String>(Arrays.asList("group", "name",
|
||||
"type", "currentTime"));
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (this.gauges == null) {
|
||||
return;
|
||||
}
|
||||
MetricsAsJsonPollerListener listener = new MetricsAsJsonPollerListener() {
|
||||
@Override
|
||||
public void handleJsonMetric(String json) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = HystrixMetricsPollerConfiguration.this.mapper
|
||||
.readValue(json, Map.class);
|
||||
if (map != null && map.containsKey("type")) {
|
||||
addMetrics(map, "hystrix.");
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
this.poller = new HystrixMetricsPoller(listener,
|
||||
metricsProperties.getPollingIntervalMs());
|
||||
// start polling and it will write directly to the listener
|
||||
this.poller.start();
|
||||
logger.info("Starting poller");
|
||||
}
|
||||
|
||||
private void addMetrics(Map<String, Object> map, String root) {
|
||||
StringBuilder prefixBuilder = new StringBuilder(root);
|
||||
if (map.containsKey("type")) {
|
||||
prefixBuilder.append((String) map.get("type"));
|
||||
if (map.containsKey("group")) {
|
||||
prefixBuilder.append(".").append(map.get("group"));
|
||||
}
|
||||
prefixBuilder.append(".").append(map.get("name"));
|
||||
}
|
||||
String prefix = prefixBuilder.toString();
|
||||
for (String key : map.keySet()) {
|
||||
Object value = map.get(key);
|
||||
if (!this.reserved.contains(key)) {
|
||||
if (value instanceof Number) {
|
||||
String name = prefix + "." + key;
|
||||
this.gauges.submit(name, ((Number) value).doubleValue());
|
||||
}
|
||||
else if (value instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> sub = (Map<String, Object>) value;
|
||||
addMetrics(sub, prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (this.poller != null) {
|
||||
this.poller.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.poller != null ? this.poller.isRunning() : false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPhase() {
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoStartup() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(Runnable callback) {
|
||||
if (this.poller != null) {
|
||||
this.poller.shutdown();
|
||||
}
|
||||
callback.run();
|
||||
}
|
||||
|
||||
}*/
|
||||
|
||||
/**
|
||||
* {@link DisposableBean} that makes sure that Hystrix internal state is cleared when
|
||||
* {@link ApplicationContext} shuts down.
|
||||
|
||||
+23
-1
@@ -18,6 +18,9 @@ package org.springframework.cloud.netflix.hystrix.security;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategyDefault;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
@@ -42,6 +45,7 @@ import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
|
||||
@Conditional(HystrixSecurityCondition.class)
|
||||
@ConditionalOnClass({ Hystrix.class, SecurityContext.class })
|
||||
public class HystrixSecurityAutoConfiguration {
|
||||
private static final Log LOGGER = LogFactory.getLog(HystrixSecurityAutoConfiguration.class);
|
||||
@Autowired(required = false)
|
||||
private HystrixConcurrencyStrategy existingConcurrencyStrategy;
|
||||
|
||||
@@ -56,18 +60,36 @@ public class HystrixSecurityAutoConfiguration {
|
||||
.getPropertiesStrategy();
|
||||
HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance()
|
||||
.getCommandExecutionHook();
|
||||
HystrixConcurrencyStrategy concurrencyStrategy = detectRegisteredConcurrencyStrategy();
|
||||
|
||||
HystrixPlugins.reset();
|
||||
|
||||
// Registers existing plugins excepts the Concurrent Strategy plugin.
|
||||
HystrixPlugins.getInstance().registerConcurrencyStrategy(
|
||||
new SecurityContextConcurrencyStrategy(existingConcurrencyStrategy));
|
||||
new SecurityContextConcurrencyStrategy(concurrencyStrategy));
|
||||
HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
|
||||
HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
|
||||
HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
|
||||
HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);
|
||||
}
|
||||
|
||||
private HystrixConcurrencyStrategy detectRegisteredConcurrencyStrategy() {
|
||||
HystrixConcurrencyStrategy registeredStrategy = HystrixPlugins.getInstance()
|
||||
.getConcurrencyStrategy();
|
||||
if (existingConcurrencyStrategy == null) {
|
||||
return registeredStrategy;
|
||||
}
|
||||
//Hystrix registered a default Strategy.
|
||||
if (registeredStrategy instanceof HystrixConcurrencyStrategyDefault){
|
||||
return existingConcurrencyStrategy;
|
||||
}
|
||||
//If registeredStrategy not the default and not some use bean of existingConcurrencyStrategy.
|
||||
if (!existingConcurrencyStrategy.equals(registeredStrategy)){
|
||||
LOGGER.warn("Multiple HystrixConcurrencyStrategy detected. Bean of HystrixConcurrencyStrategy was used.");
|
||||
}
|
||||
return existingConcurrencyStrategy;
|
||||
}
|
||||
|
||||
static class HystrixSecurityCondition extends AllNestedConditions {
|
||||
|
||||
public HystrixSecurityCondition() {
|
||||
|
||||
+38
-8
@@ -18,6 +18,7 @@
|
||||
package org.springframework.cloud.netflix.hystrix;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
|
||||
@@ -50,6 +51,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
import static org.springframework.cloud.netflix.test.TestAutoConfiguration.PASSWORD;
|
||||
import static org.springframework.cloud.netflix.test.TestAutoConfiguration.USER;
|
||||
@@ -59,7 +61,7 @@ import static org.springframework.cloud.netflix.test.TestAutoConfiguration.USER;
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = HystrixOnlyApplication.class, webEnvironment = RANDOM_PORT,
|
||||
properties = "management.endpoint.health.show-details=ALWAYS")
|
||||
properties = {"management.endpoint.health.show-details=ALWAYS"})
|
||||
@DirtiesContext
|
||||
@ActiveProfiles("proxysecurity")
|
||||
public class HystrixOnlyTests {
|
||||
@@ -101,17 +103,45 @@ public class HystrixOnlyTests {
|
||||
map.containsKey("discovery"));
|
||||
}
|
||||
|
||||
private Map getHealth() {
|
||||
ResponseEntity<Map> response = new TestRestTemplate().exchange(
|
||||
"http://localhost:" + this.port + BASE_PATH + "/health", HttpMethod.GET,
|
||||
@Test
|
||||
public void testHystrixInnerMapMetrics() {
|
||||
// We have to hit any Hystrix command before Hystrix metrics to be populated
|
||||
String url = "http://localhost:" + this.port;
|
||||
ResponseEntity<String> response = new TestRestTemplate().getForEntity(url,
|
||||
String.class);
|
||||
assertEquals("bad response code", HttpStatus.OK, response.getStatusCode());
|
||||
|
||||
// Poller takes some time to realize for new metrics
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {}
|
||||
|
||||
Map<String, List<String>> map = (Map<String, List<String>>) getMetrics();
|
||||
|
||||
assertTrue("There is no latencyTotal group key specified",
|
||||
map.get("names").contains("hystrix.latency.total"));
|
||||
assertTrue("There is no latencyExecute group key specified",
|
||||
map.get("names").contains("hystrix.latency.execution"));
|
||||
}
|
||||
|
||||
|
||||
private Map<?, ?> getMetrics() {
|
||||
return getAuthenticatedEndpoint("/metrics");
|
||||
}
|
||||
|
||||
private Map<?, ?> getHealth() {
|
||||
return getAuthenticatedEndpoint("/health");
|
||||
}
|
||||
|
||||
private Map<?, ?> getAuthenticatedEndpoint(String endpoint) {
|
||||
return new TestRestTemplate().exchange(
|
||||
"http://localhost:" + this.port + BASE_PATH + endpoint, HttpMethod.GET,
|
||||
new HttpEntity<Void>(createBasicAuthHeader(USER, PASSWORD)),
|
||||
Map.class);
|
||||
Assertions.assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
return response.getBody();
|
||||
Map.class).getBody();
|
||||
}
|
||||
|
||||
public static HttpHeaders createBasicAuthHeader(final String username,
|
||||
final String password) {
|
||||
final String password) {
|
||||
return new HttpHeaders() {
|
||||
private static final long serialVersionUID = 1766341693637204893L;
|
||||
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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
|
||||
*
|
||||
* http://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.hystrix.security;
|
||||
|
||||
import com.netflix.hystrix.strategy.HystrixPlugins;
|
||||
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
|
||||
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategyDefault;
|
||||
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
|
||||
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
|
||||
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
|
||||
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
|
||||
import org.junit.Test;
|
||||
import org.mockito.internal.util.reflection.FieldSetter;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author : ailin.zhou
|
||||
*/
|
||||
public class HystrixSecurityAutoConfigurationTest {
|
||||
|
||||
@Test
|
||||
public void testInit() throws NoSuchFieldException, IllegalAccessException {
|
||||
|
||||
//save test context
|
||||
HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance()
|
||||
.getEventNotifier();
|
||||
HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance()
|
||||
.getMetricsPublisher();
|
||||
HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance()
|
||||
.getPropertiesStrategy();
|
||||
HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance()
|
||||
.getCommandExecutionHook();
|
||||
HystrixConcurrencyStrategy concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy();
|
||||
|
||||
//test
|
||||
testForMultiConcurrentStrategy();
|
||||
|
||||
//recover test context
|
||||
HystrixPlugins.reset();
|
||||
HystrixPlugins.getInstance().registerConcurrencyStrategy(concurrencyStrategy);
|
||||
HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
|
||||
HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
|
||||
HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
|
||||
HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void testForMultiConcurrentStrategy() throws IllegalAccessException, NoSuchFieldException {
|
||||
HystrixSecurityAutoConfiguration securityStrategy = new HystrixSecurityAutoConfiguration();
|
||||
|
||||
//1.existingConcurrencyStrategy is null, registeredStrategy is default
|
||||
HystrixPlugins.reset();
|
||||
securityStrategy.init();
|
||||
//result is default
|
||||
assertEquals(HystrixConcurrencyStrategyDefault.getInstance(), getOriginalInSecurityConcurrencyStrategy());
|
||||
|
||||
//2.existingConcurrencyStrategy is null, registered strategy is customized
|
||||
HystrixPlugins.reset();
|
||||
HystrixConcurrencyStrategy customized = new HystrixConcurrencyStrategy() {
|
||||
};
|
||||
HystrixPlugins.getInstance().registerConcurrencyStrategy(customized);
|
||||
securityStrategy.init();
|
||||
//result is customized
|
||||
assertEquals(customized, getOriginalInSecurityConcurrencyStrategy());
|
||||
|
||||
//3.existingConcurrencyStrategy is not null, registeredStrategy is default.
|
||||
HystrixPlugins.reset();
|
||||
HystrixConcurrencyStrategy existingConcurrencyStrategy = new HystrixConcurrencyStrategy() {
|
||||
};
|
||||
FieldSetter.setField(securityStrategy, securityStrategy.getClass().getDeclaredField("existingConcurrencyStrategy"), existingConcurrencyStrategy);
|
||||
securityStrategy.init();
|
||||
//result is existingConcurrencyStrategy
|
||||
assertEquals(existingConcurrencyStrategy, getOriginalInSecurityConcurrencyStrategy());
|
||||
|
||||
//4.existingConcurrencyStrategy is not null, registeredStrategy is customized.
|
||||
HystrixPlugins.reset();
|
||||
HystrixPlugins.getInstance().registerConcurrencyStrategy(customized);
|
||||
FieldSetter.setField(securityStrategy, securityStrategy.getClass().getDeclaredField("existingConcurrencyStrategy"), existingConcurrencyStrategy);
|
||||
securityStrategy.init();
|
||||
assertEquals(existingConcurrencyStrategy, getOriginalInSecurityConcurrencyStrategy());
|
||||
}
|
||||
|
||||
private HystrixConcurrencyStrategy getOriginalInSecurityConcurrencyStrategy() throws IllegalAccessException, NoSuchFieldException {
|
||||
HystrixConcurrencyStrategy concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy();
|
||||
Field existingConcurrencyStrategy = concurrencyStrategy.getClass().getDeclaredField("existingConcurrencyStrategy");
|
||||
existingConcurrencyStrategy.setAccessible(true);
|
||||
HystrixConcurrencyStrategy strategyInSecurityStrategy = (HystrixConcurrencyStrategy) existingConcurrencyStrategy.get(concurrencyStrategy);
|
||||
return strategyInSecurityStrategy;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+5
@@ -197,6 +197,11 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInstanceId() {
|
||||
return this.server.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceId() {
|
||||
return this.serviceId;
|
||||
|
||||
+6
@@ -140,6 +140,12 @@ public class RibbonCommandContext {
|
||||
if (requestEntity == null) {
|
||||
return null;
|
||||
}
|
||||
//If the route is not retryable there is no point in copying the RequestEntity. This
|
||||
//has memory implications in all cases but especially when uploading large files through
|
||||
//Zuul
|
||||
if(!retryable) {
|
||||
return requestEntity;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!(requestEntity instanceof ResettableServletInputStreamWrapper)) {
|
||||
|
||||
+3
@@ -55,6 +55,7 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Tim Ysewyn
|
||||
*/
|
||||
public class RibbonLoadBalancerClientTests {
|
||||
|
||||
@@ -324,6 +325,8 @@ public class RibbonLoadBalancerClientTests {
|
||||
protected void assertServiceInstance(RibbonServer ribbonServer,
|
||||
ServiceInstance instance) {
|
||||
assertNotNull("instance was null", instance);
|
||||
assertEquals("instanceId was wrong", ribbonServer.getInstanceId(),
|
||||
instance.getInstanceId());
|
||||
assertEquals("serviceId was wrong", ribbonServer.getServiceId(),
|
||||
instance.getServiceId());
|
||||
assertEquals("host was wrong", ribbonServer.getHost(), instance.getHost());
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-sidecar</artifactId>
|
||||
|
||||
+11
-7
@@ -16,22 +16,26 @@
|
||||
|
||||
package org.springframework.cloud.netflix.sidecar;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Fabrizio Di Napoli
|
||||
*/
|
||||
public class LocalApplicationHealthIndicator extends AbstractHealthIndicator {
|
||||
|
||||
@Autowired
|
||||
private SidecarProperties properties;
|
||||
|
||||
@Autowired
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void doHealthCheck(Health.Builder builder) throws Exception {
|
||||
@@ -40,12 +44,13 @@ public class LocalApplicationHealthIndicator extends AbstractHealthIndicator {
|
||||
builder.up();
|
||||
return;
|
||||
}
|
||||
Map<String, Object> map = new RestTemplate().getForObject(uri, Map.class);
|
||||
|
||||
Map<String, Object> map = restTemplate.getForObject(uri, Map.class);
|
||||
Object status = map.get("status");
|
||||
if (status != null && status instanceof String) {
|
||||
if (status instanceof String) {
|
||||
builder.status(status.toString());
|
||||
}
|
||||
else if (status != null && status instanceof Map) {
|
||||
else if (status instanceof Map) {
|
||||
Map<String, Object> statusMap = (Map<String, Object>) status;
|
||||
Object code = statusMap.get("code");
|
||||
if (code != null) {
|
||||
@@ -63,5 +68,4 @@ public class LocalApplicationHealthIndicator extends AbstractHealthIndicator {
|
||||
private Health.Builder getWarning(Health.Builder builder) {
|
||||
return builder.unknown().withDetail("warning", "no status field in response");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+32
-3
@@ -16,6 +16,11 @@
|
||||
|
||||
package org.springframework.cloud.netflix.sidecar;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import static org.springframework.cloud.commons.util.IdUtils.getDefaultInstanceId;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -24,6 +29,7 @@ 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.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.cloud.client.actuator.HasFeatures;
|
||||
import org.springframework.cloud.commons.util.InetUtils;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
|
||||
@@ -33,10 +39,12 @@ import org.springframework.cloud.netflix.eureka.metadata.ManagementMetadataProvi
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.netflix.appinfo.HealthCheckHandler;
|
||||
import com.netflix.discovery.EurekaClientConfig;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -54,6 +62,7 @@ import java.util.Map;
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Ryan Baxter
|
||||
* @author Fabrizio Di Napoli
|
||||
*
|
||||
* @see EurekaInstanceConfigBeanConfiguration
|
||||
*/
|
||||
@@ -81,16 +90,16 @@ public class SidecarConfiguration {
|
||||
@Autowired
|
||||
private InetUtils inetUtils;
|
||||
|
||||
@Value(value = "${management.port:${MANAGEMENT_PORT:#{null}}}")
|
||||
@Value(value = "${management.server.port:${MANAGEMENT_PORT:#{null}}}")
|
||||
private Integer managementPort;
|
||||
|
||||
@Value("${server.port:${SERVER_PORT:${PORT:8080}}}")
|
||||
private int serverPort = 8080;
|
||||
|
||||
@Value("${management.context-path:${MANAGEMENT_CONTEXT_PATH:#{null}}}")
|
||||
@Value("${management.server.servlet.context-path:${MANAGEMENT_CONTEXT_PATH:#{null}}}")
|
||||
private String managementContextPath;
|
||||
|
||||
@Value("${server.context-path:${SERVER_CONTEXT_PATH:/}}")
|
||||
@Value("${server.servlet.context-path:${SERVER_CONTEXT_PATH:/}}")
|
||||
private String serverContextPath = "/";
|
||||
|
||||
@Value("${eureka.instance.hostname:${EUREKA_INSTANCE_HOSTNAME:}}")
|
||||
@@ -153,7 +162,27 @@ public class SidecarConfiguration {
|
||||
final LocalApplicationHealthIndicator healthIndicator) {
|
||||
return new LocalApplicationHealthCheckHandler(healthIndicator);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingClass("org.apache.http.client.HttpClient")
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplateBuilder().build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(HttpClient.class)
|
||||
public RestTemplate sslRestTemplate(SidecarProperties properties) {
|
||||
RestTemplateBuilder builder = new RestTemplateBuilder();
|
||||
if(properties.acceptAllSslCertificates()) {
|
||||
CloseableHttpClient httpClient = HttpClients.custom()
|
||||
.setSSLHostnameVerifier(new NoopHostnameVerifier())
|
||||
.build();
|
||||
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
|
||||
requestFactory.setHttpClient(httpClient);
|
||||
builder = builder.requestFactory(() -> requestFactory);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
+16
-3
@@ -27,6 +27,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Gregor Zurowski
|
||||
* @author Fabrizio Di Napoli
|
||||
*/
|
||||
@ConfigurationProperties("sidecar")
|
||||
public class SidecarProperties {
|
||||
@@ -43,6 +44,8 @@ public class SidecarProperties {
|
||||
|
||||
private String ipAddress;
|
||||
|
||||
private boolean acceptAllSslCertificates;
|
||||
|
||||
public URI getHealthUri() {
|
||||
return healthUri;
|
||||
}
|
||||
@@ -83,6 +86,14 @@ public class SidecarProperties {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
public boolean acceptAllSslCertificates() {
|
||||
return acceptAllSslCertificates;
|
||||
}
|
||||
|
||||
public void setAcceptAllSslCertificates(boolean acceptAllSslCertificates) {
|
||||
this.acceptAllSslCertificates = acceptAllSslCertificates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
@@ -92,12 +103,13 @@ public class SidecarProperties {
|
||||
Objects.equals(homePageUri, that.homePageUri) &&
|
||||
port == that.port &&
|
||||
Objects.equals(hostname, that.hostname) &&
|
||||
Objects.equals(ipAddress, that.ipAddress);
|
||||
Objects.equals(ipAddress, that.ipAddress) &&
|
||||
Objects.equals(acceptAllSslCertificates, that.acceptAllSslCertificates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(healthUri, homePageUri, port, hostname, ipAddress);
|
||||
return Objects.hash(healthUri, homePageUri, port, hostname, ipAddress, acceptAllSslCertificates);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -107,7 +119,8 @@ public class SidecarProperties {
|
||||
.append("homePageUri=").append(homePageUri).append(", ")
|
||||
.append("port=").append(port).append(", ")
|
||||
.append("hostname='").append(hostname).append("', ")
|
||||
.append("ipAddress='").append(ipAddress).append("'}")
|
||||
.append("ipAddress='").append(ipAddress).append("', ")
|
||||
.append("acceptAllSslCertificates='").append(acceptAllSslCertificates).append("'}")
|
||||
.toString();
|
||||
}
|
||||
|
||||
|
||||
+14
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.netflix.sidecar;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -26,6 +27,7 @@ import org.springframework.test.context.junit4.SpringRunner;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
public class SidecarApplicationTests {
|
||||
|
||||
@@ -128,4 +130,16 @@ public class SidecarApplicationTests {
|
||||
assertThat(this.config.getHealthCheckUrl(), equalTo("http://mhhost2:0/foo/health"));
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = SidecarApplication.class, webEnvironment = RANDOM_PORT, value = {"sidecar.accept-all-ssl-certificates=false"})
|
||||
public static class AcceptAllSslCertificatesContext {
|
||||
@Autowired
|
||||
RestTemplate restTemplate;
|
||||
|
||||
@Test
|
||||
public void testUseRestTemplateWhenHttpClientIsNotAvailable() {
|
||||
assertNull(restTemplate.getRequestFactory());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ spring:
|
||||
sidecar:
|
||||
port: 8000
|
||||
health-uri: http://localhost:8000/src/test/resources/health.json
|
||||
accept-all-ssl-certificates: true
|
||||
|
||||
eureka:
|
||||
instance:
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-turbine-stream</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-turbine</artifactId>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
|
||||
+50
-1
@@ -18,7 +18,9 @@
|
||||
package org.springframework.cloud.netflix.zuul;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
@@ -28,6 +30,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClas
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||
import org.springframework.boot.web.servlet.error.ErrorController;
|
||||
import org.springframework.cloud.client.actuator.HasFeatures;
|
||||
@@ -59,16 +62,23 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import com.netflix.zuul.FilterLoader;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.filters.FilterRegistry;
|
||||
import com.netflix.zuul.filters.ZuulServletFilter;
|
||||
import com.netflix.zuul.http.ZuulServlet;
|
||||
import com.netflix.zuul.monitoring.CounterFactory;
|
||||
import com.netflix.zuul.monitoring.TracerFactory;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
@@ -76,7 +86,7 @@ import io.micrometer.core.instrument.MeterRegistry;
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties({ ZuulProperties.class })
|
||||
@ConditionalOnClass(ZuulServlet.class)
|
||||
@ConditionalOnClass({ZuulServlet.class, ZuulServletFilter.class})
|
||||
@ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class)
|
||||
// Make sure to get the ServerProperties from the same place as a normal web app would
|
||||
// FIXME @Import(ServerPropertiesAutoConfiguration.class)
|
||||
@@ -91,6 +101,11 @@ public class ZuulServerAutoConfiguration {
|
||||
@Autowired(required = false)
|
||||
private ErrorController errorController;
|
||||
|
||||
private Map<String, CorsConfiguration> corsConfigurations;
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<WebMvcConfigurer> configurers = emptyList();
|
||||
|
||||
@Bean
|
||||
public HasFeatures zuulFeature() {
|
||||
return HasFeatures.namedFeature("Zuul (Simple)", ZuulServerAutoConfiguration.class);
|
||||
@@ -119,9 +134,20 @@ public class ZuulServerAutoConfiguration {
|
||||
public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {
|
||||
ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController());
|
||||
mapping.setErrorController(this.errorController);
|
||||
mapping.setCorsConfigurations(getCorsConfigurations());
|
||||
return mapping;
|
||||
}
|
||||
|
||||
protected final Map<String, CorsConfiguration> getCorsConfigurations() {
|
||||
if (this.corsConfigurations == null) {
|
||||
ZuulCorsRegistry registry = new ZuulCorsRegistry();
|
||||
this.configurers
|
||||
.forEach(configurer -> configurer.addCorsMappings(registry));
|
||||
this.corsConfigurations = registry.getCorsConfigurations();
|
||||
}
|
||||
return this.corsConfigurations;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApplicationListener<ApplicationEvent> zuulRefreshRoutesListener() {
|
||||
return new ZuulRefreshListener();
|
||||
@@ -129,6 +155,7 @@ public class ZuulServerAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "zuulServlet")
|
||||
@ConditionalOnProperty(name = "zuul.use-filter", havingValue = "false", matchIfMissing = true)
|
||||
public ServletRegistrationBean zuulServlet() {
|
||||
ServletRegistrationBean<ZuulServlet> servlet = new ServletRegistrationBean<>(new ZuulServlet(),
|
||||
this.zuulProperties.getServletPattern());
|
||||
@@ -138,6 +165,20 @@ public class ZuulServerAutoConfiguration {
|
||||
return servlet;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "zuulServletFilter")
|
||||
@ConditionalOnProperty(name = "zuul.use-filter", havingValue = "true", matchIfMissing = false)
|
||||
public FilterRegistrationBean zuulServletFilter(){
|
||||
final FilterRegistrationBean<ZuulServletFilter> filterRegistration = new FilterRegistrationBean<>();
|
||||
filterRegistration.setUrlPatterns(Collections.singleton(this.zuulProperties.getServletPattern()));
|
||||
filterRegistration.setFilter(new ZuulServletFilter());
|
||||
filterRegistration.setOrder(Ordered.LOWEST_PRECEDENCE);
|
||||
// The whole point of exposing this servlet is to provide a route that doesn't
|
||||
// buffer requests.
|
||||
filterRegistration.addInitParameter("buffer-requests", "false");
|
||||
return filterRegistration;
|
||||
}
|
||||
|
||||
// pre filters
|
||||
|
||||
@Bean
|
||||
@@ -267,4 +308,12 @@ public class ZuulServerAutoConfiguration {
|
||||
this.zuulHandlerMapping.setDirty(true);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ZuulCorsRegistry extends CorsRegistry {
|
||||
|
||||
@Override
|
||||
protected Map<String, CorsConfiguration> getCorsConfigurations() {
|
||||
return super.getCorsConfigurations();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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.zuul;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
/**
|
||||
* @author Craig Andrews
|
||||
*/
|
||||
public class ZuulServletFilter extends com.netflix.zuul.filters.ZuulServletFilter {
|
||||
@Override
|
||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
|
||||
FilterChain filterChain) throws IOException, ServletException {
|
||||
|
||||
// Workaround https://github.com/Netflix/zuul/pull/430
|
||||
// This class can be removed, and com.netflix.zuul.filters.ZuulServletFilter used in its place,
|
||||
// when using a Zuul release with that change in it.
|
||||
|
||||
// Marks this request as having passed through the "Zuul engine", as opposed to servlets
|
||||
// explicitly bound in web.xml, for which requests will not have the same data attached
|
||||
RequestContext context = RequestContext.getCurrentContext();
|
||||
context.setZuulEngineRan();
|
||||
|
||||
super.doFilter(servletRequest, servletResponse, filterChain);
|
||||
}
|
||||
}
|
||||
Regular → Executable
+7
-1
@@ -78,6 +78,8 @@ public class ProxyRequestHelper {
|
||||
|
||||
private boolean addHostHeader = false;
|
||||
|
||||
private boolean urlDecoded = true;
|
||||
|
||||
@Deprecated
|
||||
//TODO Remove in 2.1.x
|
||||
public ProxyRequestHelper() {}
|
||||
@@ -86,6 +88,7 @@ public class ProxyRequestHelper {
|
||||
this.ignoredHeaders.addAll(zuulProperties.getIgnoredHeaders());
|
||||
this.traceRequestBody = zuulProperties.isTraceRequestBody();
|
||||
this.addHostHeader = zuulProperties.isAddHostHeader();
|
||||
this.urlDecoded = zuulProperties.isDecodeUrl();
|
||||
}
|
||||
|
||||
public void setWhitelistHosts(Set<String> whitelistHosts) {
|
||||
@@ -114,7 +117,10 @@ public class ProxyRequestHelper {
|
||||
String contextURI = (String) context.get(REQUEST_URI_KEY);
|
||||
if (contextURI != null) {
|
||||
try {
|
||||
uri = UriUtils.encodePath(contextURI, characterEncoding(request));
|
||||
uri = contextURI;
|
||||
if (this.urlDecoded) {
|
||||
uri = UriUtils.encodePath(contextURI, characterEncoding(request));
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.debug(
|
||||
|
||||
Regular → Executable
+13
@@ -140,6 +140,11 @@ public class ZuulProperties {
|
||||
*/
|
||||
private boolean removeSemicolonContent = true;
|
||||
|
||||
/**
|
||||
* Flag to indicate whether to decode the matched URL or use it as is.
|
||||
*/
|
||||
private boolean decodeUrl = true;
|
||||
|
||||
/**
|
||||
* List of sensitive headers that are not passed to downstream requests. Defaults to a
|
||||
* "safe" set of headers that commonly contain user credentials. It's OK to remove
|
||||
@@ -764,6 +769,14 @@ public class ZuulProperties {
|
||||
this.removeSemicolonContent = removeSemicolonContent;
|
||||
}
|
||||
|
||||
public boolean isDecodeUrl() {
|
||||
return decodeUrl;
|
||||
}
|
||||
|
||||
public void setDecodeUrl(boolean decodeUrl) {
|
||||
this.decodeUrl = decodeUrl;
|
||||
}
|
||||
|
||||
public Set<String> getSensitiveHeaders() {
|
||||
return sensitiveHeaders;
|
||||
}
|
||||
|
||||
Regular → Executable
+28
-22
@@ -88,6 +88,7 @@ public class PreDecorationFilter extends ZuulFilter {
|
||||
this.routeLocator = routeLocator;
|
||||
this.properties = properties;
|
||||
this.urlPathHelper.setRemoveSemicolonContent(properties.isRemoveSemicolonContent());
|
||||
this.urlPathHelper.setUrlDecode(properties.isDecodeUrl());
|
||||
this.dispatcherServletPath = dispatcherServletPath;
|
||||
this.proxyRequestHelper = proxyRequestHelper;
|
||||
}
|
||||
@@ -166,34 +167,39 @@ public class PreDecorationFilter extends ZuulFilter {
|
||||
}
|
||||
else {
|
||||
log.warn("No route found for uri: " + requestURI);
|
||||
String forwardURI = getForwardUri(requestURI);
|
||||
|
||||
String fallBackUri = requestURI;
|
||||
String fallbackPrefix = this.dispatcherServletPath; // default fallback
|
||||
// servlet is
|
||||
// DispatcherServlet
|
||||
|
||||
if (RequestUtils.isZuulServletRequest()) {
|
||||
// remove the Zuul servletPath from the requestUri
|
||||
log.debug("zuulServletPath=" + this.properties.getServletPath());
|
||||
fallBackUri = fallBackUri.replaceFirst(this.properties.getServletPath(), "");
|
||||
log.debug("Replaced Zuul servlet path:" + fallBackUri);
|
||||
}
|
||||
else {
|
||||
// remove the DispatcherServlet servletPath from the requestUri
|
||||
log.debug("dispatcherServletPath=" + this.dispatcherServletPath);
|
||||
fallBackUri = fallBackUri.replaceFirst(this.dispatcherServletPath, "");
|
||||
log.debug("Replaced DispatcherServlet servlet path:" + fallBackUri);
|
||||
}
|
||||
if (!fallBackUri.startsWith("/")) {
|
||||
fallBackUri = "/" + fallBackUri;
|
||||
}
|
||||
String forwardURI = fallbackPrefix + fallBackUri;
|
||||
forwardURI = DOUBLE_SLASH.matcher(forwardURI).replaceAll("/");
|
||||
ctx.set(FORWARD_TO_KEY, forwardURI);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/* for testing */ String getForwardUri(String requestURI) {
|
||||
// default fallback servlet is DispatcherServlet
|
||||
String fallbackPrefix = this.dispatcherServletPath;
|
||||
|
||||
String fallBackUri = requestURI;
|
||||
if (RequestUtils.isZuulServletRequest()) {
|
||||
// remove the Zuul servletPath from the requestUri
|
||||
log.debug("zuulServletPath=" + this.properties.getServletPath());
|
||||
fallBackUri = fallBackUri.replaceFirst(this.properties.getServletPath(), "");
|
||||
log.debug("Replaced Zuul servlet path:" + fallBackUri);
|
||||
}
|
||||
else if (this.dispatcherServletPath != null) {
|
||||
// remove the DispatcherServlet servletPath from the requestUri
|
||||
log.debug("dispatcherServletPath=" + this.dispatcherServletPath);
|
||||
fallBackUri = fallBackUri.replaceFirst(this.dispatcherServletPath, "");
|
||||
log.debug("Replaced DispatcherServlet servlet path:" + fallBackUri);
|
||||
}
|
||||
if (!fallBackUri.startsWith("/")) {
|
||||
fallBackUri = "/" + fallBackUri;
|
||||
}
|
||||
|
||||
String forwardURI = (fallbackPrefix == null) ? fallBackUri : fallbackPrefix + fallBackUri;
|
||||
forwardURI = DOUBLE_SLASH.matcher(forwardURI).replaceAll("/");
|
||||
return forwardURI;
|
||||
}
|
||||
|
||||
private void addProxyHeaders(RequestContext ctx, Route route) {
|
||||
HttpServletRequest request = ctx.getRequest();
|
||||
String host = toHostHeader(request);
|
||||
|
||||
+71
-10
@@ -20,12 +20,11 @@ package org.springframework.cloud.netflix.zuul;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
@@ -38,27 +37,41 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = ZuulProxyApplicationTests.ZuulProxyApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT, properties = {
|
||||
"zuul.routes.simplezpat:/simplezpat/**", "logging.level.org.apache.http: DEBUG" })
|
||||
@SpringBootTest(
|
||||
classes = ZuulProxyApplicationTests.ZuulProxyApplication.class,
|
||||
webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||
properties = {
|
||||
"zuul.routes.simplezpat:/simplezpat/**",
|
||||
"logging.level.org.apache.http: DEBUG"
|
||||
})
|
||||
@DirtiesContext
|
||||
public class ZuulProxyApplicationTests {
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate testRestTemplate;
|
||||
|
||||
@Before
|
||||
public void setTestRequestcontext() {
|
||||
RequestContext context = new RequestContext();
|
||||
@@ -72,22 +85,58 @@ public class ZuulProxyApplicationTests {
|
||||
|
||||
@Test
|
||||
public void getHasCorrectTransferEncoding() {
|
||||
ResponseEntity<String> result = new TestRestTemplate().getForEntity(
|
||||
"http://localhost:" + this.port + "/simplezpat/transferencoding",
|
||||
String.class);
|
||||
ResponseEntity<String> result = testRestTemplate.getForEntity(url(), String.class);
|
||||
|
||||
assertEquals(HttpStatus.OK, result.getStatusCode());
|
||||
assertEquals("missing", result.getBody());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postHasCorrectTransferEncoding() {
|
||||
ResponseEntity<String> result = new TestRestTemplate().postForEntity(
|
||||
"http://localhost:" + this.port + "/simplezpat/transferencoding",
|
||||
new HttpEntity<>("hello"), String.class);
|
||||
ResponseEntity<String> result = testRestTemplate.postForEntity(url(), new HttpEntity<>("hello"), String.class);
|
||||
|
||||
assertEquals(HttpStatus.OK, result.getStatusCode());
|
||||
assertEquals("missing", result.getBody());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preflightRequestSucceedsForGetRequest() {
|
||||
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
|
||||
headers.put("Origin", singletonList("http://hello.com"));
|
||||
headers.put("Access-Control-Request-Method", singletonList("GET"));
|
||||
ResponseEntity<Void> result = testRestTemplate.exchange(url(), HttpMethod.OPTIONS,
|
||||
new HttpEntity<>(headers), Void.class);
|
||||
|
||||
assertEquals(HttpStatus.OK, result.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preflightRequestIsForbiddenForUnsupportedMethod() {
|
||||
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
|
||||
headers.put("Origin", singletonList("http://hello.com"));
|
||||
headers.put("Access-Control-Request-Method", singletonList("PUT"));
|
||||
ResponseEntity<Void> result = testRestTemplate.exchange(url(), HttpMethod.OPTIONS,
|
||||
new HttpEntity<>(headers), Void.class);
|
||||
|
||||
assertEquals(HttpStatus.FORBIDDEN, result.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preflightRequestIsForbiddenForUnsupportedorigin() {
|
||||
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
|
||||
headers.put("Origin", singletonList("http://unknown-origin.com"));
|
||||
headers.put("Access-Control-Request-Method", singletonList("GET"));
|
||||
ResponseEntity<Void> result = testRestTemplate.exchange(url(), HttpMethod.OPTIONS,
|
||||
new HttpEntity<>(headers), Void.class);
|
||||
|
||||
assertEquals(HttpStatus.FORBIDDEN, result.getStatusCode());
|
||||
}
|
||||
|
||||
|
||||
private String url() {
|
||||
return "http://localhost:" + this.port + "/simplezpat/transferencoding";
|
||||
}
|
||||
|
||||
// Don't use @SpringBootApplication because we don't want to component scan
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@@ -116,6 +165,18 @@ public class ZuulProxyApplicationTests {
|
||||
return transferEncoding;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WebMvcConfigurer corsConfigurer() {
|
||||
return new WebMvcConfigurer() {
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/simplezpat/**")
|
||||
.allowedOrigins("http://hello.com")
|
||||
.allowedMethods("GET", "POST")
|
||||
.allowedHeaders("Authorization");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Load balancer with fixed server list for "simplezpat" pointing to localhost
|
||||
|
||||
Regular → Executable
+39
-13
@@ -41,6 +41,7 @@ import org.springframework.cloud.test.ModifiedClassPathRunner;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -329,6 +330,22 @@ public class PreDecorationFilterTests {
|
||||
getHeader(ctx.getOriginResponseHeaders(), "x-zuul-serviceid"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dontDecodeUrl() {
|
||||
this.properties.setPrefix("/api");
|
||||
this.properties.setStripPrefix(true);
|
||||
this.properties.setDecodeUrl(false);
|
||||
this.request.setRequestURI("/api/foo/encoded%2Fpath");
|
||||
this.request.setContextPath("/context-path");
|
||||
this.routeLocator.addRoute(
|
||||
new ZuulRoute("foo", "/foo/**", "foo", null, false, null, null));
|
||||
this.filter = new PreDecorationFilter(this.routeLocator, "/", this.properties,
|
||||
this.proxyRequestHelper);
|
||||
this.filter.run();
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
assertEquals("/foo/encoded%2Fpath", ctx.get(REQUEST_URI_KEY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void routeIgnoreContextPathIfPrefixHeader() {
|
||||
this.properties.setStripPrefix(false);
|
||||
@@ -624,22 +641,31 @@ public class PreDecorationFilterTests {
|
||||
assertTrue(decodedRequestURI.equals("/oléדרעק"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headersAreProperlyIgnored() throws Exception {
|
||||
proxyRequestHelper.addIgnoredHeaders("x-forwarded-host", "x-forwarded-port");
|
||||
request.addHeader("x-forwarded-host", "B,127.0.0.1:8080");
|
||||
request.addHeader("x-forwarded-port", "A,8080");
|
||||
request.addHeader("x-forwarded-proto", "C,http");
|
||||
@Test
|
||||
public void headersAreProperlyIgnored() throws Exception {
|
||||
proxyRequestHelper.addIgnoredHeaders("x-forwarded-host", "x-forwarded-port");
|
||||
request.addHeader("x-forwarded-host", "B,127.0.0.1:8080");
|
||||
request.addHeader("x-forwarded-port", "A,8080");
|
||||
request.addHeader("x-forwarded-proto", "C,http");
|
||||
|
||||
MultiValueMap<String, String> result = proxyRequestHelper
|
||||
.buildZuulRequestHeaders(request);
|
||||
MultiValueMap<String, String> result = proxyRequestHelper
|
||||
.buildZuulRequestHeaders(request);
|
||||
|
||||
assertTrue(result.containsKey("x-forwarded-proto"));
|
||||
assertFalse(result.containsKey("x-forwarded-host"));
|
||||
assertFalse(result.containsKey("x-forwarded-port"));
|
||||
}
|
||||
assertTrue(result.containsKey("x-forwarded-proto"));
|
||||
assertFalse(result.containsKey("x-forwarded-host"));
|
||||
assertFalse(result.containsKey("x-forwarded-port"));
|
||||
}
|
||||
|
||||
private Object getHeader(List<Pair<String, String>> headers, String key) {
|
||||
@Test
|
||||
public void nullDispatcherServletPath() {
|
||||
this.filter = new PreDecorationFilter(this.routeLocator, null, this.properties,
|
||||
this.proxyRequestHelper);
|
||||
|
||||
String forwardUri = this.filter.getForwardUri("/mypath");
|
||||
assertThat(forwardUri).isEqualTo("/mypath");
|
||||
}
|
||||
|
||||
private Object getHeader(List<Pair<String, String>> headers, String key) {
|
||||
String value = null;
|
||||
for (Pair<String, String> pair : headers) {
|
||||
if (pair.first().toLowerCase().equals(key.toLowerCase())) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-netflix</artifactId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-netflix-archaius</artifactId>
|
||||
<name>Spring Cloud Starter Netflix Archaius</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
<name>Spring Cloud Starter Netflix Eureka Client</name>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
|
||||
<name>Spring Cloud Starter Netflix Eureka Server</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
|
||||
<name>Spring Cloud Starter Netflix Hystrix Dashboard</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
|
||||
<name>Spring Cloud Starter Netflix Hystrix</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
|
||||
<name>Spring Cloud Starter Netflix Ribbon</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-netflix-turbine-stream</artifactId>
|
||||
<name>Spring Cloud Starter Netflix Turbine Stream</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
|
||||
<name>Spring Cloud Starter Netflix Turbine</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix</artifactId>
|
||||
<version>2.1.0.M2</version>
|
||||
<version>2.1.0.RC3</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
|
||||
<name>Spring Cloud Starter Netflix Zuul</name>
|
||||
|
||||
Reference in New Issue
Block a user