diff --git a/samples/ziti-spring-boot-client/src/main/java/org/openziti/sample/springboot/client/config/SampleConfiguration.java b/samples/ziti-spring-boot-client/src/main/java/org/openziti/sample/springboot/client/config/SampleConfiguration.java deleted file mode 100644 index 1cee2225..00000000 --- a/samples/ziti-spring-boot-client/src/main/java/org/openziti/sample/springboot/client/config/SampleConfiguration.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2018-2022 NetFoundry Inc. - * - * 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.openziti.sample.springboot.client.config; - -import java.util.Optional; -import jakarta.annotation.PreDestroy; -import org.openziti.springboot.client.web.httpclient.ZitiConnectionSocketFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class SampleConfiguration { - - private final ZitiConnectionSocketFactory zitiConnectionSocketFactory; - - @Autowired - public SampleConfiguration(ZitiConnectionSocketFactory zitiConnectionSocketFactory) { - this.zitiConnectionSocketFactory = zitiConnectionSocketFactory; - } - - @PreDestroy - public void destroy() { - Optional.ofNullable(zitiConnectionSocketFactory).ifPresent(ZitiConnectionSocketFactory::shutdown); - } - -} diff --git a/ziti-springboot-client/src/main/java/org/openziti/springboot/client/web/config/ZitiHttpClientConfiguration.java b/ziti-springboot-client/src/main/java/org/openziti/springboot/client/web/config/ZitiHttpClientConfiguration.java index c469156a..89f14486 100644 --- a/ziti-springboot-client/src/main/java/org/openziti/springboot/client/web/config/ZitiHttpClientConfiguration.java +++ b/ziti-springboot-client/src/main/java/org/openziti/springboot/client/web/config/ZitiHttpClientConfiguration.java @@ -19,30 +19,37 @@ import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; import java.util.Iterator; import java.util.Optional; import java.util.concurrent.TimeUnit; import org.apache.hc.client5.http.ConnectionKeepAliveStrategy; import org.apache.hc.client5.http.DnsResolver; +import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.classic.HttpClient; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.impl.io.DefaultHttpClientConnectionOperator; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; -import org.apache.hc.client5.http.socket.ConnectionSocketFactory; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; +import org.apache.hc.client5.http.io.HttpClientConnectionOperator; +import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy; +import org.apache.hc.client5.http.ssl.TlsSocketStrategy; +import org.apache.hc.client5.http.ssl.TrustAllStrategy; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.URIScheme; -import org.apache.hc.core5.http.config.Registry; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.message.MessageSupport; import org.apache.hc.core5.pool.PoolConcurrencyPolicy; import org.apache.hc.core5.pool.PoolReusePolicy; +import org.apache.hc.core5.ssl.SSLContextBuilder; import org.apache.hc.core5.util.TimeValue; import org.openziti.Ziti; import org.openziti.ZitiContext; -import org.openziti.springboot.client.web.httpclient.ZitiConnectionSocketFactory; -import org.openziti.springboot.client.web.httpclient.ZitiSSLConnectionSocketFactory; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; @@ -70,9 +77,6 @@ public class ZitiHttpClientConfiguration { // The default time to keep a connection alive. private static final long DEFAULT_KEEP_ALIVE_TIME_MILLIS = 20 * 1000; - private ZitiConnectionSocketFactory zitiConnectionSocketFactory; - private ZitiSSLConnectionSocketFactory zitiSSLConnectionSocketFactory; - @ConditionalOnProperty(value = "spring.ziti.client.rest-template.enabled", havingValue = "true", matchIfMissing = true) @Bean public RestTemplate zitiRestTemplate(@Qualifier("zitiRestTemplateBuilder") RestTemplateBuilder restTemplateBuilder) { @@ -107,42 +111,39 @@ public ZitiContext context(@Value("${spring.ziti.client.identity.file:}") Resour return Ziti.newContext(identityFile.getInputStream(), password.toCharArray()); } - @ConditionalOnProperty(value = "spring.ziti.client.connection-factory.enabled", havingValue = "true", matchIfMissing = true) - @Bean("zitiConnectionSocketFactory") - public ZitiConnectionSocketFactory connectionSocketFactory(ZitiContext zitiContext) { - if (zitiConnectionSocketFactory == null) { - zitiConnectionSocketFactory = new ZitiConnectionSocketFactory(zitiContext); - } - return zitiConnectionSocketFactory; - } - - @ConditionalOnProperty(value = "spring.ziti.client.ssl-connection-factory.enabled", havingValue = "true", matchIfMissing = true) - @Bean("zitiSSLConnectionSocketFactory") - public ZitiSSLConnectionSocketFactory sslConnectionSocketFactory(ZitiContext zitiContext) { - if (zitiSSLConnectionSocketFactory == null) { - zitiSSLConnectionSocketFactory = new ZitiSSLConnectionSocketFactory(zitiContext); - } - return zitiSSLConnectionSocketFactory; + @ConditionalOnProperty(value = "spring.ziti.client.tls-socket-strategy.enabled", havingValue = "true", matchIfMissing = true) + @Bean("zitiTlsSocketStrategy") + public TlsSocketStrategy zitiTlsSocketStrategy() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { + return new DefaultClientTlsStrategy(new SSLContextBuilder().loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build()); } @ConditionalOnProperty(value = "spring.ziti.client.connection-manager.enabled", havingValue = "true", matchIfMissing = true) @Bean("zitiPoolingConnectionManager") public PoolingHttpClientConnectionManager poolingConnectionManager( - @Qualifier("zitiConnectionSocketFactory") ZitiConnectionSocketFactory zitiConnectionSocketFactory, - @Qualifier("zitiSSLConnectionSocketFactory") ZitiSSLConnectionSocketFactory zitiSSLConnectionSocketFactory, @Qualifier("zitiDnsResolver") DnsResolver zitiDnsResolver, + @Qualifier("zitiTlsSocketStrategy") TlsSocketStrategy zitiTlsSocketStrategy, @Value("${spring.ziti.client.httpclient.max-total:}") Integer maxTotal, @Value("${spring.ziti.client.httpclient.max-per-route:}") Integer maxPerRoute) { - final Registry socketFactoryRegistry = RegistryBuilder.create() - .register(URIScheme.HTTPS.getId(), zitiSSLConnectionSocketFactory) - .register(URIScheme.HTTP.getId(), zitiConnectionSocketFactory) + final PoolingHttpClientConnectionManagerBuilder connectionManagerBuilder = new PoolingHttpClientConnectionManagerBuilder() { + @Override + protected HttpClientConnectionOperator createConnectionOperator( + SchemePortResolver schemePortResolver, DnsResolver dnsResolver, TlsSocketStrategy tlsSocketStrategy) { + return new DefaultHttpClientConnectionOperator( + proxy -> Ziti.getSocketFactory().createSocket(), + schemePortResolver, + dnsResolver, + RegistryBuilder.create() + .register(URIScheme.HTTPS.id, tlsSocketStrategy) + .build()); + } + }; + final PoolingHttpClientConnectionManager poolingConnectionManager = connectionManagerBuilder + .setDnsResolver(zitiDnsResolver) + .setTlsSocketStrategy(zitiTlsSocketStrategy) + .setConnPoolPolicy(PoolReusePolicy.LIFO) + .setPoolConcurrencyPolicy(PoolConcurrencyPolicy.STRICT) .build(); - - final PoolingHttpClientConnectionManager poolingConnectionManager = - new PoolingHttpClientConnectionManager(socketFactoryRegistry, PoolConcurrencyPolicy.STRICT, PoolReusePolicy.LIFO, - TimeValue.NEG_ONE_MILLISECOND, null, zitiDnsResolver, null); - Optional.ofNullable(maxTotal).ifPresent(poolingConnectionManager::setMaxTotal); Optional.ofNullable(maxPerRoute).ifPresent(poolingConnectionManager::setDefaultMaxPerRoute); return poolingConnectionManager; @@ -185,12 +186,6 @@ public CloseableHttpClient httpClient( .build(); } - @PreDestroy - public void destroy() { - Optional.ofNullable(zitiConnectionSocketFactory).ifPresent(ZitiConnectionSocketFactory::shutdown); - Optional.ofNullable(zitiSSLConnectionSocketFactory).ifPresent(ZitiSSLConnectionSocketFactory::shutdown); - } - @ConditionalOnProperty(value = "spring.ziti.client.dns-resolver.enabled", havingValue = "true", matchIfMissing = true) @Bean("zitiDnsResolver") public static DnsResolver dnsResolver() { diff --git a/ziti-springboot-client/src/main/java/org/openziti/springboot/client/web/httpclient/AbstractZitiConnectionSocketFactory.java b/ziti-springboot-client/src/main/java/org/openziti/springboot/client/web/httpclient/AbstractZitiConnectionSocketFactory.java deleted file mode 100644 index 00670824..00000000 --- a/ziti-springboot-client/src/main/java/org/openziti/springboot/client/web/httpclient/AbstractZitiConnectionSocketFactory.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2018-2022 NetFoundry Inc. - * - * 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.openziti.springboot.client.web.httpclient; - -import java.io.File; -import java.io.InputStream; -import java.util.Optional; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import org.apache.hc.client5.http.socket.ConnectionSocketFactory; -import org.openziti.Ziti; -import org.openziti.ZitiContext; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -@Getter -@Slf4j -public abstract class AbstractZitiConnectionSocketFactory implements ConnectionSocketFactory { - - private static final long DEFAULT_WAIT_TIME = 3; - - private ZitiContext ctx; - - public AbstractZitiConnectionSocketFactory(InputStream inputStream) { - this(inputStream, DEFAULT_WAIT_TIME); - } - public AbstractZitiConnectionSocketFactory(InputStream inputStream, long waitTime) { - this(inputStream, new char[0], waitTime); - } - - public AbstractZitiConnectionSocketFactory(InputStream inputStream, char[] pwd) { - this(inputStream, pwd, DEFAULT_WAIT_TIME); - } - public AbstractZitiConnectionSocketFactory(InputStream inputStream, char[] pwd, long waitTime) { - try { - ctx = Ziti.newContext(inputStream, pwd); - - // sleep to let ZitiContext initialize - new CountDownLatch(1).await(waitTime, TimeUnit.SECONDS); - - } catch (Exception e) { - log.error("Failed to create Ziti context", e); - } - } - - public AbstractZitiConnectionSocketFactory(File file) { - this(file, DEFAULT_WAIT_TIME); - } - - public AbstractZitiConnectionSocketFactory(File file, long waitTime) { - this(file, new char[0], waitTime); - } - - public AbstractZitiConnectionSocketFactory(File file, char[] pwd) { - this(file, pwd, DEFAULT_WAIT_TIME); - } - public AbstractZitiConnectionSocketFactory(File file, char[] pwd, long waitTime) { - try { - ctx = Ziti.newContext(file, pwd); - - // sleep to let ZitiContext initialize - new CountDownLatch(1).await(waitTime, TimeUnit.SECONDS); - - } catch (Exception e) { - log.error("Failed to create Ziti context", e); - } - } - - public AbstractZitiConnectionSocketFactory(String fileName) { - this(fileName, DEFAULT_WAIT_TIME); - } - - public AbstractZitiConnectionSocketFactory(String fileName, long waitTime) { - this(fileName, new char[0], waitTime); - } - - public AbstractZitiConnectionSocketFactory(String fileName, char[] pwd) { - this(fileName, pwd, DEFAULT_WAIT_TIME); - } - - public AbstractZitiConnectionSocketFactory(String fileName, char[] pwd, long waitTime) { - try { - ctx = Ziti.newContext(fileName, pwd); - - // sleep to let ZitiContext initialize - new CountDownLatch(1).await(waitTime, TimeUnit.SECONDS); - - } catch (Exception e) { - log.error("Failed to create Ziti context", e); - } - } - - public AbstractZitiConnectionSocketFactory(ZitiContext ctx) { - this.ctx = ctx; - } - - public void shutdown() { - Optional.ofNullable(getCtx()).ifPresent(ZitiContext::destroy); - } - -} diff --git a/ziti-springboot-client/src/main/java/org/openziti/springboot/client/web/httpclient/ZitiConnectionSocketFactory.java b/ziti-springboot-client/src/main/java/org/openziti/springboot/client/web/httpclient/ZitiConnectionSocketFactory.java deleted file mode 100644 index bcd9ea2a..00000000 --- a/ziti-springboot-client/src/main/java/org/openziti/springboot/client/web/httpclient/ZitiConnectionSocketFactory.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2018-2022 NetFoundry Inc. - * - * 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.openziti.springboot.client.web.httpclient; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.InetSocketAddress; -import java.net.Socket; -import javax.net.SocketFactory; -import org.apache.hc.core5.http.HttpHost; -import org.apache.hc.core5.http.protocol.HttpContext; -import org.apache.hc.core5.util.TimeValue; -import org.openziti.Ziti; -import org.openziti.ZitiContext; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class ZitiConnectionSocketFactory extends AbstractZitiConnectionSocketFactory { - - private SocketFactory socketFactory; - - public ZitiConnectionSocketFactory(InputStream inputStream) { - super(inputStream); - } - public ZitiConnectionSocketFactory(InputStream inputStream, long waitTime) { - super(inputStream, waitTime); - } - - public ZitiConnectionSocketFactory(InputStream inputStream, char[] pwd) { - super(inputStream, pwd); - } - public ZitiConnectionSocketFactory(InputStream inputStream, char[] pwd, long waitTime) { - super(inputStream, pwd, waitTime); - } - - public ZitiConnectionSocketFactory(File file) { - super(file); - } - public ZitiConnectionSocketFactory(File file, long waitTime) { - super(file, waitTime); - } - - public ZitiConnectionSocketFactory(File file, char[] pwd) { - super(file, pwd); - } - public ZitiConnectionSocketFactory(File file, char[] pwd, long waitTime) { - super(file, pwd, waitTime); - } - - public ZitiConnectionSocketFactory(String fileName) { - super(fileName); - } - public ZitiConnectionSocketFactory(String fileName, long waitTime) { - super(fileName, waitTime); - } - - public ZitiConnectionSocketFactory(String fileName, char[] pwd) { - super(fileName, pwd); - } - public ZitiConnectionSocketFactory(String fileName, char[] pwd, long waitTime) { - super(fileName, pwd, waitTime); - } - - public ZitiConnectionSocketFactory(ZitiContext ctx) { - super(ctx); - } - - @Override - public Socket createSocket(HttpContext context) throws IOException { - return Ziti.getSocketFactory().createSocket(); - } - - @Override - public Socket connectSocket(TimeValue timeValue, Socket socket, HttpHost host, InetSocketAddress inetSocketAddress, InetSocketAddress localAddress, HttpContext context) throws IOException { - - final Socket sock = socket != null ? socket : createSocket(context); - sock.connect(inetSocketAddress, timeValue.toMillisecondsIntBound()); - if (localAddress != null) { - sock.bind(localAddress); - } - return sock; - } - -} diff --git a/ziti-springboot-client/src/main/java/org/openziti/springboot/client/web/httpclient/ZitiSSLConnectionSocketFactory.java b/ziti-springboot-client/src/main/java/org/openziti/springboot/client/web/httpclient/ZitiSSLConnectionSocketFactory.java deleted file mode 100644 index 323d1a33..00000000 --- a/ziti-springboot-client/src/main/java/org/openziti/springboot/client/web/httpclient/ZitiSSLConnectionSocketFactory.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2018-2022 NetFoundry Inc. - * - * 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.openziti.springboot.client.web.httpclient; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; -import org.apache.hc.client5.http.ssl.TrustAllStrategy; -import org.apache.hc.core5.http.HttpHost; -import org.apache.hc.core5.http.protocol.HttpContext; -import org.apache.hc.core5.ssl.SSLContextBuilder; -import org.apache.hc.core5.util.TimeValue; -import org.openziti.Ziti; -import org.openziti.ZitiContext; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class ZitiSSLConnectionSocketFactory extends AbstractZitiConnectionSocketFactory { - - private SSLSocketFactory sslSocketFactory; - - public ZitiSSLConnectionSocketFactory(InputStream inputStream) { - super(inputStream); - } - public ZitiSSLConnectionSocketFactory(InputStream inputStream, long waitTime) { - super(inputStream, waitTime); - } - - public ZitiSSLConnectionSocketFactory(InputStream inputStream, char[] pwd) { - super(inputStream, pwd); - } - public ZitiSSLConnectionSocketFactory(InputStream inputStream, char[] pwd, long waitTime) { - super(inputStream, pwd, waitTime); - } - - public ZitiSSLConnectionSocketFactory(File file) { - super(file); - } - public ZitiSSLConnectionSocketFactory(File file, long waitTime) { - super(file, waitTime); - } - - public ZitiSSLConnectionSocketFactory(File file, char[] pwd) { - super(file, pwd); - } - public ZitiSSLConnectionSocketFactory(File file, char[] pwd, long waitTime) { - super(file, pwd, waitTime); - } - - public ZitiSSLConnectionSocketFactory(String fileName) { - super(fileName); - } - public ZitiSSLConnectionSocketFactory(String fileName, long waitTime) { - super(fileName, waitTime); - } - - public ZitiSSLConnectionSocketFactory(String fileName, char[] pwd) { - super(fileName, pwd); - } - public ZitiSSLConnectionSocketFactory(String fileName, char[] pwd, long waitTime) { - super(fileName, pwd, waitTime); - } - - public ZitiSSLConnectionSocketFactory(ZitiContext ctx) { - super(ctx); - } - - @Override - public Socket createSocket(HttpContext context) throws IOException { - return Ziti.getSocketFactory().createSocket(); - } - - - @Override - public Socket connectSocket(TimeValue timeValue, Socket socket, HttpHost host, InetSocketAddress inetSocketAddress, InetSocketAddress localAddress, HttpContext context) throws IOException { - - if (this.sslSocketFactory == null) { - try { - final SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build(); - sslSocketFactory = Ziti.getSSLSocketFactory(sslContext); - } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) { - throw new RuntimeException(e); - } - } - - socket.connect(inetSocketAddress, timeValue.toMillisecondsIntBound()); - - final SSLSocket sock = (SSLSocket) sslSocketFactory.createSocket(socket, inetSocketAddress.getHostName(), inetSocketAddress.getPort(), true); - if (localAddress != null) { - sock.bind(localAddress); - } - return sock; - } - -}