move netflix code from sandbox
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -7,4 +7,5 @@
|
||||
.settings/
|
||||
.springBeans
|
||||
target/
|
||||
|
||||
.idea
|
||||
*.iml
|
||||
|
||||
163
pom.xml
163
pom.xml
@@ -1,74 +1,107 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
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>
|
||||
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>
|
||||
|
||||
<groupId>org.springframework.platform</groupId>
|
||||
<artifactId>spring-platform-netflix</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<groupId>org.springframework.platform</groupId>
|
||||
<artifactId>spring-platform-netflix</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>spring-platform-netflix</name>
|
||||
<description>Spring Platform Netflix</description>
|
||||
|
||||
<name>spring-platform-netflix</name>
|
||||
<description>Eureka integration project</description>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<relativePath/>
|
||||
<!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>1.1.5.BUILD-SNAPSHOT</version>
|
||||
<relativePath /> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.netflix.eureka</groupId>
|
||||
<artifactId>eureka-client</artifactId>
|
||||
<version>1.1.135</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.eureka</groupId>
|
||||
<artifactId>eureka-client</artifactId>
|
||||
<version>1.1.135</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.12.6</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<modules>
|
||||
<module>spring-platform-netflix-core</module>
|
||||
<module>spring-platform-netflix-zuul</module>
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.eureka</groupId>
|
||||
<artifactId>eureka-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.eureka</groupId>
|
||||
<artifactId>eureka-client</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.netflix.archaius</groupId>
|
||||
<artifactId>archaius-core</artifactId>
|
||||
<version>${archaius.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.eureka</groupId>
|
||||
<artifactId>eureka-client</artifactId>
|
||||
<version>${eureka.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.feign</groupId>
|
||||
<artifactId>feign-core</artifactId>
|
||||
<version>${feign.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.feign</groupId>
|
||||
<artifactId>feign-ribbon</artifactId>
|
||||
<version>${feign.version}</version>
|
||||
</dependency>
|
||||
<!--<dependency>
|
||||
<groupId>com.netflix.feign</groupId>
|
||||
<artifactId>feign-slf4j</artifactId>
|
||||
<version>${feign.version}</version>
|
||||
</dependency>-->
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-core</artifactId>
|
||||
<version>${hystrix.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-metrics-event-stream</artifactId>
|
||||
<version>${hystrix.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-javanica</artifactId>
|
||||
<version>${hystrix.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-core</artifactId>
|
||||
<version>${ribbon.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-httpclient</artifactId>
|
||||
<version>${ribbon.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-eureka</artifactId>
|
||||
<version>${ribbon.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.zuul</groupId>
|
||||
<artifactId>zuul-core</artifactId>
|
||||
<version>${zuul.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.12.6</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>1.7</java.version>
|
||||
</properties>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>1.7</java.version>
|
||||
<archaius.version>0.6.0</archaius.version>
|
||||
<eureka.version>1.1.135</eureka.version>
|
||||
<feign.version>6.1.2</feign.version>
|
||||
<hystrix.version>1.4.0-RC4</hystrix.version>
|
||||
<ribbon.version>0.3.12</ribbon.version>
|
||||
<zuul.version>1.0.24</zuul.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
|
||||
85
spring-platform-netflix-core/pom.xml
Normal file
85
spring-platform-netflix-core/pom.xml
Normal file
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
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>
|
||||
|
||||
<groupId>org.springframework.platform</groupId>
|
||||
<artifactId>spring-platform-netflix-core</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>spring-platform-netflix-core</name>
|
||||
<description>Spring Platform Netflix Core</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.platform</groupId>
|
||||
<artifactId>spring-platform-netflix</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.archaius</groupId>
|
||||
<artifactId>archaius-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.eureka</groupId>
|
||||
<artifactId>eureka-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.eureka</groupId>
|
||||
<artifactId>eureka-client</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.feign</groupId>
|
||||
<artifactId>feign-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.feign</groupId>
|
||||
<artifactId>feign-ribbon</artifactId>
|
||||
</dependency>
|
||||
<!--<dependency>
|
||||
<groupId>com.netflix.feign</groupId>
|
||||
<artifactId>feign-slf4j</artifactId>
|
||||
</dependency>-->
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-metrics-event-stream</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-javanica</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-eureka</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,78 @@
|
||||
package org.springframework.platform.netflix.archaius;
|
||||
|
||||
import com.netflix.config.ConcurrentCompositeConfiguration;
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.config.DynamicURLConfiguration;
|
||||
import org.apache.commons.configuration.EnvironmentConfiguration;
|
||||
import org.apache.commons.configuration.SystemConfiguration;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
import static com.netflix.config.ConfigurationBasedDeploymentContext.DEPLOYMENT_APPLICATION_ID_PROPERTY;
|
||||
import static com.netflix.config.ConfigurationManager.*;
|
||||
import static com.netflix.config.ConfigurationManager.APPLICATION_PROPERTIES;
|
||||
import static com.netflix.config.ConfigurationManager.ENV_CONFIG_NAME;
|
||||
|
||||
/**
|
||||
* Created by sgibb on 7/3/14.
|
||||
*/
|
||||
@Configuration
|
||||
public class ArchaiusAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
ConfigurableEnvironment env;
|
||||
|
||||
@Bean
|
||||
ConfigurableEnvironmentConfiguration configurableEnvironmentConfiguration() {
|
||||
ConfigurableEnvironmentConfiguration envConfig = new ConfigurableEnvironmentConfiguration(env);
|
||||
configureArchaius(envConfig);
|
||||
return envConfig;
|
||||
}
|
||||
|
||||
protected void configureArchaius(ConfigurableEnvironmentConfiguration envConfig) {
|
||||
String appName = env.getProperty("spring.application.name");
|
||||
if (appName == null) {
|
||||
throw new IllegalStateException("spring.application.name may not be null");
|
||||
}
|
||||
//this is deprecated, but currently it seams the only way to set it initially
|
||||
System.setProperty(DEPLOYMENT_APPLICATION_ID_PROPERTY, appName);
|
||||
|
||||
//TODO: support for other DeploymentContexts
|
||||
|
||||
ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration();
|
||||
|
||||
//support to add other Configurations (Jdbc, DynamoDb, Zookeeper, jclouds, etc...)
|
||||
/*if (factories != null && !factories.isEmpty()) {
|
||||
for (PropertiesSourceFactory factory: factories) {
|
||||
config.addConfiguration(factory.getConfiguration(), factory.getName());
|
||||
}
|
||||
}*/
|
||||
config.addConfiguration(envConfig, ConfigurableEnvironmentConfiguration.class.getSimpleName());
|
||||
|
||||
//below come from ConfigurationManager.createDefaultConfigInstance()
|
||||
try {
|
||||
DynamicURLConfiguration defaultURLConfig = new DynamicURLConfiguration();
|
||||
config.addConfiguration(defaultURLConfig, URL_CONFIG_NAME);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace(); //TODO: log error
|
||||
}
|
||||
|
||||
//TODO: sys/env above urls?
|
||||
if (!Boolean.getBoolean(DISABLE_DEFAULT_SYS_CONFIG)) {
|
||||
SystemConfiguration sysConfig = new SystemConfiguration();
|
||||
config.addConfiguration(sysConfig, SYS_CONFIG_NAME);
|
||||
}
|
||||
if (!Boolean.getBoolean(DISABLE_DEFAULT_ENV_CONFIG)) {
|
||||
EnvironmentConfiguration environmentConfiguration = new EnvironmentConfiguration();
|
||||
config.addConfiguration(environmentConfiguration, ENV_CONFIG_NAME);
|
||||
}
|
||||
|
||||
ConcurrentCompositeConfiguration appOverrideConfig = new ConcurrentCompositeConfiguration();
|
||||
config.addConfiguration(appOverrideConfig, APPLICATION_PROPERTIES);
|
||||
config.setContainerConfigurationIndex(config.getIndexOfConfiguration(appOverrideConfig));
|
||||
|
||||
ConfigurationManager.install(config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package org.springframework.platform.netflix.archaius;
|
||||
|
||||
import org.apache.commons.configuration.AbstractConfiguration;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.*;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Created by sgibb on 6/27/14.
|
||||
*/
|
||||
public class ConfigurableEnvironmentConfiguration extends AbstractConfiguration {
|
||||
ConfigurableEnvironment environment;
|
||||
|
||||
public ConfigurableEnvironmentConfiguration(ConfigurableEnvironment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addPropertyDirect(String key, Object value) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return !getKeys().hasNext(); //TODO: find a better way to do this
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(String key) {
|
||||
return environment.containsProperty(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProperty(String key) {
|
||||
return environment.getProperty(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> getKeys() {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (Map.Entry<String, PropertySource<?>> entry : getPropertySources().entrySet()) {
|
||||
PropertySource<?> source = entry.getValue();
|
||||
if (source instanceof EnumerablePropertySource) {
|
||||
EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) source;
|
||||
for (String name : enumerable.getPropertyNames()) {
|
||||
result.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.iterator();
|
||||
}
|
||||
|
||||
private Map<String, PropertySource<?>> getPropertySources() {
|
||||
Map<String, PropertySource<?>> map = new LinkedHashMap<String, PropertySource<?>>();
|
||||
MutablePropertySources sources = null;
|
||||
if (this.environment != null
|
||||
&& this.environment instanceof ConfigurableEnvironment) {
|
||||
sources = ((ConfigurableEnvironment) this.environment).getPropertySources();
|
||||
}
|
||||
else {
|
||||
sources = new StandardEnvironment().getPropertySources();
|
||||
}
|
||||
for (PropertySource<?> source : sources) {
|
||||
extract("", map, source);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private void extract(String root, Map<String, PropertySource<?>> map,
|
||||
PropertySource<?> source) {
|
||||
if (source instanceof CompositePropertySource) {
|
||||
Set<PropertySource<?>> nested = getNestedPropertySources((CompositePropertySource) source);
|
||||
for (PropertySource<?> nest : nested) {
|
||||
extract(source.getName() + ":", map, nest);
|
||||
}
|
||||
}
|
||||
else {
|
||||
map.put(root + source.getName(), source);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Set<PropertySource<?>> getNestedPropertySources(CompositePropertySource source) {
|
||||
try {
|
||||
Field field = ReflectionUtils.findField(CompositePropertySource.class,
|
||||
"propertySources");
|
||||
field.setAccessible(true);
|
||||
return (Set<PropertySource<?>>) field.get(source);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.springframework.platform.netflix.circuitbreaker;
|
||||
|
||||
import com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportAware;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.platform.netflix.circuitbreaker.annotations.EnableCircuitBreaker;
|
||||
import org.springframework.platform.netflix.endpoint.HystrixStreamEndpoint;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Created by sgibb on 6/19/14.
|
||||
*/
|
||||
@Configuration
|
||||
public class CircuitBreakerConfiguration implements ImportAware {
|
||||
|
||||
private AnnotationAttributes enableCircuitBreaker;
|
||||
|
||||
@Bean
|
||||
HystrixCommandAspect hystrixCommandAspect() {
|
||||
return new HystrixCommandAspect();
|
||||
}
|
||||
|
||||
@Bean
|
||||
//TODO: add enable/disable
|
||||
public HystrixStreamEndpoint hystrixStreamEndpoint() {
|
||||
return new HystrixStreamEndpoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
this.enableCircuitBreaker = AnnotationAttributes.fromMap(
|
||||
importMetadata.getAnnotationAttributes(EnableCircuitBreaker.class.getName(), false));
|
||||
Assert.notNull(this.enableCircuitBreaker,
|
||||
"@EnableCircuitBreaker is not present on importing class " + importMetadata.getClassName());
|
||||
}
|
||||
|
||||
@Autowired(required=false)
|
||||
void setConfigurers(Collection<CircuitBreakerConfigurer> configurers) {
|
||||
if (CollectionUtils.isEmpty(configurers)) {
|
||||
return;
|
||||
}
|
||||
if (configurers.size() > 1) {
|
||||
throw new IllegalStateException("Only one TransactionManagementConfigurer may exist");
|
||||
}
|
||||
//TODO: create CircuitBreakerConfigurer API
|
||||
CircuitBreakerConfigurer configurer = configurers.iterator().next();
|
||||
//this.txManager = configurer.annotationDrivenTransactionManager();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.springframework.platform.netflix.circuitbreaker;
|
||||
|
||||
import org.springframework.context.annotation.AdviceMode;
|
||||
import org.springframework.context.annotation.AdviceModeImportSelector;
|
||||
import org.springframework.context.annotation.AutoProxyRegistrar;
|
||||
import org.springframework.platform.netflix.circuitbreaker.annotations.EnableCircuitBreaker;
|
||||
|
||||
/**
|
||||
* Created by sgibb on 6/19/14.
|
||||
*/
|
||||
public class CircuitBreakerConfigurationSelector extends AdviceModeImportSelector<EnableCircuitBreaker> {
|
||||
/**
|
||||
* The name of the AspectJ transaction management @{@code Configuration} class.
|
||||
*/
|
||||
public static final String TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME =
|
||||
"org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration";
|
||||
|
||||
@Override
|
||||
protected String[] selectImports(AdviceMode adviceMode) {
|
||||
switch (adviceMode) {
|
||||
case PROXY:
|
||||
return new String[]{AutoProxyRegistrar.class.getName(), CircuitBreakerConfiguration.class.getName()};
|
||||
case ASPECTJ:
|
||||
return new String[]{TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.springframework.platform.netflix.circuitbreaker;
|
||||
|
||||
/**
|
||||
* Created by sgibb on 6/19/14.
|
||||
*/
|
||||
public interface CircuitBreakerConfigurer {
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.springframework.platform.netflix.circuitbreaker.annotations;
|
||||
|
||||
import org.springframework.context.annotation.AdviceMode;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.platform.netflix.circuitbreaker.CircuitBreakerConfigurationSelector;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Created by sgibb on 6/19/14.
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Import(CircuitBreakerConfigurationSelector.class)
|
||||
public @interface EnableCircuitBreaker {
|
||||
|
||||
/**
|
||||
* Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as
|
||||
* opposed to standard Java interface-based proxies ({@code false}). The default is
|
||||
* {@code false}. <strong>Applicable only if {@link #mode()} is set to
|
||||
* {@link org.springframework.context.annotation.AdviceMode#PROXY}</strong>.
|
||||
* <p>Note that setting this attribute to {@code true} will affect <em>all</em>
|
||||
* Spring-managed beans requiring proxying, not just those marked with
|
||||
* {@code @Transactional}. For example, other beans marked with Spring's
|
||||
* {@code @Async} annotation will be upgraded to subclass proxying at the same
|
||||
* time. This approach has no negative impact in practice unless one is explicitly
|
||||
* expecting one type of proxy vs another, e.g. in tests.
|
||||
*/
|
||||
boolean proxyTargetClass() default false;
|
||||
|
||||
/**
|
||||
* Indicate how transactional advice should be applied. The default is
|
||||
* {@link org.springframework.context.annotation.AdviceMode#PROXY}.
|
||||
* @see org.springframework.context.annotation.AdviceMode
|
||||
*/
|
||||
AdviceMode mode() default AdviceMode.PROXY;
|
||||
|
||||
/**
|
||||
* Indicate the ordering of the execution of the transaction advisor
|
||||
* when multiple advices are applied at a specific joinpoint.
|
||||
* The default is {@link org.springframework.core.Ordered#LOWEST_PRECEDENCE}.
|
||||
*/
|
||||
int order() default Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.springframework.platform.netflix.endpoint;
|
||||
|
||||
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
|
||||
|
||||
/**
|
||||
* User: spencergibb
|
||||
* Date: 4/22/14
|
||||
* Time: 3:16 PM
|
||||
*/
|
||||
public class HystrixStreamEndpoint extends ServletWrappingEndpoint {
|
||||
|
||||
public HystrixStreamEndpoint() {
|
||||
super(HystrixMetricsStreamServlet.class, "hystrixStream", "hystrix.stream", false, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package org.springframework.platform.netflix.endpoint;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.context.ServletContextAware;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ServletWrappingController;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* TODO: move to spring-boot?
|
||||
* User: spencergibb
|
||||
* Date: 4/24/14
|
||||
* Time: 9:13 PM
|
||||
*/
|
||||
public abstract class ServletWrappingEndpoint implements InitializingBean,
|
||||
ApplicationContextAware, ServletContextAware, MvcEndpoint {
|
||||
|
||||
protected String path;
|
||||
protected boolean sensitive;
|
||||
protected boolean enabled = true;
|
||||
|
||||
protected final ServletWrappingController controller = new ServletWrappingController();
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.controller.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setServletContext(ServletContext servletContext) {
|
||||
this.controller.setServletContext(servletContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.controller.setApplicationContext(applicationContext);
|
||||
}
|
||||
|
||||
protected ServletWrappingEndpoint(Class<?> servletClass, String servletName, String path,
|
||||
boolean sensitive, boolean enabled) {
|
||||
controller.setServletClass(servletClass);
|
||||
controller.setServletName(servletName);
|
||||
this.path = path;
|
||||
this.sensitive = sensitive;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@RequestMapping("**")
|
||||
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
return this.controller.handleRequest(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSensitive() {
|
||||
return sensitive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Endpoint> getEndpointType() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -147,16 +147,16 @@ public class EurekaClientConfigBean implements EurekaClientConfig {
|
||||
|
||||
@Override
|
||||
public List<String> getEurekaServerServiceUrls(String myZone) {
|
||||
String serviceUrls = serviceUrl .get(myZone);
|
||||
String serviceUrls = serviceUrl.get(myZone);
|
||||
if (serviceUrls == null || serviceUrls.isEmpty()) {
|
||||
serviceUrls = serviceUrl.get("default" + myZone);
|
||||
serviceUrls = serviceUrl.get("default");
|
||||
|
||||
}
|
||||
if (serviceUrls != null) {
|
||||
return Arrays.asList(serviceUrls.split(","));
|
||||
}
|
||||
|
||||
return new ArrayList<String>();
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,7 +46,7 @@ public class EurekaInstanceConfigBean implements EurekaInstanceConfig {
|
||||
@Getter(AccessLevel.PRIVATE) @Setter(AccessLevel.PRIVATE)
|
||||
private String[] hostInfo = initHostInfo();
|
||||
|
||||
@Value("${spring.application.name:unkown}")
|
||||
@Value("${spring.application.name:unknown}")
|
||||
private String appname = "unknown";
|
||||
|
||||
private String appGroupName;
|
||||
@@ -66,7 +66,7 @@ public class EurekaInstanceConfigBean implements EurekaInstanceConfig {
|
||||
|
||||
private int leaseExpirationDurationInSeconds = 90;
|
||||
|
||||
@Value("${spring.application.name:unkown}.mydomain.net")
|
||||
@Value("${spring.application.name:unknown}.mydomain.net")
|
||||
private String virtualHostName;
|
||||
|
||||
private String secureVirtualHostName;
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.springframework.platform.netflix.feign;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.Logger;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.platform.netflix.archaius.ArchaiusAutoConfiguration;
|
||||
|
||||
/**
|
||||
* Created by sgibb on 7/3/14.
|
||||
*/
|
||||
@Configuration
|
||||
@AutoConfigureAfter(ArchaiusAutoConfiguration.class)
|
||||
public class FeignAutoConfiguration {
|
||||
@Bean
|
||||
SpringDecoder feignDecoder() {
|
||||
return new SpringDecoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
SpringEncoder feignEncoder() {
|
||||
return new SpringEncoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Logger feignLogger() {
|
||||
//return new Slf4jLogger(); //TODO pass Client classname in
|
||||
return new Logger.JavaLogger();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Contract feignContract() {
|
||||
return new SpringMvcContract();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package org.springframework.platform.netflix.feign;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.ResourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter;
|
||||
import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import javax.xml.transform.Source;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by sgibb on 6/27/14.
|
||||
*/
|
||||
public class FeignBase {
|
||||
public static final boolean romePresent =
|
||||
ClassUtils.isPresent("com.sun.syndication.feed.WireFeed", FeignBase.class.getClassLoader());
|
||||
|
||||
public static final boolean jaxb2Present =
|
||||
ClassUtils.isPresent("javax.xml.bind.Binder", FeignBase.class.getClassLoader());
|
||||
|
||||
public static final boolean jackson2Present =
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", FeignBase.class.getClassLoader()) &&
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", FeignBase.class.getClassLoader());
|
||||
|
||||
public static final boolean jacksonPresent =
|
||||
ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", FeignBase.class.getClassLoader()) &&
|
||||
ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator", FeignBase.class.getClassLoader());
|
||||
|
||||
protected final List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
|
||||
|
||||
public FeignBase() {
|
||||
addDefaultConverters(messageConverters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of the {@link SpringDecoder} using the given list of
|
||||
* {@link HttpMessageConverter} to use
|
||||
* @param messageConverters the list of {@link HttpMessageConverter} to use
|
||||
*/
|
||||
public FeignBase(List<HttpMessageConverter<?>> messageConverters) {
|
||||
Assert.notEmpty(messageConverters, "'messageConverters' must not be empty");
|
||||
this.messageConverters.addAll(messageConverters);
|
||||
}
|
||||
|
||||
protected void addDefaultConverters(List<HttpMessageConverter<?>> messageConverters) {
|
||||
messageConverters.add(new ByteArrayHttpMessageConverter());
|
||||
messageConverters.add(new StringHttpMessageConverter());
|
||||
messageConverters.add(new ResourceHttpMessageConverter());
|
||||
messageConverters.add(new SourceHttpMessageConverter<Source>());
|
||||
messageConverters.add(new AllEncompassingFormHttpMessageConverter());
|
||||
|
||||
if (romePresent) {
|
||||
messageConverters.add(new AtomFeedHttpMessageConverter());
|
||||
messageConverters.add(new RssChannelHttpMessageConverter());
|
||||
}
|
||||
if (jaxb2Present) {
|
||||
messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
|
||||
}
|
||||
if (jackson2Present) {
|
||||
messageConverters.add(new MappingJackson2HttpMessageConverter());
|
||||
}
|
||||
else if (jacksonPresent) {
|
||||
messageConverters.add(new org.springframework.http.converter.json.MappingJacksonHttpMessageConverter());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the message body converters to use.
|
||||
* <p>These converters are used to convert from and to HTTP requests and responses.
|
||||
*/
|
||||
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
|
||||
Assert.notEmpty(messageConverters, "'messageConverters' must not be empty");
|
||||
this.messageConverters.clear();
|
||||
this.messageConverters.addAll(messageConverters);
|
||||
}
|
||||
|
||||
public List<HttpMessageConverter<?>> getMessageConverters() {
|
||||
return messageConverters;
|
||||
}
|
||||
|
||||
protected HttpHeaders getHttpHeaders(Map<String, Collection<String>> headers) {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
for (Map.Entry<String, Collection<String>> entry : headers.entrySet()) {
|
||||
httpHeaders.put(entry.getKey(), new ArrayList<>(entry.getValue()));
|
||||
}
|
||||
return httpHeaders;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package org.springframework.platform.netflix.feign;
|
||||
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList;
|
||||
import feign.Contract;
|
||||
import feign.Feign;
|
||||
import feign.Logger;
|
||||
import feign.codec.Decoder;
|
||||
import feign.codec.Encoder;
|
||||
import feign.ribbon.LoadBalancingTarget;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.platform.netflix.archaius.ConfigurableEnvironmentConfiguration;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* Created by sgibb on 7/3/14.
|
||||
*/
|
||||
@Configuration
|
||||
public class FeignConfigurer {
|
||||
@Autowired
|
||||
ConfigurableEnvironmentConfiguration envConfig; //FIXME: howto enforce this?
|
||||
|
||||
@Autowired
|
||||
Decoder decoder;
|
||||
|
||||
@Autowired
|
||||
Encoder encoder;
|
||||
|
||||
@Autowired
|
||||
Logger logger;
|
||||
|
||||
@Autowired
|
||||
Contract contract;
|
||||
|
||||
protected Feign.Builder feign() {
|
||||
//ConfigurationManager.getConfigInstance().setProperty("exampleBackend.ribbon.listOfServers", "localhost:7080");
|
||||
//exampleBackend.ribbon.NIWSServerListClassName=my.package.MyServerList
|
||||
return Feign.builder()
|
||||
.logger(logger)
|
||||
.encoder(encoder)
|
||||
.decoder(decoder)
|
||||
.contract(contract);
|
||||
}
|
||||
|
||||
protected <T> T loadBalance(Class<T> type, String schemeName) {
|
||||
return loadBalance(feign(), type, schemeName);
|
||||
}
|
||||
|
||||
protected <T> T loadBalance(Feign.Builder builder, Class<T> type, String schemeName) {
|
||||
String name = URI.create(schemeName).getHost();
|
||||
setServiceListClass(name);
|
||||
return builder.target(LoadBalancingTarget.create(type, schemeName));
|
||||
}
|
||||
|
||||
public static void setServiceListClass(String serviceId) {
|
||||
setProp(serviceId, "NIWSServerListClassName", DiscoveryEnabledNIWSServerList.class.getName());
|
||||
setProp(serviceId, "DeploymentContextBasedVipAddresses", serviceId); //FIXME: what should this be?
|
||||
}
|
||||
|
||||
private static void setProp(String serviceId, String suffix, String value) {
|
||||
ConfigurationManager.getConfigInstance().setProperty(serviceId + ".ribbon." + suffix, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package org.springframework.platform.netflix.feign;
|
||||
|
||||
import feign.FeignException;
|
||||
import feign.Response;
|
||||
import feign.codec.DecodeException;
|
||||
import feign.codec.Decoder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.web.client.HttpMessageConverterExtractor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by sgibb on 6/26/14.
|
||||
*/
|
||||
public class SpringDecoder extends FeignBase implements Decoder {
|
||||
private static final Logger logger = LoggerFactory.getLogger(SpringDecoder.class);
|
||||
|
||||
public SpringDecoder() {
|
||||
}
|
||||
|
||||
public SpringDecoder(List<HttpMessageConverter<?>> messageConverters) {
|
||||
super(messageConverters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decode(final Response response, Type type) throws IOException, DecodeException, FeignException {
|
||||
if (type instanceof Class) {
|
||||
HttpMessageConverterExtractor<?> extractor =
|
||||
new HttpMessageConverterExtractor((Class<?>) type, getMessageConverters());
|
||||
|
||||
Object data = extractor.extractData(new FeignResponseAdapter(response));
|
||||
return data;
|
||||
}
|
||||
throw new DecodeException("type is not an instance of Class: "+type);
|
||||
}
|
||||
|
||||
private class FeignResponseAdapter implements ClientHttpResponse {
|
||||
private final Response response;
|
||||
|
||||
private FeignResponseAdapter(Response response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpStatus getStatusCode() throws IOException {
|
||||
return HttpStatus.valueOf(response.status());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRawStatusCode() throws IOException {
|
||||
return response.status();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusText() throws IOException {
|
||||
return response.reason();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
response.body().close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getBody() throws IOException {
|
||||
return response.body().asInputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return getHttpHeaders(response.headers());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package org.springframework.platform.netflix.feign;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import feign.RequestTemplate;
|
||||
import feign.codec.EncodeException;
|
||||
import feign.codec.Encoder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by sgibb on 6/26/14.
|
||||
*/
|
||||
public class SpringEncoder extends FeignBase implements Encoder {
|
||||
private static final Logger logger = LoggerFactory.getLogger(SpringEncoder.class);
|
||||
|
||||
public SpringEncoder() {
|
||||
}
|
||||
|
||||
public SpringEncoder(List<HttpMessageConverter<?>> messageConverters) {
|
||||
super(messageConverters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Object requestBody, RequestTemplate request) throws EncodeException {
|
||||
//template.body(conversionService.convert(object, String.class));
|
||||
if (requestBody != null) {
|
||||
Class<?> requestType = requestBody.getClass();
|
||||
Collection<String> contentTypes = request.headers().get("Content-Type");
|
||||
|
||||
MediaType requestContentType = null;
|
||||
if (contentTypes != null && !contentTypes.isEmpty()) {
|
||||
String type = contentTypes.iterator().next();
|
||||
requestContentType = MediaType.valueOf(type);
|
||||
}
|
||||
|
||||
for (HttpMessageConverter<?> messageConverter : getMessageConverters()) {
|
||||
if (messageConverter.canWrite(requestType, requestContentType)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (requestContentType != null) {
|
||||
logger.debug("Writing [" + requestBody + "] as \"" + requestContentType +
|
||||
"\" using [" + messageConverter + "]");
|
||||
}
|
||||
else {
|
||||
logger.debug("Writing [" + requestBody + "] using [" + messageConverter + "]");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FeignOutputMessage outputMessage = new FeignOutputMessage(request);
|
||||
try {
|
||||
((HttpMessageConverter<Object>) messageConverter).write(
|
||||
requestBody, requestContentType, outputMessage);
|
||||
} catch (IOException e) {
|
||||
throw new EncodeException("Error converting request body", e);
|
||||
}
|
||||
request.body(outputMessage.getOutputStream().toByteArray(), Charsets.UTF_8); //TODO: set charset
|
||||
return;
|
||||
}
|
||||
}
|
||||
String message = "Could not write request: no suitable HttpMessageConverter found for request type [" +
|
||||
requestType.getName() + "]";
|
||||
if (requestContentType != null) {
|
||||
message += " and content type [" + requestContentType + "]";
|
||||
}
|
||||
throw new EncodeException(message);
|
||||
}
|
||||
}
|
||||
|
||||
private class FeignOutputMessage implements HttpOutputMessage {
|
||||
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
RequestTemplate request;
|
||||
|
||||
private FeignOutputMessage(RequestTemplate request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getBody() throws IOException {
|
||||
return outputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return getHttpHeaders(request.headers());
|
||||
}
|
||||
|
||||
public ByteArrayOutputStream getOutputStream() {
|
||||
return outputStream;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package org.springframework.platform.netflix.feign;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.MethodMetadata;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static feign.Util.checkState;
|
||||
import static feign.Util.emptyToNull;
|
||||
|
||||
/**
|
||||
* Created by sgibb on 6/27/14.
|
||||
*/
|
||||
public class SpringMvcContract extends Contract.BaseContract {
|
||||
static final String ACCEPT = "Accept";
|
||||
static final String CONTENT_TYPE = "Content-Type";
|
||||
|
||||
@Override
|
||||
protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) {
|
||||
Class<? extends Annotation> annotationType = methodAnnotation.annotationType();
|
||||
RequestMapping mapping = RequestMapping.class.cast(methodAnnotation);
|
||||
if (mapping != null) {
|
||||
//HTTP Method
|
||||
checkOne(method, mapping.method(), "method");
|
||||
data.template().method(mapping.method()[0].name());
|
||||
|
||||
//path
|
||||
checkOne(method, mapping.value(), "value");
|
||||
|
||||
String methodAnnotationValue = mapping.value()[0];
|
||||
String pathValue = emptyToNull(methodAnnotationValue);
|
||||
checkState(pathValue != null, "value was empty on method %s", method.getName());
|
||||
if (!methodAnnotationValue.startsWith("/") && !data.template().toString().endsWith("/")) {
|
||||
methodAnnotationValue = "/" + methodAnnotationValue;
|
||||
}
|
||||
data.template().append(methodAnnotationValue);
|
||||
|
||||
//produces
|
||||
checkAtMostOne(method, mapping.produces(), "produces");
|
||||
String[] serverProduces = mapping.produces();
|
||||
String clientAccepts = serverProduces.length == 0 ? null: emptyToNull(serverProduces[0]);
|
||||
if (clientAccepts != null) {
|
||||
data.template().header(ACCEPT, clientAccepts);
|
||||
}
|
||||
|
||||
//consumes
|
||||
checkAtMostOne(method, mapping.consumes(), "consumes");
|
||||
String[] serverConsumes = mapping.consumes();
|
||||
String clientProduces = serverConsumes.length == 0 ? null: emptyToNull(serverConsumes[0]);
|
||||
if (clientProduces != null) {
|
||||
data.template().header(CONTENT_TYPE, clientProduces);
|
||||
}
|
||||
|
||||
//headers
|
||||
//TODO: only supports one header value per key
|
||||
if (mapping.headers() != null && mapping.headers().length > 0)
|
||||
for (String header : mapping.headers()) {
|
||||
int colon = header.indexOf(':');
|
||||
data.template().header(header.substring(0, colon), header.substring(colon + 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAtMostOne(Method method, Object[] values, String fieldName) {
|
||||
checkState(values != null && (values.length == 0 || values.length == 1),
|
||||
"Method %s can only contain at most 1 %s field. Found: %s", method.getName(), fieldName,
|
||||
values == null ? null : Arrays.asList(values));
|
||||
}
|
||||
|
||||
private void checkOne(Method method, Object[] values, String fieldName) {
|
||||
checkState(values != null && values.length == 1,
|
||||
"Method %s can only contain 1 %s field. Found: %s", method.getName(), fieldName,
|
||||
values == null ? null : Arrays.asList(values));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {
|
||||
//TODO: support spring parameter annotations?
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.platform.netflix.archaius.ArchaiusAutoConfiguration,\
|
||||
org.springframework.platform.netflix.eureka.EurekaClientAutoConfiguration,\
|
||||
org.springframework.platform.netflix.feign.FeignAutoConfiguration
|
||||
81
spring-platform-netflix-zuul/pom.xml
Normal file
81
spring-platform-netflix-zuul/pom.xml
Normal file
@@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>
|
||||
<groupId>org.springframework.platform</groupId>
|
||||
<artifactId>spring-platform-netflix-zuul</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Platform Netflix Zuul</name>
|
||||
<url>http://projects.spring.io/spring-platform/</url>
|
||||
<parent>
|
||||
<groupId>org.springframework.platform</groupId>
|
||||
<artifactId>spring-platform-netflix</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
<spring-platform-config.version>1.0.0.BUILD-SNAPSHOT</spring-platform-config.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.platform</groupId>
|
||||
<artifactId>spring-platform-config</artifactId>
|
||||
<version>${spring-platform-config.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.platform</groupId>
|
||||
<artifactId>spring-platform-netflix-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.platform</groupId>
|
||||
<artifactId>spring-platform-config-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.zuul</groupId>
|
||||
<artifactId>zuul-core</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.springframework.platform.netflix.zuul;
|
||||
|
||||
import com.netflix.zuul.context.ContextLifecycleFilter;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.context.embedded.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.platform.netflix.endpoint.HystrixStreamEndpoint;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* User: spencergibb
|
||||
* Date: 4/24/14
|
||||
* Time: 8:57 PM
|
||||
*/
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@EnableScheduling
|
||||
@ComponentScan(basePackageClasses = Application.class)
|
||||
public class Application {
|
||||
|
||||
@Bean
|
||||
ZuulProperties routerProperties() {
|
||||
return new ZuulProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean contextLifecycleFilter() {
|
||||
Collection<String> urlPatterns = new ArrayList<>();
|
||||
urlPatterns.add("/*");
|
||||
|
||||
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new ContextLifecycleFilter());
|
||||
filterRegistrationBean.setUrlPatterns(urlPatterns);
|
||||
|
||||
return filterRegistrationBean;
|
||||
}
|
||||
|
||||
//TODO: replace with auto config
|
||||
@Bean
|
||||
HystrixStreamEndpoint hystrixStreamEndpoint() {
|
||||
return new HystrixStreamEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Routes routes() {
|
||||
return new Routes();
|
||||
}
|
||||
|
||||
@Bean
|
||||
FilterIntializer filterIntializer() {
|
||||
return new FilterIntializer();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ZuulController zuulController() {
|
||||
return new ZuulController();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package org.springframework.platform.netflix.zuul;
|
||||
|
||||
import com.netflix.zuul.FilterFileManager;
|
||||
import com.netflix.zuul.FilterLoader;
|
||||
import com.netflix.zuul.groovy.GroovyCompiler;
|
||||
import com.netflix.zuul.groovy.GroovyFileFilter;
|
||||
import com.netflix.zuul.monitoring.MonitoringHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
//import javax.servlet.http.HttpSessionEvent;
|
||||
|
||||
/**
|
||||
* User: spencergibb
|
||||
* Date: 4/24/14
|
||||
* Time: 9:23 PM
|
||||
* TODO: .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
*/
|
||||
public class FilterIntializer implements ServletContextListener/*, HttpSessionListener*/ {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(FilterIntializer.class);
|
||||
|
||||
@Autowired
|
||||
ZuulProperties props;
|
||||
|
||||
@Override
|
||||
/*public void sessionCreated(HttpSessionEvent se) {
|
||||
contextInitialized(null);
|
||||
}*/
|
||||
public void contextInitialized(ServletContextEvent sce) {
|
||||
|
||||
LOGGER.info("Starting filter initialzer context listener");
|
||||
|
||||
//FIXME: mocks monitoring infrastructure as we don't need it for this simple app
|
||||
MonitoringHelper.initMocks();
|
||||
|
||||
// initializes groovy filesystem poller
|
||||
initGroovyFilterManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
/*public void sessionDestroyed(HttpSessionEvent se) {
|
||||
contextDestroyed(null);
|
||||
}*/
|
||||
public void contextDestroyed(ServletContextEvent sce) {
|
||||
LOGGER.info("Stopping filter initializer context listener");
|
||||
}
|
||||
|
||||
private void initGroovyFilterManager() {
|
||||
|
||||
//TODO: pluggable filter initialzer
|
||||
FilterLoader.getInstance().setCompiler(new GroovyCompiler());
|
||||
|
||||
final String scriptRoot = props.getFilterRoot();
|
||||
LOGGER.info("Using file system script: " + scriptRoot);
|
||||
|
||||
try {
|
||||
FilterFileManager.setFilenameFilter(new GroovyFileFilter());
|
||||
FilterFileManager.init(5,
|
||||
scriptRoot + "/pre",
|
||||
scriptRoot + "/route",
|
||||
scriptRoot + "/post"
|
||||
);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package org.springframework.platform.netflix.zuul;
|
||||
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
import com.netflix.client.http.HttpResponse;
|
||||
import com.netflix.config.DynamicPropertyFactory;
|
||||
import com.netflix.hystrix.HystrixCommand;
|
||||
import com.netflix.hystrix.HystrixCommandGroupKey;
|
||||
import com.netflix.hystrix.HystrixCommandProperties;
|
||||
import com.netflix.niws.client.http.RestClient;
|
||||
import com.netflix.zuul.constants.ZuulConstants;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.List;
|
||||
|
||||
import static com.netflix.client.http.HttpRequest.Builder;
|
||||
import static com.netflix.client.http.HttpRequest.Verb;
|
||||
|
||||
/**
|
||||
* Hystrix wrapper around Eureka Ribbon command
|
||||
*
|
||||
* see original https://github.com/Netflix/zuul/blob/master/zuul-netflix/src/main/java/com/netflix/zuul/dependency/ribbon/hystrix/RibbonCommand.java
|
||||
*/
|
||||
public class RibbonCommand extends HystrixCommand<HttpResponse> {
|
||||
|
||||
RestClient restClient;
|
||||
Verb verb;
|
||||
URI uri;
|
||||
MultivaluedMap<String, String> headers;
|
||||
MultivaluedMap<String, String> params;
|
||||
InputStream requestEntity;
|
||||
|
||||
|
||||
public RibbonCommand(RestClient restClient,
|
||||
Verb verb,
|
||||
String uri,
|
||||
MultivaluedMap<String, String> headers,
|
||||
MultivaluedMap<String, String> params,
|
||||
InputStream requestEntity) throws URISyntaxException {
|
||||
this("default", restClient, verb, uri, headers, params, requestEntity);
|
||||
}
|
||||
|
||||
|
||||
public RibbonCommand(String commandKey,
|
||||
RestClient restClient,
|
||||
Verb verb,
|
||||
String uri,
|
||||
MultivaluedMap<String, String> headers,
|
||||
MultivaluedMap<String, String> params,
|
||||
InputStream requestEntity) throws URISyntaxException {
|
||||
|
||||
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(commandKey)).andCommandPropertiesDefaults(
|
||||
// we want to default to semaphore-isolation since this wraps
|
||||
// 2 others commands that are already thread isolated
|
||||
HystrixCommandProperties.Setter().withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)
|
||||
.withExecutionIsolationSemaphoreMaxConcurrentRequests(DynamicPropertyFactory.getInstance().
|
||||
getIntProperty(ZuulConstants.ZUUL_EUREKA + commandKey + ".semaphore.maxSemaphores", 100).get())));
|
||||
|
||||
this.restClient = restClient;
|
||||
this.verb = verb;
|
||||
this.uri = new URI(uri);
|
||||
this.headers = headers;
|
||||
this.params = params;
|
||||
this.requestEntity = requestEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpResponse run() throws Exception {
|
||||
try {
|
||||
return forward();
|
||||
} catch (Exception e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
HttpResponse forward() throws Exception {
|
||||
|
||||
RequestContext context = RequestContext.getCurrentContext();
|
||||
|
||||
|
||||
Builder builder = HttpRequest.newBuilder().
|
||||
verb(verb).
|
||||
uri(uri).
|
||||
entity(requestEntity);
|
||||
|
||||
for (String name : headers.keySet()) {
|
||||
List<String> values = headers.get(name);
|
||||
for (String value : values) {
|
||||
builder.header(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
for (String name : params.keySet()) {
|
||||
List<String> values = params.get(name);
|
||||
for (String value : values) {
|
||||
builder.queryParams(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
HttpRequest httpClientRequest = builder.build();
|
||||
|
||||
HttpResponse response = restClient.executeWithLoadBalancer(httpClientRequest);
|
||||
context.set("ribbonResponse", response);
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package org.springframework.platform.netflix.zuul;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.bind.PropertySourceUtils;
|
||||
import org.springframework.core.env.*;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by sgibb on 7/10/14.
|
||||
*/
|
||||
public class Routes {
|
||||
private static final Logger logger = LoggerFactory.getLogger(Routes.class);
|
||||
|
||||
public static final String DEFAULT_ROUTE = "/";
|
||||
|
||||
@Autowired
|
||||
ConfigurableEnvironment env;
|
||||
private final Field propertySourcesField;
|
||||
|
||||
public Routes() {
|
||||
propertySourcesField = ReflectionUtils.findField(CompositePropertySource.class, "propertySources");
|
||||
propertySourcesField.setAccessible(true);
|
||||
}
|
||||
|
||||
//TODO: cache routes or respond to environment event and refresh all routes
|
||||
public LinkedHashMap<String, String> getRoutes() {
|
||||
LinkedHashMap<String, String> routes = new LinkedHashMap<>();
|
||||
MutablePropertySources propertySources = env.getPropertySources();
|
||||
for (PropertySource<?> propertySource : propertySources) {
|
||||
getRoutes(propertySource, routes);
|
||||
}
|
||||
|
||||
String defaultServiceId = routes.get(DEFAULT_ROUTE);
|
||||
|
||||
if (defaultServiceId != null) {
|
||||
//move the defaultServiceId to the end
|
||||
routes.remove(DEFAULT_ROUTE);
|
||||
routes.put(DEFAULT_ROUTE, defaultServiceId);
|
||||
}
|
||||
|
||||
return routes;
|
||||
}
|
||||
|
||||
public void getRoutes(PropertySource<?> propertySource, LinkedHashMap<String, String> routes) {
|
||||
if (propertySource instanceof CompositePropertySource) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<PropertySource<?>> sources = (Set<PropertySource<?>>) propertySourcesField.get(propertySource);
|
||||
for (PropertySource<?> source : sources) {
|
||||
getRoutes(source, routes);
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
//EnumerablePropertySource enumerable = (EnumerablePropertySource) propertySource;
|
||||
MutablePropertySources propertySources = new MutablePropertySources();
|
||||
propertySources.addLast(propertySource);
|
||||
Map<String, Object> routeEntries = PropertySourceUtils.getSubProperties(propertySources, "zuul.route.");
|
||||
for (Map.Entry<String, Object> entry : routeEntries.entrySet()) {
|
||||
String serviceId = entry.getKey();
|
||||
String route = entry.getValue().toString();
|
||||
|
||||
if (routes.containsKey(route)) {
|
||||
logger.warn("Overwriting route {}: already defined by {}", route, routes.get(route));
|
||||
}
|
||||
routes.put(route, serviceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.springframework.platform.netflix.zuul;
|
||||
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* User: spencergibb
|
||||
* Date: 5/1/14
|
||||
* Time: 10:59 PM
|
||||
*/
|
||||
public abstract class SpringFilter extends ZuulFilter {
|
||||
|
||||
protected <T> T getBean(Class<T> beanClass) {
|
||||
//FIXME: hack because zuul uses servlet 2.5?
|
||||
RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
|
||||
if (!(requestAttr instanceof ServletRequestAttributes)) {
|
||||
throw new IllegalStateException("Current request is not a servlet request");
|
||||
}
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttr;
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
|
||||
WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
|
||||
return context.getBean(beanClass);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.springframework.platform.netflix.zuul;
|
||||
|
||||
import com.netflix.zuul.http.ZuulServlet;
|
||||
import org.springframework.platform.util.AbstractServletWrappingController;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* User: spencergibb
|
||||
* Date: 4/24/14
|
||||
* Time: 9:12 PM
|
||||
*/
|
||||
@Controller
|
||||
public class ZuulController extends AbstractServletWrappingController {
|
||||
|
||||
public ZuulController() {
|
||||
super(ZuulServlet.class, "zuulServlet");
|
||||
}
|
||||
|
||||
@RequestMapping("/**")
|
||||
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
return this.controller.handleRequest(request, response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.springframework.platform.netflix.zuul;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
|
||||
/**
|
||||
* User: spencergibb
|
||||
* Date: 5/2/14
|
||||
* Time: 9:20 PM
|
||||
*/
|
||||
@ConfigurationProperties("zuul")
|
||||
public class ZuulProperties {
|
||||
private String filterRoot;
|
||||
private long cacheRefresh = MINUTES.toMillis(5L);
|
||||
|
||||
public String getFilterRoot() {
|
||||
return filterRoot;
|
||||
}
|
||||
|
||||
public void setFilterRoot(String filterRoot) {
|
||||
this.filterRoot = filterRoot;
|
||||
}
|
||||
|
||||
public long getCacheRefresh() {
|
||||
return cacheRefresh;
|
||||
}
|
||||
|
||||
public void setCacheRefresh(long cacheRefresh) {
|
||||
this.cacheRefresh = cacheRefresh;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.springframework.platform.util;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.web.context.ServletContextAware;
|
||||
import org.springframework.web.servlet.mvc.ServletWrappingController;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
/**
|
||||
* User: spencergibb
|
||||
* Date: 5/1/14
|
||||
* Time: 10:03 AM
|
||||
*/
|
||||
public abstract class AbstractServletWrappingController implements InitializingBean,
|
||||
ApplicationContextAware, ServletContextAware {
|
||||
|
||||
protected final ServletWrappingController controller = new ServletWrappingController();
|
||||
|
||||
public AbstractServletWrappingController(Class<?> servletClass, String servletName) {
|
||||
controller.setServletClass(servletClass);
|
||||
controller.setServletName(servletName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.controller.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setServletContext(ServletContext servletContext) {
|
||||
this.controller.setServletContext(servletContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.controller.setApplicationContext(applicationContext);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#org.springframework.context.ApplicationListener=\
|
||||
#org.springframework.platform.netflix.eureka.EurekaStartingListener,\
|
||||
#org.springframework.platform.netflix.eureka.EurekaUpListener,\
|
||||
#org.springframework.platform.netflix.eureka.EurekaOutOfServiceListener
|
||||
|
||||
#org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
#org.springframework.platform.netflix.archaius.ArchaiusAutoConfiguration,\
|
||||
#org.springframework.platform.netflix.feign.FeignAutoConfiguration
|
||||
@@ -0,0 +1,48 @@
|
||||
info:
|
||||
component: Platform Router (Zuul)
|
||||
|
||||
endpoints:
|
||||
restart:
|
||||
enabled: true
|
||||
shutdown:
|
||||
enabled: true
|
||||
|
||||
server:
|
||||
port: 6080
|
||||
|
||||
management:
|
||||
port: 6081
|
||||
|
||||
logging:
|
||||
level: INFO
|
||||
|
||||
logging:
|
||||
level:
|
||||
com:
|
||||
netflix: WARN
|
||||
|
||||
|
||||
eureka:
|
||||
client:
|
||||
#Region where eureka is deployed -For AWS specify one of the AWS regions, for other datacenters specify a arbitrary string
|
||||
#indicating the region.This is normally specified as a -D option (eg) -Deureka.region=us-east-1
|
||||
region: default
|
||||
|
||||
|
||||
#For eureka clients running in eureka server, it needs to connect to servers in other zones
|
||||
preferSameZone: false
|
||||
|
||||
#Change this if you want to use a DNS based lookup for determining other eureka servers. For example
|
||||
#of specifying the DNS entries, check the eureka-client-test.properties, eureka-client-prod.properties
|
||||
#shouldUseDns: false
|
||||
|
||||
us-east-1:
|
||||
availabilityZones: default
|
||||
|
||||
serviceUrl:
|
||||
default: http://localhost:8080/v2/
|
||||
defaultZone: http://localhost:8080/v2/
|
||||
|
||||
instance:
|
||||
#Virtual host name by which the clients identifies this service
|
||||
virtualHostName: ${spring.application.name}
|
||||
@@ -0,0 +1,6 @@
|
||||
spring:
|
||||
application:
|
||||
name: zuul
|
||||
platform:
|
||||
config:
|
||||
uri: http://localhost:${config.port:8888}
|
||||
@@ -0,0 +1,170 @@
|
||||
package filters.post
|
||||
|
||||
import com.netflix.config.DynamicBooleanProperty
|
||||
|
||||
import com.netflix.config.DynamicIntProperty
|
||||
import com.netflix.config.DynamicPropertyFactory
|
||||
import com.netflix.util.Pair
|
||||
import com.netflix.zuul.ZuulFilter
|
||||
import com.netflix.zuul.constants.ZuulConstants
|
||||
import com.netflix.zuul.constants.ZuulHeaders
|
||||
import com.netflix.zuul.context.Debug
|
||||
import com.netflix.zuul.context.RequestContext
|
||||
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
import java.util.zip.GZIPInputStream
|
||||
|
||||
class SendResponseFilter extends ZuulFilter {
|
||||
|
||||
static DynamicBooleanProperty INCLUDE_DEBUG_HEADER =
|
||||
DynamicPropertyFactory.getInstance().getBooleanProperty(ZuulConstants.ZUUL_INCLUDE_DEBUG_HEADER, false);
|
||||
|
||||
static DynamicIntProperty INITIAL_STREAM_BUFFER_SIZE =
|
||||
DynamicPropertyFactory.getInstance().getIntProperty(ZuulConstants.ZUUL_INITIAL_STREAM_BUFFER_SIZE, 1024);
|
||||
|
||||
static DynamicBooleanProperty SET_CONTENT_LENGTH = DynamicPropertyFactory.getInstance().getBooleanProperty(ZuulConstants.ZUUL_SET_CONTENT_LENGTH, false);
|
||||
|
||||
@Override
|
||||
String filterType() {
|
||||
return 'post'
|
||||
}
|
||||
|
||||
@Override
|
||||
int filterOrder() {
|
||||
return 1000
|
||||
}
|
||||
|
||||
boolean shouldFilter() {
|
||||
return !RequestContext.currentContext.getZuulResponseHeaders().isEmpty() ||
|
||||
RequestContext.currentContext.getResponseDataStream() != null ||
|
||||
RequestContext.currentContext.responseBody != null
|
||||
}
|
||||
|
||||
Object run() {
|
||||
addResponseHeaders()
|
||||
writeResponse()
|
||||
}
|
||||
|
||||
void writeResponse() {
|
||||
RequestContext context = RequestContext.currentContext
|
||||
|
||||
// there is no body to send
|
||||
if (context.getResponseBody() == null && context.getResponseDataStream() == null) return;
|
||||
|
||||
HttpServletResponse servletResponse = context.getResponse()
|
||||
servletResponse.setCharacterEncoding("UTF-8")
|
||||
|
||||
OutputStream outStream = servletResponse.getOutputStream();
|
||||
InputStream is = null
|
||||
try {
|
||||
if (RequestContext.currentContext.responseBody != null) {
|
||||
String body = RequestContext.currentContext.responseBody
|
||||
writeResponse(new ByteArrayInputStream(body.bytes), outStream)
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isGzipRequested = false
|
||||
final String requestEncoding = context.getRequest().getHeader(ZuulHeaders.ACCEPT_ENCODING)
|
||||
if (requestEncoding != null && requestEncoding.equals("gzip"))
|
||||
isGzipRequested = true;
|
||||
|
||||
is = context.getResponseDataStream();
|
||||
InputStream inputStream = is
|
||||
if (is != null) {
|
||||
if (context.sendZuulResponse()) {
|
||||
// if origin response is gzipped, and client has not requested gzip, decompress stream
|
||||
// before sending to client
|
||||
// else, stream gzip directly to client
|
||||
if (context.getResponseGZipped() && !isGzipRequested)
|
||||
try {
|
||||
inputStream = new GZIPInputStream(is);
|
||||
|
||||
} catch (java.util.zip.ZipException e) {
|
||||
println("gzip expected but not received assuming unencoded response" + RequestContext.currentContext.getRequest().getRequestURL().toString())
|
||||
inputStream = is
|
||||
}
|
||||
else if (context.getResponseGZipped() && isGzipRequested)
|
||||
servletResponse.setHeader(ZuulHeaders.CONTENT_ENCODING, "gzip")
|
||||
writeResponse(inputStream, outStream)
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
try {
|
||||
is?.close();
|
||||
|
||||
outStream.flush()
|
||||
outStream.close()
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def writeResponse(InputStream zin, OutputStream out) {
|
||||
byte[] bytes = new byte[INITIAL_STREAM_BUFFER_SIZE.get()];
|
||||
int bytesRead = -1;
|
||||
while ((bytesRead = zin.read(bytes)) != -1) {
|
||||
// if (Debug.debugRequest() && !Debug.debugRequestHeadersOnly()) {
|
||||
// Debug.addRequestDebug("OUTBOUND: < " + new String(bytes, 0, bytesRead));
|
||||
// }
|
||||
|
||||
try {
|
||||
out.write(bytes, 0, bytesRead);
|
||||
out.flush();
|
||||
} catch (IOException e) {
|
||||
//ignore
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
// doubles buffer size if previous read filled it
|
||||
if (bytesRead == bytes.length) {
|
||||
bytes = new byte[bytes.length * 2]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addResponseHeaders() {
|
||||
RequestContext context = RequestContext.getCurrentContext();
|
||||
HttpServletResponse servletResponse = context.getResponse();
|
||||
List<Pair<String, String>> zuulResponseHeaders = context.getZuulResponseHeaders();
|
||||
String debugHeader = ""
|
||||
|
||||
List<String> rd
|
||||
|
||||
rd = (List<String>) RequestContext.getCurrentContext().get("routingDebug");
|
||||
rd?.each {
|
||||
debugHeader += "[[[${it}]]]";
|
||||
}
|
||||
|
||||
/*
|
||||
rd = (List<String>) RequestContext.getCurrentContext().get("requestDebug");
|
||||
rd?.each {
|
||||
debugHeader += "[[[REQUEST_DEBUG::${it}]]]";
|
||||
}
|
||||
*/
|
||||
|
||||
if (INCLUDE_DEBUG_HEADER.get()) servletResponse.addHeader("X-Zuul-Debug-Header", debugHeader)
|
||||
|
||||
if (Debug.debugRequest()) {
|
||||
zuulResponseHeaders?.each { Pair<String, String> it ->
|
||||
servletResponse.addHeader(it.first(), it.second())
|
||||
Debug.addRequestDebug("OUTBOUND: < " + it.first() + ":" + it.second())
|
||||
}
|
||||
} else {
|
||||
zuulResponseHeaders?.each { Pair<String, String> it ->
|
||||
servletResponse.addHeader(it.first(), it.second())
|
||||
}
|
||||
}
|
||||
|
||||
RequestContext ctx = RequestContext.getCurrentContext()
|
||||
Integer contentLength = ctx.getOriginContentLength()
|
||||
|
||||
// only inserts Content-Length if origin provides it and origin response is not gzipped
|
||||
if (SET_CONTENT_LENGTH.get()) {
|
||||
if (contentLength != null && !ctx.getResponseGZipped())
|
||||
servletResponse.setContentLength(contentLength)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
43
spring-platform-netflix-zuul/src/main/resources/filters/post/Stats.groovy
Executable file
43
spring-platform-netflix-zuul/src/main/resources/filters/post/Stats.groovy
Executable file
@@ -0,0 +1,43 @@
|
||||
package filters.post
|
||||
|
||||
import com.netflix.zuul.ZuulFilter
|
||||
|
||||
import com.netflix.zuul.context.RequestContext
|
||||
|
||||
class Stats extends ZuulFilter {
|
||||
@Override
|
||||
String filterType() {
|
||||
return "post"
|
||||
}
|
||||
|
||||
@Override
|
||||
int filterOrder() {
|
||||
return 2000
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean shouldFilter() {
|
||||
return true
|
||||
}
|
||||
|
||||
@Override
|
||||
Object run() {
|
||||
dumpRoutingDebug()
|
||||
dumpRequestDebug()
|
||||
}
|
||||
|
||||
public void dumpRequestDebug() {
|
||||
List<String> rd = (List<String>) RequestContext.getCurrentContext().get("requestDebug");
|
||||
rd?.each {
|
||||
println("REQUEST_DEBUG::${it}");
|
||||
}
|
||||
}
|
||||
|
||||
public void dumpRoutingDebug() {
|
||||
List<String> rd = (List<String>) RequestContext.getCurrentContext().get("routingDebug");
|
||||
rd?.each {
|
||||
println("ZUUL_DEBUG::${it}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package filters.pre
|
||||
|
||||
import com.netflix.config.DynamicBooleanProperty
|
||||
import com.netflix.config.DynamicPropertyFactory
|
||||
import com.netflix.config.DynamicStringProperty
|
||||
import com.netflix.zuul.ZuulFilter
|
||||
import com.netflix.zuul.constants.ZuulConstants
|
||||
import com.netflix.zuul.context.RequestContext
|
||||
|
||||
class DebugFilter extends ZuulFilter {
|
||||
|
||||
static final DynamicBooleanProperty routingDebug = DynamicPropertyFactory.getInstance().getBooleanProperty(ZuulConstants.ZUUL_DEBUG_REQUEST, true)
|
||||
static final DynamicStringProperty debugParameter = DynamicPropertyFactory.getInstance().getStringProperty(ZuulConstants.ZUUL_DEBUG_PARAMETER, "d")
|
||||
|
||||
@Override
|
||||
String filterType() {
|
||||
return 'pre'
|
||||
}
|
||||
|
||||
@Override
|
||||
int filterOrder() {
|
||||
return 1
|
||||
}
|
||||
|
||||
boolean shouldFilter() {
|
||||
if ("true".equals(RequestContext.getCurrentContext().getRequest().getParameter(debugParameter.get()))) return true;
|
||||
return routingDebug.get();
|
||||
|
||||
}
|
||||
|
||||
Object run() {
|
||||
RequestContext ctx = RequestContext.getCurrentContext()
|
||||
ctx.setDebugRouting(true)
|
||||
ctx.setDebugRequest(true)
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package filters.pre
|
||||
|
||||
import com.netflix.zuul.ZuulFilter
|
||||
|
||||
import com.netflix.zuul.context.Debug
|
||||
import com.netflix.zuul.context.RequestContext
|
||||
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
class DebugRequest extends ZuulFilter {
|
||||
@Override
|
||||
String filterType() {
|
||||
return 'pre'
|
||||
}
|
||||
|
||||
@Override
|
||||
int filterOrder() {
|
||||
return 10000
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean shouldFilter() {
|
||||
return Debug.debugRequest()
|
||||
}
|
||||
|
||||
@Override
|
||||
Object run() {
|
||||
HttpServletRequest req = RequestContext.currentContext.request as HttpServletRequest
|
||||
|
||||
Debug.addRequestDebug("REQUEST:: " + req.getScheme() + " " + req.getRemoteAddr() + ":" + req.getRemotePort())
|
||||
|
||||
Debug.addRequestDebug("REQUEST:: > " + req.getMethod() + " " + req.getRequestURI() + " " + req.getProtocol())
|
||||
|
||||
Iterator headerIt = req.getHeaderNames().iterator()
|
||||
while (headerIt.hasNext()) {
|
||||
String name = (String) headerIt.next()
|
||||
String value = req.getHeader(name)
|
||||
Debug.addRequestDebug("REQUEST:: > " + name + ":" + value)
|
||||
|
||||
}
|
||||
|
||||
final RequestContext ctx = RequestContext.getCurrentContext()
|
||||
if (!ctx.isChunkedRequestBody()) {
|
||||
InputStream inp = ctx.request.getInputStream()
|
||||
String body = null
|
||||
if (inp != null) {
|
||||
body = inp.getText()
|
||||
Debug.addRequestDebug("REQUEST:: > " + body)
|
||||
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package filters.pre
|
||||
|
||||
import com.netflix.zuul.context.RequestContext
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.platform.netflix.zuul.Routes
|
||||
import org.springframework.platform.netflix.zuul.SpringFilter
|
||||
|
||||
class PreDecorationFilter extends SpringFilter {
|
||||
private static Logger LOG = LoggerFactory.getLogger(PreDecorationFilter.class);
|
||||
|
||||
@Override
|
||||
int filterOrder() {
|
||||
return 5
|
||||
}
|
||||
|
||||
@Override
|
||||
String filterType() {
|
||||
return "pre"
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean shouldFilter() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
Object run() {
|
||||
RequestContext ctx = RequestContext.getCurrentContext()
|
||||
|
||||
def requestURI = ctx.getRequest().getRequestURI()
|
||||
|
||||
Routes routes = getBean(Routes.class)
|
||||
|
||||
def routesMap = routes.getRoutes()
|
||||
def route = routesMap.keySet().find { path ->
|
||||
//TODO: use ant matchers?
|
||||
if (requestURI.startsWith(path)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
def serviceId = routesMap.get(route)
|
||||
|
||||
if (serviceId != null) {
|
||||
// set serviceId for use in filters.route.RibbonRequest
|
||||
ctx.set("serviceId", serviceId)
|
||||
ctx.setRouteHost(null)
|
||||
ctx.addOriginResponseHeader("X-Zuul-ServiceId", serviceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
package filters.route
|
||||
|
||||
import com.netflix.client.ClientException
|
||||
import com.netflix.client.ClientFactory
|
||||
import com.netflix.client.IClient
|
||||
import com.netflix.client.http.HttpRequest
|
||||
import com.netflix.client.http.HttpResponse
|
||||
import com.netflix.hystrix.exception.HystrixRuntimeException
|
||||
import com.netflix.niws.client.http.RestClient
|
||||
import com.netflix.zuul.ZuulFilter
|
||||
import com.netflix.zuul.context.Debug
|
||||
import com.netflix.zuul.context.RequestContext
|
||||
import com.netflix.zuul.exception.ZuulException
|
||||
import com.netflix.zuul.util.HTTPRequestUtils
|
||||
import com.sun.jersey.core.util.MultivaluedMapImpl
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.platform.netflix.zuul.RibbonCommand
|
||||
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.ws.rs.core.MultivaluedMap
|
||||
import java.util.zip.GZIPInputStream
|
||||
|
||||
import static HttpRequest.Verb
|
||||
import static org.springframework.platform.netflix.feign.FeignConfigurer.setServiceListClass
|
||||
|
||||
class RibbonRequest extends ZuulFilter {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RibbonRequest.class);
|
||||
|
||||
public static final String CONTENT_ENCODING = "Content-Encoding";
|
||||
|
||||
@Override
|
||||
String filterType() {
|
||||
return 'route'
|
||||
}
|
||||
|
||||
@Override
|
||||
int filterOrder() {
|
||||
return 10
|
||||
}
|
||||
|
||||
boolean shouldFilter() {
|
||||
def ctx = RequestContext.currentContext
|
||||
return (ctx.getRouteHost() == null && ctx.get("serviceId") != null && ctx.sendZuulResponse())
|
||||
}
|
||||
|
||||
Object run() {
|
||||
RequestContext context = RequestContext.currentContext
|
||||
HttpServletRequest request = context.getRequest();
|
||||
|
||||
MultivaluedMap<String, String> headers = buildZuulRequestHeaders(request)
|
||||
MultivaluedMap<String, String> params = buildZuulRequestQueryParams(request)
|
||||
Verb verb = getVerb(request);
|
||||
Object requestEntity = getRequestBody(request)
|
||||
|
||||
def serviceId = context.get("serviceId")
|
||||
|
||||
//TODO: can this be set be default? or an implementation of an interface?
|
||||
setServiceListClass(serviceId)
|
||||
|
||||
IClient restClient = ClientFactory.getNamedClient(serviceId);
|
||||
|
||||
String uri = request.getRequestURI()
|
||||
if (context.requestURI != null) {
|
||||
uri = context.requestURI
|
||||
}
|
||||
//remove double slashes
|
||||
uri = uri.replace("//", "/")
|
||||
|
||||
HttpResponse response = forward(restClient, verb, uri, headers, params, requestEntity)
|
||||
setResponse(response)
|
||||
return response
|
||||
}
|
||||
|
||||
|
||||
|
||||
void debug(RestClient restClient, Verb verb, uri, MultivaluedMap<String, String> headers, MultivaluedMap<String, String> params, InputStream requestEntity) {
|
||||
|
||||
if (Debug.debugRequest()) {
|
||||
|
||||
headers.each {
|
||||
Debug.addRequestDebug("ZUUL:: > ${it.key} ${it.value[0]}")
|
||||
}
|
||||
String query = ""
|
||||
params.each {
|
||||
it.value.each { v ->
|
||||
query += it.key + "=" + v + "&"
|
||||
}
|
||||
}
|
||||
|
||||
Debug.addRequestDebug("ZUUL:: > ${verb.verb()} ${uri}?${query} HTTP/1.1")
|
||||
RequestContext ctx = RequestContext.getCurrentContext()
|
||||
if (!ctx.isChunkedRequestBody()) {
|
||||
if (requestEntity != null) {
|
||||
debugRequestEntity(ctx.request.getInputStream())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void debugRequestEntity(InputStream inputStream) {
|
||||
if (!Debug.debugRequestHeadersOnly()) {
|
||||
String entity = inputStream.getText()
|
||||
Debug.addRequestDebug("ZUUL:: > ${entity}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
def HttpResponse forward(RestClient restClient, Verb verb, uri, MultivaluedMap<String, String> headers,
|
||||
MultivaluedMap<String, String> params, InputStream requestEntity) {
|
||||
debug(restClient, verb, uri, headers, params, requestEntity)
|
||||
|
||||
RibbonCommand command = new RibbonCommand(restClient, verb, uri, headers, params, requestEntity);
|
||||
try {
|
||||
HttpResponse response = command.execute();
|
||||
return response
|
||||
} catch (HystrixRuntimeException e) {
|
||||
if (e?.fallbackException?.cause instanceof ClientException) {
|
||||
ClientException ex = e.fallbackException.cause as ClientException
|
||||
throw new ZuulException(ex, "Forwarding error", 500, ex.getErrorType().toString())
|
||||
}
|
||||
throw new ZuulException(e, "Forwarding error", 500, e.failureType.toString())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
def getRequestBody(HttpServletRequest request) {
|
||||
Object requestEntity = null;
|
||||
try {
|
||||
requestEntity = RequestContext.currentContext.requestEntity
|
||||
if (requestEntity == null) {
|
||||
requestEntity = request.getInputStream();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
|
||||
return requestEntity
|
||||
}
|
||||
|
||||
|
||||
|
||||
def MultivaluedMap<String, String> buildZuulRequestQueryParams(HttpServletRequest request) {
|
||||
|
||||
Map<String, List<String>> map = HTTPRequestUtils.getInstance().getQueryParams()
|
||||
|
||||
MultivaluedMap<String, String> params = new MultivaluedMapImpl<String, String>();
|
||||
if (map == null) return params;
|
||||
map.entrySet().each {
|
||||
it.value.each { v ->
|
||||
params.add(it.key, v)
|
||||
}
|
||||
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
|
||||
def MultivaluedMap<String, String> buildZuulRequestHeaders(HttpServletRequest request) {
|
||||
|
||||
RequestContext context = RequestContext.currentContext
|
||||
|
||||
MultivaluedMap<String, String> headers = new MultivaluedMapImpl<String, String>();
|
||||
Enumeration headerNames = request.getHeaderNames();
|
||||
while (headerNames?.hasMoreElements()) {
|
||||
String name = (String) headerNames.nextElement();
|
||||
String value = request.getHeader(name);
|
||||
if (!name.toLowerCase().contains("content-length")) headers.putSingle(name, value);
|
||||
}
|
||||
Map zuulRequestHeaders = context.getZuulRequestHeaders();
|
||||
|
||||
zuulRequestHeaders.keySet().each {
|
||||
headers.putSingle((String) it, (String) zuulRequestHeaders[it])
|
||||
}
|
||||
|
||||
headers.putSingle("accept-encoding", "deflate, gzip")
|
||||
|
||||
if (headers.containsKey("transfer-encoding"))
|
||||
headers.remove("transfer-encoding")
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
|
||||
|
||||
Verb getVerb(HttpServletRequest request) {
|
||||
String sMethod = request.getMethod();
|
||||
return getVerb(sMethod);
|
||||
}
|
||||
|
||||
Verb getVerb(String sMethod) {
|
||||
if (sMethod == null) return Verb.GET;
|
||||
sMethod = sMethod.toLowerCase();
|
||||
if (sMethod.equals("post")) return Verb.POST;
|
||||
if (sMethod.equals("put")) return Verb.PUT;
|
||||
if (sMethod.equals("delete")) return Verb.DELETE;
|
||||
if (sMethod.equals("options")) return Verb.OPTIONS;
|
||||
if (sMethod.equals("head")) return Verb.HEAD;
|
||||
return Verb.GET;
|
||||
}
|
||||
|
||||
void setResponse(HttpResponse resp) {
|
||||
RequestContext context = RequestContext.getCurrentContext()
|
||||
|
||||
context.setResponseStatusCode(resp.getStatus());
|
||||
if (resp.hasEntity()) {
|
||||
context.responseDataStream = resp.inputStream;
|
||||
}
|
||||
|
||||
String contentEncoding = resp.getHeaders().get(CONTENT_ENCODING)?.first();
|
||||
|
||||
if (contentEncoding != null && HTTPRequestUtils.getInstance().isGzipped(contentEncoding)) {
|
||||
context.setResponseGZipped(true);
|
||||
} else {
|
||||
context.setResponseGZipped(false);
|
||||
}
|
||||
|
||||
if (Debug.debugRequest()) {
|
||||
resp.getHeaders().keySet().each { key ->
|
||||
boolean isValidHeader = isValidHeader(key)
|
||||
|
||||
Collection<String> list = resp.getHeaders().get(key)
|
||||
list.each { header ->
|
||||
context.addOriginResponseHeader(key, header)
|
||||
|
||||
if (key.equalsIgnoreCase("content-length"))
|
||||
context.setOriginContentLength(header);
|
||||
|
||||
if (isValidHeader) {
|
||||
context.addZuulResponseHeader(key, header);
|
||||
Debug.addRequestDebug("ORIGIN_RESPONSE:: < ${key} ${header}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (context.responseDataStream) {
|
||||
byte[] origBytes = context.getResponseDataStream().bytes
|
||||
InputStream inStream = new ByteArrayInputStream(origBytes);
|
||||
if (context.getResponseGZipped())
|
||||
inStream = new GZIPInputStream(inStream);
|
||||
String responseEntity = inStream.getText()
|
||||
Debug.addRequestDebug("ORIGIN_RESPONSE:: < ${responseEntity}")
|
||||
context.setResponseDataStream(new ByteArrayInputStream(origBytes))
|
||||
}
|
||||
|
||||
} else {
|
||||
resp.getHeaders().keySet().each { key ->
|
||||
boolean isValidHeader = isValidHeader(key)
|
||||
Collection<java.lang.String> list = resp.getHeaders().get(key)
|
||||
list.each { header ->
|
||||
context.addOriginResponseHeader(key, header)
|
||||
|
||||
if (key.equalsIgnoreCase("content-length"))
|
||||
context.setOriginContentLength(header);
|
||||
|
||||
if (isValidHeader) {
|
||||
context.addZuulResponseHeader(key, header);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
boolean isValidHeader(String headerName) {
|
||||
switch (headerName.toLowerCase()) {
|
||||
case "connection":
|
||||
case "content-length":
|
||||
case "content-encoding":
|
||||
case "server":
|
||||
case "transfer-encoding":
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,434 @@
|
||||
package filters.route
|
||||
|
||||
import com.netflix.config.DynamicIntProperty
|
||||
import com.netflix.config.DynamicPropertyFactory
|
||||
import com.netflix.zuul.ZuulFilter
|
||||
import com.netflix.zuul.constants.ZuulConstants
|
||||
import com.netflix.zuul.context.Debug
|
||||
import com.netflix.zuul.context.RequestContext
|
||||
import com.netflix.zuul.util.HTTPRequestUtils
|
||||
import org.apache.http.Header
|
||||
import org.apache.http.HttpHost
|
||||
import org.apache.http.HttpRequest
|
||||
import org.apache.http.HttpResponse
|
||||
import org.apache.http.client.HttpClient
|
||||
import org.apache.http.client.methods.HttpPost
|
||||
import org.apache.http.client.methods.HttpPut
|
||||
import org.apache.http.client.params.ClientPNames
|
||||
import org.apache.http.conn.ClientConnectionManager
|
||||
import org.apache.http.conn.scheme.PlainSocketFactory
|
||||
import org.apache.http.conn.scheme.Scheme
|
||||
import org.apache.http.conn.scheme.SchemeRegistry
|
||||
import org.apache.http.conn.ssl.SSLSocketFactory
|
||||
import org.apache.http.entity.InputStreamEntity
|
||||
import org.apache.http.impl.client.DefaultHttpClient
|
||||
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler
|
||||
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager
|
||||
import org.apache.http.message.BasicHeader
|
||||
import org.apache.http.message.BasicHttpRequest
|
||||
import org.apache.http.params.CoreConnectionPNames
|
||||
import org.apache.http.params.HttpParams
|
||||
import org.apache.http.protocol.HttpContext
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import javax.net.ssl.SSLContext
|
||||
import javax.net.ssl.TrustManager
|
||||
import javax.net.ssl.X509TrustManager
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import java.security.*
|
||||
import java.security.cert.CertificateException
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import java.util.zip.GZIPInputStream
|
||||
|
||||
class SimpleHostRoutingFilter extends ZuulFilter {
|
||||
|
||||
public static final String CONTENT_ENCODING = "Content-Encoding";
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SimpleHostRoutingFilter.class);
|
||||
private static final Runnable CLIENTLOADER = new Runnable() {
|
||||
@Override
|
||||
void run() {
|
||||
loadClient();
|
||||
}
|
||||
}
|
||||
|
||||
private static final DynamicIntProperty SOCKET_TIMEOUT = DynamicPropertyFactory.getInstance().getIntProperty(ZuulConstants.ZUUL_HOST_SOCKET_TIMEOUT_MILLIS, 10000)
|
||||
private static final DynamicIntProperty CONNECTION_TIMEOUT = DynamicPropertyFactory.getInstance().getIntProperty(ZuulConstants.ZUUL_HOST_CONNECT_TIMEOUT_MILLIS, 2000)
|
||||
|
||||
private static final AtomicReference<HttpClient> CLIENT = new AtomicReference<HttpClient>(newClient());
|
||||
|
||||
private static final Timer CONNECTION_MANAGER_TIMER = new Timer(true);
|
||||
|
||||
// cleans expired connections at an interval
|
||||
static {
|
||||
SOCKET_TIMEOUT.addCallback(CLIENTLOADER)
|
||||
CONNECTION_TIMEOUT.addCallback(CLIENTLOADER)
|
||||
CONNECTION_MANAGER_TIMER.schedule(new TimerTask() {
|
||||
@Override
|
||||
void run() {
|
||||
try {
|
||||
final HttpClient hc = CLIENT.get();
|
||||
if (hc == null) return;
|
||||
hc.getConnectionManager().closeExpiredConnections();
|
||||
} catch (Throwable t) {
|
||||
LOG.error("error closing expired connections", t);
|
||||
}
|
||||
}
|
||||
}, 30000, 5000)
|
||||
}
|
||||
|
||||
public SimpleHostRoutingFilter() {}
|
||||
|
||||
private static final ClientConnectionManager newConnectionManager() {
|
||||
|
||||
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
trustStore.load(null, null);
|
||||
|
||||
SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
|
||||
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
|
||||
|
||||
SchemeRegistry registry = new SchemeRegistry();
|
||||
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
|
||||
registry.register(new Scheme("https", sf, 443));
|
||||
registry.register(new Scheme("https", sf, 8443));
|
||||
|
||||
ClientConnectionManager cm = new ThreadSafeClientConnManager(registry);
|
||||
cm.setMaxTotal(Integer.parseInt(System.getProperty("zuul.max.host.connections", "200")));
|
||||
cm.setDefaultMaxPerRoute(Integer.parseInt(System.getProperty("zuul.max.host.connections", "20")));
|
||||
return cm;
|
||||
}
|
||||
|
||||
@Override
|
||||
String filterType() {
|
||||
return 'route'
|
||||
}
|
||||
|
||||
@Override
|
||||
int filterOrder() {
|
||||
return 100
|
||||
}
|
||||
|
||||
boolean shouldFilter() {
|
||||
return RequestContext.currentContext.getRouteHost() != null && RequestContext.currentContext.sendZuulResponse()
|
||||
}
|
||||
|
||||
private static final void loadClient() {
|
||||
final HttpClient oldClient = CLIENT.get();
|
||||
CLIENT.set(newClient())
|
||||
if (oldClient != null) {
|
||||
CONNECTION_MANAGER_TIMER.schedule(new TimerTask() {
|
||||
@Override
|
||||
void run() {
|
||||
try {
|
||||
oldClient.getConnectionManager().shutdown();
|
||||
} catch (Throwable t) {
|
||||
LOG.error("error shutting down old connection manager", t);
|
||||
}
|
||||
}
|
||||
}, 30000);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final HttpClient newClient() {
|
||||
// I could statically cache the connection manager but we will probably want to make some of its properties
|
||||
// dynamic in the near future also
|
||||
HttpClient httpclient = new DefaultHttpClient(newConnectionManager());
|
||||
HttpParams httpParams = httpclient.getParams();
|
||||
httpParams.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, SOCKET_TIMEOUT.get())
|
||||
httpParams.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, CONNECTION_TIMEOUT.get())
|
||||
httpclient.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(0, false))
|
||||
httpParams.setParameter(ClientPNames.COOKIE_POLICY, org.apache.http.client.params.CookiePolicy.IGNORE_COOKIES);
|
||||
httpclient.setRedirectStrategy(new org.apache.http.client.RedirectStrategy() {
|
||||
@Override
|
||||
boolean isRedirected(HttpRequest httpRequest, HttpResponse httpResponse, HttpContext httpContext) {
|
||||
return false
|
||||
}
|
||||
|
||||
@Override
|
||||
org.apache.http.client.methods.HttpUriRequest getRedirect(HttpRequest httpRequest, HttpResponse httpResponse, HttpContext httpContext) {
|
||||
return null
|
||||
}
|
||||
})
|
||||
return httpclient
|
||||
}
|
||||
|
||||
Object run() {
|
||||
HttpServletRequest request = RequestContext.currentContext.getRequest();
|
||||
Header[] headers = buildZuulRequestHeaders(request)
|
||||
String verb = getVerb(request);
|
||||
InputStream requestEntity = getRequestBody(request)
|
||||
HttpClient httpclient = CLIENT.get()
|
||||
|
||||
String uri = request.getRequestURI()
|
||||
if (RequestContext.currentContext.requestURI != null) {
|
||||
uri = RequestContext.currentContext.requestURI
|
||||
}
|
||||
|
||||
try {
|
||||
HttpResponse response = forward(httpclient, verb, uri, request, headers, requestEntity)
|
||||
setResponse(response)
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (Debug.debugRequest()) {
|
||||
Debug.addRequestDebug("ZUUL:: ERROR " + e.getMessage())
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
def InputStream debug(HttpClient httpclient, String verb, String uri, HttpServletRequest request, Header[] headers, InputStream requestEntity) {
|
||||
|
||||
if (Debug.debugRequest()) {
|
||||
|
||||
Debug.addRequestDebug("ZUUL:: host=${RequestContext.currentContext.getRouteHost()}")
|
||||
|
||||
headers.each {
|
||||
Debug.addRequestDebug("ZUUL::> ${it.name} ${it.value}")
|
||||
}
|
||||
String query = request.queryString
|
||||
|
||||
Debug.addRequestDebug("ZUUL:: > ${verb} ${uri}?${query} HTTP/1.1")
|
||||
if (requestEntity != null) {
|
||||
requestEntity = debugRequestEntity(requestEntity)
|
||||
}
|
||||
|
||||
}
|
||||
return requestEntity
|
||||
}
|
||||
|
||||
InputStream debugRequestEntity(InputStream inputStream) {
|
||||
if (Debug.debugRequestHeadersOnly()) return inputStream
|
||||
if (inputStream == null) return null
|
||||
String entity = inputStream.getText()
|
||||
Debug.addRequestDebug("ZUUL::> ${entity}")
|
||||
return new ByteArrayInputStream(entity.bytes)
|
||||
}
|
||||
|
||||
def HttpResponse forward(HttpClient httpclient, String verb, String uri, HttpServletRequest request, Header[] headers, InputStream requestEntity) {
|
||||
|
||||
requestEntity = debug(httpclient, verb, uri, request, headers, requestEntity)
|
||||
|
||||
org.apache.http.HttpHost httpHost
|
||||
|
||||
httpHost = getHttpHost()
|
||||
|
||||
org.apache.http.HttpRequest httpRequest;
|
||||
|
||||
switch (verb) {
|
||||
case 'POST':
|
||||
httpRequest = new HttpPost(uri + getQueryString())
|
||||
InputStreamEntity entity = new InputStreamEntity(requestEntity, request.getContentLength())
|
||||
httpRequest.setEntity(entity)
|
||||
break
|
||||
case 'PUT':
|
||||
httpRequest = new HttpPut(uri + getQueryString())
|
||||
InputStreamEntity entity = new InputStreamEntity(requestEntity, request.getContentLength())
|
||||
httpRequest.setEntity(entity)
|
||||
break;
|
||||
default:
|
||||
httpRequest = new BasicHttpRequest(verb, uri + getQueryString())
|
||||
LOG.debug(uri + getQueryString())
|
||||
}
|
||||
|
||||
try {
|
||||
httpRequest.setHeaders(headers)
|
||||
LOG.debug(httpHost.getHostName() + " " + httpHost.getPort() + " " + httpHost.getSchemeName())
|
||||
HttpResponse zuulResponse = forwardRequest(httpclient, httpHost, httpRequest)
|
||||
return zuulResponse
|
||||
} finally {
|
||||
// When HttpClient instance is no longer needed,
|
||||
// shut down the connection manager to ensure
|
||||
// immediate deallocation of all system resources
|
||||
// httpclient.getConnectionManager().shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
HttpResponse forwardRequest(HttpClient httpclient, HttpHost httpHost, HttpRequest httpRequest) {
|
||||
return httpclient.execute(httpHost, httpRequest);
|
||||
}
|
||||
|
||||
String getQueryString() {
|
||||
HttpServletRequest request = RequestContext.currentContext.getRequest();
|
||||
String query = request.getQueryString()
|
||||
return (query != null) ? "?${query}" : "";
|
||||
}
|
||||
|
||||
HttpHost getHttpHost() {
|
||||
HttpHost httpHost
|
||||
URL host = RequestContext.currentContext.getRouteHost()
|
||||
|
||||
httpHost = new HttpHost(host.getHost(), host.getPort(), host.getProtocol())
|
||||
|
||||
return httpHost
|
||||
}
|
||||
|
||||
|
||||
def getRequestBody(HttpServletRequest request) {
|
||||
Object requestEntity = null;
|
||||
try {
|
||||
requestEntity = request.getInputStream();
|
||||
} catch (IOException e) {
|
||||
//no requestBody is ok.
|
||||
}
|
||||
return requestEntity
|
||||
}
|
||||
|
||||
boolean isValidHeader(String name) {
|
||||
if (name.toLowerCase().contains("content-length")) return false;
|
||||
if (!RequestContext.currentContext.responseGZipped) {
|
||||
if (name.toLowerCase().contains("accept-encoding")) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
def Header[] buildZuulRequestHeaders(HttpServletRequest request) {
|
||||
|
||||
def headers = new ArrayList()
|
||||
Enumeration headerNames = request.getHeaderNames();
|
||||
while (headerNames.hasMoreElements()) {
|
||||
String name = (String) headerNames.nextElement();
|
||||
String value = request.getHeader(name);
|
||||
if (isValidHeader(name)) headers.add(new BasicHeader(name, value))
|
||||
}
|
||||
|
||||
Map zuulRequestHeaders = RequestContext.getCurrentContext().getZuulRequestHeaders();
|
||||
|
||||
zuulRequestHeaders.keySet().each {
|
||||
String name = it.toLowerCase()
|
||||
BasicHeader h = headers.find { BasicHeader he -> he.name == name }
|
||||
if (h != null) {
|
||||
headers.remove(h)
|
||||
}
|
||||
headers.add(new BasicHeader((String) it, (String) zuulRequestHeaders[it]))
|
||||
}
|
||||
|
||||
if (RequestContext.currentContext.responseGZipped) {
|
||||
headers.add(new BasicHeader("accept-encoding", "deflate, gzip"))
|
||||
}
|
||||
return headers
|
||||
}
|
||||
|
||||
|
||||
|
||||
String getVerb(HttpServletRequest request) {
|
||||
String sMethod = request.getMethod();
|
||||
return sMethod.toUpperCase();
|
||||
}
|
||||
|
||||
String getVerb(String sMethod) {
|
||||
if (sMethod == null) return "GET";
|
||||
sMethod = sMethod.toLowerCase();
|
||||
if (sMethod.equalsIgnoreCase("post")) return "POST"
|
||||
if (sMethod.equalsIgnoreCase("put")) return "PUT"
|
||||
if (sMethod.equalsIgnoreCase("delete")) return "DELETE"
|
||||
if (sMethod.equalsIgnoreCase("options")) return "OPTIONS"
|
||||
if (sMethod.equalsIgnoreCase("head")) return "HEAD"
|
||||
return "GET"
|
||||
}
|
||||
|
||||
void setResponse(HttpResponse response) {
|
||||
RequestContext context = RequestContext.getCurrentContext()
|
||||
|
||||
RequestContext.currentContext.set("hostZuulResponse", response)
|
||||
RequestContext.getCurrentContext().setResponseStatusCode(response.getStatusLine().statusCode)
|
||||
RequestContext.getCurrentContext().responseDataStream = response?.entity?.content
|
||||
|
||||
boolean isOriginResponseGzipped = false
|
||||
|
||||
for (Header h : response.getHeaders(CONTENT_ENCODING)) {
|
||||
if (HTTPRequestUtils.getInstance().isGzipped(h.value)) {
|
||||
isOriginResponseGzipped = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
context.setResponseGZipped(isOriginResponseGzipped);
|
||||
|
||||
|
||||
if (Debug.debugRequest()) {
|
||||
response.getAllHeaders()?.each { Header header ->
|
||||
if (isValidHeader(header)) {
|
||||
RequestContext.getCurrentContext().addZuulResponseHeader(header.name, header.value);
|
||||
Debug.addRequestDebug("ORIGIN_RESPONSE:: < ${header.name}, ${header.value}")
|
||||
}
|
||||
}
|
||||
|
||||
if (context.responseDataStream) {
|
||||
byte[] origBytes = context.getResponseDataStream().bytes
|
||||
ByteArrayInputStream byteStream = new ByteArrayInputStream(origBytes)
|
||||
InputStream inputStream = byteStream
|
||||
if (RequestContext.currentContext.responseGZipped) {
|
||||
inputStream = new GZIPInputStream(byteStream);
|
||||
}
|
||||
|
||||
|
||||
context.setResponseDataStream(new ByteArrayInputStream(origBytes))
|
||||
}
|
||||
|
||||
} else {
|
||||
response.getAllHeaders()?.each { Header header ->
|
||||
RequestContext ctx = RequestContext.getCurrentContext()
|
||||
ctx.addOriginResponseHeader(header.name, header.value)
|
||||
|
||||
if (header.name.equalsIgnoreCase("content-length"))
|
||||
ctx.setOriginContentLength(header.value)
|
||||
|
||||
if (isValidHeader(header)) {
|
||||
ctx.addZuulResponseHeader(header.name, header.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
boolean isValidHeader(Header header) {
|
||||
switch (header.name.toLowerCase()) {
|
||||
case "connection":
|
||||
case "content-length":
|
||||
case "content-encoding":
|
||||
case "server":
|
||||
case "transfer-encoding":
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
public static class MySSLSocketFactory extends SSLSocketFactory {
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
|
||||
public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
|
||||
super(truststore);
|
||||
|
||||
TrustManager tm = new X509TrustManager() {
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
TrustManager[] tms = new TrustManager[1];
|
||||
tms[0] = tm;
|
||||
sslContext.init(null, tms, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
|
||||
return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket() throws IOException {
|
||||
return sslContext.getSocketFactory().createSocket();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.platform.netflix.eureka.EurekaClientAutoConfiguration
|
||||
Reference in New Issue
Block a user