From 799577edb7b6955374e780ed4943a072bf3bd1bb Mon Sep 17 00:00:00 2001 From: Spencer Gibb Date: Thu, 27 Feb 2020 16:35:14 -0500 Subject: [PATCH] Creates HttpClientCustomizer. This allows users to customize the Reactor Netty HttpClient without loosing auto configuration. fixes gh-1546 --- .../config/GatewayAutoConfiguration.java | 15 +++++++-- .../gateway/config/HttpClientCustomizer.java | 31 +++++++++++++++++++ .../config/GatewayAutoConfigurationTests.java | 26 ++++++++++++++-- 3 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/HttpClientCustomizer.java diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java index dc27a9bb..741ae0d0 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java @@ -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 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; } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/HttpClientCustomizer.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/HttpClientCustomizer.java new file mode 100644 index 00000000..e10ecb7e --- /dev/null +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/HttpClientCustomizer.java @@ -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); + +} diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/config/GatewayAutoConfigurationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/config/GatewayAutoConfigurationTests.java index a49eb870..7c574267 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/config/GatewayAutoConfigurationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/config/GatewayAutoConfigurationTests.java @@ -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; + }; + } + + } + }