Creates HttpClientCustomizer.

This allows users to customize the Reactor Netty HttpClient without loosing auto configuration.

fixes gh-1546
This commit is contained in:
Spencer Gibb
2020-02-27 16:35:14 -05:00
parent 60653ee240
commit 799577edb7
3 changed files with 68 additions and 4 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2019 the original author or authors.
* Copyright 2013-2020 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.
@@ -137,9 +137,11 @@ import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.env.Environment;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.Validator;
import org.springframework.web.reactive.DispatcherHandler;
@@ -579,7 +581,8 @@ public class GatewayAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public HttpClient gatewayHttpClient(HttpClientProperties properties) {
public HttpClient gatewayHttpClient(HttpClientProperties properties,
List<HttpClientCustomizer> customizers) {
// configure pool resources
HttpClientProperties.Pool pool = properties.getPool();
@@ -599,6 +602,7 @@ public class GatewayAutoConfiguration {
}
HttpClient httpClient = HttpClient.create(connectionProvider)
// TODO: move customizations to HttpClientCustomizers
.httpResponseDecoder(spec -> {
if (properties.getMaxHeaderSize() != null) {
// cast to int is ok, since @Max is Integer.MAX_VALUE
@@ -677,6 +681,13 @@ public class GatewayAutoConfiguration {
httpClient = httpClient.wiretap(true);
}
if (!CollectionUtils.isEmpty(customizers)) {
customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
for (HttpClientCustomizer customizer : customizers) {
httpClient = customizer.customize(httpClient);
}
}
return httpClient;
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2013-2020 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
*
* https://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.gateway.config;
import reactor.netty.http.client.HttpClient;
@FunctionalInterface
public interface HttpClientCustomizer {
/**
* Customize the specified {@link HttpClient}.
* @param httpClient the http client to customize.
* @return the customized HttpClient.
*/
HttpClient customize(HttpClient httpClient);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2019 the original author or authors.
* Copyright 2013-2020 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.
@@ -16,6 +16,8 @@
package org.springframework.cloud.gateway.config;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Test;
import reactor.netty.http.client.HttpClient;
@@ -30,6 +32,8 @@ import org.springframework.boot.test.context.runner.ReactiveWebApplicationContex
import org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint;
import org.springframework.cloud.gateway.actuate.GatewayLegacyControllerEndpoint;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.reactive.HiddenHttpMethodFilter;
import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient;
@@ -81,7 +85,7 @@ public class GatewayAutoConfigurationTests {
.withConfiguration(AutoConfigurations.of(WebFluxAutoConfiguration.class,
MetricsAutoConfiguration.class,
SimpleMetricsExportAutoConfiguration.class,
GatewayAutoConfiguration.class))
GatewayAutoConfiguration.class, HttpClientCustomizedConfig.class))
.withPropertyValues(
"spring.cloud.gateway.httpclient.ssl.use-insecure-trust-manager=true",
"spring.cloud.gateway.httpclient.connect-timeout=10",
@@ -113,6 +117,9 @@ public class GatewayAutoConfigurationTests {
.getBean(ReactorNettyWebSocketClient.class);
assertThat(webSocketClient.getMaxFramePayloadLength())
.isEqualTo(1024);
HttpClientCustomizedConfig config = context
.getBean(HttpClientCustomizedConfig.class);
assertThat(config.called.get()).isTrue();
});
}
@@ -143,4 +150,19 @@ public class GatewayAutoConfigurationTests {
}
@Configuration
protected static class HttpClientCustomizedConfig {
private final AtomicBoolean called = new AtomicBoolean();
@Bean
HttpClientCustomizer myCustomCustomizer() {
return httpClient -> {
called.compareAndSet(false, true);
return httpClient;
};
}
}
}