Updated from netflix

This commit is contained in:
Ryan Baxter
2018-02-20 15:34:38 -05:00
parent 26d8e6450b
commit 30d9245821
9 changed files with 273 additions and 9 deletions

View File

@@ -161,6 +161,9 @@ feign:
- com.example.FooRequestInterceptor
- com.example.BarRequestInterceptor
decode404: false
encoder: com.example.SimpleEncoder
decoder: com.example.SimpleDecoder
contract: com.example.SimpleContract
----
Default configurations can be specified in the `@EnableFeignClients` attribute `defaultConfiguration` in a similar manner as described above. The difference is that this configuration will apply to _all_ feign clients.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -179,6 +179,18 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
builder.decode404();
}
}
if (Objects.nonNull(config.getEncoder())) {
builder.encoder(getOrInstantiate(config.getEncoder()));
}
if (Objects.nonNull(config.getDecoder())) {
builder.decoder(getOrInstantiate(config.getDecoder()));
}
if (Objects.nonNull(config.getContract())) {
builder.contract(getOrInstantiate(config.getContract()));
}
}
private <T> T getOrInstantiate(Class<T> tClass) {

View File

@@ -15,9 +15,12 @@
*/
package org.springframework.cloud.openfeign;
import feign.Contract;
import feign.Logger;
import feign.RequestInterceptor;
import feign.Retryer;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.codec.ErrorDecoder;
import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -93,6 +96,12 @@ public class FeignClientProperties {
private Boolean decode404;
private Class<Decoder> decoder;
private Class<Encoder> encoder;
private Class<Contract> contract;
public Logger.Level getLoggerLevel() {
return loggerLevel;
}
@@ -149,6 +158,30 @@ public class FeignClientProperties {
this.decode404 = decode404;
}
public Class<Decoder> getDecoder() {
return decoder;
}
public void setDecoder(Class<Decoder> decoder) {
this.decoder = decoder;
}
public Class<Encoder> getEncoder() {
return encoder;
}
public void setEncoder(Class<Encoder> encoder) {
this.encoder = encoder;
}
public Class<Contract> getContract() {
return contract;
}
public void setContract(Class<Contract> contract) {
this.contract = contract;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -160,13 +193,16 @@ public class FeignClientProperties {
Objects.equals(retryer, that.retryer) &&
Objects.equals(errorDecoder, that.errorDecoder) &&
Objects.equals(requestInterceptors, that.requestInterceptors) &&
Objects.equals(decode404, that.decode404);
Objects.equals(decode404, that.decode404) &&
Objects.equals(encoder, that.encoder) &&
Objects.equals(decoder, that.decoder) &&
Objects.equals(contract, that.contract);
}
@Override
public int hashCode() {
return Objects.hash(loggerLevel, connectTimeout, readTimeout, retryer,
errorDecoder, requestInterceptors, decode404);
errorDecoder, requestInterceptors, decode404, encoder, decoder, contract);
}
}

View File

@@ -65,7 +65,7 @@ class HttpClientFeignLoadBalancedConfiguration {
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
FeignHttpClientProperties httpClientProperties) {
final HttpClientConnectionManager connectionManager = connectionManagerFactory
.newConnectionManager(false, httpClientProperties.getMaxConnections(),
.newConnectionManager(httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(),
httpClientProperties.getMaxConnectionsPerRoute(),
httpClientProperties.getTimeToLive(),
httpClientProperties.getTimeToLiveUnit(), registryBuilder);

View File

@@ -61,7 +61,7 @@ class OkHttpFeignLoadBalancedConfiguration {
ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
Boolean followRedirects = httpClientProperties.isFollowRedirects();
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
this.okHttpClient = httpClientFactory.createBuilder(false).
this.okHttpClient = httpClientFactory.createBuilder(httpClientProperties.isDisableSslValidation()).
connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).
followRedirects(followRedirects).
connectionPool(connectionPool).build();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,8 @@ import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.RetryableException;
import feign.Retryer;
import feign.codec.EncodeException;
import feign.codec.Encoder;
import feign.codec.ErrorDecoder;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -30,6 +32,8 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -38,8 +42,11 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.Map;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
@@ -64,6 +71,8 @@ public class FeignClientUsingPropertiesTests {
private FeignClientFactoryBean barFactoryBean;
private FeignClientFactoryBean formFactoryBean;
public FeignClientUsingPropertiesTests() {
fooFactoryBean = new FeignClientFactoryBean();
fooFactoryBean.setName("foo");
@@ -72,6 +81,10 @@ public class FeignClientUsingPropertiesTests {
barFactoryBean = new FeignClientFactoryBean();
barFactoryBean.setName("bar");
barFactoryBean.setType(FeignClientFactoryBean.class);
formFactoryBean = new FeignClientFactoryBean();
formFactoryBean.setName("form");
formFactoryBean.setType(FeignClientFactoryBean.class);
}
public FooClient fooClient() {
@@ -84,10 +97,15 @@ public class FeignClientUsingPropertiesTests {
return barFactoryBean.feign(context).target(BarClient.class, "http://localhost:" + this.port);
}
public FormClient formClient() {
formFactoryBean.setApplicationContext(applicationContext);
return formFactoryBean.feign(context).target(FormClient.class, "http://localhost:" + this.port);
}
@Test
public void testFoo() {
String response = fooClient().foo();
assertNotNull("OK", response);
assertEquals("OK", response);
}
@Test(expected = RetryableException.class)
@@ -96,6 +114,13 @@ public class FeignClientUsingPropertiesTests {
fail("it should timeout");
}
@Test
public void testForm() {
Map<String, String> request = Collections.singletonMap("form", "Data");
String response = formClient().form(request);
assertEquals("Data", response);
}
protected interface FooClient {
@RequestMapping(method = RequestMethod.GET, value = "/foo")
@@ -108,6 +133,14 @@ public class FeignClientUsingPropertiesTests {
String bar();
}
protected interface FormClient {
@RequestMapping(value = "/form", method = RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
String form(Map<String, String> form);
}
@Configuration
@EnableAutoConfiguration
@RestController
@@ -129,6 +162,12 @@ public class FeignClientUsingPropertiesTests {
return "OK";
}
@RequestMapping(value = "/form", method = RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String form(HttpServletRequest request) {
return request.getParameter("form");
}
}
public static class FooRequestInterceptor implements RequestInterceptor {
@@ -161,4 +200,19 @@ public class FeignClientUsingPropertiesTests {
public static class DefaultErrorDecoder extends ErrorDecoder.Default {
}
public static class FormEncoder implements Encoder {
@Override
public void encode(Object o, Type type, RequestTemplate requestTemplate) throws EncodeException {
Map<String, String> form = (Map<String, String>) o;
StringBuilder builder = new StringBuilder();
form.forEach((key, value) -> {
builder.append(key + "=" + value + "&");
});
requestTemplate.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
requestTemplate.body(builder.toString());
}
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.openfeign.ribbon;
import java.lang.reflect.Field;
import javax.net.ssl.SSLContextSpi;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import org.apache.http.config.Lookup;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.impl.conn.DefaultHttpClientConnectionOperator;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RestController;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
/**
* @author Ryan Baxter
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = FeignRibbonHttpClientConfigurationTests.Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {"debug=true","feign.httpclient.disableSslValidation=true"})
public class FeignRibbonHttpClientConfigurationTests {
@Autowired
HttpClientConnectionManager connectionManager;
@Test
public void disableSslTest() throws Exception {
Lookup<ConnectionSocketFactory> socketFactoryRegistry = getConnectionSocketFactoryLookup(connectionManager);
assertNotNull(socketFactoryRegistry.lookup("https"));
assertNull(this.getX509TrustManager(socketFactoryRegistry).getAcceptedIssuers());
}
private Lookup<ConnectionSocketFactory> getConnectionSocketFactoryLookup(HttpClientConnectionManager connectionManager) {
DefaultHttpClientConnectionOperator connectionOperator = (DefaultHttpClientConnectionOperator)this.getField(connectionManager, "connectionOperator");
return (Lookup)this.getField(connectionOperator, "socketFactoryRegistry");
}
private X509TrustManager getX509TrustManager(Lookup<ConnectionSocketFactory> socketFactoryRegistry) {
ConnectionSocketFactory connectionSocketFactory = (ConnectionSocketFactory)socketFactoryRegistry.lookup("https");
SSLSocketFactory sslSocketFactory = (SSLSocketFactory)this.getField(connectionSocketFactory, "socketfactory");
SSLContextSpi sslContext = (SSLContextSpi)this.getField(sslSocketFactory, "context");
return (X509TrustManager)this.getField(sslContext, "trustManager");
}
protected <T> Object getField(Object target, String name) {
Field field = ReflectionUtils.findField(target.getClass(), name);
ReflectionUtils.makeAccessible(field);
Object value = ReflectionUtils.getField(field, target);
return value;
}
@Configuration
@EnableAutoConfiguration
@RestController
public static class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(FeignRibbonClientRetryTests.Application.class)
.run(args);
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.openfeign.ribbon;
import okhttp3.OkHttpClient;
import java.lang.reflect.Field;
import javax.net.ssl.HostnameVerifier;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Ryan Baxter
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = FeignRibbonOkHttpClientConfigurationTests.Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {"debug=true","feign.httpclient.disableSslValidation=true",
"feign.okhttp.enabled=true", "feign.httpclient.enabled=false"})
public class FeignRibbonOkHttpClientConfigurationTests {
@Autowired
OkHttpClient httpClient;
@Test
public void disableSslTest() throws Exception {
HostnameVerifier hostnameVerifier = (HostnameVerifier)this.getField(httpClient, "hostnameVerifier");
Assert.assertTrue(OkHttpClientFactory.TrustAllHostnames.class.isInstance(hostnameVerifier));
}
protected <T> Object getField(Object target, String name) {
Field field = ReflectionUtils.findField(target.getClass(), name);
ReflectionUtils.makeAccessible(field);
Object value = ReflectionUtils.getField(field, target);
return value;
}
@Configuration
@EnableAutoConfiguration
@RestController
public static class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(FeignRibbonClientRetryTests.Application.class)
.run(args);
}
}
}

View File

@@ -16,4 +16,6 @@ feign.client.config.foo.requestInterceptors[0]=org.springframework.cloud.openfei
feign.client.config.foo.requestInterceptors[1]=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.BarRequestInterceptor
feign.client.config.bar.connectTimeout=1000
feign.client.config.bar.readTimeout=1000
feign.client.config.bar.readTimeout=1000
feign.client.config.form.encoder=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.FormEncoder