SOLR-14260: SolrJ pluggable ConnectionSocketFactory in HttpClientUtil

see SocketFactoryRegistryProvider
Fixes #1261
This commit is contained in:
Andy Throgmorton 2020-03-26 22:15:50 -04:00 committed by David Smiley
parent a31ecd2eb8
commit d1601f6fdf
8 changed files with 70 additions and 50 deletions

View File

@ -61,7 +61,10 @@ New Features
Improvements
---------------------
* SOLR-14316: Remove unchecked type conversion warning in JavaBinCodec's readMapEntry's equals() method
(Aroop Ganguly, Tomás Fernández Löbbe, Noble Paul, Anshum Gupta)
(Aroop Ganguly, Tomás Fernández Löbbe, Noble Paul, Anshum Gupta)
* SOLR-14260: Make SolrJ ConnectionSocketFactory pluggable via SocketFactoryRegistryProvider setting on HttpClientUtil
(Andy Throgmorton via David Smiley)
Optimizations
---------------------

View File

@ -95,9 +95,9 @@ public class UpdateShardHandler implements SolrInfoBean {
private int connectionTimeout = HttpClientUtil.DEFAULT_CONNECT_TIMEOUT;
public UpdateShardHandler(UpdateShardHandlerConfig cfg) {
updateOnlyConnectionManager = new InstrumentedPoolingHttpClientConnectionManager(HttpClientUtil.getSchemaRegisteryProvider().getSchemaRegistry());
recoveryOnlyConnectionManager = new InstrumentedPoolingHttpClientConnectionManager(HttpClientUtil.getSchemaRegisteryProvider().getSchemaRegistry());
defaultConnectionManager = new InstrumentedPoolingHttpClientConnectionManager(HttpClientUtil.getSchemaRegisteryProvider().getSchemaRegistry());
updateOnlyConnectionManager = new InstrumentedPoolingHttpClientConnectionManager(HttpClientUtil.getSocketFactoryRegistryProvider().getSocketFactoryRegistry());
recoveryOnlyConnectionManager = new InstrumentedPoolingHttpClientConnectionManager(HttpClientUtil.getSocketFactoryRegistryProvider().getSocketFactoryRegistry());
defaultConnectionManager = new InstrumentedPoolingHttpClientConnectionManager(HttpClientUtil.getSocketFactoryRegistryProvider().getSocketFactoryRegistry());
ModifiableSolrParams clientParams = new ModifiableSolrParams();
if (cfg != null ) {
updateOnlyConnectionManager.setMaxTotal(cfg.getMaxUpdateConnections());

View File

@ -71,7 +71,7 @@ public class SSLMigrationTest extends AbstractFullDistribZkTestBase {
runner.stop();
}
HttpClientUtil.setSchemaRegistryProvider(sslConfig.buildClientSchemaRegistryProvider());
HttpClientUtil.setSocketFactoryRegistryProvider(sslConfig.buildClientSocketFactoryRegistryProvider());
for(int i = 0; i < this.jettys.size(); i++) {
JettySolrRunner runner = jettys.get(i);
JettyConfig config = JettyConfig.builder()

View File

@ -87,13 +87,13 @@ public class TestMiniSolrCloudClusterSSL extends SolrTestCaseJ4 {
public void before() {
// undo the randomization of our super class
log.info("NOTE: This Test ignores the randomized SSL & clientAuth settings selected by base class");
HttpClientUtil.resetHttpClientBuilder(); // also resets SchemaRegistryProvider
HttpClientUtil.resetHttpClientBuilder(); // also resets SocketFactoryRegistryProvider
Http2SolrClient.resetSslContextFactory();
System.clearProperty(ZkStateReader.URL_SCHEME);
}
@After
public void after() {
HttpClientUtil.resetHttpClientBuilder(); // also resets SchemaRegistryProvider
HttpClientUtil.resetHttpClientBuilder(); // also resets SocketFactoryRegistryProvider
Http2SolrClient.resetSslContextFactory();
System.clearProperty(ZkStateReader.URL_SCHEME);
SSLContext.setDefault(DEFAULT_SSL_CONTEXT);
@ -101,7 +101,7 @@ public class TestMiniSolrCloudClusterSSL extends SolrTestCaseJ4 {
public void testNoSsl() throws Exception {
final SSLTestConfig sslConfig = new SSLTestConfig(false, false);
HttpClientUtil.setSchemaRegistryProvider(sslConfig.buildClientSchemaRegistryProvider());
HttpClientUtil.setSocketFactoryRegistryProvider(sslConfig.buildClientSocketFactoryRegistryProvider());
Http2SolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig());
System.setProperty(ZkStateReader.URL_SCHEME, "http");
checkClusterWithNodeReplacement(sslConfig);
@ -112,7 +112,7 @@ public class TestMiniSolrCloudClusterSSL extends SolrTestCaseJ4 {
// but we test it anyway for completeness of sanity checking the behavior of code that looks at those
// options.
final SSLTestConfig sslConfig = new SSLTestConfig(false, true);
HttpClientUtil.setSchemaRegistryProvider(sslConfig.buildClientSchemaRegistryProvider());
HttpClientUtil.setSocketFactoryRegistryProvider(sslConfig.buildClientSocketFactoryRegistryProvider());
Http2SolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig());
System.setProperty(ZkStateReader.URL_SCHEME, "http");
checkClusterWithNodeReplacement(sslConfig);
@ -120,7 +120,7 @@ public class TestMiniSolrCloudClusterSSL extends SolrTestCaseJ4 {
public void testSslAndNoClientAuth() throws Exception {
final SSLTestConfig sslConfig = new SSLTestConfig(true, false);
HttpClientUtil.setSchemaRegistryProvider(sslConfig.buildClientSchemaRegistryProvider());
HttpClientUtil.setSocketFactoryRegistryProvider(sslConfig.buildClientSocketFactoryRegistryProvider());
Http2SolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig());
System.setProperty(ZkStateReader.URL_SCHEME, "https");
checkClusterWithNodeReplacement(sslConfig);
@ -131,7 +131,7 @@ public class TestMiniSolrCloudClusterSSL extends SolrTestCaseJ4 {
final SSLTestConfig sslConfig = new SSLTestConfig(true, true);
HttpClientUtil.setSchemaRegistryProvider(sslConfig.buildClientSchemaRegistryProvider());
HttpClientUtil.setSocketFactoryRegistryProvider(sslConfig.buildClientSocketFactoryRegistryProvider());
Http2SolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig());
System.setProperty(ZkStateReader.URL_SCHEME, "https");
checkClusterWithNodeReplacement(sslConfig);
@ -140,7 +140,7 @@ public class TestMiniSolrCloudClusterSSL extends SolrTestCaseJ4 {
// commented out on: 17-Feb-2019 @LuceneTestCase.BadApple(bugUrl="https://issues.apache.org/jira/browse/SOLR-12028") // 2-Aug-2018
public void testSslWithCheckPeerName() throws Exception {
final SSLTestConfig sslConfig = new SSLTestConfig(true, false, true);
HttpClientUtil.setSchemaRegistryProvider(sslConfig.buildClientSchemaRegistryProvider());
HttpClientUtil.setSocketFactoryRegistryProvider(sslConfig.buildClientSocketFactoryRegistryProvider());
Http2SolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig());
System.setProperty(ZkStateReader.URL_SCHEME, "https");
checkClusterWithNodeReplacement(sslConfig);
@ -185,7 +185,7 @@ public class TestMiniSolrCloudClusterSSL extends SolrTestCaseJ4 {
// NOTE: first initialize the cluster w/o peer name checks, which means our server will use
// certs with a bogus hostname/ip and clients shouldn't care...
final SSLTestConfig sslConfig = new SSLTestConfig(true, false, false);
HttpClientUtil.setSchemaRegistryProvider(sslConfig.buildClientSchemaRegistryProvider());
HttpClientUtil.setSocketFactoryRegistryProvider(sslConfig.buildClientSocketFactoryRegistryProvider());
Http2SolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig());
System.setProperty(ZkStateReader.URL_SCHEME, "https");
final JettyConfig config = JettyConfig.builder().withSSLConfig(sslConfig.buildServerSSLConfig()).build();

View File

@ -100,12 +100,12 @@ public class HttpClientUtil {
public static final String PROP_BASIC_AUTH_PASS = "httpBasicAuthPassword";
/**
* System property consulted to determine if the default {@link SchemaRegistryProvider}
* System property consulted to determine if the default {@link SocketFactoryRegistryProvider}
* will require hostname validation of SSL Certificates. The default behavior is to enforce
* peer name validation.
* <p>
* This property will have no effect if {@link #setSchemaRegistryProvider} is used to override
* the default {@link SchemaRegistryProvider}
* This property will have no effect if {@link #setSocketFactoryRegistryProvider} is used to override
* the default {@link SocketFactoryRegistryProvider}
* </p>
*/
public static final String SYS_PROP_CHECK_PEER_NAME = "solr.ssl.checkPeerName";
@ -132,6 +132,12 @@ public class HttpClientUtil {
*/
public static final String SYS_PROP_HTTP_CLIENT_BUILDER_FACTORY = "solr.httpclient.builder.factory";
/**
* A Java system property to select the {@linkplain SocketFactoryRegistryProvider} used for
* configuring the Apache HTTP clients.
*/
public static final String SYS_PROP_SOCKET_FACTORY_REGISTRY_PROVIDER = "solr.httpclient.socketFactory.registry.provider";
static final DefaultHttpRequestRetryHandler NO_RETRY = new DefaultHttpRequestRetryHandler(
0, false);
@ -139,7 +145,7 @@ public class HttpClientUtil {
private static SolrHttpClientContextBuilder httpClientRequestContextBuilder = new SolrHttpClientContextBuilder();
private static volatile SchemaRegistryProvider schemaRegistryProvider;
private static volatile SocketFactoryRegistryProvider socketFactoryRegistryProvider;
private static volatile String cookiePolicy;
private static final List<HttpRequestInterceptor> interceptors = new CopyOnWriteArrayList<>();
@ -147,6 +153,17 @@ public class HttpClientUtil {
static {
resetHttpClientBuilder();
// Configure the SocketFactoryRegistryProvider if user has specified the provider type.
String socketFactoryRegistryProviderClassName = System.getProperty(SYS_PROP_SOCKET_FACTORY_REGISTRY_PROVIDER);
if (socketFactoryRegistryProviderClassName != null) {
log.debug("Using " + socketFactoryRegistryProviderClassName);
try {
socketFactoryRegistryProvider = (SocketFactoryRegistryProvider)Class.forName(socketFactoryRegistryProviderClassName).getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException("Unable to instantiate Solr SocketFactoryRegistryProvider", e);
}
}
// Configure the HttpClientBuilder if user has specified the factory type.
String factoryClassName = System.getProperty(SYS_PROP_HTTP_CLIENT_BUILDER_FACTORY);
if (factoryClassName != null) {
@ -160,9 +177,9 @@ public class HttpClientUtil {
}
}
public static abstract class SchemaRegistryProvider {
public static abstract class SocketFactoryRegistryProvider {
/** Must be non-null */
public abstract Registry<ConnectionSocketFactory> getSchemaRegistry();
public abstract Registry<ConnectionSocketFactory> getSocketFactoryRegistry();
}
private static class DynamicInterceptor implements HttpRequestInterceptor {
@ -198,8 +215,8 @@ public class HttpClientUtil {
/**
* @see #SYS_PROP_CHECK_PEER_NAME
*/
public static void setSchemaRegistryProvider(SchemaRegistryProvider newRegistryProvider) {
schemaRegistryProvider = newRegistryProvider;
public static void setSocketFactoryRegistryProvider(SocketFactoryRegistryProvider newRegistryProvider) {
socketFactoryRegistryProvider = newRegistryProvider;
}
public static SolrHttpClientBuilder getHttpClientBuilder() {
@ -209,18 +226,18 @@ public class HttpClientUtil {
/**
* @see #SYS_PROP_CHECK_PEER_NAME
*/
public static SchemaRegistryProvider getSchemaRegisteryProvider() {
return schemaRegistryProvider;
public static SocketFactoryRegistryProvider getSocketFactoryRegistryProvider() {
return socketFactoryRegistryProvider;
}
public static void resetHttpClientBuilder() {
schemaRegistryProvider = new DefaultSchemaRegistryProvider();
socketFactoryRegistryProvider = new DefaultSocketFactoryRegistryProvider();
httpClientBuilder = SolrHttpClientBuilder.create();
}
private static final class DefaultSchemaRegistryProvider extends SchemaRegistryProvider {
private static final class DefaultSocketFactoryRegistryProvider extends SocketFactoryRegistryProvider {
@Override
public Registry<ConnectionSocketFactory> getSchemaRegistry() {
public Registry<ConnectionSocketFactory> getSocketFactoryRegistry() {
// this mimics PoolingHttpClientConnectionManager's default behavior,
// except that we explicitly use SSLConnectionSocketFactory.getSystemSocketFactory()
// to pick up the system level default SSLContext (where javax.net.ssl.* properties
@ -258,7 +275,7 @@ public class HttpClientUtil {
/** test usage subject to change @lucene.experimental */
static PoolingHttpClientConnectionManager createPoolingConnectionManager() {
return new PoolingHttpClientConnectionManager(schemaRegistryProvider.getSchemaRegistry());
return new PoolingHttpClientConnectionManager(socketFactoryRegistryProvider.getSocketFactoryRegistry());
}
public static CloseableHttpClient createClient(SolrParams params, PoolingHttpClientConnectionManager cm) {

View File

@ -20,7 +20,7 @@ import javax.net.ssl.HostnameVerifier;
import java.io.IOException;
import org.apache.solr.SolrTestCase;
import org.apache.solr.client.solrj.impl.HttpClientUtil.SchemaRegistryProvider;
import org.apache.solr.client.solrj.impl.HttpClientUtil.SocketFactoryRegistryProvider;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.http.conn.socket.ConnectionSocketFactory;
@ -50,26 +50,26 @@ public class HttpClientUtilTest extends SolrTestCase {
public void testSSLSystemProperties() throws IOException {
assertNotNull("HTTPS scheme could not be created using system defaults",
HttpClientUtil.getSchemaRegisteryProvider().getSchemaRegistry().lookup("https"));
HttpClientUtil.getSocketFactoryRegistryProvider().getSocketFactoryRegistry().lookup("https"));
assertSSLHostnameVerifier(DefaultHostnameVerifier.class, HttpClientUtil.getSchemaRegisteryProvider());
assertSSLHostnameVerifier(DefaultHostnameVerifier.class, HttpClientUtil.getSocketFactoryRegistryProvider());
System.setProperty(HttpClientUtil.SYS_PROP_CHECK_PEER_NAME, "true");
resetHttpClientBuilder();
assertSSLHostnameVerifier(DefaultHostnameVerifier.class, HttpClientUtil.getSchemaRegisteryProvider());
assertSSLHostnameVerifier(DefaultHostnameVerifier.class, HttpClientUtil.getSocketFactoryRegistryProvider());
System.setProperty(HttpClientUtil.SYS_PROP_CHECK_PEER_NAME, "");
resetHttpClientBuilder();
assertSSLHostnameVerifier(DefaultHostnameVerifier.class, HttpClientUtil.getSchemaRegisteryProvider());
assertSSLHostnameVerifier(DefaultHostnameVerifier.class, HttpClientUtil.getSocketFactoryRegistryProvider());
System.setProperty(HttpClientUtil.SYS_PROP_CHECK_PEER_NAME, "false");
resetHttpClientBuilder();
assertSSLHostnameVerifier(NoopHostnameVerifier.class, HttpClientUtil.getSchemaRegisteryProvider());
assertSSLHostnameVerifier(NoopHostnameVerifier.class, HttpClientUtil.getSocketFactoryRegistryProvider());
}
private void assertSSLHostnameVerifier(Class<? extends HostnameVerifier> expected,
SchemaRegistryProvider provider) {
ConnectionSocketFactory socketFactory = provider.getSchemaRegistry().lookup("https");
SocketFactoryRegistryProvider provider) {
ConnectionSocketFactory socketFactory = provider.getSocketFactoryRegistry().lookup("https");
assertNotNull("unable to lookup https", socketFactory);
assertTrue("socketFactory is not an SSLConnectionSocketFactory: " + socketFactory.getClass(),
socketFactory instanceof SSLConnectionSocketFactory);

View File

@ -302,8 +302,8 @@ public abstract class SolrTestCaseJ4 extends SolrTestCase {
newRandomConfig();
sslConfig = buildSSLConfig();
// based on randomized SSL config, set SchemaRegistryProvider appropriately
HttpClientUtil.setSchemaRegistryProvider(sslConfig.buildClientSchemaRegistryProvider());
// based on randomized SSL config, set SocketFactoryRegistryProvider appropriately
HttpClientUtil.setSocketFactoryRegistryProvider(sslConfig.buildClientSocketFactoryRegistryProvider());
Http2SolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig());
if(isSSLMode()) {
// SolrCloud tests should usually clear this

View File

@ -39,7 +39,7 @@ import org.apache.http.ssl.SSLContexts;
import org.apache.lucene.util.Constants;
import org.apache.solr.client.solrj.embedded.SSLConfig;
import org.apache.solr.client.solrj.impl.HttpClientUtil;
import org.apache.solr.client.solrj.impl.HttpClientUtil.SchemaRegistryProvider;
import org.apache.solr.client.solrj.impl.HttpClientUtil.SocketFactoryRegistryProvider;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.security.CertificateUtils;
import org.eclipse.jetty.util.ssl.SslContextFactory;
@ -47,7 +47,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
import com.carrotsearch.randomizedtesting.RandomizedTest;
/**
* An SSLConfig that provides {@link SSLConfig} and {@link SchemaRegistryProvider} for both clients and servers
* An SSLConfig that provides {@link SSLConfig} and {@link SocketFactoryRegistryProvider} for both clients and servers
* that supports reading key/trust store information directly from resource files provided with the
* Solr test-framework classes
*/
@ -134,17 +134,17 @@ public class SSLTestConfig {
}
/**
* Creates a {@link SchemaRegistryProvider} for HTTP <b>clients</b> to use when communicating with servers
* Creates a {@link SocketFactoryRegistryProvider} for HTTP <b>clients</b> to use when communicating with servers
* which have been configured based on the settings of this object. When {@link #isSSLMode} is true, this
* <code>SchemaRegistryProvider</code> will <i>only</i> support HTTPS (no HTTP scheme) using the
* <code>SocketFactoryRegistryProvider</code> will <i>only</i> support HTTPS (no HTTP scheme) using the
* appropriate certs. When {@link #isSSLMode} is false, <i>only</i> HTTP (no HTTPS scheme) will be
* supported.
*/
public SchemaRegistryProvider buildClientSchemaRegistryProvider() {
public SocketFactoryRegistryProvider buildClientSocketFactoryRegistryProvider() {
if (isSSLMode()) {
SSLConnectionSocketFactory sslConnectionFactory = buildClientSSLConnectionSocketFactory();
assert null != sslConnectionFactory;
return new SSLSchemaRegistryProvider(sslConnectionFactory);
return new SSLSocketFactoryRegistryProvider(sslConnectionFactory);
} else {
return HTTP_ONLY_SCHEMA_PROVIDER;
}
@ -268,23 +268,23 @@ public class SSLTestConfig {
return sslConnectionFactory;
}
/** A SchemaRegistryProvider that only knows about SSL using a specified SSLConnectionSocketFactory */
private static class SSLSchemaRegistryProvider extends SchemaRegistryProvider {
/** A SocketFactoryRegistryProvider that only knows about SSL using a specified SSLConnectionSocketFactory */
private static class SSLSocketFactoryRegistryProvider extends SocketFactoryRegistryProvider {
private final SSLConnectionSocketFactory sslConnectionFactory;
public SSLSchemaRegistryProvider(SSLConnectionSocketFactory sslConnectionFactory) {
public SSLSocketFactoryRegistryProvider(SSLConnectionSocketFactory sslConnectionFactory) {
this.sslConnectionFactory = sslConnectionFactory;
}
@Override
public Registry<ConnectionSocketFactory> getSchemaRegistry() {
public Registry<ConnectionSocketFactory> getSocketFactoryRegistry() {
return RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", sslConnectionFactory).build();
}
}
/** A SchemaRegistryProvider that only knows about HTTP */
private static final SchemaRegistryProvider HTTP_ONLY_SCHEMA_PROVIDER = new SchemaRegistryProvider() {
/** A SocketFactoryRegistryProvider that only knows about HTTP */
private static final SocketFactoryRegistryProvider HTTP_ONLY_SCHEMA_PROVIDER = new SocketFactoryRegistryProvider() {
@Override
public Registry<ConnectionSocketFactory> getSchemaRegistry() {
public Registry<ConnectionSocketFactory> getSocketFactoryRegistry() {
return RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory()).build();
}