Compare commits

..
55 Commits
Author SHA1 Message Date
buildmaster 74f769d8dd Update SNAPSHOT to 2.1.0.RC3 2018-12-20 21:06:15 +00:00
Ryan Baxter b3e4ced142 Merge remote-tracking branch 'origin/2.0.x' 2018-12-19 15:15:42 -05:00
Ryan Baxter 66a67bb353 Code replaced by micrometer. Fixes #3326 2018-12-19 15:14:38 -05:00
Ryan Baxter e219783d40 Add documentation for maintenance mode modules 2018-12-19 11:43:50 -05:00
Ryan Baxter ed56c5c1f2 Merge remote-tracking branch 'origin/2.0.x' 2018-12-18 19:36:43 -05:00
Zhanwei Wang 55884acd85 Fix compatible issue with spring boot 2.0 (#3323) 2018-12-18 19:35:28 -05:00
Ryan Baxter 3659a9206a Merge branch 'master' of github.com:spring-cloud/spring-cloud-netflix 2018-12-17 11:26:18 -05:00
Ryan Baxter 91ce95babe Merge remote-tracking branch 'origin/2.0.x' 2018-12-17 11:26:03 -05:00
Ryan Baxter 153bf0ea63 Merge remote-tracking branch 'origin/1.4.x' into 2.0.x 2018-12-17 11:10:43 -05:00
Ryan Baxter 4ab9897b3b Merge remote-tracking branch 'origin/1.3.x' into 1.4.x 2018-12-17 10:09:28 -05:00
ivansjg 2fffb2606e Inner map metrics with same name are being overwritten on actuator me… (#3317)
* Inner map metrics with same name are being overwritten on actuator metrics endpoint

* Add Hystrix Inner map metrics tests
2018-12-17 10:08:19 -05:00
buildmaster 0bab3451b5 Going back to snapshots 2018-12-11 21:49:06 +00:00
buildmaster d25103ebdb Update SNAPSHOT to 2.1.0.RC2 2018-12-11 21:46:16 +00:00
buildmaster af99c1b403 Going back to snapshots 2018-12-11 16:35:06 +00:00
buildmaster 5eb8ceadf2 Update SNAPSHOT to 2.1.0.RC1 2018-12-11 16:32:01 +00:00
Ryan Baxter 7bca5019f6 Fixing merge issue. 2018-12-10 19:33:19 -05:00
Spencer Gibb 129e4618ea Merge branch '2.0.x' 2018-12-10 15:41:21 -05:00
Craig Andrews 89ed8219cb Conditionally use ZuulServletFilter instead of ZuulServlet
Using the filter instead of the servlet allow unhandled/ignored requests to pass through Zuul to be handled by something else, as opposed to the servlet which cannot do so.

See https://github.com/spring-cloud/spring-cloud-netflix/issues/2887
2018-12-10 15:40:06 -05:00
Spencer Gibb 703b4ce577 Merge branch 'candrews-patch-1' 2018-12-10 15:38:12 -05:00
Craig Andrews 74eee18701 Conditionally use ZuulServletFilter instead of ZuulServlet
Using the filter instead of the servlet allow unhandled/ignored requests to pass through Zuul to be handled by something else, as opposed to the servlet which cannot do so.

See https://github.com/spring-cloud/spring-cloud-netflix/issues/2887
2018-12-10 15:37:51 -05:00
Spencer Gibb 25aca6b548 Handles null dispatcherServletPath
fixes gh-3259
2018-12-10 13:22:47 -05:00
Spencer Gibb 0ad43e1018 Fixes formatting 2018-12-10 13:13:20 -05:00
Ryan Baxter ec92f48c76 Fixing image name 2018-12-10 12:23:00 -05:00
Ryan Baxter cfdaf345e3 Add documentation about JAXB dependencies when using JDK 11. Fixes #3244 2018-12-10 12:18:04 -05:00
Eric Bussieres b0e4250a4b Fix typo in documentation (#3312) 2018-12-09 00:45:23 -05:00
Ryan Baxter 3892a3a711 back to jdk 8 2018-12-07 12:35:22 -05:00
Ryan Baxter 438f7360c7 using jdk 11 2018-12-07 12:29:35 -05:00
bhattankit a1f7bab91a Added option to not decode URL. Fixes gh-2178 (#3306)
* Added a way for consumers to override the default behavior or decoding and re-encoding URL using zuul.decodeUrl property.
* Fixed gh-2178
2018-12-06 21:22:53 +01:00
ailin zhou 60bb89293c Submit PR #3276 against the 2.0.x branch (#3297)
* An enhancement when multiple HystrixConcurrencyStrategy detected. Fixes #3256

* replace exception with wran log
2018-12-04 09:19:28 +01:00
Tim Ysewyn 3ad07748b2 Added the instance id to the EurekaServiceInstance, EurekaRegistration and RibbonLoadBalancerClient (#3215)
* Added the instance id to the `EurekaServiceInstance`, `EurekaRegistration` and `RibbonLoadBalancerClient`

* Fixed failing test
2018-11-29 07:38:39 -05:00
Spencer Gibb 1540329624 Avoid early references to DiscoveryClient.
Along with similar changes in
https://github.com/spring-cloud/spring-cloud-commons/issues/451
this allows the port to be resolved before registering with eureka.

Fixes gh-3294
2018-11-28 15:07:12 -05:00
Stephan Windmüller 151fad898b Document workaround for conflicting Thymeleaf dependency (#3291)
Fixes gh-3290
2018-11-26 16:19:42 -05:00
Spencer Gibb ffc2dc804c Merge branch '2.0.x' 2018-11-20 16:52:25 -05:00
Kristine Jetzke aa757592b6 Upgrade Hystrix to 1.5.18 (#3288)
Hystrix 1.5.11 was re-released as 1.5.18, s. Netflix/Hystrix#1891 and https://github.com/Netflix/Hystrix/releases/tag/v1.5.18

This PR replaces #3281
2018-11-20 16:50:13 -05:00
Spencer Gibb 78adf15eea Restores and deprecates removed properties in InstanceRegistryProperties
Happened in #3257.

fixes gh-3286
2018-11-20 11:27:19 -05:00
buildmaster 5264f3fa26 Going back to snapshots 2018-11-19 23:15:15 +00:00
buildmaster 5edbf114a6 Update SNAPSHOT to 2.1.0.M3 2018-11-19 23:12:21 +00:00
Ryan Baxter 63d07cc5fd Merge remote-tracking branch 'origin/2.0.x' 2018-11-19 16:16:23 -05:00
saga a09a55eea7 upgrade eureka version to latest (#3280) 2018-11-19 09:24:02 -05:00
buildmaster 9f5f246209 Going back to snapshots 2018-11-18 09:32:12 +00:00
Nastya Smirnova 90290757cb Add support for CORS requests (#3278) 2018-11-16 16:15:49 -05:00
Ryan Baxter dfdc6c39e9 Merge remote-tracking branch 'origin/1.4.x' into 2.0.x 2018-10-30 19:39:03 -04:00
Spencer Gibb 00587bae01 Updates s-c-build to 2.0.5.BUILD-SNAPSHOT 2018-10-23 17:09:42 -04:00
buildmaster 504ff8e7d4 Bumping versions to 2.0.3.BUILD-SNAPSHOT after release 2018-10-23 18:03:02 +00:00
buildmaster 1510258e2b Going back to snapshots 2018-10-23 18:03:01 +00:00
buildmaster 59a4e430a0 Update SNAPSHOT to 2.0.2.RELEASE 2018-10-23 17:54:55 +00:00
Ryan Baxter 70008f7717 Fix documentation about backoff policies. 2018-10-23 09:12:53 -04:00
Ryan Baxter 1897c958d1 Check ssl certificate property inside the bean creation method so that when disabled we still create a RestTemplate bean. Fixes #3239 (#3241) 2018-10-19 13:58:01 -04:00
Ryan Baxter 0c3fbc34f6 Do Not Use AIM Proxy In EurekaClient (#3240)
* By not using the proxy in the EurekaClient we avoid the problem of trying to fetch the bean during shutdown.  Fixes #3174
2018-10-19 10:42:25 -04:00
buildmaster 01ac98df8f Bumping versions to 1.4.7.BUILD-SNAPSHOT after release 2018-10-15 20:55:16 +00:00
buildmaster 3a62567ec8 Going back to snapshots 2018-10-15 20:55:15 +00:00
buildmaster 3f0a43f913 Update SNAPSHOT to Edgware.SR5 2018-10-15 20:44:32 +00:00
Fabri Di Napoli 53e7464a6e Sidecar: add accept-all-ssl-certificates property in order to be able to use https health checks with self-signed certificates (#3224)
* Add acceptAllSslCertificates in SidecarProperties
2018-10-15 11:02:09 -04:00
David Liu 9eb5e64cd0 Use the appname and id from in mem instanceInfo object instead of raw config (#3212) 2018-10-15 09:13:28 -04:00
Ryan Baxter edd24bc51b Only copy the requestentity in zuul if the request is retryable. Fixes #2903 (#3221) 2018-10-09 09:38:24 -04:00
57 changed files with 813 additions and 249 deletions
+1 -1
View File
@@ -39,4 +39,4 @@ jobs:
destination: artifacts
- store_test_results:
path: ~/junit/
destination: testartifacts
destination: testartifacts
+1 -1
View File
@@ -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>
+100 -4
View File
@@ -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.
+5 -5
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+4 -4
View File
@@ -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>
+1 -1
View File
@@ -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>
@@ -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
@@ -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;
}
}
@@ -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();
@@ -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<>();
@@ -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 {
}
}
@@ -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();
@@ -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);
@@ -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;
+1 -1
View File
@@ -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>
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
@@ -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.
@@ -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() {
@@ -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;
@@ -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;
}
}
+1 -1
View File
@@ -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>
@@ -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;
@@ -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)) {
@@ -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());
+1 -1
View File
@@ -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>
@@ -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");
}
}
@@ -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
@@ -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();
}
@@ -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:
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
@@ -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();
}
}
}
@@ -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);
}
}
@@ -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(
@@ -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;
}
@@ -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);
@@ -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
@@ -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())) {
+1 -1
View File
@@ -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>