diff --git a/components/camel-http-common/src/main/java/org/apache/camel/http/common/DefaultHttpBinding.java b/components/camel-http-common/src/main/java/org/apache/camel/http/common/DefaultHttpBinding.java index b7cb755c818d0..73a4549b05b8a 100644 --- a/components/camel-http-common/src/main/java/org/apache/camel/http/common/DefaultHttpBinding.java +++ b/components/camel-http-common/src/main/java/org/apache/camel/http/common/DefaultHttpBinding.java @@ -199,6 +199,9 @@ protected void readHeaders(HttpServletRequest request, Message message) { protected void readBody(HttpServletRequest request, Message message) { LOG.trace("readBody {}", request); + // Process attachments first as some servlet containers expect the body to not have been read at this point + populateAttachments(request, message); + // lets parse the body Object body = message.getBody(); // reset the stream cache if the body is the instance of StreamCache @@ -226,8 +229,6 @@ protected void readBody(HttpServletRequest request, Message message) { message.setBody(null); } } - - populateAttachments(request, message); } protected void populateRequestParameters(HttpServletRequest request, Message message) { diff --git a/components/camel-servlet/src/test/java/org/apache/camel/component/servlet/MultipartUploadTest.java b/components/camel-servlet/src/test/java/org/apache/camel/component/servlet/MultipartUploadTest.java new file mode 100644 index 0000000000000..fcc9f05ecd070 --- /dev/null +++ b/components/camel-servlet/src/test/java/org/apache/camel/component/servlet/MultipartUploadTest.java @@ -0,0 +1,78 @@ +/* + * 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.servlet; + +import java.io.IOException; +import java.io.InputStream; + +import jakarta.activation.DataHandler; +import jakarta.servlet.MultipartConfigElement; + +import io.undertow.servlet.api.DeploymentInfo; +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.RoutesBuilder; +import org.apache.camel.attachment.AttachmentMessage; +import org.apache.camel.builder.RouteBuilder; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class MultipartUploadTest extends ServletCamelRouterTestSupport { + + @Override + protected DeploymentInfo getDeploymentInfo() { + DeploymentInfo deploymentInfo = super.getDeploymentInfo(); + String tmpDir = System.getProperty("java.io.tmpdir"); + MultipartConfigElement defaultMultipartConfig = new MultipartConfigElement(tmpDir); + deploymentInfo.setDefaultMultipartConfig(defaultMultipartConfig); + return deploymentInfo; + } + + @Test + void testMultipartUpload() throws IOException { + String content = "Hello World"; + InputStream inputStream = context.getTypeConverter().convertTo(InputStream.class, content); + PostMethodWebRequest request + = new PostMethodWebRequest( + contextUrl + "/services/multipartUpload", inputStream, "multipart/form-data; boundary=----Boundary"); + WebResponse response = query(request); + assertEquals(content, response.getText()); + } + + @Override + protected RoutesBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("servlet:multipartUpload?attachmentMultipartBinding=true") + .process(new Processor() { + @Override + public void process(Exchange exchange) throws Exception { + AttachmentMessage message = exchange.getMessage(AttachmentMessage.class); + DataHandler file = message.getAttachment("file"); + if (file != null) { + exchange.getMessage().setBody(file.getContent()); + } else { + exchange.getMessage().setBody(null); + } + } + }); + } + }; + } +} diff --git a/components/camel-servlet/src/test/java/org/apache/camel/component/servlet/ServletCamelRouterTestSupport.java b/components/camel-servlet/src/test/java/org/apache/camel/component/servlet/ServletCamelRouterTestSupport.java index d1da5d6a98c80..c9f1debc04ca3 100644 --- a/components/camel-servlet/src/test/java/org/apache/camel/component/servlet/ServletCamelRouterTestSupport.java +++ b/components/camel-servlet/src/test/java/org/apache/camel/component/servlet/ServletCamelRouterTestSupport.java @@ -23,6 +23,7 @@ import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; @@ -36,6 +37,7 @@ import org.apache.camel.test.AvailablePortFinder; import org.apache.camel.test.junit5.CamelTestSupport; import org.apache.camel.util.IOHelper; +import org.apache.camel.util.ObjectHelper; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -101,10 +103,24 @@ protected WebResponse query(WebRequest req, boolean exceptionsThrownOnErrorStatu con.setRequestMethod(req.getMethod()); if (req instanceof PostMethodWebRequest) { con.setDoOutput(true); + String contentType = con.getRequestProperty("Content-Type"); + boolean isMultipart = ObjectHelper.isNotEmpty(contentType) && contentType.startsWith("multipart/form-data"); + if (isMultipart) { + StringBuilder builder = new StringBuilder(); + builder.append("------Boundary\r\n"); + builder.append("Content-Disposition: form-data; name=\"file\"; filename=\"test.txt\"\r\n"); + builder.append("Content-Type: application/octet-stream\r\n\r\n"); + con.getOutputStream().write(builder.toString().getBytes(StandardCharsets.UTF_8)); + } + InputStream is = ((PostMethodWebRequest) req).content; if (is != null) { IOHelper.copy(is, con.getOutputStream()); } + + if (isMultipart) { + con.getOutputStream().write("\r\n------Boundary--\r\n".getBytes(StandardCharsets.UTF_8)); + } } int code = con.getResponseCode(); if (exceptionsThrownOnErrorStatus && code >= HttpURLConnection.HTTP_BAD_REQUEST) {