Add HttpClient factory support to ReactorHttpClientBuilder
Improve `ReactorHttpClientBuilder` support for creating the initial `HttpClient`. Fixes gh-45378
This commit is contained in:
@@ -20,12 +20,14 @@ import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.http.client.ReactorClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ReactorResourceFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -63,6 +65,33 @@ public final class ReactorClientHttpRequestFactoryBuilder
|
||||
return new ReactorClientHttpRequestFactoryBuilder(mergedCustomizers(customizers), this.httpClientBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link ReactorClientHttpRequestFactoryBuilder} that uses the given
|
||||
* {@link ReactorResourceFactory} to create the underlying {@link HttpClient}.
|
||||
* @param reactorResourceFactory the {@link ReactorResourceFactory} to use
|
||||
* @return a new {@link ReactorClientHttpRequestFactoryBuilder} instance
|
||||
* @since 3.5.0
|
||||
*/
|
||||
public ReactorClientHttpRequestFactoryBuilder withReactorResourceFactory(
|
||||
ReactorResourceFactory reactorResourceFactory) {
|
||||
Assert.notNull(reactorResourceFactory, "'reactorResourceFactory' must not be null");
|
||||
return new ReactorClientHttpRequestFactoryBuilder(getCustomizers(),
|
||||
this.httpClientBuilder.withReactorResourceFactory(reactorResourceFactory));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link ReactorClientHttpRequestFactoryBuilder} that uses the given
|
||||
* factory to create the underlying {@link HttpClient}.
|
||||
* @param factory the factory to use
|
||||
* @return a new {@link ReactorClientHttpRequestFactoryBuilder} instance
|
||||
* @since 3.5.0
|
||||
*/
|
||||
public ReactorClientHttpRequestFactoryBuilder withHttpClientFactory(Supplier<HttpClient> factory) {
|
||||
Assert.notNull(factory, "'factory' must not be null");
|
||||
return new ReactorClientHttpRequestFactoryBuilder(getCustomizers(),
|
||||
this.httpClientBuilder.withHttpClientFactory(factory));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link ReactorClientHttpRequestFactoryBuilder} that applies additional
|
||||
* customization to the underlying {@link HttpClient}.
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.boot.http.client;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
@@ -30,6 +31,7 @@ import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.boot.ssl.SslManagerBundle;
|
||||
import org.springframework.boot.ssl.SslOptions;
|
||||
import org.springframework.http.client.ReactorResourceFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.function.ThrowingConsumer;
|
||||
|
||||
@@ -43,16 +45,42 @@ import org.springframework.util.function.ThrowingConsumer;
|
||||
*/
|
||||
public final class ReactorHttpClientBuilder {
|
||||
|
||||
private final Supplier<HttpClient> factory;
|
||||
|
||||
private final UnaryOperator<HttpClient> customizer;
|
||||
|
||||
public ReactorHttpClientBuilder() {
|
||||
this(UnaryOperator.identity());
|
||||
this(HttpClient::create, UnaryOperator.identity());
|
||||
}
|
||||
|
||||
private ReactorHttpClientBuilder(UnaryOperator<HttpClient> customizer) {
|
||||
private ReactorHttpClientBuilder(Supplier<HttpClient> httpClientFactory, UnaryOperator<HttpClient> customizer) {
|
||||
this.factory = httpClientFactory;
|
||||
this.customizer = customizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link ReactorHttpClientBuilder} that uses the given
|
||||
* {@link ReactorResourceFactory} to create the {@link HttpClient}.
|
||||
* @param reactorResourceFactory the {@link ReactorResourceFactory} to use
|
||||
* @return a new {@link ReactorHttpClientBuilder} instance
|
||||
*/
|
||||
public ReactorHttpClientBuilder withReactorResourceFactory(ReactorResourceFactory reactorResourceFactory) {
|
||||
Assert.notNull(reactorResourceFactory, "'reactorResourceFactory' must not be null");
|
||||
return new ReactorHttpClientBuilder(() -> HttpClient.create(reactorResourceFactory.getConnectionProvider()),
|
||||
(httpClient) -> this.customizer.apply(httpClient).runOn(reactorResourceFactory.getLoopResources()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link ReactorHttpClientBuilder} that uses the given factory to create
|
||||
* the {@link HttpClient}.
|
||||
* @param factory the factory to use
|
||||
* @return a new {@link ReactorHttpClientBuilder} instance
|
||||
*/
|
||||
public ReactorHttpClientBuilder withHttpClientFactory(Supplier<HttpClient> factory) {
|
||||
Assert.notNull(factory, "'factory' must not be null");
|
||||
return new ReactorHttpClientBuilder(factory, this.customizer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link ReactorHttpClientBuilder} that applies additional customization
|
||||
* to the underlying {@link HttpClient}.
|
||||
@@ -72,7 +100,7 @@ public final class ReactorHttpClientBuilder {
|
||||
*/
|
||||
public HttpClient build(HttpClientSettings settings) {
|
||||
settings = (settings != null) ? settings : HttpClientSettings.DEFAULTS;
|
||||
HttpClient httpClient = applyDefaults(HttpClient.create());
|
||||
HttpClient httpClient = applyDefaults(this.factory.get());
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
httpClient = map.from(settings::connectTimeout).to(httpClient, this::setConnectTimeout);
|
||||
httpClient = map.from(settings::readTimeout).to(httpClient, HttpClient::responseTimeout);
|
||||
|
||||
@@ -19,11 +19,13 @@ package org.springframework.boot.http.client.reactive;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
|
||||
import org.springframework.boot.http.client.ReactorHttpClientBuilder;
|
||||
import org.springframework.http.client.ReactorResourceFactory;
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -60,6 +62,30 @@ public final class ReactorClientHttpConnectorBuilder
|
||||
return new ReactorClientHttpConnectorBuilder(mergedCustomizers(customizers), this.httpClientBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link ReactorClientHttpConnectorBuilder} that uses the given
|
||||
* {@link ReactorResourceFactory} to create the underlying {@link HttpClient}.
|
||||
* @param reactorResourceFactory the {@link ReactorResourceFactory} to use
|
||||
* @return a new {@link ReactorClientHttpConnectorBuilder} instance
|
||||
*/
|
||||
public ReactorClientHttpConnectorBuilder withReactorResourceFactory(ReactorResourceFactory reactorResourceFactory) {
|
||||
Assert.notNull(reactorResourceFactory, "'reactorResourceFactory' must not be null");
|
||||
return new ReactorClientHttpConnectorBuilder(getCustomizers(),
|
||||
this.httpClientBuilder.withReactorResourceFactory(reactorResourceFactory));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link ReactorClientHttpConnectorBuilder} that uses the given factory
|
||||
* to create the underlying {@link HttpClient}.
|
||||
* @param factory the factory to use
|
||||
* @return a new {@link ReactorClientHttpConnectorBuilder} instance
|
||||
*/
|
||||
public ReactorClientHttpConnectorBuilder withHttpClientFactory(Supplier<HttpClient> factory) {
|
||||
Assert.notNull(factory, "'factory' must not be null");
|
||||
return new ReactorClientHttpConnectorBuilder(getCustomizers(),
|
||||
this.httpClientBuilder.withHttpClientFactory(factory));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link ReactorClientHttpConnectorBuilder} that applies additional
|
||||
* customization to the underlying {@link HttpClient}.
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.boot.http.client;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import io.netty.channel.ChannelOption;
|
||||
@@ -26,9 +27,12 @@ import org.junit.jupiter.api.Test;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
|
||||
import org.springframework.http.client.ReactorClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ReactorResourceFactory;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReactorClientHttpRequestFactoryBuilder} and
|
||||
@@ -44,6 +48,25 @@ class ReactorClientHttpRequestFactoryBuilderTests
|
||||
super(ReactorClientHttpRequestFactory.class, ClientHttpRequestFactoryBuilder.reactor());
|
||||
}
|
||||
|
||||
@Test
|
||||
void withwithHttpClientFactory() {
|
||||
boolean[] called = new boolean[1];
|
||||
Supplier<HttpClient> httpClientFactory = () -> {
|
||||
called[0] = true;
|
||||
return HttpClient.create();
|
||||
};
|
||||
ClientHttpRequestFactoryBuilder.reactor().withHttpClientFactory(httpClientFactory).build();
|
||||
assertThat(called).containsExactly(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withReactorResourceFactory() {
|
||||
ReactorResourceFactory resourceFactory = spy(new ReactorResourceFactory());
|
||||
ClientHttpRequestFactoryBuilder.reactor().withReactorResourceFactory(resourceFactory).build();
|
||||
then(resourceFactory).should().getConnectionProvider();
|
||||
then(resourceFactory).should().getLoopResources();
|
||||
}
|
||||
|
||||
@Test
|
||||
void withCustomizers() {
|
||||
List<HttpClient> httpClients = new ArrayList<>();
|
||||
|
||||
@@ -18,18 +18,21 @@ package org.springframework.boot.http.client.reactive;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import io.netty.channel.ChannelOption;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
|
||||
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
|
||||
import org.springframework.boot.http.client.ReactorHttpClientBuilder;
|
||||
import org.springframework.http.client.ReactorResourceFactory;
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReactorClientHttpConnectorBuilder} and
|
||||
@@ -44,6 +47,25 @@ class ReactorClientHttpConnectorBuilderTests
|
||||
super(ReactorClientHttpConnector.class, ClientHttpConnectorBuilder.reactor());
|
||||
}
|
||||
|
||||
@Test
|
||||
void withwithHttpClientFactory() {
|
||||
boolean[] called = new boolean[1];
|
||||
Supplier<HttpClient> httpClientFactory = () -> {
|
||||
called[0] = true;
|
||||
return HttpClient.create();
|
||||
};
|
||||
ClientHttpConnectorBuilder.reactor().withHttpClientFactory(httpClientFactory).build();
|
||||
assertThat(called).containsExactly(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withReactorResourceFactory() {
|
||||
ReactorResourceFactory resourceFactory = spy(new ReactorResourceFactory());
|
||||
ClientHttpConnectorBuilder.reactor().withReactorResourceFactory(resourceFactory).build();
|
||||
then(resourceFactory).should().getConnectionProvider();
|
||||
then(resourceFactory).should().getLoopResources();
|
||||
}
|
||||
|
||||
@Test
|
||||
void withCustomizers() {
|
||||
List<HttpClient> httpClients = new ArrayList<>();
|
||||
|
||||
Reference in New Issue
Block a user