NIFI-4790 - support HTTPS Proxy in InvokeHTTP Signed-off-by: Pierre Villard <[email protected]>
This closes #2426. Project: http://git-wip-us.apache.org/repos/asf/nifi/repo Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/4829f519 Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/4829f519 Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/4829f519 Branch: refs/heads/HDF-3.1-maint Commit: 4829f519d2482e0e872a65cccadbc12217cdce36 Parents: b32e1a0 Author: Marco Gaido <[email protected]> Authored: Fri Jan 19 11:02:56 2018 +0100 Committer: Matt Gilman <[email protected]> Committed: Wed Feb 7 08:59:11 2018 -0500 ---------------------------------------------------------------------- .../nifi/processors/standard/InvokeHTTP.java | 40 ++++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/nifi/blob/4829f519/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/InvokeHTTP.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/InvokeHTTP.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/InvokeHTTP.java index 300e9cb..5f6e66c 100644 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/InvokeHTTP.java +++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/InvokeHTTP.java @@ -148,6 +148,9 @@ public final class InvokeHTTP extends AbstractProcessor { EXCEPTION_CLASS, EXCEPTION_MESSAGE, "uuid", "filename", "path"))); + public static final String HTTP = "http"; + public static final String HTTPS = "https"; + // properties public static final PropertyDescriptor PROP_METHOD = new PropertyDescriptor.Builder() .name("HTTP Method") @@ -213,11 +216,21 @@ public final class InvokeHTTP extends AbstractProcessor { public static final PropertyDescriptor PROP_SSL_CONTEXT_SERVICE = new PropertyDescriptor.Builder() .name("SSL Context Service") - .description("The SSL Context Service used to provide client certificate information for TLS/SSL (https) connections.") + .description("The SSL Context Service used to provide client certificate information for TLS/SSL (https) connections." + + " It is also used to connect to HTTPS Proxy.") .required(false) .identifiesControllerService(SSLContextService.class) .build(); + public static final PropertyDescriptor PROP_PROXY_TYPE = new PropertyDescriptor.Builder() + .name("Proxy Type") + .displayName("Proxy Type") + .description("The type of the proxy we are connecting to. Must be either " + HTTP + " or " + HTTPS) + .defaultValue(HTTP) + .expressionLanguageSupported(true) + .addValidator(StandardValidators.NON_EMPTY_EL_VALIDATOR) + .build(); + public static final PropertyDescriptor PROP_PROXY_HOST = new PropertyDescriptor.Builder() .name("Proxy Host") .description("The fully qualified hostname or IP address of the proxy server") @@ -388,6 +401,7 @@ public final class InvokeHTTP extends AbstractProcessor { PROP_BASIC_AUTH_PASSWORD, PROP_PROXY_HOST, PROP_PROXY_PORT, + PROP_PROXY_TYPE, PROP_PROXY_USER, PROP_PROXY_PASSWORD, PROP_PUT_OUTPUT_IN_ATTRIBUTE, @@ -509,10 +523,22 @@ public final class InvokeHTTP extends AbstractProcessor { if ((proxyUserSet && !proxyPwdSet) || (!proxyUserSet && proxyPwdSet)) { results.add(new ValidationResult.Builder().subject("Proxy User and Password").valid(false).explanation("If Proxy Username or Proxy Password is set, both must be set").build()); } - if(proxyUserSet && !proxyHostSet) { + if (proxyUserSet && !proxyHostSet) { results.add(new ValidationResult.Builder().subject("Proxy").valid(false).explanation("If Proxy username is set, proxy host must be set").build()); } + final String proxyType = validationContext.getProperty(PROP_PROXY_TYPE).evaluateAttributeExpressions().getValue(); + + if (!HTTP.equals(proxyType) && !HTTPS.equals(proxyType)) { + results.add(new ValidationResult.Builder().subject(PROP_PROXY_TYPE.getDisplayName()).valid(false) + .explanation(PROP_PROXY_TYPE.getDisplayName() + " must be either " + HTTP + " or " + HTTPS).build()); + } + + if (HTTPS.equals(proxyType) + && !validationContext.getProperty(PROP_SSL_CONTEXT_SERVICE).isSet()) { + results.add(new ValidationResult.Builder().subject("SSL Context Service").valid(false).explanation("If Proxy Type is HTTPS, SSL Context Service must be set").build()); + } + return results; } @@ -525,9 +551,12 @@ public final class InvokeHTTP extends AbstractProcessor { // Add a proxy if set final String proxyHost = context.getProperty(PROP_PROXY_HOST).evaluateAttributeExpressions().getValue(); final Integer proxyPort = context.getProperty(PROP_PROXY_PORT).evaluateAttributeExpressions().asInteger(); + final String proxyType = context.getProperty(PROP_PROXY_TYPE).evaluateAttributeExpressions().getValue(); + boolean isHttpsProxy = false; if (proxyHost != null && proxyPort != null) { final Proxy proxy = new Proxy(Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)); okHttpClientBuilder.proxy(proxy); + isHttpsProxy = HTTPS.equals(proxyType); } // Set timeouts @@ -542,7 +571,7 @@ public final class InvokeHTTP extends AbstractProcessor { // check if the ssl context is set and add the factory if so if (sslContext != null) { - setSslSocketFactory(okHttpClientBuilder, sslService, sslContext); + setSslSocketFactory(okHttpClientBuilder, sslService, sslContext, isHttpsProxy); } // check the trusted hostname property and override the HostnameVerifier @@ -566,7 +595,7 @@ public final class InvokeHTTP extends AbstractProcessor { In-depth documentation on Java Secure Socket Extension (JSSE) Classes and interfaces: https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#JSSEClasses */ - private void setSslSocketFactory(OkHttpClient.Builder okHttpClientBuilder, SSLContextService sslService, SSLContext sslContext) + private void setSslSocketFactory(OkHttpClient.Builder okHttpClientBuilder, SSLContextService sslService, SSLContext sslContext, boolean setAsSocketFactory) throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException { final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); @@ -622,6 +651,9 @@ public final class InvokeHTTP extends AbstractProcessor { final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); okHttpClientBuilder.sslSocketFactory(sslSocketFactory, x509TrustManager); + if (setAsSocketFactory) { + okHttpClientBuilder.socketFactory(sslSocketFactory); + } } private void setAuthenticator(OkHttpClient.Builder okHttpClientBuilder, ProcessContext context) {
