Skip to content

Commit

Permalink
Fix timout-overriding using RestTemplateBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
larsgrefer committed Nov 6, 2019
1 parent c2c73e2 commit 6a4d463
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,12 @@
import okhttp3.*;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;

import java.io.File;
import java.io.IOException;
Expand Down Expand Up @@ -95,20 +92,4 @@ public Cache okHttp3Cache() throws IOException {
}
return new Cache(directory, okHttpProperties.getCache().getMaxSize().toBytes());
}

/**
* @author Lars Grefer
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(OkHttp3ClientHttpRequestFactory.class)
@AutoConfigureBefore(OkHttpRestTemplateAutoConfiguration.class)
@AutoConfigureAfter(OkHttp3AutoConfiguration.class)
public static class RequestFactoryAutoConfiguration {

@Bean
@ConditionalOnMissingBean(OkHttp3ClientHttpRequestFactory.class)
public OkHttp3ClientHttpRequestFactory okHttp3ClientHttpRequestFactory(OkHttpClient okHttpClient) {
return new OkHttp3ClientHttpRequestFactory(okHttpClient);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,80 @@
package io.freefair.spring.okhttp;

import okhttp3.OkHttpClient;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.NoneNestedConditions;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.boot.web.client.RestTemplateRequestCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import java.util.Collection;
import java.util.List;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

/**
* @author Lars Grefer
* @see RestTemplateAutoConfiguration
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({RestTemplateCustomizer.class, RestTemplate.class})
@AutoConfigureBefore(RestTemplateAutoConfiguration.class)
@AutoConfigureAfter(HttpMessageConvertersAutoConfiguration.class)
@Conditional(OkHttpRestTemplateAutoConfiguration.NotReactiveWebApplicationCondition.class)
public class OkHttpRestTemplateAutoConfiguration {

@Bean
@Order(2)
@ConditionalOnBean(OkHttp3ClientHttpRequestFactory.class)
public RestTemplateCustomizer okHttp3RestTemplateCustomizer(OkHttp3ClientHttpRequestFactory requestFactory) {
return restTemplate -> restTemplate.setRequestFactory(requestFactory);
@ConditionalOnMissingBean
public RestTemplateBuilder restTemplateBuilder(ObjectProvider<HttpMessageConverters> messageConverters,
ObjectProvider<RestTemplateCustomizer> restTemplateCustomizers,
ObjectProvider<RestTemplateRequestCustomizer<?>> restTemplateRequestCustomizers,
OkHttpClient okHttpClient) {
RestTemplateBuilder builder = new RestTemplateBuilder();
HttpMessageConverters converters = messageConverters.getIfUnique();
if (converters != null) {
builder = builder.messageConverters(converters.getConverters());
}
builder = addCustomizers(builder, restTemplateCustomizers, RestTemplateBuilder::customizers);
builder = addCustomizers(builder, restTemplateRequestCustomizers, RestTemplateBuilder::requestCustomizers);

builder = builder.requestFactory(() -> new OkHttp3ClientHttpRequestFactory(okHttpClient));

return builder;
}

private <T> RestTemplateBuilder addCustomizers(RestTemplateBuilder builder, ObjectProvider<T> objectProvider,
BiFunction<RestTemplateBuilder, Collection<T>, RestTemplateBuilder> method) {
List<T> customizers = objectProvider.orderedStream().collect(Collectors.toList());
if (!customizers.isEmpty()) {
return method.apply(builder, customizers);
}
return builder;
}

static class NotReactiveWebApplicationCondition extends NoneNestedConditions {

NotReactiveWebApplicationCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}

@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
private static class ReactiveWebApplication {

}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package io.freefair.spring.okhttp;

import okhttp3.OkHttpClient;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.client.AbstractClientHttpRequestFactoryWrapper;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import java.lang.reflect.Field;
import java.time.Duration;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = {
"okhttp.read-timeout=21s",
"okhttp.connect-timeout=21s",
"okhttp.write-timeout=21s"
})
class OkHttpRestTemplateAutoConfigurationTest {

@Autowired
private RestTemplateBuilder restTemplateBuilder;

@Test
void testTimeouts() throws NoSuchFieldException, IllegalAccessException {
RestTemplate restTemplate = restTemplateBuilder.setConnectTimeout(Duration.ofSeconds(42)).build();

OkHttpClient client = extractClient(restTemplate);

assertThat(client.connectTimeoutMillis()).isEqualTo(Duration.ofSeconds(42).toMillis());
assertThat(client.readTimeoutMillis()).isEqualTo(Duration.ofSeconds(21).toMillis());
assertThat(client.writeTimeoutMillis()).isEqualTo(Duration.ofSeconds(21).toMillis());
}

private OkHttpClient extractClient(RestTemplate restTemplate) throws NoSuchFieldException, IllegalAccessException {
ClientHttpRequestFactory requestFactory = restTemplate.getRequestFactory();

while (requestFactory instanceof AbstractClientHttpRequestFactoryWrapper) {
Field field = AbstractClientHttpRequestFactoryWrapper.class.getDeclaredField("requestFactory");
field.setAccessible(true);
requestFactory = (ClientHttpRequestFactory) field.get(requestFactory);
}

assertThat(requestFactory).isInstanceOf(OkHttp3ClientHttpRequestFactory.class);

Field field = OkHttp3ClientHttpRequestFactory.class.getDeclaredField("client");
field.setAccessible(true);
return (OkHttpClient) field.get(requestFactory);
}

@SpringBootConfiguration
@EnableAutoConfiguration
public static class TestConfiguration {

}
}
1 change: 0 additions & 1 deletion config/checkstyle/checkstyle.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
<module name="EmptyStatement"/>
<module name="EqualsAvoidNull"/>
<module name="EqualsHashCode"/>
<module name="HiddenField"/>
<module name="IllegalThrows"/>
<module name="IllegalType"/>
<module name="InnerAssignment"/>
Expand Down

0 comments on commit 6a4d463

Please sign in to comment.