Polish 'Apply SslConfigurer in addition to configured mappers'
See gh-35914
This commit is contained in:
		
							parent
							
								
									e6b5322f3e
								
							
						
					
					
						commit
						3c7fbf3423
					
				| 
						 | 
				
			
			@ -16,7 +16,10 @@
 | 
			
		|||
 | 
			
		||||
package org.springframework.boot.autoconfigure.web.reactive.function.client;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.function.Supplier;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
import java.util.stream.Stream;
 | 
			
		||||
 | 
			
		||||
import javax.net.ssl.SSLException;
 | 
			
		||||
| 
						 | 
				
			
			@ -36,6 +39,7 @@ import org.springframework.util.function.ThrowingConsumer;
 | 
			
		|||
 * {@link ClientHttpConnectorFactory} for {@link ReactorClientHttpConnector}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Phillip Webb
 | 
			
		||||
 * @author Fernando Cappi
 | 
			
		||||
 */
 | 
			
		||||
class ReactorClientHttpConnectorFactory implements ClientHttpConnectorFactory<ReactorClientHttpConnector> {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -55,20 +59,19 @@ class ReactorClientHttpConnectorFactory implements ClientHttpConnectorFactory<Re
 | 
			
		|||
 | 
			
		||||
	@Override
 | 
			
		||||
	public ReactorClientHttpConnector createClientHttpConnector(SslBundle sslBundle) {
 | 
			
		||||
		final ReactorNettyHttpClientMapper mapper =
 | 
			
		||||
				Stream.concat(
 | 
			
		||||
					this.mappers.get(),
 | 
			
		||||
					Stream.ofNullable(sslBundle != null ? (ReactorNettyHttpClientMapper) new SslConfigurer(sslBundle)::configure : null))
 | 
			
		||||
				.reduce((before, after) -> (client) -> after.configure(before.configure(client)))
 | 
			
		||||
				.orElse((client) -> client);
 | 
			
		||||
 | 
			
		||||
		return new ReactorClientHttpConnector(this.reactorResourceFactory, mapper::configure);
 | 
			
		||||
		List<ReactorNettyHttpClientMapper> mappers = this.mappers.get()
 | 
			
		||||
			.collect(Collectors.toCollection(ArrayList::new));
 | 
			
		||||
		if (sslBundle != null) {
 | 
			
		||||
			mappers.add(new SslConfigurer(sslBundle));
 | 
			
		||||
		}
 | 
			
		||||
		return new ReactorClientHttpConnector(this.reactorResourceFactory,
 | 
			
		||||
				ReactorNettyHttpClientMapper.of(mappers)::configure);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Configures the Netty {@link HttpClient} with SSL.
 | 
			
		||||
	 */
 | 
			
		||||
	private static class SslConfigurer {
 | 
			
		||||
	private static class SslConfigurer implements ReactorNettyHttpClientMapper {
 | 
			
		||||
 | 
			
		||||
		private final SslBundle sslBundle;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -76,7 +79,8 @@ class ReactorClientHttpConnectorFactory implements ClientHttpConnectorFactory<Re
 | 
			
		|||
			this.sslBundle = sslBundle;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		HttpClient configure(HttpClient httpClient) {
 | 
			
		||||
		@Override
 | 
			
		||||
		public HttpClient configure(HttpClient httpClient) {
 | 
			
		||||
			return httpClient.secure(ThrowingConsumer.of(this::customizeSsl).throwing(IllegalStateException::new));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2020 the original author or authors.
 | 
			
		||||
 * Copyright 2012-2023 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
| 
						 | 
				
			
			@ -16,15 +16,19 @@
 | 
			
		|||
 | 
			
		||||
package org.springframework.boot.autoconfigure.web.reactive.function.client;
 | 
			
		||||
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
 | 
			
		||||
import reactor.netty.http.client.HttpClient;
 | 
			
		||||
 | 
			
		||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
 | 
			
		||||
import org.springframework.util.Assert;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Mapper that allows for custom modification of a {@link HttpClient} before it is used as
 | 
			
		||||
 * the basis for a {@link ReactorClientHttpConnector}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Brian Clozel
 | 
			
		||||
 * @author Phillip Webb
 | 
			
		||||
 * @since 2.3.0
 | 
			
		||||
 */
 | 
			
		||||
@FunctionalInterface
 | 
			
		||||
| 
						 | 
				
			
			@ -37,4 +41,31 @@ public interface ReactorNettyHttpClientMapper {
 | 
			
		|||
	 */
 | 
			
		||||
	HttpClient configure(HttpClient httpClient);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Return a new {@link ReactorNettyHttpClientMapper} composed of the given mappers.
 | 
			
		||||
	 * @param mappers the mappers to compose
 | 
			
		||||
	 * @return a composed {@link ReactorNettyHttpClientMapper} instance
 | 
			
		||||
	 * @since 3.1.1
 | 
			
		||||
	 */
 | 
			
		||||
	static ReactorNettyHttpClientMapper of(Collection<ReactorNettyHttpClientMapper> mappers) {
 | 
			
		||||
		Assert.notNull(mappers, "Mappers must not be null");
 | 
			
		||||
		return of(mappers.toArray(ReactorNettyHttpClientMapper[]::new));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Return a new {@link ReactorNettyHttpClientMapper} composed of the given mappers.
 | 
			
		||||
	 * @param mappers the mappers to compose
 | 
			
		||||
	 * @return a composed {@link ReactorNettyHttpClientMapper} instance
 | 
			
		||||
	 * @since 3.1.1
 | 
			
		||||
	 */
 | 
			
		||||
	static ReactorNettyHttpClientMapper of(ReactorNettyHttpClientMapper... mappers) {
 | 
			
		||||
		Assert.notNull(mappers, "Mappers must not be null");
 | 
			
		||||
		return (httpClient) -> {
 | 
			
		||||
			for (ReactorNettyHttpClientMapper mapper : mappers) {
 | 
			
		||||
				httpClient = mapper.configure(httpClient);
 | 
			
		||||
			}
 | 
			
		||||
			return httpClient;
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,6 +38,8 @@ import org.springframework.test.util.ReflectionTestUtils;
 | 
			
		|||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.mockito.Mockito.mock;
 | 
			
		||||
import static org.mockito.Mockito.spy;
 | 
			
		||||
import static org.mockito.Mockito.verify;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests for {@link ClientHttpConnectorFactoryConfiguration}.
 | 
			
		||||
| 
						 | 
				
			
			@ -86,13 +88,14 @@ class ClientHttpConnectorFactoryConfigurationTests {
 | 
			
		|||
	void shouldApplyHttpClientMapper() {
 | 
			
		||||
		JksSslStoreDetails storeDetails = JksSslStoreDetails.forLocation("classpath:test.jks");
 | 
			
		||||
		JksSslStoreBundle stores = new JksSslStoreBundle(storeDetails, storeDetails);
 | 
			
		||||
		SslBundle sslBundle = SslBundle.of(stores, SslBundleKey.of("password"));
 | 
			
		||||
		SslBundle sslBundle = spy(SslBundle.of(stores, SslBundleKey.of("password")));
 | 
			
		||||
		new ReactiveWebApplicationContextRunner()
 | 
			
		||||
			.withConfiguration(AutoConfigurations.of(ClientHttpConnectorFactoryConfiguration.ReactorNetty.class))
 | 
			
		||||
			.withUserConfiguration(CustomHttpClientMapper.class)
 | 
			
		||||
			.run((context) -> {
 | 
			
		||||
				context.getBean(ReactorClientHttpConnectorFactory.class).createClientHttpConnector(sslBundle);
 | 
			
		||||
				assertThat(CustomHttpClientMapper.called).isTrue();
 | 
			
		||||
				verify(sslBundle).getManagers();
 | 
			
		||||
			});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,99 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2023 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      https://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.boot.autoconfigure.web.reactive.function.client;
 | 
			
		||||
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
import reactor.netty.http.client.HttpClient;
 | 
			
		||||
import reactor.netty.http.client.HttpClientConfig;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests for {@link ReactorNettyHttpClientMapper}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Phillip Webb
 | 
			
		||||
 */
 | 
			
		||||
class ReactorNettyHttpClientMapperTests {
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void ofWithCollectionCreatesComposite() {
 | 
			
		||||
		ReactorNettyHttpClientMapper one = (httpClient) -> new TestHttpClient(httpClient, "1");
 | 
			
		||||
		ReactorNettyHttpClientMapper two = (httpClient) -> new TestHttpClient(httpClient, "2");
 | 
			
		||||
		ReactorNettyHttpClientMapper three = (httpClient) -> new TestHttpClient(httpClient, "3");
 | 
			
		||||
		ReactorNettyHttpClientMapper compose = ReactorNettyHttpClientMapper.of(List.of(one, two, three));
 | 
			
		||||
		TestHttpClient httpClient = (TestHttpClient) compose.configure(new TestHttpClient());
 | 
			
		||||
		assertThat(httpClient.getContent()).isEqualTo("123");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void ofWhenCollectionIsNullThrowsException() {
 | 
			
		||||
		Collection<ReactorNettyHttpClientMapper> mappers = null;
 | 
			
		||||
		assertThatIllegalArgumentException().isThrownBy(() -> ReactorNettyHttpClientMapper.of(mappers))
 | 
			
		||||
			.withMessage("Mappers must not be null");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void ofWithArrayCreatesComposite() {
 | 
			
		||||
		ReactorNettyHttpClientMapper one = (httpClient) -> new TestHttpClient(httpClient, "1");
 | 
			
		||||
		ReactorNettyHttpClientMapper two = (httpClient) -> new TestHttpClient(httpClient, "2");
 | 
			
		||||
		ReactorNettyHttpClientMapper three = (httpClient) -> new TestHttpClient(httpClient, "3");
 | 
			
		||||
		ReactorNettyHttpClientMapper compose = ReactorNettyHttpClientMapper.of(one, two, three);
 | 
			
		||||
		TestHttpClient httpClient = (TestHttpClient) compose.configure(new TestHttpClient());
 | 
			
		||||
		assertThat(httpClient.getContent()).isEqualTo("123");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void ofWhenArrayIsNullThrowsException() {
 | 
			
		||||
		ReactorNettyHttpClientMapper[] mappers = null;
 | 
			
		||||
		assertThatIllegalArgumentException().isThrownBy(() -> ReactorNettyHttpClientMapper.of(mappers))
 | 
			
		||||
			.withMessage("Mappers must not be null");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static class TestHttpClient extends HttpClient {
 | 
			
		||||
 | 
			
		||||
		private final String content;
 | 
			
		||||
 | 
			
		||||
		TestHttpClient() {
 | 
			
		||||
			this.content = "";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		TestHttpClient(HttpClient httpClient, String content) {
 | 
			
		||||
			this.content = (httpClient instanceof TestHttpClient testHttpClient) ? testHttpClient.content + content
 | 
			
		||||
					: content;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public HttpClientConfig configuration() {
 | 
			
		||||
			throw new UnsupportedOperationException("Auto-generated method stub");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		protected HttpClient duplicate() {
 | 
			
		||||
			throw new UnsupportedOperationException("Auto-generated method stub");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		String getContent() {
 | 
			
		||||
			return this.content;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue