From b337f67f93c138c318236afb6008e1665699d348 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Sun, 26 Apr 2020 22:01:34 +0200 Subject: [PATCH] Add ReactorNettyHttpClientMapper This new customizer-like interface make it possible to configure the Reactor Netty `HttpClient` that is going to be used by the `WebClient.Builder` provided by Spring Boot. Closes gh-21135 --- .../ClientHttpConnectorConfiguration.java | 11 +++-- .../client/ReactorNettyHttpClientMapper.java | 40 +++++++++++++++++++ ...ClientHttpConnectorConfigurationTests.java | 26 +++++++++++- 3 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ReactorNettyHttpClientMapper.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorConfiguration.java index 99c3787ee5..6fc950ce30 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorConfiguration.java @@ -16,11 +16,10 @@ package org.springframework.boot.autoconfigure.web.reactive.function.client; -import java.util.function.Function; - import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; @@ -56,8 +55,12 @@ class ClientHttpConnectorConfiguration { @Bean @Lazy - public ReactorClientHttpConnector reactorClientHttpConnector(ReactorResourceFactory reactorResourceFactory) { - return new ReactorClientHttpConnector(reactorResourceFactory, Function.identity()); + public ReactorClientHttpConnector reactorClientHttpConnector(ReactorResourceFactory reactorResourceFactory, + ObjectProvider mapperProvider) { + ReactorNettyHttpClientMapper mapper = mapperProvider.orderedStream() + .reduce((before, after) -> (client) -> after.configure(before.configure(client))) + .orElse((client) -> client); + return new ReactorClientHttpConnector(reactorResourceFactory, mapper::configure); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ReactorNettyHttpClientMapper.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ReactorNettyHttpClientMapper.java new file mode 100644 index 0000000000..34a8d37762 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ReactorNettyHttpClientMapper.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012-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.boot.autoconfigure.web.reactive.function.client; + +import reactor.netty.http.client.HttpClient; + +import org.springframework.http.client.reactive.ReactorClientHttpConnector; + +/** + * Mapper that allows for custom modification of a {@link HttpClient} before it is used as + * the basis for a {@link ReactorClientHttpConnector}. + * + * @author Brian Clozel + * @since 2.3.0 + */ +@FunctionalInterface +public interface ReactorNettyHttpClientMapper { + + /** + * Configure the given {@link HttpClient} and return the newly created instance. + * @param httpClient the client to configure + * @return the new client instance + */ + HttpClient configure(HttpClient httpClient); + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorConfigurationTests.java index abd0dafb69..adf1d9d972 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-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. @@ -23,6 +23,9 @@ import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.util.thread.Scheduler; import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; +import org.springframework.context.annotation.Bean; import org.springframework.http.client.reactive.JettyClientHttpConnector; import org.springframework.http.client.reactive.JettyResourceFactory; import org.springframework.test.util.ReflectionTestUtils; @@ -34,6 +37,7 @@ import static org.mockito.Mockito.mock; * Tests for {@link ClientHttpConnectorConfiguration}. * * @author Phillip Webb + * @author Brian Clozel */ class ClientHttpConnectorConfigurationTests { @@ -68,4 +72,24 @@ class ClientHttpConnectorConfigurationTests { return ReflectionTestUtils.invokeMethod(jettyClient, "jettyClientHttpConnector", jettyResourceFactory); } + @Test + void shouldApplyHttpClientMapper() { + new ReactiveWebApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(ClientHttpConnectorConfiguration.ReactorNetty.class)) + .withUserConfiguration(CustomHttpClientMapper.class) + .run((context) -> assertThat(CustomHttpClientMapper.called).isTrue()); + } + + static class CustomHttpClientMapper { + + static boolean called = false; + + @Bean + ReactorNettyHttpClientMapper clientMapper() { + called = true; + return (client) -> client.baseUrl("/test"); + } + + } + }