You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ja...@apache.org on 2024/03/01 19:19:59 UTC
(camel-quarkus) branch main updated: Add multipart configuration options to servlet extension
This is an automated email from the ASF dual-hosted git repository.
jamesnetherton pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
The following commit(s) were added to refs/heads/main by this push:
new 7fc2932adb Add multipart configuration options to servlet extension
7fc2932adb is described below
commit 7fc2932adbe3ad55ee99703ef94b5ef6cdd5fe92
Author: James Netherton <ja...@gmail.com>
AuthorDate: Fri Mar 1 13:30:51 2024 +0000
Add multipart configuration options to servlet extension
Fixes #5326
---
.../ROOT/pages/reference/extensions/servlet.adoc | 48 ++++++++++++++++++++++
.../servlet/deployment/ServletProcessor.java | 11 +++++
.../servlet/runtime/CamelServletConfig.java | 36 ++++++++++++++++
integration-tests/servlet/pom.xml | 17 ++++++++
.../quarkus/component/servlet/CamelRoute.java | 17 ++++----
.../component/servlet/MultiPartProcessor.java | 37 +++++++++++++++++
.../src/main/resources/application.properties | 6 +++
.../component/servlet/CamelServletTest.java | 41 ++++++++++++++++--
8 files changed, 202 insertions(+), 11 deletions(-)
diff --git a/docs/modules/ROOT/pages/reference/extensions/servlet.adoc b/docs/modules/ROOT/pages/reference/extensions/servlet.adoc
index 39c76cf246..fd56d2364a 100644
--- a/docs/modules/ROOT/pages/reference/extensions/servlet.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/servlet.adoc
@@ -83,6 +83,30 @@ A servletName as it would be defined in a `web.xml` file or in the `jakarta.serv
| `string`
| `CamelServlet`
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.multipart.location]]`link:#quarkus.camel.servlet.multipart.location[quarkus.camel.servlet.multipart.location]`
+
+An absolute path to a directory on the file system to store files temporarily while the parts are processed or when the size of the file exceeds the specified file-size-threshold configuration value.
+| `string`
+| `${java.io.tmpdir}`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.multipart.max-file-size]]`link:#quarkus.camel.servlet.multipart.max-file-size[quarkus.camel.servlet.multipart.max-file-size]`
+
+The maximum size allowed in bytes for uploaded files. The default size (-1) allows an unlimited size.
+| `long`
+| `-1`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.multipart.max-request-size]]`link:#quarkus.camel.servlet.multipart.max-request-size[quarkus.camel.servlet.multipart.max-request-size]`
+
+The maximum size allowed in bytes for a multipart/form-data request. The default size (-1) allows an unlimited size.
+| `long`
+| `-1`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.multipart.file-size-threshold]]`link:#quarkus.camel.servlet.multipart.file-size-threshold[quarkus.camel.servlet.multipart.file-size-threshold]`
+
+The file size in bytes after which the file will be temporarily stored on disk.
+| `int`
+| `0`
+
|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.-named-servlets-.url-patterns]]`link:#quarkus.camel.servlet.-named-servlets-.url-patterns[quarkus.camel.servlet."named-servlets".url-patterns]`
A comma separated list of path patterns under which the CamelServlet should be accessible. Example path patterns: `/++*++`, `/services/++*++`
@@ -100,6 +124,30 @@ A fully qualified name of a servlet class to serve paths that match `url-pattern
A servletName as it would be defined in a `web.xml` file or in the `jakarta.servlet.annotation.WebServlet++#++name()` annotation.
| `string`
| `CamelServlet`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.-named-servlets-.multipart.location]]`link:#quarkus.camel.servlet.-named-servlets-.multipart.location[quarkus.camel.servlet."named-servlets".multipart.location]`
+
+An absolute path to a directory on the file system to store files temporarily while the parts are processed or when the size of the file exceeds the specified file-size-threshold configuration value.
+| `string`
+| `${java.io.tmpdir}`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.-named-servlets-.multipart.max-file-size]]`link:#quarkus.camel.servlet.-named-servlets-.multipart.max-file-size[quarkus.camel.servlet."named-servlets".multipart.max-file-size]`
+
+The maximum size allowed in bytes for uploaded files. The default size (-1) allows an unlimited size.
+| `long`
+| `-1`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.-named-servlets-.multipart.max-request-size]]`link:#quarkus.camel.servlet.-named-servlets-.multipart.max-request-size[quarkus.camel.servlet."named-servlets".multipart.max-request-size]`
+
+The maximum size allowed in bytes for a multipart/form-data request. The default size (-1) allows an unlimited size.
+| `long`
+| `-1`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.-named-servlets-.multipart.file-size-threshold]]`link:#quarkus.camel.servlet.-named-servlets-.multipart.file-size-threshold[quarkus.camel.servlet."named-servlets".multipart.file-size-threshold]`
+
+The file size in bytes after which the file will be temporarily stored on disk.
+| `int`
+| `0`
|===
[.configuration-legend]
diff --git a/extensions/servlet/deployment/src/main/java/org/apache/camel/quarkus/component/servlet/deployment/ServletProcessor.java b/extensions/servlet/deployment/src/main/java/org/apache/camel/quarkus/component/servlet/deployment/ServletProcessor.java
index c6d159f413..308f5489db 100644
--- a/extensions/servlet/deployment/src/main/java/org/apache/camel/quarkus/component/servlet/deployment/ServletProcessor.java
+++ b/extensions/servlet/deployment/src/main/java/org/apache/camel/quarkus/component/servlet/deployment/ServletProcessor.java
@@ -26,8 +26,10 @@ import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.undertow.deployment.ServletBuildItem;
import io.quarkus.undertow.deployment.ServletBuildItem.Builder;
+import jakarta.servlet.MultipartConfigElement;
import org.apache.camel.quarkus.servlet.runtime.CamelServletConfig;
import org.apache.camel.quarkus.servlet.runtime.CamelServletConfig.ServletConfig;
+import org.apache.camel.quarkus.servlet.runtime.CamelServletConfig.ServletConfig.MultipartConfig;
class ServletProcessor {
private static final String FEATURE = "camel-servlet";
@@ -81,6 +83,15 @@ class ServletProcessor {
builder.addMapping(pattern);
}
+ MultipartConfig multipartConfig = servletConfig.multipart;
+ if (multipartConfig != null) {
+ builder.setMultipartConfig(new MultipartConfigElement(
+ multipartConfig.location,
+ multipartConfig.maxFileSize,
+ multipartConfig.maxRequestSize,
+ multipartConfig.fileSizeThreshold));
+ }
+
return builder.build();
}
}
diff --git a/extensions/servlet/runtime/src/main/java/org/apache/camel/quarkus/servlet/runtime/CamelServletConfig.java b/extensions/servlet/runtime/src/main/java/org/apache/camel/quarkus/servlet/runtime/CamelServletConfig.java
index 311b95e47f..62ac5c7131 100644
--- a/extensions/servlet/runtime/src/main/java/org/apache/camel/quarkus/servlet/runtime/CamelServletConfig.java
+++ b/extensions/servlet/runtime/src/main/java/org/apache/camel/quarkus/servlet/runtime/CamelServletConfig.java
@@ -64,6 +64,11 @@ public final class CamelServletConfig {
@ConfigItem(defaultValue = DEFAULT_SERVLET_NAME)
public String servletName;
+ /**
+ * Servlet multipart request configuration.
+ */
+ public MultipartConfig multipart;
+
/**
* @return {@code true} if this {@link ServletConfig} is valid as a whole. This currently translates to
* {@link #urlPatterns} being non-empty because {@link #servletClass} and {@link #servletName} have
@@ -87,6 +92,37 @@ public final class CamelServletConfig {
return DEFAULT_SERVLET_NAME.equals(servletName) ? key : servletName;
}
+ /**
+ * Servlet multipart request configuration.
+ */
+ @ConfigGroup
+ public static class MultipartConfig {
+ /**
+ * An absolute path to a directory on the file system to store files temporarily while the parts are
+ * processed or when the size of the file exceeds the specified file-size-threshold configuration value.
+ */
+ @ConfigItem(defaultValue = "${java.io.tmpdir}")
+ public String location;
+
+ /**
+ * The maximum size allowed in bytes for uploaded files. The default size (-1) allows an unlimited size.
+ */
+ @ConfigItem(defaultValue = "-1")
+ public long maxFileSize;
+
+ /**
+ * The maximum size allowed in bytes for a multipart/form-data request. The default size (-1) allows an unlimited
+ * size.
+ */
+ @ConfigItem(defaultValue = "-1")
+ public long maxRequestSize;
+
+ /**
+ * The file size in bytes after which the file will be temporarily stored on disk.
+ */
+ @ConfigItem(defaultValue = "0")
+ public int fileSizeThreshold;
+ }
}
}
diff --git a/integration-tests/servlet/pom.xml b/integration-tests/servlet/pom.xml
index 6c786e98d2..bcee3b9655 100644
--- a/integration-tests/servlet/pom.xml
+++ b/integration-tests/servlet/pom.xml
@@ -31,6 +31,10 @@
<description>Integration tests for Camel Servlet component</description>
<dependencies>
+ <dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-attachments</artifactId>
+ </dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-core-cloud</artifactId>
@@ -99,6 +103,19 @@
</activation>
<dependencies>
<!-- The following dependencies guarantee that this module is built after them. You can update them by running `mvn process-resources -Pformat -N` from the source tree root directory -->
+ <dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-attachments-deployment</artifactId>
+ <version>${project.version}</version>
+ <type>pom</type>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>*</groupId>
+ <artifactId>*</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-core-cloud-deployment</artifactId>
diff --git a/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/CamelRoute.java b/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/CamelRoute.java
index 7e984433ad..aabc992357 100644
--- a/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/CamelRoute.java
+++ b/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/CamelRoute.java
@@ -17,12 +17,13 @@
package org.apache.camel.quarkus.component.servlet;
import jakarta.enterprise.context.ApplicationScoped;
-import org.apache.camel.Exchange;
-import org.apache.camel.Processor;
+import jakarta.inject.Inject;
import org.apache.camel.builder.RouteBuilder;
@ApplicationScoped
public class CamelRoute extends RouteBuilder {
+ @Inject
+ MultiPartProcessor multiPartProcessor;
@Override
public void configure() {
@@ -49,13 +50,13 @@ public class CamelRoute extends RouteBuilder {
.setBody(constant("GET: /favorite"));
from("direct:echoMethodPath")
- .process(new Processor() {
- @Override
- public void process(Exchange exchange) throws Exception {
- exchange.toString();
- }
- })
.setBody().simple("${header.CamelHttpMethod}: ${header.CamelServletContextPath}");
+
+ from("servlet://multipart/default?attachmentMultipartBinding=true")
+ .process(multiPartProcessor);
+
+ from("servlet://multipart?servletName=my-named-servlet&attachmentMultipartBinding=true")
+ .process(multiPartProcessor);
}
}
diff --git a/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/MultiPartProcessor.java b/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/MultiPartProcessor.java
new file mode 100644
index 0000000000..7d0cff8801
--- /dev/null
+++ b/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/MultiPartProcessor.java
@@ -0,0 +1,37 @@
+/*
+ * 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.quarkus.component.servlet;
+
+import jakarta.activation.DataHandler;
+import jakarta.inject.Singleton;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.attachment.AttachmentMessage;
+
+@Singleton
+public class MultiPartProcessor implements Processor {
+ @Override
+ public void process(Exchange exchange) throws Exception {
+ AttachmentMessage message = exchange.getMessage(AttachmentMessage.class);
+ DataHandler file = message.getAttachment("file");
+ if (file == null) {
+ throw new IllegalStateException("Attachment named 'file' is not present");
+ }
+
+ message.setBody(file.getInputStream());
+ }
+}
diff --git a/integration-tests/servlet/src/main/resources/application.properties b/integration-tests/servlet/src/main/resources/application.properties
index 7f11f67737..0251262894 100644
--- a/integration-tests/servlet/src/main/resources/application.properties
+++ b/integration-tests/servlet/src/main/resources/application.properties
@@ -19,7 +19,13 @@
# Quarkus :: Camel :: Servlet
#
quarkus.camel.servlet.url-patterns=/folder-1/*,/folder-2/*
+
quarkus.camel.servlet.my-named-servlet.url-patterns=/my-named-folder/*
quarkus.camel.servlet.my-named-servlet.servlet-class=org.apache.camel.quarkus.component.servlet.CustomServlet
+quarkus.camel.servlet.my-named-servlet.multipart.location=${java.io.tmpdir}/my-named-servlet
+quarkus.camel.servlet.my-named-servlet.multipart.max-file-size=11
+quarkus.camel.servlet.my-named-servlet.multipart.max-request-size=11
+quarkus.camel.servlet.my-named-servlet.multipart.file-size-threshold=5
+
quarkus.camel.servlet.ignored-key.servlet-name=my-favorite-servlet
quarkus.camel.servlet.ignored-key.url-patterns=/my-favorite-folder/*
diff --git a/integration-tests/servlet/src/test/java/org/apache/camel/quarkus/component/servlet/CamelServletTest.java b/integration-tests/servlet/src/test/java/org/apache/camel/quarkus/component/servlet/CamelServletTest.java
index 24e7672309..593e56cb9b 100644
--- a/integration-tests/servlet/src/test/java/org/apache/camel/quarkus/component/servlet/CamelServletTest.java
+++ b/integration-tests/servlet/src/test/java/org/apache/camel/quarkus/component/servlet/CamelServletTest.java
@@ -16,16 +16,21 @@
*/
package org.apache.camel.quarkus.component.servlet;
+import java.nio.charset.StandardCharsets;
+
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.RestAssured;
import org.hamcrest.core.IsEqual;
import org.junit.jupiter.api.Test;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.oneOf;
+
@QuarkusTest
public class CamelServletTest {
@Test
- public void multiplePaths() throws Throwable {
+ public void multiplePaths() {
RestAssured.when().get("/folder-1/rest-get").then().body(IsEqual.equalTo("GET: /rest-get"));
RestAssured.when().get("/folder-2/rest-get").then().body(IsEqual.equalTo("GET: /rest-get"));
RestAssured.when().post("/folder-1/rest-post").then().body(IsEqual.equalTo("POST: /rest-post"));
@@ -35,15 +40,45 @@ public class CamelServletTest {
}
@Test
- public void namedWithservletClass() throws Throwable {
+ public void namedWithservletClass() {
RestAssured.when().get("/my-named-folder/custom").then()
.body(IsEqual.equalTo("GET: /custom"))
.and().header("x-servlet-class-name", CustomServlet.class.getName());
}
@Test
- public void ignoredKey() throws Throwable {
+ public void ignoredKey() {
RestAssured.when().get("/my-favorite-folder/favorite").then()
.body(IsEqual.equalTo("GET: /favorite"));
}
+
+ @Test
+ public void multipartDefaultConfig() {
+ String body = "Hello World";
+ RestAssured.given()
+ .multiPart("file", "file", body.getBytes(StandardCharsets.UTF_8))
+ .post("/folder-1/multipart/default")
+ .then()
+ .statusCode(200)
+ .body(is(body));
+ }
+
+ @Test
+ public void multipartCustomConfig() {
+ String body = "Hello World";
+ RestAssured.given()
+ .multiPart("file", "file", body.getBytes(StandardCharsets.UTF_8))
+ .post("/folder-1/multipart/default")
+ .then()
+ .statusCode(200)
+ .body(is(body));
+
+ // Request body exceeding the limits defined on the multipart config
+ RestAssured.given()
+ .multiPart("test-multipart", "file", body.repeat(10).getBytes(StandardCharsets.UTF_8))
+ .post("/my-named-folder/multipart")
+ .then()
+ // TODO: Expect 413 only - https://github.com/apache/camel-quarkus/issues/5830
+ .statusCode(oneOf(413, 500));
+ }
}