This is an automated email from the ASF dual-hosted git repository.

pvillard pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new ceb9dff  NIFI-7761 Allow HandleHttpRequest to add specified form data 
to FlowFile attributes
ceb9dff is described below

commit ceb9dff3b9a8511c61e39e0728fcdd63f545ce91
Author: Otto Fowler <[email protected]>
AuthorDate: Mon Sep 7 01:10:31 2020 -0400

    NIFI-7761 Allow HandleHttpRequest to add specified form data to FlowFile 
attributes
    
    Signed-off-by: Pierre Villard <[email protected]>
    
    This closes #4513.
---
 .../processors/standard/HandleHttpRequest.java     | 38 ++++++++++-
 .../standard/ITestHandleHttpRequest.java           | 79 ++++++++++++++++++++--
 2 files changed, 112 insertions(+), 5 deletions(-)

diff --git 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/HandleHttpRequest.java
 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/HandleHttpRequest.java
index dbdb995ea..ad9d95d 100644
--- 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/HandleHttpRequest.java
+++ 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/HandleHttpRequest.java
@@ -89,6 +89,7 @@ import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.regex.Pattern;
 
 @InputRequirement(Requirement.INPUT_FORBIDDEN)
@@ -115,6 +116,8 @@ import java.util.regex.Pattern;
     @WritesAttribute(attribute = "http.principal.name", description = "The 
name of the authenticated user making the request"),
     @WritesAttribute(attribute = "http.query.param.XXX", description = "Each 
of query parameters in the request will be added as an attribute, "
             + "prefixed with \"http.query.param.\""),
+    @WritesAttribute(attribute = "http.param.XXX", description = "Form 
parameters in the request that are configured by \"Parameters to Attributes 
List\" will be added as an attribute, "
+        + "prefixed with \"http.param.\". Putting form parameters of large 
size is not recommended."),
     @WritesAttribute(attribute = HTTPUtils.HTTP_SSL_CERT, description = "The 
Distinguished Name of the requestor. This value will not be populated "
             + "unless the Processor is configured to use an SSLContext 
Service"),
     @WritesAttribute(attribute = "http.issuer.dn", description = "The 
Distinguished Name of the entity that issued the Subject's certificate. "
@@ -122,7 +125,7 @@ import java.util.regex.Pattern;
     @WritesAttribute(attribute = "http.headers.XXX", description = "Each of 
the HTTP Headers that is received in the request will be added as an "
             + "attribute, prefixed with \"http.headers.\" For example, if the 
request contains an HTTP Header named \"x-my-header\", then the value "
             + "will be added to an attribute named 
\"http.headers.x-my-header\""),
-    @WritesAttribute(attribute = "http.headers.multipart.XXX", description = 
"Each of the HTTP Headers that is received in the mulipart request will be 
added as an "
+    @WritesAttribute(attribute = "http.headers.multipart.XXX", description = 
"Each of the HTTP Headers that is received in the multipart request will be 
added as an "
         + "attribute, prefixed with \"http.headers.multipart.\" For example, 
if the multipart request contains an HTTP Header named \"content-disposition\", 
then the value "
         + "will be added to an attribute named 
\"http.headers.multipart.content-disposition\""),
     @WritesAttribute(attribute = "http.multipart.size",
@@ -247,6 +250,14 @@ public class HandleHttpRequest extends AbstractProcessor {
             .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
             .expressionLanguageSupported(ExpressionLanguageScope.NONE)
             .build();
+    public static final PropertyDescriptor PARAMETERS_TO_ATTRIBUTES = new 
PropertyDescriptor.Builder()
+            .name("parameters-to-attributes")
+            .displayName("Parameters to Attributes List")
+            .description("A comma-separated list of HTTP parameters or form 
data to output as attributes")
+            .required(false)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .expressionLanguageSupported(ExpressionLanguageScope.NONE)
+            .build();
     public static final PropertyDescriptor CLIENT_AUTH = new 
PropertyDescriptor.Builder()
             .name("Client Authentication")
             .description("Specifies whether or not the Processor should 
authenticate clients. This value is ignored if the <SSL Context Service> "
@@ -304,6 +315,7 @@ public class HandleHttpRequest extends AbstractProcessor {
         descriptors.add(CONTAINER_QUEUE_SIZE);
         descriptors.add(MULTIPART_REQUEST_MAX_SIZE);
         descriptors.add(MULTIPART_READ_BUFFER_SIZE);
+        descriptors.add(PARAMETERS_TO_ATTRIBUTES);
         propertyDescriptors = Collections.unmodifiableList(descriptors);
     }
 
@@ -312,6 +324,7 @@ public class HandleHttpRequest extends AbstractProcessor {
     private AtomicBoolean initialized = new AtomicBoolean(false);
     private volatile BlockingQueue<HttpRequestContainer> containerQueue;
     private AtomicBoolean runOnPrimary = new AtomicBoolean(false);
+    private AtomicReference<Set<String>> parameterToAttributesReference = new 
AtomicReference<>(null);
 
     @Override
     protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
@@ -427,6 +440,18 @@ public class HandleHttpRequest extends AbstractProcessor {
             }
         }
 
+        final Set<String> parametersToMakeAttributes = new HashSet<>();
+        final String parametersToAttributesPropertyValue = 
context.getProperty(PARAMETERS_TO_ATTRIBUTES).getValue();
+        if (parametersToAttributesPropertyValue != null) {
+            for (final String paremeterName : 
parametersToAttributesPropertyValue.split(",")) {
+                final String trimmed = paremeterName.trim();
+                if (!trimmed.isEmpty()) {
+                    parametersToMakeAttributes.add(trimmed);
+                }
+            }
+            parameterToAttributesReference.set(parametersToMakeAttributes);
+        }
+
         final String pathRegex = context.getProperty(PATH_REGEX).getValue();
         final Pattern pathPattern = (pathRegex == null) ? null : 
Pattern.compile(pathRegex);
 
@@ -740,6 +765,17 @@ public class HandleHttpRequest extends AbstractProcessor {
           putAttribute(attributes, "http.server.name", 
request.getServerName());
           putAttribute(attributes, HTTPUtils.HTTP_PORT, 
request.getServerPort());
 
+          Set<String> parametersToAttributes = 
parameterToAttributesReference.get();
+          if (parametersToAttributes != null && 
!parametersToAttributes.isEmpty()){
+              final Enumeration<String> paramEnumeration = 
request.getParameterNames();
+              while (paramEnumeration.hasMoreElements()) {
+                  final String paramName = paramEnumeration.nextElement();
+                  if (parametersToAttributes.contains(paramName)){
+                    attributes.put("http.param." + paramName, 
request.getParameter(paramName));
+                }
+              }
+          }
+
           final Cookie[] cookies = request.getCookies();
           if (cookies != null) {
               for (final Cookie cookie : cookies) {
diff --git 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/ITestHandleHttpRequest.java
 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/ITestHandleHttpRequest.java
index ece1a93..9ca9f04 100644
--- 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/ITestHandleHttpRequest.java
+++ 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/ITestHandleHttpRequest.java
@@ -19,10 +19,6 @@ package org.apache.nifi.processors.standard;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import com.google.api.client.util.Charsets;
-import com.google.common.base.Optional;
-import com.google.common.collect.Iterables;
-import com.google.common.io.Files;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -46,6 +42,11 @@ import javax.net.ssl.SSLContext;
 import javax.servlet.AsyncContext;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+
+import com.google.api.client.util.Charsets;
+import com.google.common.base.Optional;
+import com.google.common.collect.Iterables;
+import com.google.common.io.Files;
 import okhttp3.Call;
 import okhttp3.Callback;
 import okhttp3.MediaType;
@@ -304,6 +305,76 @@ public class ITestHandleHttpRequest {
     }
 
     @Test(timeout = 30000)
+    public void testMultipartFormDataRequestCaptureFormAttributes() throws 
InitializationException, IOException,
+        InterruptedException {
+        CountDownLatch serverReady = new CountDownLatch(1);
+        CountDownLatch requestSent = new CountDownLatch(1);
+
+        processor = createProcessor(serverReady, requestSent);
+        final TestRunner runner = TestRunners.newTestRunner(processor);
+        runner.setProperty(HandleHttpRequest.PORT, "0");
+        runner.setProperty(HandleHttpRequest.PARAMETERS_TO_ATTRIBUTES, 
"p1,p2");
+
+        final MockHttpContextMap contextMap = new MockHttpContextMap();
+        runner.addControllerService("http-context-map", contextMap);
+        runner.enableControllerService(contextMap);
+        runner.setProperty(HandleHttpRequest.HTTP_CONTEXT_MAP, 
"http-context-map");
+
+        final Thread httpThread = new Thread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    serverReady.await();
+
+                    final int port = ((HandleHttpRequest) 
runner.getProcessor()).getPort();
+
+                    MultipartBody multipartBody = new MultipartBody.Builder()
+                        .setType(MultipartBody.FORM)
+                        .addFormDataPart("p1", "v1")
+                        .addFormDataPart("p2", "v2")
+                        .addFormDataPart("p3", "v3")
+                        .build();
+
+                    Request request = new Request.Builder()
+                        .url(String.format("http://localhost:%s/my/path";, 
port))
+                        .post(multipartBody).build();
+
+                    OkHttpClient client =
+                        new OkHttpClient.Builder()
+                            .readTimeout(3000, TimeUnit.MILLISECONDS)
+                            .writeTimeout(3000, TimeUnit.MILLISECONDS)
+                            .build();
+
+                    sendRequest(client, request, requestSent);
+                } catch (Exception e) {
+                    // Do nothing as HandleHttpRequest doesn't respond normally
+                }
+            }
+        });
+
+        httpThread.start();
+        runner.run(1, false, false);
+
+        runner.assertAllFlowFilesTransferred(HandleHttpRequest.REL_SUCCESS, 3);
+        assertEquals(1, contextMap.size());
+
+        List<MockFlowFile> flowFilesForRelationship = 
runner.getFlowFilesForRelationship(HandleHttpRequest.REL_SUCCESS);
+
+        // Part fragments are not processed in the order we submitted them.
+        // We cannot rely on the order we sent them in.
+        for (int i = 1; i < 4; i++) {
+            MockFlowFile mff = findFlowFile(flowFilesForRelationship, 
"http.multipart.name", String.format("p%d", i));
+            String contextId = mff.getAttribute(HTTPUtils.HTTP_CONTEXT_ID);
+            mff.assertAttributeEquals("http.multipart.name", 
String.format("p%d", i));
+            mff.assertAttributeExists("http.param.p1");
+            mff.assertAttributeEquals("http.param.p1", "v1");
+            mff.assertAttributeExists("http.param.p2");
+            mff.assertAttributeEquals("http.param.p2", "v2");
+            mff.assertAttributeNotExists("http.param.p3");
+        }
+    }
+
+    @Test(timeout = 30000)
     public void testMultipartFormDataRequestFailToRegisterContext() throws 
InitializationException, IOException, InterruptedException {
         CountDownLatch serverReady = new CountDownLatch(1);
         CountDownLatch requestSent = new CountDownLatch(1);

Reply via email to