You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2016/11/09 08:49:28 UTC

[2/2] camel git commit: CAMEL-10451 camel-undertow - Add multipart request support

CAMEL-10451 camel-undertow - Add multipart request support


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/015f8128
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/015f8128
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/015f8128

Branch: refs/heads/master
Commit: 015f8128df5a50dba32661c6351b6e1839df0d59
Parents: cf83fca
Author: Tomohisa Igarashi <tm...@gmail.com>
Authored: Wed Nov 9 01:29:53 2016 +0900
Committer: Claus Ibsen <da...@apache.org>
Committed: Wed Nov 9 09:47:54 2016 +0100

----------------------------------------------------------------------
 .../undertow/DefaultUndertowHttpBinding.java    |  62 +++++++++--
 .../component/undertow/UndertowComponent.java   |   2 +-
 .../component/undertow/UndertowConsumer.java    |   6 ++
 .../component/undertow/MultiPartFormTest.java   | 102 +++++++++++++++++++
 .../PreservePostFormUrlEncodedBodyTest.java     |  71 +++++++++++++
 5 files changed, 236 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/015f8128/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/DefaultUndertowHttpBinding.java
----------------------------------------------------------------------
diff --git a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/DefaultUndertowHttpBinding.java b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/DefaultUndertowHttpBinding.java
index a0bd90b..3c231fd 100644
--- a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/DefaultUndertowHttpBinding.java
+++ b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/DefaultUndertowHttpBinding.java
@@ -17,21 +17,30 @@
 package org.apache.camel.component.undertow;
 
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.IOException;
 import java.io.ObjectOutputStream;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.nio.ByteBuffer;
+import java.nio.file.Path;
 import java.util.Deque;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.Map;
 
+import javax.activation.DataHandler;
+import javax.activation.FileDataSource;
+
 import io.undertow.client.ClientExchange;
 import io.undertow.client.ClientRequest;
 import io.undertow.client.ClientResponse;
 import io.undertow.predicate.Predicate;
 import io.undertow.server.HttpServerExchange;
+import io.undertow.server.handlers.form.FormData;
+import io.undertow.server.handlers.form.FormData.FormValue;
+import io.undertow.server.handlers.form.FormDataParser;
 import io.undertow.util.Headers;
 import io.undertow.util.HttpString;
 import io.undertow.util.Methods;
@@ -39,6 +48,7 @@ import io.undertow.util.Methods;
 import org.apache.camel.Exchange;
 import org.apache.camel.Message;
 import org.apache.camel.TypeConverter;
+import org.apache.camel.impl.DefaultAttachment;
 import org.apache.camel.impl.DefaultMessage;
 import org.apache.camel.spi.HeaderFilterStrategy;
 import org.apache.camel.util.ExchangeHelper;
@@ -94,14 +104,33 @@ public class DefaultUndertowHttpBinding implements UndertowHttpBinding {
 
         populateCamelHeaders(httpExchange, result.getHeaders(), exchange);
 
-        //extract body if the method is allowed to have one
-        //body is extracted as byte[] then auto TypeConverter kicks in
-        if (Methods.POST.equals(httpExchange.getRequestMethod()) || Methods.PUT.equals(httpExchange.getRequestMethod())) {
-            result.setBody(readFromChannel(httpExchange.getRequestChannel()));
+        // Map form data which is parsed by undertow form parsers
+        FormData formData = httpExchange.getAttachment(FormDataParser.FORM_DATA);
+        if (formData != null) {
+            Map<String, Object> body = new HashMap<>();
+            formData.forEach(key -> {
+                formData.get(key).forEach(value -> {
+                    if (value.isFile()) {
+                        DefaultAttachment attachment = new DefaultAttachment(new FilePartDataSource(value));
+                        result.addAttachmentObject(key, attachment);
+                        body.put(key, attachment.getDataHandler());
+                    } else if (headerFilterStrategy != null
+                        && !headerFilterStrategy.applyFilterToExternalHeaders(key, value.getValue(), exchange)) {
+                        UndertowHelper.appendHeader(result.getHeaders(), key, value.getValue());
+                        UndertowHelper.appendHeader(body, key, value.getValue());
+                    }
+                });
+            });
+            result.setBody(body);
         } else {
-            result.setBody(null);
+            //extract body by myself if undertow parser didn't handle and the method is allowed to have one
+            //body is extracted as byte[] then auto TypeConverter kicks in
+            if (Methods.POST.equals(httpExchange.getRequestMethod()) || Methods.PUT.equals(httpExchange.getRequestMethod())) {
+                result.setBody(readFromChannel(httpExchange.getRequestChannel()));
+            } else {
+                result.setBody(null);
+            }
         }
-
         return result;
     }
 
@@ -390,4 +419,25 @@ public class DefaultUndertowHttpBinding implements UndertowHttpBinding {
             }
         }
     }
+
+    class FilePartDataSource extends FileDataSource {
+        private String name;
+        private String contentType;
+
+        FilePartDataSource(FormValue value) {
+            super(value.getPath().toFile());
+            this.name = value.getFileName();
+            this.contentType = value.getHeaders().getFirst(Headers.CONTENT_TYPE);
+        }
+
+        @Override
+        public String getName() {
+            return this.name;
+        }
+
+        @Override
+        public String getContentType() {
+            return this.contentType;
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/015f8128/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponent.java
----------------------------------------------------------------------
diff --git a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponent.java b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponent.java
index d757070..37891f4 100644
--- a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponent.java
+++ b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponent.java
@@ -287,7 +287,7 @@ public class UndertowComponent extends UriEndpointComponent implements RestConsu
             undertowRegistry.put(key, host);
         }
         host.validateEndpointURI(uri);
-        host.registerHandler(consumer.getHttpHandlerRegistrationInfo(), consumer);
+        host.registerHandler(consumer.getHttpHandlerRegistrationInfo(), consumer.getHttpHandler());
     }
 
     public void unregisterConsumer(UndertowConsumer consumer) {

http://git-wip-us.apache.org/repos/asf/camel/blob/015f8128/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowConsumer.java
----------------------------------------------------------------------
diff --git a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowConsumer.java b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowConsumer.java
index 6a80892..846fe63 100644
--- a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowConsumer.java
+++ b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowConsumer.java
@@ -21,6 +21,7 @@ import java.nio.ByteBuffer;
 
 import io.undertow.server.HttpHandler;
 import io.undertow.server.HttpServerExchange;
+import io.undertow.server.handlers.form.EagerFormParsingHandler;
 import io.undertow.util.Headers;
 import io.undertow.util.HttpString;
 import io.undertow.util.Methods;
@@ -75,6 +76,11 @@ public class UndertowConsumer extends DefaultConsumer implements HttpHandler {
         return registrationInfo;
     }
 
+    public HttpHandler getHttpHandler() {
+        // wrap with EagerFormParsingHandler to enable undertow form parsers
+        return new EagerFormParsingHandler().setNext(this);
+    }
+
     @Override
     public void handleRequest(HttpServerExchange httpExchange) throws Exception {
         HttpString requestMethod = httpExchange.getRequestMethod();

http://git-wip-us.apache.org/repos/asf/camel/blob/015f8128/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/MultiPartFormTest.java
----------------------------------------------------------------------
diff --git a/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/MultiPartFormTest.java b/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/MultiPartFormTest.java
new file mode 100644
index 0000000..d8a1512
--- /dev/null
+++ b/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/MultiPartFormTest.java
@@ -0,0 +1,102 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.undertow;
+
+import java.io.File;
+import java.util.Map;
+
+import javax.activation.DataHandler;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.methods.RequestEntity;
+import org.apache.commons.httpclient.methods.multipart.FilePart;
+import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
+import org.apache.commons.httpclient.methods.multipart.Part;
+import org.apache.commons.httpclient.methods.multipart.StringPart;
+import org.apache.commons.httpclient.params.HttpMethodParams;
+import org.junit.Test;
+
+public class MultiPartFormTest extends BaseUndertowTest {
+    private RequestEntity createMultipartRequestEntity() throws Exception {
+        File file = new File("src/main/resources/META-INF/NOTICE.txt");
+
+        Part[] parts = {new StringPart("comment", "A binary file of some kind"),
+                        new FilePart(file.getName(), file)};
+
+        return new MultipartRequestEntity(parts, new HttpMethodParams());
+
+    }
+
+    @Test
+    public void testSendMultiPartForm() throws Exception {
+        HttpClient httpclient = new HttpClient();
+
+        PostMethod httppost = new PostMethod("http://localhost:" + getPort() + "/test");
+        
+        httppost.setRequestEntity(createMultipartRequestEntity());
+
+        int status = httpclient.executeMethod(httppost);
+
+        assertEquals("Get a wrong response status", 200, status);
+        String result = httppost.getResponseBodyAsString();
+
+        assertEquals("Get a wrong result", "A binary file of some kind", result);
+
+    }
+
+    @Test
+    public void testSendMultiPartFormFromCamelHttpComponnent() throws Exception {
+        String result = template.requestBody("http://localhost:" + getPort() + "/test", createMultipartRequestEntity(), String.class);
+        assertEquals("Get a wrong result", "A binary file of some kind", result);
+    }
+
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            public void configure() throws Exception {
+                from("undertow://http://localhost:{{port}}/test").process(new Processor() {
+
+                    public void process(Exchange exchange) throws Exception {
+                        Message in = exchange.getIn();
+                        assertEquals("Get a wrong attachement size", 1, in.getAttachments().size());
+                        // The file name is attachment id
+                        DataHandler data = in.getAttachment("NOTICE.txt");
+
+                        assertNotNull("Should get the DataHandler NOTICE.txt", data);
+                        assertEquals("Got the wrong name", "NOTICE.txt", data.getName());
+
+                        assertTrue("We should get the data from the DataHandler", data.getDataSource()
+                            .getInputStream().available() > 0);
+
+                        // form data should also be available as a body
+                        Map body = in.getBody(Map.class);
+                        assertEquals("A binary file of some kind", body.get("comment"));
+                        assertEquals(data, body.get("NOTICE.txt"));
+                        exchange.getOut().setBody(in.getHeader("comment"));
+                    }
+
+                });
+                // END SNIPPET: e1
+            }
+        };
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/015f8128/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/PreservePostFormUrlEncodedBodyTest.java
----------------------------------------------------------------------
diff --git a/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/PreservePostFormUrlEncodedBodyTest.java b/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/PreservePostFormUrlEncodedBodyTest.java
new file mode 100644
index 0000000..9cd52e8
--- /dev/null
+++ b/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/PreservePostFormUrlEncodedBodyTest.java
@@ -0,0 +1,71 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.undertow;
+
+import java.util.Map;
+
+import io.undertow.server.handlers.form.FormData;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.http.HttpMethods;
+import org.junit.Test;
+
+public class PreservePostFormUrlEncodedBodyTest extends BaseUndertowTest {
+    
+    @Test
+    public void testSendToUndertow() throws Exception {
+        Exchange exchange = template.request("http://localhost:{{port}}/myapp/myservice?query1=a&query2=b", new Processor() {
+
+            public void process(Exchange exchange) throws Exception {
+                exchange.getIn().setBody("b1=x&b2=y");
+                exchange.getIn().setHeader("content-type", "application/x-www-form-urlencoded");
+                exchange.getIn().setHeader(Exchange.HTTP_METHOD, HttpMethods.POST);
+            }
+                                        
+        });
+        // convert the response to a String
+        String body = exchange.getOut().getBody(String.class);
+        assertEquals("Request message is OK", body);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            public void configure() throws Exception {
+                from("undertow:http://localhost:{{port}}/myapp/myservice?map").process(new Processor() {
+                    public void process(Exchange exchange) throws Exception {
+                        Map body = exchange.getIn().getBody(Map.class);
+                        
+                        // for unit testing make sure we got right message
+                        assertNotNull(body);
+                        assertEquals("x", body.get("b1"));
+                        assertEquals("y", body.get("b2"));
+                        assertEquals("a", exchange.getIn().getHeader("query1"));
+                        assertEquals("b", exchange.getIn().getHeader("query2"));
+                        assertEquals("x", exchange.getIn().getHeader("b1"));
+                        assertEquals("y", exchange.getIn().getHeader("b2"));
+                        
+                        // send a response
+                        exchange.getOut().setBody("Request message is OK");
+                    }
+                });
+            }
+        };
+    }
+
+}