Introduce `ClientHttpRequestFactoryBuilder` support
Add a new `ClientHttpRequestFactoryBuilder` interface to support the creation of `ClientHttpRequestFactory` instances. The new code will ultimately replace the existing `ClientHttpRequestFactories` class. The `ClientHttpRequestFactoryBuilder` is a functional interface with additional static factory methods for the various supported `ClientHttpRequestFactory` types. Each type has it's own builder which should allow us to support additional customization in the future. Unlike `ClientHttpRequestFactories`, the builder aligns with Spring Framework defaults and will detect the `JdkClientHttpRequestFactory` in preference of `SimpleClientHttpRequestFactory`. This commit also relocates `ClientHttpRequestFactorySettings` to bring it into the new `http.client` package. See gh-36266
This commit is contained in:
parent
0a4ac283d0
commit
78458afa17
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2012-2024 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.http.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.boot.util.LambdaSafe;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Internal base class used for {@link ClientHttpRequestFactoryBuilder} implementations.
|
||||
*
|
||||
* @param <T> the {@link ClientHttpRequestFactory} type
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
abstract class AbstractClientHttpRequestFactoryBuilder<T extends ClientHttpRequestFactory>
|
||||
implements ClientHttpRequestFactoryBuilder<T> {
|
||||
|
||||
private final List<Consumer<T>> customizers;
|
||||
|
||||
protected AbstractClientHttpRequestFactoryBuilder(List<Consumer<T>> customizers) {
|
||||
this.customizers = (customizers != null) ? customizers : Collections.emptyList();
|
||||
}
|
||||
|
||||
protected final List<Consumer<T>> mergedCustomizers(Consumer<T> customizer) {
|
||||
Assert.notNull(this.customizers, "'customizer' must not be null");
|
||||
return merge(this.customizers, List.of(customizer));
|
||||
}
|
||||
|
||||
protected final List<Consumer<T>> mergedCustomizers(Collection<Consumer<T>> customizers) {
|
||||
Assert.notNull(customizers, "'customizers' must not be null");
|
||||
Assert.noNullElements(customizers, "'customizers' must not contain null elements");
|
||||
return merge(this.customizers, customizers);
|
||||
}
|
||||
|
||||
private <E> List<E> merge(Collection<E> list, Collection<? extends E> additional) {
|
||||
List<E> merged = new ArrayList<>(list);
|
||||
merged.addAll(additional);
|
||||
return List.copyOf(merged);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public final T build(ClientHttpRequestFactorySettings settings) {
|
||||
T factory = createClientHttpRequestFactory(
|
||||
(settings != null) ? settings : ClientHttpRequestFactorySettings.defaults());
|
||||
LambdaSafe.callbacks(Consumer.class, this.customizers, factory).invoke((consumer) -> consumer.accept(factory));
|
||||
return factory;
|
||||
}
|
||||
|
||||
protected abstract T createClientHttpRequestFactory(ClientHttpRequestFactorySettings settings);
|
||||
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* Copyright 2012-2024 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.http.client;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.boot.util.LambdaSafe;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.http.client.JdkClientHttpRequestFactory;
|
||||
import org.springframework.http.client.JettyClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ReactorClientHttpRequestFactory;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Interface used to build a fully configured {@link ClientHttpRequestFactory}. Builders
|
||||
* for {@link #httpComponents() Apache HTTP Components}, {@link #jetty() Jetty},
|
||||
* {@link #reactor() Reactor}, {@link #jdk() JDK} and {@link #simple() simple client} can
|
||||
* be obtained using the factory methods on this interface. The {@link #of(Class)} and
|
||||
* {@link #of(Supplier)} methods may be used to instantiate other
|
||||
* {@link ClientHttpRequestFactory} instances using reflection.
|
||||
*
|
||||
* @param <T> the {@link ClientHttpRequestFactory} type
|
||||
* @author Phillip Webb
|
||||
* @since 3.4.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ClientHttpRequestFactoryBuilder<T extends ClientHttpRequestFactory> {
|
||||
|
||||
/**
|
||||
* Build a default configured {@link ClientHttpRequestFactory}.
|
||||
* @return a default configured {@link ClientHttpRequestFactory}.
|
||||
*/
|
||||
default T build() {
|
||||
return build(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a fully configured {@link ClientHttpRequestFactory}, applying the given
|
||||
* {@code settings} if they are provided.
|
||||
* @param settings the settings to apply or {@code null}
|
||||
* @return a fully configured {@link ClientHttpRequestFactory}.
|
||||
*/
|
||||
T build(ClientHttpRequestFactorySettings settings);
|
||||
|
||||
/**
|
||||
* Return a new {@link ClientHttpRequestFactoryBuilder} that applies the given
|
||||
* customizer to the {@link ClientHttpRequestFactory} after it has been built.
|
||||
* @param customizer the customizers to apply
|
||||
* @return a new {@link ClientHttpRequestFactoryBuilder} instance
|
||||
*/
|
||||
default ClientHttpRequestFactoryBuilder<T> withCustomizer(Consumer<T> customizer) {
|
||||
return withCustomizers(List.of(customizer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link ClientHttpRequestFactoryBuilder} that applies the given
|
||||
* customizers to the {@link ClientHttpRequestFactory} after it has been built.
|
||||
* @param customizers the customizers to apply
|
||||
* @return a new {@link ClientHttpRequestFactoryBuilder} instance
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
default ClientHttpRequestFactoryBuilder<T> withCustomizers(Collection<Consumer<T>> customizers) {
|
||||
Assert.notNull(customizers, "'customizers' must not be null");
|
||||
Assert.noNullElements(customizers, "'customizers' must not contain null elements");
|
||||
return (settings) -> {
|
||||
T factory = build(settings);
|
||||
LambdaSafe.callbacks(Consumer.class, customizers, factory).invoke((consumer) -> consumer.accept(factory));
|
||||
return factory;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link HttpComponentsClientHttpRequestFactoryBuilder} that can be used to
|
||||
* build a {@link HttpComponentsClientHttpRequestFactory}.
|
||||
* @return a new {@link HttpComponentsClientHttpRequestFactoryBuilder}
|
||||
*/
|
||||
static HttpComponentsClientHttpRequestFactoryBuilder httpComponents() {
|
||||
return new HttpComponentsClientHttpRequestFactoryBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link JettyClientHttpRequestFactoryBuilder} that can be used to build a
|
||||
* {@link JettyClientHttpRequestFactory}.
|
||||
* @return a new {@link JettyClientHttpRequestFactoryBuilder}
|
||||
*/
|
||||
static JettyClientHttpRequestFactoryBuilder jetty() {
|
||||
return new JettyClientHttpRequestFactoryBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ReactorClientHttpRequestFactoryBuilder} that can be used to build a
|
||||
* {@link ReactorClientHttpRequestFactory}.
|
||||
* @return a new {@link ReactorClientHttpRequestFactoryBuilder}
|
||||
*/
|
||||
static ReactorClientHttpRequestFactoryBuilder reactor() {
|
||||
return new ReactorClientHttpRequestFactoryBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link JdkClientHttpRequestFactoryBuilder} that can be used to build a
|
||||
* {@link JdkClientHttpRequestFactory} .
|
||||
* @return a new {@link JdkClientHttpRequestFactoryBuilder}
|
||||
*/
|
||||
static JdkClientHttpRequestFactoryBuilder jdk() {
|
||||
return new JdkClientHttpRequestFactoryBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link SimpleClientHttpRequestFactoryBuilder} that can be used to build a
|
||||
* {@link SimpleClientHttpRequestFactory} .
|
||||
* @return a new {@link SimpleClientHttpRequestFactoryBuilder}
|
||||
*/
|
||||
static SimpleClientHttpRequestFactoryBuilder simple() {
|
||||
return new SimpleClientHttpRequestFactoryBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link ClientHttpRequestFactoryBuilder} for the given
|
||||
* {@code requestFactoryType}. The following implementations are supported without the
|
||||
* use of reflection:
|
||||
* <ul>
|
||||
* <li>{@link HttpComponentsClientHttpRequestFactory}</li>
|
||||
* <li>{@link JdkClientHttpRequestFactory}</li>
|
||||
* <li>{@link JettyClientHttpRequestFactory}</li>
|
||||
* <li>{@link ReactorClientHttpRequestFactory}</li>
|
||||
* <li>{@link SimpleClientHttpRequestFactory}</li>
|
||||
* </ul>
|
||||
* @param <T> the {@link ClientHttpRequestFactory} type
|
||||
* @param requestFactoryType the {@link ClientHttpRequestFactory} type
|
||||
* @return a new {@link ClientHttpRequestFactoryBuilder}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T extends ClientHttpRequestFactory> ClientHttpRequestFactoryBuilder<T> of(Class<T> requestFactoryType) {
|
||||
Assert.notNull(requestFactoryType, "'requestFactoryType' must not be null");
|
||||
Assert.isTrue(requestFactoryType != ClientHttpRequestFactory.class,
|
||||
"'requestFactoryType' must be an implementation of ClientHttpRequestFactory");
|
||||
if (requestFactoryType == HttpComponentsClientHttpRequestFactory.class) {
|
||||
return (ClientHttpRequestFactoryBuilder<T>) httpComponents();
|
||||
}
|
||||
if (requestFactoryType == JettyClientHttpRequestFactory.class) {
|
||||
return (ClientHttpRequestFactoryBuilder<T>) jetty();
|
||||
}
|
||||
if (requestFactoryType == ReactorClientHttpRequestFactory.class) {
|
||||
return (ClientHttpRequestFactoryBuilder<T>) reactor();
|
||||
}
|
||||
if (requestFactoryType == JdkClientHttpRequestFactory.class) {
|
||||
return (ClientHttpRequestFactoryBuilder<T>) jdk();
|
||||
}
|
||||
if (requestFactoryType == SimpleClientHttpRequestFactory.class) {
|
||||
return (ClientHttpRequestFactoryBuilder<T>) simple();
|
||||
}
|
||||
return new ReflectiveComponentsClientHttpRequestFactoryBuilder<>(requestFactoryType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link ClientHttpRequestFactoryBuilder} from the given supplier, using
|
||||
* reflection to ultimately apply the {@link ClientHttpRequestFactorySettings}.
|
||||
* @param <T> the {@link ClientHttpRequestFactory} type
|
||||
* @param requestFactorySupplier the {@link ClientHttpRequestFactory} supplier
|
||||
* @return a new {@link ClientHttpRequestFactoryBuilder}
|
||||
*/
|
||||
static <T extends ClientHttpRequestFactory> ClientHttpRequestFactoryBuilder<T> of(
|
||||
Supplier<T> requestFactorySupplier) {
|
||||
return new ReflectiveComponentsClientHttpRequestFactoryBuilder<>(requestFactorySupplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the most suitable {@link ClientHttpRequestFactoryBuilder} based on the
|
||||
* classpath. The methods favors builders in the following order:
|
||||
* <ol>
|
||||
* <li>{@link #httpComponents()}</li>
|
||||
* <li>{@link #jetty()}</li>
|
||||
* <li>{@link #reactor()}</li>
|
||||
* <li>{@link #jdk()}</li>
|
||||
* <li>{@link #simple()}</li>
|
||||
* </ol>
|
||||
* @return the most suitable {@link ClientHttpRequestFactoryBuilder} for the classpath
|
||||
*/
|
||||
static ClientHttpRequestFactoryBuilder<? extends ClientHttpRequestFactory> detect() {
|
||||
if (HttpComponentsClientHttpRequestFactoryBuilder.Classes.PRESENT) {
|
||||
return httpComponents();
|
||||
}
|
||||
if (JettyClientHttpRequestFactoryBuilder.Classes.PRESENT) {
|
||||
return jetty();
|
||||
}
|
||||
if (ReactorClientHttpRequestFactoryBuilder.Classes.PRESENT) {
|
||||
return reactor();
|
||||
}
|
||||
if (JdkClientHttpRequestFactoryBuilder.Classes.PRESENT) {
|
||||
return jdk();
|
||||
}
|
||||
return simple();
|
||||
}
|
||||
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.web.client;
|
||||
package org.springframework.boot.http.client;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
@ -28,6 +28,7 @@ import org.springframework.aot.hint.TypeReference;
|
|||
import org.springframework.http.client.AbstractClientHttpRequestFactoryWrapper;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.http.client.JdkClientHttpRequestFactory;
|
||||
import org.springframework.http.client.JettyClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ReactorClientHttpRequestFactory;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
|
@ -36,12 +37,12 @@ import org.springframework.util.ClassUtils;
|
|||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* {@link RuntimeHintsRegistrar} for {@link ClientHttpRequestFactories}.
|
||||
* {@link RuntimeHintsRegistrar} for {@link ClientHttpRequestFactory} implementations.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ClientHttpRequestFactoriesRuntimeHints implements RuntimeHintsRegistrar {
|
||||
class ClientHttpRequestFactoryRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
|
@ -52,21 +53,29 @@ class ClientHttpRequestFactoriesRuntimeHints implements RuntimeHintsRegistrar {
|
|||
|
||||
private void registerHints(ReflectionHints hints, ClassLoader classLoader) {
|
||||
hints.registerField(findField(AbstractClientHttpRequestFactoryWrapper.class, "requestFactory"));
|
||||
hints.registerTypeIfPresent(classLoader, ClientHttpRequestFactories.APACHE_HTTP_CLIENT_CLASS, (typeHint) -> {
|
||||
typeHint.onReachableType(TypeReference.of(ClientHttpRequestFactories.APACHE_HTTP_CLIENT_CLASS));
|
||||
registerReflectionHints(hints, HttpComponentsClientHttpRequestFactory.class);
|
||||
});
|
||||
hints.registerTypeIfPresent(classLoader, ClientHttpRequestFactories.JETTY_CLIENT_CLASS, (typeHint) -> {
|
||||
typeHint.onReachableType(TypeReference.of(ClientHttpRequestFactories.JETTY_CLIENT_CLASS));
|
||||
registerReflectionHints(hints, JettyClientHttpRequestFactory.class, long.class);
|
||||
});
|
||||
registerClientHttpRequestFactoryHints(hints, classLoader,
|
||||
HttpComponentsClientHttpRequestFactoryBuilder.Classes.HTTP_CLIENTS,
|
||||
() -> registerReflectionHints(hints, HttpComponentsClientHttpRequestFactory.class));
|
||||
registerClientHttpRequestFactoryHints(hints, classLoader,
|
||||
JettyClientHttpRequestFactoryBuilder.Classes.HTTP_CLIENT,
|
||||
() -> registerReflectionHints(hints, JettyClientHttpRequestFactory.class, long.class));
|
||||
registerClientHttpRequestFactoryHints(hints, classLoader,
|
||||
ReactorClientHttpRequestFactoryBuilder.Classes.HTTP_CLIENT,
|
||||
() -> registerReflectionHints(hints, ReactorClientHttpRequestFactory.class, long.class));
|
||||
registerClientHttpRequestFactoryHints(hints, classLoader,
|
||||
JdkClientHttpRequestFactoryBuilder.Classes.HTTP_CLIENT,
|
||||
() -> registerReflectionHints(hints, JdkClientHttpRequestFactory.class));
|
||||
hints.registerType(SimpleClientHttpRequestFactory.class, (typeHint) -> {
|
||||
typeHint.onReachableType(HttpURLConnection.class);
|
||||
registerReflectionHints(hints, SimpleClientHttpRequestFactory.class);
|
||||
});
|
||||
hints.registerTypeIfPresent(classLoader, ClientHttpRequestFactories.REACTOR_CLIENT_CLASS, (typeHint) -> {
|
||||
typeHint.onReachableType(TypeReference.of(ClientHttpRequestFactories.REACTOR_CLIENT_CLASS));
|
||||
registerReflectionHints(hints, ReactorClientHttpRequestFactory.class, long.class);
|
||||
}
|
||||
|
||||
private void registerClientHttpRequestFactoryHints(ReflectionHints hints, ClassLoader classLoader, String className,
|
||||
Runnable action) {
|
||||
hints.registerTypeIfPresent(classLoader, className, (typeHint) -> {
|
||||
typeHint.onReachableType(TypeReference.of(className));
|
||||
action.run();
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright 2012-2024 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.http.client;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
|
||||
/**
|
||||
* Settings that can be applied when creating a {@link ClientHttpRequestFactory}.
|
||||
*
|
||||
* @param connectTimeout the connect timeout
|
||||
* @param readTimeout the read timeout
|
||||
* @param sslBundle the SSL bundle providing SSL configuration
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @author Scott Frederick
|
||||
* @since 3.4.0
|
||||
* @see ClientHttpRequestFactoryBuilder
|
||||
*/
|
||||
public record ClientHttpRequestFactorySettings(Duration connectTimeout, Duration readTimeout, SslBundle sslBundle) {
|
||||
|
||||
private static final ClientHttpRequestFactorySettings defaults = new ClientHttpRequestFactorySettings(null, null,
|
||||
null);
|
||||
|
||||
/**
|
||||
* Return a new {@link ClientHttpRequestFactorySettings} instance with an updated
|
||||
* connect timeout setting .
|
||||
* @param connectTimeout the new connect timeout setting
|
||||
* @return a new {@link ClientHttpRequestFactorySettings} instance
|
||||
*/
|
||||
public ClientHttpRequestFactorySettings withConnectTimeout(Duration connectTimeout) {
|
||||
return new ClientHttpRequestFactorySettings(connectTimeout, this.readTimeout, this.sslBundle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link ClientHttpRequestFactorySettings} instance with an updated read
|
||||
* timeout setting.
|
||||
* @param readTimeout the new read timeout setting
|
||||
* @return a new {@link ClientHttpRequestFactorySettings} instance
|
||||
*/
|
||||
|
||||
public ClientHttpRequestFactorySettings withReadTimeout(Duration readTimeout) {
|
||||
return new ClientHttpRequestFactorySettings(this.connectTimeout, readTimeout, this.sslBundle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link ClientHttpRequestFactorySettings} instance with an updated SSL
|
||||
* bundle setting.
|
||||
* @param sslBundle the new SSL bundle setting
|
||||
* @return a new {@link ClientHttpRequestFactorySettings} instance
|
||||
*/
|
||||
public ClientHttpRequestFactorySettings withSslBundle(SslBundle sslBundle) {
|
||||
return new ClientHttpRequestFactorySettings(this.connectTimeout, this.readTimeout, sslBundle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link ClientHttpRequestFactorySettings} using defaults for all
|
||||
* settings other than the provided SSL bundle.
|
||||
* @param sslBundle the SSL bundle setting
|
||||
* @return a new {@link ClientHttpRequestFactorySettings} instance
|
||||
*/
|
||||
public static ClientHttpRequestFactorySettings ofSslBundle(SslBundle sslBundle) {
|
||||
return defaults().withSslBundle(sslBundle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use defaults for the {@link ClientHttpRequestFactory} which can differ depending on
|
||||
* the implementation.
|
||||
* @return default settings
|
||||
*/
|
||||
public static ClientHttpRequestFactorySettings defaults() {
|
||||
return defaults;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright 2012-2024 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.http.client;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.apache.hc.client5.http.classic.HttpClient;
|
||||
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
|
||||
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
|
||||
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
|
||||
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
|
||||
import org.apache.hc.client5.http.ssl.DefaultHostnameVerifier;
|
||||
import org.apache.hc.core5.http.io.SocketConfig;
|
||||
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.boot.ssl.SslOptions;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Builder for {@link ClientHttpRequestFactoryBuilder#httpComponents()}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Scott Frederick
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public final class HttpComponentsClientHttpRequestFactoryBuilder
|
||||
extends AbstractClientHttpRequestFactoryBuilder<HttpComponentsClientHttpRequestFactory> {
|
||||
|
||||
HttpComponentsClientHttpRequestFactoryBuilder() {
|
||||
this(Collections.emptyList());
|
||||
}
|
||||
|
||||
private HttpComponentsClientHttpRequestFactoryBuilder(
|
||||
List<Consumer<HttpComponentsClientHttpRequestFactory>> customizers) {
|
||||
super(customizers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpComponentsClientHttpRequestFactoryBuilder withCustomizer(
|
||||
Consumer<HttpComponentsClientHttpRequestFactory> customizer) {
|
||||
return new HttpComponentsClientHttpRequestFactoryBuilder(mergedCustomizers(customizer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpComponentsClientHttpRequestFactoryBuilder withCustomizers(
|
||||
Collection<Consumer<HttpComponentsClientHttpRequestFactory>> customizers) {
|
||||
return new HttpComponentsClientHttpRequestFactoryBuilder(mergedCustomizers(customizers));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpComponentsClientHttpRequestFactory createClientHttpRequestFactory(
|
||||
ClientHttpRequestFactorySettings settings) {
|
||||
HttpClient httpClient = createHttpClient(settings.readTimeout(), settings.sslBundle());
|
||||
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(settings::connectTimeout).asInt(Duration::toMillis).to(factory::setConnectTimeout);
|
||||
return factory;
|
||||
}
|
||||
|
||||
private HttpClient createHttpClient(Duration readTimeout, SslBundle sslBundle) {
|
||||
return HttpClientBuilder.create()
|
||||
.useSystemProperties()
|
||||
.setConnectionManager(createConnectionManager(readTimeout, sslBundle))
|
||||
.build();
|
||||
}
|
||||
|
||||
private PoolingHttpClientConnectionManager createConnectionManager(Duration readTimeout, SslBundle sslBundle) {
|
||||
PoolingHttpClientConnectionManagerBuilder connectionManagerBuilder = PoolingHttpClientConnectionManagerBuilder
|
||||
.create();
|
||||
if (readTimeout != null) {
|
||||
connectionManagerBuilder.setDefaultSocketConfig(createSocketConfig(readTimeout));
|
||||
}
|
||||
if (sslBundle != null) {
|
||||
connectionManagerBuilder.setTlsSocketStrategy(createTlsSocketStrategy(sslBundle));
|
||||
}
|
||||
PoolingHttpClientConnectionManager connectionManager = connectionManagerBuilder.useSystemProperties().build();
|
||||
return connectionManager;
|
||||
}
|
||||
|
||||
private DefaultClientTlsStrategy createTlsSocketStrategy(SslBundle sslBundle) {
|
||||
SslOptions options = sslBundle.getOptions();
|
||||
DefaultClientTlsStrategy tlsSocketStrategy = new DefaultClientTlsStrategy(sslBundle.createSslContext(),
|
||||
options.getEnabledProtocols(), options.getCiphers(), null, new DefaultHostnameVerifier());
|
||||
return tlsSocketStrategy;
|
||||
}
|
||||
|
||||
private SocketConfig createSocketConfig(Duration readTimeout) {
|
||||
return SocketConfig.custom().setSoTimeout((int) readTimeout.toMillis(), TimeUnit.MILLISECONDS).build();
|
||||
}
|
||||
|
||||
static class Classes {
|
||||
|
||||
static final String HTTP_CLIENTS = "org.apache.hc.client5.http.impl.classic.HttpClients";
|
||||
|
||||
static final boolean PRESENT = ClassUtils.isPresent(HTTP_CLIENTS, null);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright 2012-2024 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.http.client;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.http.client.JdkClientHttpRequestFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Builder for {@link ClientHttpRequestFactoryBuilder#jdk()}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Scott Frederick
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public class JdkClientHttpRequestFactoryBuilder
|
||||
extends AbstractClientHttpRequestFactoryBuilder<JdkClientHttpRequestFactory> {
|
||||
|
||||
JdkClientHttpRequestFactoryBuilder() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
private JdkClientHttpRequestFactoryBuilder(List<Consumer<JdkClientHttpRequestFactory>> customizers) {
|
||||
super(customizers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdkClientHttpRequestFactoryBuilder withCustomizer(Consumer<JdkClientHttpRequestFactory> customizer) {
|
||||
return new JdkClientHttpRequestFactoryBuilder(mergedCustomizers(customizer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdkClientHttpRequestFactoryBuilder withCustomizers(
|
||||
Collection<Consumer<JdkClientHttpRequestFactory>> customizers) {
|
||||
return new JdkClientHttpRequestFactoryBuilder(mergedCustomizers(customizers));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JdkClientHttpRequestFactory createClientHttpRequestFactory(ClientHttpRequestFactorySettings settings) {
|
||||
HttpClient httpClient = createHttpClient(settings.connectTimeout(), settings.sslBundle());
|
||||
JdkClientHttpRequestFactory requestFactory = new JdkClientHttpRequestFactory(httpClient);
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(settings::readTimeout).to(requestFactory::setReadTimeout);
|
||||
return requestFactory;
|
||||
}
|
||||
|
||||
private HttpClient createHttpClient(Duration connectTimeout, SslBundle sslBundle) {
|
||||
HttpClient.Builder httpClientBuilder = HttpClient.newBuilder();
|
||||
if (connectTimeout != null) {
|
||||
httpClientBuilder.connectTimeout(connectTimeout);
|
||||
}
|
||||
if (sslBundle != null) {
|
||||
httpClientBuilder.sslContext(sslBundle.createSslContext());
|
||||
}
|
||||
return httpClientBuilder.build();
|
||||
}
|
||||
|
||||
static class Classes {
|
||||
|
||||
static final String HTTP_CLIENT = "java.net.http.HttpClient";
|
||||
|
||||
static final boolean PRESENT = ClassUtils.isPresent(HTTP_CLIENT, null);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright 2012-2024 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.http.client;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.transport.HttpClientTransportDynamic;
|
||||
import org.eclipse.jetty.io.ClientConnector;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.http.client.JettyClientHttpRequestFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Builder for {@link ClientHttpRequestFactoryBuilder#jetty()}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Scott Frederick
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public final class JettyClientHttpRequestFactoryBuilder
|
||||
extends AbstractClientHttpRequestFactoryBuilder<JettyClientHttpRequestFactory> {
|
||||
|
||||
JettyClientHttpRequestFactoryBuilder() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
private JettyClientHttpRequestFactoryBuilder(List<Consumer<JettyClientHttpRequestFactory>> customizers) {
|
||||
super(customizers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JettyClientHttpRequestFactoryBuilder withCustomizer(Consumer<JettyClientHttpRequestFactory> customizer) {
|
||||
return new JettyClientHttpRequestFactoryBuilder(mergedCustomizers(customizer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public JettyClientHttpRequestFactoryBuilder withCustomizers(
|
||||
Collection<Consumer<JettyClientHttpRequestFactory>> customizers) {
|
||||
return new JettyClientHttpRequestFactoryBuilder(mergedCustomizers(customizers));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JettyClientHttpRequestFactory createClientHttpRequestFactory(ClientHttpRequestFactorySettings settings) {
|
||||
JettyClientHttpRequestFactory requestFactory = createRequestFactory(settings.sslBundle());
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(settings::connectTimeout).asInt(Duration::toMillis).to(requestFactory::setConnectTimeout);
|
||||
map.from(settings::readTimeout).asInt(Duration::toMillis).to(requestFactory::setReadTimeout);
|
||||
return requestFactory;
|
||||
}
|
||||
|
||||
private static JettyClientHttpRequestFactory createRequestFactory(SslBundle sslBundle) {
|
||||
if (sslBundle != null) {
|
||||
SSLContext sslContext = sslBundle.createSslContext();
|
||||
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
|
||||
sslContextFactory.setSslContext(sslContext);
|
||||
ClientConnector connector = new ClientConnector();
|
||||
connector.setSslContextFactory(sslContextFactory);
|
||||
HttpClient httpClient = new HttpClient(new HttpClientTransportDynamic(connector));
|
||||
return new JettyClientHttpRequestFactory(httpClient);
|
||||
}
|
||||
return new JettyClientHttpRequestFactory();
|
||||
}
|
||||
|
||||
static class Classes {
|
||||
|
||||
static final String HTTP_CLIENT = "org.eclipse.jetty.client.HttpClient";
|
||||
|
||||
static final boolean PRESENT = ClassUtils.isPresent(HTTP_CLIENT, null);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Copyright 2012-2024 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.http.client;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
import reactor.netty.tcp.SslProvider.SslContextSpec;
|
||||
|
||||
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.ReactorClientHttpRequestFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.function.ThrowingConsumer;
|
||||
|
||||
/**
|
||||
* Builder for {@link ClientHttpRequestFactoryBuilder#reactor()}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Scott Frederick
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public final class ReactorClientHttpRequestFactoryBuilder
|
||||
extends AbstractClientHttpRequestFactoryBuilder<ReactorClientHttpRequestFactory> {
|
||||
|
||||
ReactorClientHttpRequestFactoryBuilder() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
private ReactorClientHttpRequestFactoryBuilder(List<Consumer<ReactorClientHttpRequestFactory>> customizers) {
|
||||
super(customizers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactorClientHttpRequestFactoryBuilder withCustomizer(Consumer<ReactorClientHttpRequestFactory> customizer) {
|
||||
return new ReactorClientHttpRequestFactoryBuilder(mergedCustomizers(customizer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactorClientHttpRequestFactoryBuilder withCustomizers(
|
||||
Collection<Consumer<ReactorClientHttpRequestFactory>> customizers) {
|
||||
return new ReactorClientHttpRequestFactoryBuilder(mergedCustomizers(customizers));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReactorClientHttpRequestFactory createClientHttpRequestFactory(
|
||||
ClientHttpRequestFactorySettings settings) {
|
||||
ReactorClientHttpRequestFactory requestFactory = createRequestFactory(settings.sslBundle());
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(settings::connectTimeout).asInt(Duration::toMillis).to(requestFactory::setConnectTimeout);
|
||||
map.from(settings::readTimeout).asInt(Duration::toMillis).to(requestFactory::setReadTimeout);
|
||||
return requestFactory;
|
||||
}
|
||||
|
||||
private ReactorClientHttpRequestFactory createRequestFactory(SslBundle sslBundle) {
|
||||
HttpClient httpClient = HttpClient.create();
|
||||
httpClient = applyDefaults(httpClient);
|
||||
if (sslBundle != null) {
|
||||
httpClient = httpClient.secure((ThrowingConsumer.of((spec) -> configureSsl(spec, sslBundle))));
|
||||
}
|
||||
return new ReactorClientHttpRequestFactory(httpClient);
|
||||
}
|
||||
|
||||
HttpClient applyDefaults(HttpClient httpClient) {
|
||||
// Aligns with ReactorClientHttpRequestFactory defaults
|
||||
return httpClient.compress(true);
|
||||
}
|
||||
|
||||
private void configureSsl(SslContextSpec spec, SslBundle sslBundle) throws SSLException {
|
||||
SslOptions options = sslBundle.getOptions();
|
||||
SslManagerBundle managers = sslBundle.getManagers();
|
||||
SslContextBuilder builder = SslContextBuilder.forClient()
|
||||
.keyManager(managers.getKeyManagerFactory())
|
||||
.trustManager(managers.getTrustManagerFactory())
|
||||
.ciphers(SslOptions.asSet(options.getCiphers()))
|
||||
.protocols(options.getEnabledProtocols());
|
||||
spec.sslContext(builder.build());
|
||||
}
|
||||
|
||||
static class Classes {
|
||||
|
||||
static final String HTTP_CLIENT = "reactor.netty.http.client.HttpClient";
|
||||
|
||||
static final boolean PRESENT = ClassUtils.isPresent(HTTP_CLIENT, null);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Copyright 2012-2024 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.http.client;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.Duration;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.http.client.AbstractClientHttpRequestFactoryWrapper;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Internal builder for {@link ClientHttpRequestFactoryBuilder#of(Class)} and
|
||||
* {@link ClientHttpRequestFactoryBuilder#of(Supplier)}.
|
||||
*
|
||||
* @param <T> the {@link ClientHttpRequestFactory} type
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
final class ReflectiveComponentsClientHttpRequestFactoryBuilder<T extends ClientHttpRequestFactory>
|
||||
implements ClientHttpRequestFactoryBuilder<T> {
|
||||
|
||||
private Supplier<T> requestFactorySupplier;
|
||||
|
||||
ReflectiveComponentsClientHttpRequestFactoryBuilder(Supplier<T> requestFactorySupplier) {
|
||||
Assert.notNull(requestFactorySupplier, "'requestFactorySupplier' must not be null");
|
||||
this.requestFactorySupplier = requestFactorySupplier;
|
||||
}
|
||||
|
||||
ReflectiveComponentsClientHttpRequestFactoryBuilder(Class<T> requestFactoryType) {
|
||||
Assert.notNull(requestFactoryType, "'requestFactoryType' must not be null");
|
||||
this.requestFactorySupplier = () -> createRequestFactory(requestFactoryType);
|
||||
}
|
||||
|
||||
private static <T extends ClientHttpRequestFactory> T createRequestFactory(Class<T> requestFactory) {
|
||||
try {
|
||||
Constructor<T> constructor = requestFactory.getDeclaredConstructor();
|
||||
constructor.setAccessible(true);
|
||||
return constructor.newInstance();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T build(ClientHttpRequestFactorySettings settings) {
|
||||
T requestFactory = this.requestFactorySupplier.get();
|
||||
if (settings != null) {
|
||||
configure(requestFactory, settings);
|
||||
}
|
||||
return requestFactory;
|
||||
}
|
||||
|
||||
private void configure(ClientHttpRequestFactory requestFactory, ClientHttpRequestFactorySettings settings) {
|
||||
Assert.state(settings.sslBundle() == null, "Unable to set SSL bundler using reflection");
|
||||
ClientHttpRequestFactory unwrapped = unwrapRequestFactoryIfNecessary(requestFactory);
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(settings::connectTimeout).to((connectTimeout) -> setConnectTimeout(unwrapped, connectTimeout));
|
||||
map.from(settings::readTimeout).to((readTimeout) -> setReadTimeout(unwrapped, readTimeout));
|
||||
}
|
||||
|
||||
private ClientHttpRequestFactory unwrapRequestFactoryIfNecessary(ClientHttpRequestFactory requestFactory) {
|
||||
if (!(requestFactory instanceof AbstractClientHttpRequestFactoryWrapper)) {
|
||||
return requestFactory;
|
||||
}
|
||||
Field field = ReflectionUtils.findField(AbstractClientHttpRequestFactoryWrapper.class, "requestFactory");
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
ClientHttpRequestFactory unwrappedRequestFactory = requestFactory;
|
||||
while (unwrappedRequestFactory instanceof AbstractClientHttpRequestFactoryWrapper) {
|
||||
unwrappedRequestFactory = (ClientHttpRequestFactory) ReflectionUtils.getField(field,
|
||||
unwrappedRequestFactory);
|
||||
}
|
||||
return unwrappedRequestFactory;
|
||||
}
|
||||
|
||||
private void setConnectTimeout(ClientHttpRequestFactory factory, Duration connectTimeout) {
|
||||
Method method = tryFindMethod(factory, "setConnectTimeout", Duration.class);
|
||||
if (method != null) {
|
||||
invoke(factory, method, connectTimeout);
|
||||
return;
|
||||
}
|
||||
method = findMethod(factory, "setConnectTimeout", int.class);
|
||||
int timeout = Math.toIntExact(connectTimeout.toMillis());
|
||||
invoke(factory, method, timeout);
|
||||
}
|
||||
|
||||
private void setReadTimeout(ClientHttpRequestFactory factory, Duration readTimeout) {
|
||||
Method method = tryFindMethod(factory, "setReadTimeout", Duration.class);
|
||||
if (method != null) {
|
||||
invoke(factory, method, readTimeout);
|
||||
return;
|
||||
}
|
||||
method = findMethod(factory, "setReadTimeout", int.class);
|
||||
int timeout = Math.toIntExact(readTimeout.toMillis());
|
||||
invoke(factory, method, timeout);
|
||||
}
|
||||
|
||||
private Method findMethod(ClientHttpRequestFactory requestFactory, String methodName, Class<?>... parameters) {
|
||||
Method method = ReflectionUtils.findMethod(requestFactory.getClass(), methodName, parameters);
|
||||
Assert.state(method != null, () -> "Request factory %s does not have a suitable %s method"
|
||||
.formatted(requestFactory.getClass().getName(), methodName));
|
||||
Assert.state(!method.isAnnotationPresent(Deprecated.class),
|
||||
() -> "Request factory %s has the %s method marked as deprecated"
|
||||
.formatted(requestFactory.getClass().getName(), methodName));
|
||||
return method;
|
||||
}
|
||||
|
||||
private Method tryFindMethod(ClientHttpRequestFactory requestFactory, String methodName, Class<?>... parameters) {
|
||||
Method method = ReflectionUtils.findMethod(requestFactory.getClass(), methodName, parameters);
|
||||
if (method == null) {
|
||||
return null;
|
||||
}
|
||||
if (method.isAnnotationPresent(Deprecated.class)) {
|
||||
return null;
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
private void invoke(ClientHttpRequestFactory requestFactory, Method method, Object... parameters) {
|
||||
ReflectionUtils.invokeMethod(method, requestFactory, parameters);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright 2012-2024 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.http.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Builder for {@link ClientHttpRequestFactoryBuilder#simple()}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Scott Frederick
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public final class SimpleClientHttpRequestFactoryBuilder
|
||||
extends AbstractClientHttpRequestFactoryBuilder<SimpleClientHttpRequestFactory> {
|
||||
|
||||
SimpleClientHttpRequestFactoryBuilder() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
private SimpleClientHttpRequestFactoryBuilder(List<Consumer<SimpleClientHttpRequestFactory>> customizers) {
|
||||
super(customizers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleClientHttpRequestFactoryBuilder withCustomizer(Consumer<SimpleClientHttpRequestFactory> customizer) {
|
||||
return new SimpleClientHttpRequestFactoryBuilder(mergedCustomizers(customizer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleClientHttpRequestFactoryBuilder withCustomizers(
|
||||
Collection<Consumer<SimpleClientHttpRequestFactory>> customizers) {
|
||||
return new SimpleClientHttpRequestFactoryBuilder(mergedCustomizers(customizers));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SimpleClientHttpRequestFactory createClientHttpRequestFactory(ClientHttpRequestFactorySettings settings) {
|
||||
SslBundle sslBundle = settings.sslBundle();
|
||||
SimpleClientHttpRequestFactory requestFactory = (sslBundle != null)
|
||||
? new SimpleClientHttpsRequestFactory(sslBundle) : new SimpleClientHttpRequestFactory();
|
||||
Assert.state(sslBundle == null || !sslBundle.getOptions().isSpecified(),
|
||||
"SSL Options cannot be specified with Java connections");
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(settings::readTimeout).asInt(Duration::toMillis).to(requestFactory::setReadTimeout);
|
||||
map.from(settings::connectTimeout).asInt(Duration::toMillis).to(requestFactory::setConnectTimeout);
|
||||
return requestFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link SimpleClientHttpsRequestFactory} to configure SSL from an {@link SslBundle}.
|
||||
*/
|
||||
private static class SimpleClientHttpsRequestFactory extends SimpleClientHttpRequestFactory {
|
||||
|
||||
private final SslBundle sslBundle;
|
||||
|
||||
SimpleClientHttpsRequestFactory(SslBundle sslBundle) {
|
||||
this.sslBundle = sslBundle;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
|
||||
super.prepareConnection(connection, httpMethod);
|
||||
if (this.sslBundle != null && connection instanceof HttpsURLConnection secureConnection) {
|
||||
SSLSocketFactory socketFactory = this.sslBundle.createSslContext().getSocketFactory();
|
||||
secureConnection.setSSLSocketFactory(socketFactory);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2012-2024 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Client-side HTTP support classes.
|
||||
*/
|
||||
package org.springframework.boot.http.client;
|
|
@ -18,6 +18,7 @@ package org.springframework.boot.web.client;
|
|||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
|
||||
|
@ -31,7 +32,7 @@ import org.springframework.http.client.ClientHttpRequestFactory;
|
|||
* @author Phillip Webb
|
||||
* @author Scott Frederick
|
||||
* @since 3.0.0
|
||||
* @see ClientHttpRequestFactories
|
||||
* @see ClientHttpRequestFactoryBuilder
|
||||
*/
|
||||
public record ClientHttpRequestFactorySettings(Duration connectTimeout, Duration readTimeout, SslBundle sslBundle) {
|
||||
|
||||
|
@ -74,4 +75,15 @@ public record ClientHttpRequestFactorySettings(Duration connectTimeout, Duration
|
|||
return new ClientHttpRequestFactorySettings(this.connectTimeout, this.readTimeout, sslBundle);
|
||||
}
|
||||
|
||||
org.springframework.boot.http.client.ClientHttpRequestFactorySettings adapt() {
|
||||
return new org.springframework.boot.http.client.ClientHttpRequestFactorySettings(connectTimeout(),
|
||||
readTimeout(), sslBundle());
|
||||
}
|
||||
|
||||
static ClientHttpRequestFactorySettings of(
|
||||
org.springframework.boot.http.client.ClientHttpRequestFactorySettings settings) {
|
||||
return new ClientHttpRequestFactorySettings(settings.connectTimeout(), settings.readTimeout(),
|
||||
settings.sslBundle());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,11 +5,11 @@ org.springframework.boot.WebApplicationType.WebApplicationTypeRuntimeHints,\
|
|||
org.springframework.boot.context.config.ConfigDataLocationRuntimeHints,\
|
||||
org.springframework.boot.context.config.ConfigDataPropertiesRuntimeHints,\
|
||||
org.springframework.boot.env.PropertySourceRuntimeHints,\
|
||||
org.springframework.boot.http.client.ClientHttpRequestFactoryRuntimeHints,\
|
||||
org.springframework.boot.jdbc.DataSourceBuilderRuntimeHints,\
|
||||
org.springframework.boot.json.JacksonRuntimeHints,\
|
||||
org.springframework.boot.logging.java.JavaLoggingSystemRuntimeHints,\
|
||||
org.springframework.boot.logging.logback.LogbackRuntimeHints,\
|
||||
org.springframework.boot.web.client.ClientHttpRequestFactoriesRuntimeHints,\
|
||||
org.springframework.boot.web.embedded.undertow.UndertowWebServer.UndertowWebServerRuntimeHints,\
|
||||
org.springframework.boot.web.server.MimeMappings.MimeMappingsRuntimeHints
|
||||
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright 2012-2024 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.http.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServlet;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.boot.ssl.SslBundleKey;
|
||||
import org.springframework.boot.ssl.jks.JksSslStoreBundle;
|
||||
import org.springframework.boot.ssl.jks.JksSslStoreDetails;
|
||||
import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.Ssl;
|
||||
import org.springframework.boot.web.server.Ssl.ClientAuth;
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Base class for {@link ClientHttpRequestFactoryBuilder} tests.
|
||||
*
|
||||
* @param <T> The {@link ClientHttpRequestFactory} type
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@DirtiesUrlFactories
|
||||
abstract class AbstractClientHttpRequestFactoryBuilderTests<T extends ClientHttpRequestFactory> {
|
||||
|
||||
private final Class<T> requestFactoryType;
|
||||
|
||||
private final ClientHttpRequestFactoryBuilder<T> builder;
|
||||
|
||||
AbstractClientHttpRequestFactoryBuilderTests(Class<T> requestFactoryType,
|
||||
ClientHttpRequestFactoryBuilder<T> builder) {
|
||||
this.requestFactoryType = requestFactoryType;
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildReturnsRequestFactoryOfExpectedType() {
|
||||
T requestFactory = this.builder.build();
|
||||
assertThat(requestFactory).isInstanceOf(this.requestFactoryType);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildWhenHasConnectTimeout() {
|
||||
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.defaults()
|
||||
.withConnectTimeout(Duration.ofSeconds(60));
|
||||
T requestFactory = this.builder.build(settings);
|
||||
assertThat(connectTimeout(requestFactory)).isEqualTo(Duration.ofSeconds(60).toMillis());
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildWhenHadReadTimeout() {
|
||||
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.defaults()
|
||||
.withReadTimeout(Duration.ofSeconds(120));
|
||||
T requestFactory = this.builder.build(settings);
|
||||
assertThat(readTimeout(requestFactory)).isEqualTo(Duration.ofSeconds(120).toMillis());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "GET", "POST" })
|
||||
void connectWithSslBundle(String httpMethod) throws Exception {
|
||||
TomcatServletWebServerFactory webServerFactory = new TomcatServletWebServerFactory(0);
|
||||
webServerFactory.setSsl(ssl());
|
||||
WebServer webServer = webServerFactory
|
||||
.getWebServer((context) -> context.addServlet("test", TestServlet.class).addMapping("/"));
|
||||
try {
|
||||
webServer.start();
|
||||
int port = webServer.getPort();
|
||||
URI uri = new URI("https://localhost:%s".formatted(port));
|
||||
ClientHttpRequestFactory insecureRequestFactory = this.builder.build();
|
||||
ClientHttpRequest insecureRequest = request(insecureRequestFactory, uri, httpMethod);
|
||||
assertThatExceptionOfType(SSLHandshakeException.class)
|
||||
.isThrownBy(() -> insecureRequest.execute().getBody());
|
||||
ClientHttpRequestFactory secureRequestFactory = this.builder
|
||||
.build(ClientHttpRequestFactorySettings.ofSslBundle(sslBundle()));
|
||||
ClientHttpRequest secureRequest = request(secureRequestFactory, uri, httpMethod);
|
||||
String secureResponse = StreamUtils.copyToString(secureRequest.execute().getBody(), StandardCharsets.UTF_8);
|
||||
assertThat(secureResponse).contains("Received " + httpMethod + " request to /");
|
||||
}
|
||||
finally {
|
||||
webServer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private ClientHttpRequest request(ClientHttpRequestFactory factory, URI uri, String method) throws IOException {
|
||||
return factory.createRequest(uri, HttpMethod.valueOf(method));
|
||||
}
|
||||
|
||||
private Ssl ssl() {
|
||||
Ssl ssl = new Ssl();
|
||||
ssl.setClientAuth(ClientAuth.NEED);
|
||||
ssl.setKeyPassword("password");
|
||||
ssl.setKeyStore("classpath:test.jks");
|
||||
ssl.setTrustStore("classpath:test.jks");
|
||||
return ssl;
|
||||
}
|
||||
|
||||
protected final SslBundle sslBundle() {
|
||||
JksSslStoreDetails storeDetails = JksSslStoreDetails.forLocation("classpath:test.jks");
|
||||
JksSslStoreBundle stores = new JksSslStoreBundle(storeDetails, storeDetails);
|
||||
return SslBundle.of(stores, SslBundleKey.of("password"));
|
||||
}
|
||||
|
||||
protected abstract long connectTimeout(T requestFactory);
|
||||
|
||||
protected abstract long readTimeout(T requestFactory);
|
||||
|
||||
public static class TestServlet extends HttpServlet {
|
||||
|
||||
@Override
|
||||
public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
|
||||
res.getWriter().println("Received " + req.getMethod() + " request to " + req.getRequestURI());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* Copyright 2012-2024 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.http.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.http.client.JdkClientHttpRequestFactory;
|
||||
import org.springframework.http.client.JettyClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ReactorClientHttpRequestFactory;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link ClientHttpRequestFactoryBuilder}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ClientHttpRequestFactoryBuilderTests {
|
||||
|
||||
@Test
|
||||
void withCustomizerAppliesCustomizers() {
|
||||
ClientHttpRequestFactoryBuilder<JettyClientHttpRequestFactory> builder = (
|
||||
settings) -> new JettyClientHttpRequestFactory();
|
||||
builder = builder.withCustomizer(this::setJettyReadTimeout);
|
||||
JettyClientHttpRequestFactory factory = builder.build(null);
|
||||
assertThat(factory).extracting("readTimeout").isEqualTo(5000L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withCustomizersAppliesCustomizers() {
|
||||
ClientHttpRequestFactoryBuilder<JettyClientHttpRequestFactory> builder = (
|
||||
settings) -> new JettyClientHttpRequestFactory();
|
||||
builder = builder.withCustomizers(List.of(this::setJettyReadTimeout));
|
||||
JettyClientHttpRequestFactory factory = builder.build(null);
|
||||
assertThat(factory).extracting("readTimeout").isEqualTo(5000L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void httpComponentsReturnsHttpComponentsFactoryBuilder() {
|
||||
assertThat(ClientHttpRequestFactoryBuilder.httpComponents())
|
||||
.isInstanceOf(HttpComponentsClientHttpRequestFactoryBuilder.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void jettyReturnsJettyFactoryBuilder() {
|
||||
assertThat(ClientHttpRequestFactoryBuilder.jetty()).isInstanceOf(JettyClientHttpRequestFactoryBuilder.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void reactorReturnsReactorFactoryBuilder() {
|
||||
assertThat(ClientHttpRequestFactoryBuilder.reactor())
|
||||
.isInstanceOf(ReactorClientHttpRequestFactoryBuilder.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void jdkReturnsJdkFactoryBuilder() {
|
||||
assertThat(ClientHttpRequestFactoryBuilder.jdk()).isInstanceOf(JdkClientHttpRequestFactoryBuilder.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void simpleReturnsSimpleFactoryBuilder() {
|
||||
assertThat(ClientHttpRequestFactoryBuilder.simple()).isInstanceOf(SimpleClientHttpRequestFactoryBuilder.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWhenExactlyClientHttpRequestFactoryTypeThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> ClientHttpRequestFactoryBuilder.of(ClientHttpRequestFactory.class))
|
||||
.withMessage("'requestFactoryType' must be an implementation of ClientHttpRequestFactory");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWhenSimpleFactoryReturnsSimpleFactoryBuilder() {
|
||||
assertThat(ClientHttpRequestFactoryBuilder.of(SimpleClientHttpRequestFactory.class))
|
||||
.isInstanceOf(SimpleClientHttpRequestFactoryBuilder.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWhenHttpComponentsFactoryReturnsHttpComponentsFactoryBuilder() {
|
||||
assertThat(ClientHttpRequestFactoryBuilder.of(HttpComponentsClientHttpRequestFactory.class))
|
||||
.isInstanceOf(HttpComponentsClientHttpRequestFactoryBuilder.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWhenReactorFactoryReturnsReactorFactoryBuilder() {
|
||||
assertThat(ClientHttpRequestFactoryBuilder.of(ReactorClientHttpRequestFactory.class))
|
||||
.isInstanceOf(ReactorClientHttpRequestFactoryBuilder.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWhenJdkFactoryReturnsJdkFactoryBuilder() {
|
||||
assertThat(ClientHttpRequestFactoryBuilder.of(JdkClientHttpRequestFactory.class))
|
||||
.isInstanceOf(JdkClientHttpRequestFactoryBuilder.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWhenUnknownTypeReturnsReflectiveFactoryBuilder() {
|
||||
ClientHttpRequestFactoryBuilder<TestClientHttpRequestFactory> builder = ClientHttpRequestFactoryBuilder
|
||||
.of(TestClientHttpRequestFactory.class);
|
||||
assertThat(builder).isInstanceOf(ReflectiveComponentsClientHttpRequestFactoryBuilder.class);
|
||||
assertThat(builder.build(null)).isInstanceOf(TestClientHttpRequestFactory.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWithSupplierWhenSupplierIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> ClientHttpRequestFactoryBuilder.of((Supplier<ClientHttpRequestFactory>) null))
|
||||
.withMessage("'requestFactorySupplier' must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWithSupplierReturnsReflectiveFactoryBuilder() {
|
||||
assertThat(ClientHttpRequestFactoryBuilder.of(SimpleClientHttpRequestFactory::new))
|
||||
.isInstanceOf(ReflectiveComponentsClientHttpRequestFactoryBuilder.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void detectWhenHttpComponents() {
|
||||
assertThat(ClientHttpRequestFactoryBuilder.detect())
|
||||
.isInstanceOf(HttpComponentsClientHttpRequestFactoryBuilder.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ClassPathExclusions("httpclient5-*.jar")
|
||||
void detectWhenJetty() {
|
||||
assertThat(ClientHttpRequestFactoryBuilder.detect()).isInstanceOf(JettyClientHttpRequestFactoryBuilder.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ClassPathExclusions({ "httpclient5-*.jar", "jetty-client-*.jar" })
|
||||
void detectWhenReactor() {
|
||||
assertThat(ClientHttpRequestFactoryBuilder.detect()).isInstanceOf(ReactorClientHttpRequestFactoryBuilder.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ClassPathExclusions({ "httpclient5-*.jar", "jetty-client-*.jar", "reactor-netty-http-*.jar" })
|
||||
void detectWhenJdk() {
|
||||
assertThat(ClientHttpRequestFactoryBuilder.detect()).isInstanceOf(JdkClientHttpRequestFactoryBuilder.class);
|
||||
}
|
||||
|
||||
private void setJettyReadTimeout(JettyClientHttpRequestFactory factory) {
|
||||
factory.setReadTimeout(Duration.ofSeconds(5));
|
||||
}
|
||||
|
||||
public static class TestClientHttpRequestFactory implements ClientHttpRequestFactory {
|
||||
|
||||
@Override
|
||||
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.web.client;
|
||||
package org.springframework.boot.http.client;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
@ -34,17 +34,17 @@ import org.springframework.util.ReflectionUtils;
|
|||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ClientHttpRequestFactoriesRuntimeHints}.
|
||||
* Tests for {@link ClientHttpRequestFactoryRuntimeHints}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class ClientHttpRequestFactoriesRuntimeHintsTests {
|
||||
class ClientHttpRequestFactoryRuntimeHintsTests {
|
||||
|
||||
@Test
|
||||
void shouldRegisterHints() {
|
||||
RuntimeHints hints = new RuntimeHints();
|
||||
new ClientHttpRequestFactoriesRuntimeHints().registerHints(hints, getClass().getClassLoader());
|
||||
new ClientHttpRequestFactoryRuntimeHints().registerHints(hints, getClass().getClassLoader());
|
||||
ReflectionHintsPredicates reflection = RuntimeHintsPredicates.reflection();
|
||||
Field requestFactoryField = ReflectionUtils.findField(AbstractClientHttpRequestFactoryWrapper.class,
|
||||
"requestFactory");
|
||||
|
@ -55,7 +55,7 @@ class ClientHttpRequestFactoriesRuntimeHintsTests {
|
|||
@Test
|
||||
void shouldRegisterHttpComponentHints() {
|
||||
RuntimeHints hints = new RuntimeHints();
|
||||
new ClientHttpRequestFactoriesRuntimeHints().registerHints(hints, getClass().getClassLoader());
|
||||
new ClientHttpRequestFactoryRuntimeHints().registerHints(hints, getClass().getClassLoader());
|
||||
ReflectionHintsPredicates reflection = RuntimeHintsPredicates.reflection();
|
||||
assertThat(reflection
|
||||
.onMethod(method(HttpComponentsClientHttpRequestFactory.class, "setConnectTimeout", int.class)))
|
||||
|
@ -65,7 +65,7 @@ class ClientHttpRequestFactoriesRuntimeHintsTests {
|
|||
@Test
|
||||
void shouldRegisterJettyClientHints() {
|
||||
RuntimeHints hints = new RuntimeHints();
|
||||
new ClientHttpRequestFactoriesRuntimeHints().registerHints(hints, getClass().getClassLoader());
|
||||
new ClientHttpRequestFactoryRuntimeHints().registerHints(hints, getClass().getClassLoader());
|
||||
ReflectionHintsPredicates reflection = RuntimeHintsPredicates.reflection();
|
||||
assertThat(reflection.onMethod(method(JettyClientHttpRequestFactory.class, "setConnectTimeout", int.class)))
|
||||
.accepts(hints);
|
||||
|
@ -76,7 +76,7 @@ class ClientHttpRequestFactoriesRuntimeHintsTests {
|
|||
@Test
|
||||
void shouldRegisterReactorHints() {
|
||||
RuntimeHints hints = new RuntimeHints();
|
||||
new ClientHttpRequestFactoriesRuntimeHints().registerHints(hints, getClass().getClassLoader());
|
||||
new ClientHttpRequestFactoryRuntimeHints().registerHints(hints, getClass().getClassLoader());
|
||||
ReflectionHintsPredicates reflection = RuntimeHintsPredicates.reflection();
|
||||
assertThat(reflection.onMethod(method(ReactorClientHttpRequestFactory.class, "setConnectTimeout", int.class)))
|
||||
.accepts(hints);
|
||||
|
@ -87,7 +87,7 @@ class ClientHttpRequestFactoriesRuntimeHintsTests {
|
|||
@Test
|
||||
void shouldRegisterSimpleHttpHints() {
|
||||
RuntimeHints hints = new RuntimeHints();
|
||||
new ClientHttpRequestFactoriesRuntimeHints().registerHints(hints, getClass().getClassLoader());
|
||||
new ClientHttpRequestFactoryRuntimeHints().registerHints(hints, getClass().getClassLoader());
|
||||
ReflectionHintsPredicates reflection = RuntimeHintsPredicates.reflection();
|
||||
assertThat(reflection.onMethod(method(SimpleClientHttpRequestFactory.class, "setConnectTimeout", int.class)))
|
||||
.accepts(hints);
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2012-2024 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.http.client;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link ClientHttpRequestFactorySettings}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ClientHttpRequestFactorySettingsTests {
|
||||
|
||||
private static final Duration ONE_SECOND = Duration.ofSeconds(1);
|
||||
|
||||
@Test
|
||||
void defaultsHasNullValues() {
|
||||
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.defaults();
|
||||
assertThat(settings.connectTimeout()).isNull();
|
||||
assertThat(settings.readTimeout()).isNull();
|
||||
assertThat(settings.sslBundle()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void withConnectTimeoutReturnsInstanceWithUpdatedConnectionTimeout() {
|
||||
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.defaults()
|
||||
.withConnectTimeout(ONE_SECOND);
|
||||
assertThat(settings.connectTimeout()).isEqualTo(ONE_SECOND);
|
||||
assertThat(settings.readTimeout()).isNull();
|
||||
assertThat(settings.sslBundle()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void withReadTimeoutReturnsInstanceWithUpdatedReadTimeout() {
|
||||
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.defaults()
|
||||
.withReadTimeout(ONE_SECOND);
|
||||
assertThat(settings.connectTimeout()).isNull();
|
||||
assertThat(settings.readTimeout()).isEqualTo(ONE_SECOND);
|
||||
assertThat(settings.sslBundle()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void withSslBundleReturnsInstanceWithUpdatedSslBundle() {
|
||||
SslBundle sslBundle = mock(SslBundle.class);
|
||||
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.defaults()
|
||||
.withSslBundle(sslBundle);
|
||||
assertThat(settings.connectTimeout()).isNull();
|
||||
assertThat(settings.readTimeout()).isNull();
|
||||
assertThat(settings.sslBundle()).isSameAs(sslBundle);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2012-2024 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.http.client;
|
||||
|
||||
import org.apache.hc.client5.http.HttpRoute;
|
||||
import org.apache.hc.client5.http.classic.HttpClient;
|
||||
import org.apache.hc.core5.function.Resolver;
|
||||
import org.apache.hc.core5.http.io.SocketConfig;
|
||||
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
* Tests for {@link HttpComponentsClientHttpRequestFactoryBuilder}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class HttpComponentsClientHttpRequestFactoryBuilderTests
|
||||
extends AbstractClientHttpRequestFactoryBuilderTests<HttpComponentsClientHttpRequestFactory> {
|
||||
|
||||
HttpComponentsClientHttpRequestFactoryBuilderTests() {
|
||||
super(HttpComponentsClientHttpRequestFactory.class, ClientHttpRequestFactoryBuilder.httpComponents());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long connectTimeout(HttpComponentsClientHttpRequestFactory requestFactory) {
|
||||
return (long) ReflectionTestUtils.getField(requestFactory, "connectTimeout");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected long readTimeout(HttpComponentsClientHttpRequestFactory requestFactory) {
|
||||
HttpClient httpClient = requestFactory.getHttpClient();
|
||||
Object connectionManager = ReflectionTestUtils.getField(httpClient, "connManager");
|
||||
SocketConfig socketConfig = ((Resolver<HttpRoute, SocketConfig>) ReflectionTestUtils.getField(connectionManager,
|
||||
"socketConfigResolver"))
|
||||
.resolve(null);
|
||||
return socketConfig.getSoTimeout().toMilliseconds();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2012-2024 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.http.client;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.springframework.http.client.JdkClientHttpRequestFactory;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
* Tests for {@link JdkClientHttpRequestFactoryBuilder}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class JdkClientHttpRequestFactoryBuilderTests
|
||||
extends AbstractClientHttpRequestFactoryBuilderTests<JdkClientHttpRequestFactory> {
|
||||
|
||||
JdkClientHttpRequestFactoryBuilderTests() {
|
||||
super(JdkClientHttpRequestFactory.class, ClientHttpRequestFactoryBuilder.jdk());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long connectTimeout(JdkClientHttpRequestFactory requestFactory) {
|
||||
HttpClient httpClient = (HttpClient) ReflectionTestUtils.getField(requestFactory, "httpClient");
|
||||
return httpClient.connectTimeout().get().toMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long readTimeout(JdkClientHttpRequestFactory requestFactory) {
|
||||
Duration readTimeout = (Duration) ReflectionTestUtils.getField(requestFactory, "readTimeout");
|
||||
return readTimeout.toMillis();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2012-2024 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.http.client;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
|
||||
import org.springframework.http.client.JettyClientHttpRequestFactory;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
* Tests for {@link JettyClientHttpRequestFactoryBuilder}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class JettyClientHttpRequestFactoryBuilderTests
|
||||
extends AbstractClientHttpRequestFactoryBuilderTests<JettyClientHttpRequestFactory> {
|
||||
|
||||
JettyClientHttpRequestFactoryBuilderTests() {
|
||||
super(JettyClientHttpRequestFactory.class, ClientHttpRequestFactoryBuilder.jetty());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long connectTimeout(JettyClientHttpRequestFactory requestFactory) {
|
||||
return ((HttpClient) ReflectionTestUtils.getField(requestFactory, "httpClient")).getConnectTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long readTimeout(JettyClientHttpRequestFactory requestFactory) {
|
||||
return (long) ReflectionTestUtils.getField(requestFactory, "readTimeout");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright 2012-2024 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.http.client;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import io.netty.channel.ChannelOption;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
|
||||
import org.springframework.http.client.ReactorClientHttpRequestFactory;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReactorClientHttpRequestFactoryBuilder}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class ReactorClientHttpRequestFactoryBuilderTests
|
||||
extends AbstractClientHttpRequestFactoryBuilderTests<ReactorClientHttpRequestFactory> {
|
||||
|
||||
ReactorClientHttpRequestFactoryBuilderTests() {
|
||||
super(ReactorClientHttpRequestFactory.class, ClientHttpRequestFactoryBuilder.reactor());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long connectTimeout(ReactorClientHttpRequestFactory requestFactory) {
|
||||
return (int) ((HttpClient) ReflectionTestUtils.getField(requestFactory, "httpClient")).configuration()
|
||||
.options()
|
||||
.get(ChannelOption.CONNECT_TIMEOUT_MILLIS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long readTimeout(ReactorClientHttpRequestFactory requestFactory) {
|
||||
return ((Duration) ReflectionTestUtils.getField(requestFactory, "readTimeout")).toMillis();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* Copyright 2012-2024 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.http.client;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.BufferingClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.JettyClientHttpRequestFactory;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReflectiveComponentsClientHttpRequestFactoryBuilder}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ReflectiveComponentsClientHttpRequestFactoryBuilderTests
|
||||
extends AbstractClientHttpRequestFactoryBuilderTests<ClientHttpRequestFactory> {
|
||||
|
||||
ReflectiveComponentsClientHttpRequestFactoryBuilderTests() {
|
||||
super(ClientHttpRequestFactory.class, ClientHttpRequestFactoryBuilder.of(JettyClientHttpRequestFactory::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
void connectWithSslBundle(String httpMethod) throws Exception {
|
||||
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.ofSslBundle(sslBundle());
|
||||
assertThatIllegalStateException().isThrownBy(() -> ofTestRequestFactory().build(settings))
|
||||
.withMessage("Unable to set SSL bundler using reflection");
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildWithClassCreatesFactory() {
|
||||
assertThat(ofTestRequestFactory().build()).isInstanceOf(TestClientHttpRequestFactory.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildWithClassWhenHasConnectTimeout() {
|
||||
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.defaults()
|
||||
.withConnectTimeout(Duration.ofSeconds(60));
|
||||
TestClientHttpRequestFactory requestFactory = ofTestRequestFactory().build(settings);
|
||||
assertThat(requestFactory.connectTimeout).isEqualTo(Duration.ofSeconds(60).toMillis());
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildWithClassWhenHasReadTimeout() {
|
||||
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.defaults()
|
||||
.withReadTimeout(Duration.ofSeconds(90));
|
||||
TestClientHttpRequestFactory requestFactory = ofTestRequestFactory().build(settings);
|
||||
assertThat(requestFactory.readTimeout).isEqualTo(Duration.ofSeconds(90).toMillis());
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildWithClassWhenUnconfigurableTypeWithConnectTimeoutThrowsException() {
|
||||
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.defaults()
|
||||
.withConnectTimeout(Duration.ofSeconds(60));
|
||||
assertThatIllegalStateException().isThrownBy(() -> ofUnconfigurableRequestFactory().build(settings))
|
||||
.withMessageContaining("suitable setConnectTimeout method");
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildWithClassWhenUnconfigurableTypeWithReadTimeoutThrowsException() {
|
||||
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.defaults()
|
||||
.withReadTimeout(Duration.ofSeconds(60));
|
||||
assertThatIllegalStateException().isThrownBy(() -> ofUnconfigurableRequestFactory().build(settings))
|
||||
.withMessageContaining("suitable setReadTimeout method");
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildWithClassWhenDeprecatedMethodsTypeWithConnectTimeoutThrowsException() {
|
||||
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.defaults()
|
||||
.withConnectTimeout(Duration.ofSeconds(60));
|
||||
assertThatIllegalStateException().isThrownBy(() -> ofDeprecatedMethodsRequestFactory().build(settings))
|
||||
.withMessageContaining("setConnectTimeout method marked as deprecated");
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildWithClassWhenDeprecatedMethodsTypeWithReadTimeoutThrowsException() {
|
||||
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.defaults()
|
||||
.withReadTimeout(Duration.ofSeconds(60));
|
||||
assertThatIllegalStateException().isThrownBy(() -> ofDeprecatedMethodsRequestFactory().build(settings))
|
||||
.withMessageContaining("setReadTimeout method marked as deprecated");
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildWithSupplierWhenWrappedRequestFactoryTypeWithConnectTimeout() {
|
||||
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.defaults()
|
||||
.withConnectTimeout(Duration.ofMillis(1234));
|
||||
SimpleClientHttpRequestFactory wrappedRequestFactory = new SimpleClientHttpRequestFactory();
|
||||
ClientHttpRequestFactory requestFactory = ClientHttpRequestFactoryBuilder
|
||||
.of(() -> new BufferingClientHttpRequestFactory(wrappedRequestFactory))
|
||||
.build(settings);
|
||||
assertThat(requestFactory).extracting("requestFactory").isSameAs(wrappedRequestFactory);
|
||||
assertThat(wrappedRequestFactory).hasFieldOrPropertyWithValue("connectTimeout", 1234);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildWithSupplierWhenWrappedRequestFactoryTypeWithReadTimeout() {
|
||||
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.defaults()
|
||||
.withReadTimeout(Duration.ofMillis(1234));
|
||||
SimpleClientHttpRequestFactory wrappedRequestFactory = new SimpleClientHttpRequestFactory();
|
||||
ClientHttpRequestFactory requestFactory = ClientHttpRequestFactoryBuilder
|
||||
.of(() -> new BufferingClientHttpRequestFactory(wrappedRequestFactory))
|
||||
.build(settings);
|
||||
assertThat(requestFactory).extracting("requestFactory").isSameAs(wrappedRequestFactory);
|
||||
assertThat(wrappedRequestFactory).hasFieldOrPropertyWithValue("readTimeout", 1234);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildWithClassWhenHasMultipleTimeoutSettersFavorsDurationMethods() {
|
||||
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.defaults()
|
||||
.withConnectTimeout(Duration.ofSeconds(1))
|
||||
.withReadTimeout(Duration.ofSeconds(2));
|
||||
IntAndDurationTimeoutsClientHttpRequestFactory requestFactory = ClientHttpRequestFactoryBuilder
|
||||
.of(IntAndDurationTimeoutsClientHttpRequestFactory.class)
|
||||
.build(settings);
|
||||
assertThat((requestFactory).connectTimeout).isZero();
|
||||
assertThat((requestFactory).readTimeout).isZero();
|
||||
assertThat((requestFactory).connectTimeoutDuration).isEqualTo(Duration.ofSeconds(1));
|
||||
assertThat((requestFactory).readTimeoutDuration).isEqualTo(Duration.ofSeconds(2));
|
||||
}
|
||||
|
||||
private ClientHttpRequestFactoryBuilder<TestClientHttpRequestFactory> ofTestRequestFactory() {
|
||||
return ClientHttpRequestFactoryBuilder.of(TestClientHttpRequestFactory.class);
|
||||
}
|
||||
|
||||
private ClientHttpRequestFactoryBuilder<UnconfigurableClientHttpRequestFactory> ofUnconfigurableRequestFactory() {
|
||||
return ClientHttpRequestFactoryBuilder.of(UnconfigurableClientHttpRequestFactory.class);
|
||||
}
|
||||
|
||||
private ClientHttpRequestFactoryBuilder<DeprecatedMethodsClientHttpRequestFactory> ofDeprecatedMethodsRequestFactory() {
|
||||
return ClientHttpRequestFactoryBuilder.of(DeprecatedMethodsClientHttpRequestFactory.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long connectTimeout(ClientHttpRequestFactory requestFactory) {
|
||||
return ((HttpClient) ReflectionTestUtils.getField(requestFactory, "httpClient")).getConnectTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long readTimeout(ClientHttpRequestFactory requestFactory) {
|
||||
return (long) ReflectionTestUtils.getField(requestFactory, "readTimeout");
|
||||
}
|
||||
|
||||
public static class TestClientHttpRequestFactory implements ClientHttpRequestFactory {
|
||||
|
||||
private int connectTimeout;
|
||||
|
||||
private int readTimeout;
|
||||
|
||||
@Override
|
||||
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void setConnectTimeout(int timeout) {
|
||||
this.connectTimeout = timeout;
|
||||
}
|
||||
|
||||
public void setReadTimeout(int timeout) {
|
||||
this.readTimeout = timeout;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class UnconfigurableClientHttpRequestFactory implements ClientHttpRequestFactory {
|
||||
|
||||
@Override
|
||||
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class DeprecatedMethodsClientHttpRequestFactory implements ClientHttpRequestFactory {
|
||||
|
||||
@Override
|
||||
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Deprecated(since = "3.0.0", forRemoval = false)
|
||||
public void setConnectTimeout(int timeout) {
|
||||
}
|
||||
|
||||
@Deprecated(since = "3.0.0", forRemoval = false)
|
||||
public void setReadTimeout(int timeout) {
|
||||
}
|
||||
|
||||
@Deprecated(since = "3.0.0", forRemoval = false)
|
||||
public void setBufferRequestBody(boolean bufferRequestBody) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class IntAndDurationTimeoutsClientHttpRequestFactory implements ClientHttpRequestFactory {
|
||||
|
||||
private int readTimeout;
|
||||
|
||||
private int connectTimeout;
|
||||
|
||||
private Duration readTimeoutDuration;
|
||||
|
||||
private Duration connectTimeoutDuration;
|
||||
|
||||
@Override
|
||||
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void setConnectTimeout(int timeout) {
|
||||
this.connectTimeout = timeout;
|
||||
}
|
||||
|
||||
public void setReadTimeout(int timeout) {
|
||||
this.readTimeout = timeout;
|
||||
}
|
||||
|
||||
public void setConnectTimeout(Duration timeout) {
|
||||
this.connectTimeoutDuration = timeout;
|
||||
}
|
||||
|
||||
public void setReadTimeout(Duration timeout) {
|
||||
this.readTimeoutDuration = timeout;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2012-2024 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.http.client;
|
||||
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
* Tests for {@link SimpleClientHttpRequestFactoryBuilder}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class SimpleClientHttpRequestFactoryBuilderTests
|
||||
extends AbstractClientHttpRequestFactoryBuilderTests<SimpleClientHttpRequestFactory> {
|
||||
|
||||
SimpleClientHttpRequestFactoryBuilderTests() {
|
||||
super(SimpleClientHttpRequestFactory.class, ClientHttpRequestFactoryBuilder.simple());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long connectTimeout(SimpleClientHttpRequestFactory requestFactory) {
|
||||
return (int) ReflectionTestUtils.getField(requestFactory, "connectTimeout");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long readTimeout(SimpleClientHttpRequestFactory requestFactory) {
|
||||
return (int) ReflectionTestUtils.getField(requestFactory, "readTimeout");
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue