From b541b6939df5395d88c2ecc87f628fdf297d9f23 Mon Sep 17 00:00:00 2001 From: Chandrasekar Rajasekar Date: Thu, 25 Jul 2019 11:31:24 -0500 Subject: [PATCH 1/2] Add custom sslcontext support in fili --- .../application/AbstractBinderFactory.java | 14 +++- .../client/impl/AsyncDruidWebServiceImpl.java | 71 +++++++++++++++++-- 2 files changed, 77 insertions(+), 8 deletions(-) diff --git a/fili-core/src/main/java/com/yahoo/bard/webservice/application/AbstractBinderFactory.java b/fili-core/src/main/java/com/yahoo/bard/webservice/application/AbstractBinderFactory.java index 562840ba91..a1361c1b2f 100644 --- a/fili-core/src/main/java/com/yahoo/bard/webservice/application/AbstractBinderFactory.java +++ b/fili-core/src/main/java/com/yahoo/bard/webservice/application/AbstractBinderFactory.java @@ -135,6 +135,7 @@ import com.codahale.metrics.health.HealthCheckRegistry; import com.fasterxml.jackson.databind.ObjectMapper; +import io.netty.handler.ssl.SslContext; import org.glassfish.hk2.api.TypeLiteral; import org.glassfish.hk2.utilities.Binder; import org.glassfish.hk2.utilities.binding.AbstractBinder; @@ -1208,6 +1209,14 @@ protected Class getPhysicalTableResolver() { return new HashMap<>(0); } + /** + * Get a custom configured ssl context. + * @return SSL context + */ + protected SslContext getSSLContext() { + return null; + } + /** * Create a DruidWebService. *

@@ -1220,16 +1229,19 @@ protected Class getPhysicalTableResolver() { */ protected DruidWebService buildDruidWebService(DruidServiceConfig druidServiceConfig, ObjectMapper mapper) { Supplier> supplier = buildDruidWebServiceHeaderSupplier(); + SslContext sslContext = getSSLContext(); + return DRUID_UNCOVERED_INTERVAL_LIMIT > 0 ? new AsyncDruidWebServiceImpl( druidServiceConfig, mapper, supplier, + sslContext, new HeaderNestingJsonBuilderStrategy( AsyncDruidWebServiceImpl.DEFAULT_JSON_NODE_BUILDER_STRATEGY ) ) - : new AsyncDruidWebServiceImpl(druidServiceConfig, mapper, supplier); + : new AsyncDruidWebServiceImpl(druidServiceConfig, mapper, supplier, sslContext); } /** diff --git a/fili-core/src/main/java/com/yahoo/bard/webservice/druid/client/impl/AsyncDruidWebServiceImpl.java b/fili-core/src/main/java/com/yahoo/bard/webservice/druid/client/impl/AsyncDruidWebServiceImpl.java index c19e989002..2b22c9870a 100644 --- a/fili-core/src/main/java/com/yahoo/bard/webservice/druid/client/impl/AsyncDruidWebServiceImpl.java +++ b/fili-core/src/main/java/com/yahoo/bard/webservice/druid/client/impl/AsyncDruidWebServiceImpl.java @@ -31,6 +31,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; +import io.netty.handler.ssl.SslContext; import org.asynchttpclient.AsyncCompletionHandler; import org.asynchttpclient.AsyncHttpClient; import org.asynchttpclient.AsyncHttpClientConfig; @@ -109,7 +110,7 @@ public AsyncDruidWebServiceImpl( ) { this( serviceConfig, - initializeWebClient(serviceConfig.getTimeout()), + initializeWebClient(serviceConfig.getTimeout(), null), mapper, HashMap::new, DEFAULT_JSON_NODE_BUILDER_STRATEGY @@ -150,7 +151,32 @@ public AsyncDruidWebServiceImpl( ) { this( serviceConfig, - initializeWebClient(serviceConfig.getTimeout()), + initializeWebClient(serviceConfig.getTimeout(), null), + mapper, + headersToAppend, + DEFAULT_JSON_NODE_BUILDER_STRATEGY + ); + } + + /** + * Friendly non-DI constructor useful for manual tests. + *

+ * This constructor uses default JSON builder, which only uses response body to build the JSON response. + * + * @param serviceConfig Configuration for the Druid Service + * @param mapper A shared jackson object mapper resource + * @param headersToAppend Supplier for map of headers for Druid requests + * @param sslContext Custom Configured SslContext (null if default SSL context has to be used) + */ + public AsyncDruidWebServiceImpl( + DruidServiceConfig serviceConfig, + ObjectMapper mapper, + Supplier> headersToAppend, + SslContext sslContext + ) { + this( + serviceConfig, + initializeWebClient(serviceConfig.getTimeout(), sslContext), mapper, headersToAppend, DEFAULT_JSON_NODE_BUILDER_STRATEGY @@ -173,7 +199,33 @@ public AsyncDruidWebServiceImpl( ) { this( serviceConfig, - initializeWebClient(serviceConfig.getTimeout()), + initializeWebClient(serviceConfig.getTimeout(), null), + mapper, + headersToAppend, + jsonNodeBuilderStrategy + ); + } + + + /** + * Friendly non-DI constructor useful for manual tests. + * + * @param serviceConfig Configuration for the Druid Service + * @param mapper A shared jackson object mapper resource + * @param headersToAppend Supplier for map of headers for Druid requests + * @param jsonNodeBuilderStrategy A function to build JSON nodes from the response + * @param sslContext Custom Configured SslContext (null if default SSL context has to be used) + */ + public AsyncDruidWebServiceImpl( + DruidServiceConfig serviceConfig, + ObjectMapper mapper, + Supplier> headersToAppend, + SslContext sslContext, + Function jsonNodeBuilderStrategy + ) { + this( + serviceConfig, + initializeWebClient(serviceConfig.getTimeout(), sslContext), mapper, headersToAppend, jsonNodeBuilderStrategy @@ -237,10 +289,11 @@ public AsyncDruidWebServiceImpl( * Initialize the client config. * * @param requestTimeout Timeout to use for the client configuration. + * @param sslContext Custom Configured SslContext. (null if default ssl context has to be used) * * @return the set up client */ - private static AsyncHttpClient initializeWebClient(int requestTimeout) { + private static AsyncHttpClient initializeWebClient(int requestTimeout, SslContext sslContext) { LOG.debug("Druid request timeout: {}ms", requestTimeout); List cipherSuites = SYSTEM_CONFIG.getListProperty(SSL_ENABLED_CIPHER_KEY, null); @@ -249,15 +302,19 @@ private static AsyncHttpClient initializeWebClient(int requestTimeout) { : cipherSuites.toArray(new String[cipherSuites.size()]); // Build the configuration - AsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder() + DefaultAsyncHttpClientConfig.Builder asyncHttpClientConfigBuilder = new DefaultAsyncHttpClientConfig.Builder() .setReadTimeout(requestTimeout) .setRequestTimeout(requestTimeout) .setConnectTimeout(requestTimeout) .setConnectionTtl(requestTimeout) .setPooledConnectionIdleTimeout(requestTimeout) .setEnabledCipherSuites(enabledCipherSuites) - .setFollowRedirect(true) - .build(); + .setFollowRedirect(true); + + if (sslContext != null) { + asyncHttpClientConfigBuilder.setSslContext(sslContext); + } + AsyncHttpClientConfig config = asyncHttpClientConfigBuilder.build(); return new DefaultAsyncHttpClient(config); } From 49a27b39ea31338115b6b683e5769967a400a7d0 Mon Sep 17 00:00:00 2001 From: Chandrasekar Rajasekar Date: Wed, 7 Aug 2019 10:54:18 -0500 Subject: [PATCH 2/2] Added test and changelog --- CHANGELOG.md | 7 +++++++ .../application/AbstractBinderFactory.java | 8 +++----- .../client/impl/AsyncDruidWebServiceImpl.java | 11 ++++------- .../impl/AsyncDruidWebServiceImplSpec.groovy | 14 ++++++++++++++ 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 019dbf1e49..3215c2d57a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2235,3 +2235,10 @@ Jobs resource. Here are the highlights of what's in this release: - [`DruidDimensionsLoader` doesn't set the dimension's lastUpdated date](https://github.com/yahoo/fili/pull/24) * `DruidDimensionsLoader` now properly sets the `lastUpdated` field after it finished processing the Druid response +### Added: + +- [Add custom ssl context support to druid AsyncHttp requests](https://github.com/yahoo/fili/pull/943) + * Added new Constructors in `AsyncDruidWebServiceImpl` class that accept custom `SslContext` as additional argument. + This custom SslContext if not null, replaces the default ssl context while making the request to druid. + * Added `getSSLContext()` method in `AbstractBinderFactory` class that returns null as default. Custom Ssl Context is passed by overriding this method. + diff --git a/fili-core/src/main/java/com/yahoo/bard/webservice/application/AbstractBinderFactory.java b/fili-core/src/main/java/com/yahoo/bard/webservice/application/AbstractBinderFactory.java index a1361c1b2f..3af6872ade 100644 --- a/fili-core/src/main/java/com/yahoo/bard/webservice/application/AbstractBinderFactory.java +++ b/fili-core/src/main/java/com/yahoo/bard/webservice/application/AbstractBinderFactory.java @@ -1210,7 +1210,7 @@ protected Class getPhysicalTableResolver() { } /** - * Get a custom configured ssl context. + * Get a custom configured ssl context. If null, the system default SSL Context will be applied * @return SSL context */ protected SslContext getSSLContext() { @@ -1229,19 +1229,17 @@ protected SslContext getSSLContext() { */ protected DruidWebService buildDruidWebService(DruidServiceConfig druidServiceConfig, ObjectMapper mapper) { Supplier> supplier = buildDruidWebServiceHeaderSupplier(); - SslContext sslContext = getSSLContext(); - return DRUID_UNCOVERED_INTERVAL_LIMIT > 0 ? new AsyncDruidWebServiceImpl( druidServiceConfig, mapper, supplier, - sslContext, + getSSLContext(), new HeaderNestingJsonBuilderStrategy( AsyncDruidWebServiceImpl.DEFAULT_JSON_NODE_BUILDER_STRATEGY ) ) - : new AsyncDruidWebServiceImpl(druidServiceConfig, mapper, supplier, sslContext); + : new AsyncDruidWebServiceImpl(druidServiceConfig, mapper, supplier, getSSLContext()); } /** diff --git a/fili-core/src/main/java/com/yahoo/bard/webservice/druid/client/impl/AsyncDruidWebServiceImpl.java b/fili-core/src/main/java/com/yahoo/bard/webservice/druid/client/impl/AsyncDruidWebServiceImpl.java index 2b22c9870a..f73f4dc2cb 100644 --- a/fili-core/src/main/java/com/yahoo/bard/webservice/druid/client/impl/AsyncDruidWebServiceImpl.java +++ b/fili-core/src/main/java/com/yahoo/bard/webservice/druid/client/impl/AsyncDruidWebServiceImpl.java @@ -302,19 +302,16 @@ private static AsyncHttpClient initializeWebClient(int requestTimeout, SslContex : cipherSuites.toArray(new String[cipherSuites.size()]); // Build the configuration - DefaultAsyncHttpClientConfig.Builder asyncHttpClientConfigBuilder = new DefaultAsyncHttpClientConfig.Builder() + AsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder() .setReadTimeout(requestTimeout) .setRequestTimeout(requestTimeout) .setConnectTimeout(requestTimeout) .setConnectionTtl(requestTimeout) .setPooledConnectionIdleTimeout(requestTimeout) .setEnabledCipherSuites(enabledCipherSuites) - .setFollowRedirect(true); - - if (sslContext != null) { - asyncHttpClientConfigBuilder.setSslContext(sslContext); - } - AsyncHttpClientConfig config = asyncHttpClientConfigBuilder.build(); + .setFollowRedirect(true) + .setSslContext(sslContext) + .build(); return new DefaultAsyncHttpClient(config); } diff --git a/fili-core/src/test/groovy/com/yahoo/bard/webservice/druid/client/impl/AsyncDruidWebServiceImplSpec.groovy b/fili-core/src/test/groovy/com/yahoo/bard/webservice/druid/client/impl/AsyncDruidWebServiceImplSpec.groovy index 28c697e218..551433aab2 100644 --- a/fili-core/src/test/groovy/com/yahoo/bard/webservice/druid/client/impl/AsyncDruidWebServiceImplSpec.groovy +++ b/fili-core/src/test/groovy/com/yahoo/bard/webservice/druid/client/impl/AsyncDruidWebServiceImplSpec.groovy @@ -10,6 +10,8 @@ import com.yahoo.bard.webservice.druid.model.query.WeightEvaluationQuery import com.fasterxml.jackson.databind.ObjectMapper import io.netty.handler.codec.http.HttpHeaders +import io.netty.handler.ssl.SslContext +import org.asynchttpclient.AsyncHttpClient; import spock.lang.Specification import java.util.function.Supplier @@ -71,4 +73,16 @@ class AsyncDruidWebServiceImplSpec extends Specification { assert actualHeaders.get(header.getKey()) == header.getValue() } } + + def "Check whether given ssl context was set in HttpClient"() { + setup: + SslContext sslContext = Mock(SslContext); + + when: + AsyncHttpClient httpClient = AsyncDruidWebServiceImpl.initializeWebClient(6000, sslContext); + + then: + assert httpClient.getConfig().getSslContext() == sslContext; + + } }