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 300e9cbcec..5f6e66cd5e 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) {