This is an automated email from the ASF dual-hosted git repository. fmariani pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-spring-boot.git
commit 8acc2de79b53a7a42c335a58979ca24278c474b6 Author: Croway <[email protected]> AuthorDate: Fri Jan 31 10:37:26 2025 +0100 CAMEL-21461: platform-http features --- .../springboot/CamelRequestHandlerMapping.java | 14 ++++-- .../http/springboot/PlatformHttpMessage.java | 4 +- .../springboot/SpringBootPlatformHttpBinding.java | 50 ++++++++++++++++++++-- .../SpringBootPlatformHttpCertificationTest.java | 25 ----------- .../springboot/SpringBootPlatformHttpCorsTest.java | 1 - .../SpringBootPlatformHttpEngineTest.java | 27 +++++------- .../SpringBootPlatformHttpProxyTest.java | 6 +-- .../SpringBootPlatformHttpValidationTest.java | 37 ++++++++++------ 8 files changed, 97 insertions(+), 67 deletions(-) diff --git a/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/CamelRequestHandlerMapping.java b/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/CamelRequestHandlerMapping.java index 9eb332f45c4..6959eff4da6 100644 --- a/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/CamelRequestHandlerMapping.java +++ b/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/CamelRequestHandlerMapping.java @@ -37,9 +37,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; public class CamelRequestHandlerMapping extends RequestMappingHandlerMapping implements PlatformHttpListener { @@ -191,8 +188,17 @@ public class CamelRequestHandlerMapping extends RequestMappingHandlerMapping imp methods = new RequestMethod[]{rm}; } + SpringBootPlatformHttpConsumer consumer = (SpringBootPlatformHttpConsumer) model.getConsumer(); + + String uri = model.getUri(); + if (consumer.getEndpoint().isMatchOnUriPrefix()) { + // rewrite the uri so that PathPattern is used + uri += uri.endsWith("/") ? "" : "/"; + uri += "{*matchOnUriPrefix}"; + } + RequestMappingInfo.Builder info = RequestMappingInfo - .paths(model.getUri()) + .paths(uri) .methods(methods) .options(this.getBuilderConfiguration()); diff --git a/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/PlatformHttpMessage.java b/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/PlatformHttpMessage.java index 9d8e151ea38..00060236dfc 100644 --- a/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/PlatformHttpMessage.java +++ b/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/PlatformHttpMessage.java @@ -35,13 +35,13 @@ public class PlatformHttpMessage extends DefaultMessage { private boolean requestRead; public PlatformHttpMessage(Exchange exchange, HttpBinding binding, HttpServletRequest request, - HttpServletResponse response) { + HttpServletResponse response) { super(exchange); this.init(exchange, binding, request, response); } private PlatformHttpMessage(HttpServletRequest request, HttpServletResponse response, Exchange exchange, - HttpBinding binding, boolean requestRead) { + HttpBinding binding, boolean requestRead) { super(exchange); this.request = request; this.response = response; diff --git a/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpBinding.java b/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpBinding.java index 657eb8f9089..7728fe8def0 100644 --- a/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpBinding.java +++ b/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpBinding.java @@ -21,32 +21,51 @@ import jakarta.servlet.ServletContext; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import org.apache.camel.*; +import org.apache.camel.Exchange; +import org.apache.camel.InvalidPayloadException; +import org.apache.camel.Message; +import org.apache.camel.NoTypeConversionAvailableException; +import org.apache.camel.RuntimeCamelException; +import org.apache.camel.StreamCache; +import org.apache.camel.TypeConverter; import org.apache.camel.attachment.AttachmentMessage; import org.apache.camel.attachment.CamelFileDataSource; import org.apache.camel.component.platform.http.PlatformHttpEndpoint; +import org.apache.camel.component.platform.http.spi.Method; import org.apache.camel.converter.stream.CachedOutputStream; import org.apache.camel.http.base.HttpHelper; import org.apache.camel.http.common.DefaultHttpBinding; import org.apache.camel.support.ExchangeHelper; import org.apache.camel.util.FileUtil; import org.apache.camel.util.IOHelper; +import org.apache.camel.util.URISupport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest; -import java.io.*; -import java.nio.Buffer; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.List; import java.util.Locale; import java.util.UUID; public class SpringBootPlatformHttpBinding extends DefaultHttpBinding { private static final Logger LOG = LoggerFactory.getLogger(SpringBootPlatformHttpBinding.class); + private static final String CONTENT_TYPE_FORM_URLENCODED = "application/x-www-form-urlencoded"; + private static final List<Method> METHODS_WITH_BODY_ALLOWED = List.of(Method.POST, + Method.PUT, Method.PATCH, Method.DELETE); + protected void populateRequestParameters(HttpServletRequest request, Message message) { super.populateRequestParameters(request, message); String path = request.getRequestURI(); @@ -119,6 +138,31 @@ public class SpringBootPlatformHttpBinding extends DefaultHttpBinding { return super.parseBody(request, message); } + @Override + public void readRequest(HttpServletRequest request, Message message) { + super.readRequest(request, message); + + if (METHODS_WITH_BODY_ALLOWED.contains(Method.valueOf(request.getMethod())) && + message.getBody() instanceof StreamCache && + request.getContentType() != null && + request.getContentType().contains(CONTENT_TYPE_FORM_URLENCODED)) { + // FormContentFilter is NOT executed for POST requests + if ("POST".equals(request.getMethod())) { + String body = message.getBody(String.class); + try { + message.setBody(URISupport.parseQuery(body)); + } catch (URISyntaxException e) { + LOG.error("Cannot parse body: {}", body, e); + throw new RuntimeCamelException(e); + } + } else { + // FormContentFilter is executed, the request.getReader is already read + // and the parameters can be found in the parameterMap + message.setBody(request.getParameterMap()); + } + } + } + protected void doWriteDirectResponse(Message message, HttpServletResponse response, Exchange exchange) throws IOException { String contentType = (String)message.getHeader("Content-Type", String.class); if ("application/x-java-serialized-object".equals(contentType)) { diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCertificationTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCertificationTest.java index 9f9af941ec0..cbfbd282579 100644 --- a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCertificationTest.java +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCertificationTest.java @@ -45,7 +45,6 @@ import org.apache.camel.test.spring.junit5.CamelSpringBootTest; import org.apache.camel.util.IOHelper; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledIfSystemProperty; import org.springframework.beans.factory.annotation.Autowired; @@ -130,20 +129,6 @@ public class SpringBootPlatformHttpCertificationTest extends PlatformHttpBase { from("platform-http:/streamingWithSpecificEncoding?useStreaming=true") .log("Done echoing back request body as response body"); - from("platform-http:/streamingWithClosedInputStreamResponse?useStreaming=true") - .process(new Processor() { - @Override - public void process(Exchange exchange) throws Exception { - // Simulate an error processing an input stream by closing it ahead of the response being written - // Verifies the response promise.fail is called correctly - InputStream stream = getClass().getResourceAsStream("/application.properties"); - if (stream != null) { - stream.close(); - } - exchange.getMessage().setBody(stream); - } - }); - from("platform-http:/streamingWithUnconvertableResponseType?useStreaming=true") .process(new Processor() { @Override @@ -298,16 +283,6 @@ public class SpringBootPlatformHttpCertificationTest extends PlatformHttpBase { assertEquals(fileContent, Files.readString(output, StandardCharsets.ISO_8859_1)); } - @Test - @Disabled("Test is failing, work in progress") - void streamingWithClosedInputStreamResponse() throws Exception { - - given() - .get("/streamingWithClosedInputStreamResponse") - .then() - .statusCode(500); - } - @Test void streamingWithUnconvertableResponseType() throws Exception { given() diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCorsTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCorsTest.java index fd7e3cd7f35..70e5452399b 100644 --- a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCorsTest.java +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCorsTest.java @@ -22,7 +22,6 @@ import org.apache.camel.spi.RestConfiguration; import org.apache.camel.spring.boot.CamelAutoConfiguration; import org.apache.camel.test.spring.junit5.CamelSpringBootTest; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration; diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpEngineTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpEngineTest.java index 01d3885965e..a743b939b8d 100644 --- a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpEngineTest.java +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpEngineTest.java @@ -25,7 +25,6 @@ import org.apache.camel.builder.RouteBuilder; import org.apache.camel.spring.boot.CamelAutoConfiguration; import org.apache.camel.test.spring.junit5.CamelSpringBootTest; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -114,9 +113,6 @@ public class SpringBootPlatformHttpEngineTest { from("platform-http:/error/response") // Set the response to something that can't be type converted .setBody().constant(Collections.EMPTY_SET); - - from("platform-http:/error/query/param") - .setBody().constant("Error"); } }; } @@ -148,11 +144,10 @@ public class SpringBootPlatformHttpEngineTest { .post("/form/post") .then() .statusCode(200) - .body(is("foo=bar&cheese=wine")); + .body(is("{foo=bar, cheese=wine}")); } @Test - @Disabled("Test is failing, work in progress") public void matchOnUriPrefix() { final String greeting = "Hello Camel"; given() @@ -218,7 +213,6 @@ public class SpringBootPlatformHttpEngineTest { } @Test - @Disabled("Test is failing, work in progress") public void consumerSuspended() throws Exception { given() .when() @@ -231,22 +225,23 @@ public class SpringBootPlatformHttpEngineTest { given() .when() - .get("/get") + .get("/consumerSuspended") .then() .statusCode(503); - } - @Test - public void responseTypeConversionErrorHandled() { - get("/error/response") + camelContext.getRouteController().resumeRoute("consumerSuspended"); + + given() + .when() + .get("/consumerSuspended") .then() - .statusCode(500); + .statusCode(200) + .body(equalTo("get")); } @Test - @Disabled("Test is failing, work in progress") - public void responseBadQueryParamErrorHandled() { - get("/error/query/param?::") + public void responseTypeConversionErrorHandled() { + get("/error/response") .then() .statusCode(500); } diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpProxyTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpProxyTest.java index fe75c7ad177..6dace7b836e 100644 --- a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpProxyTest.java +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpProxyTest.java @@ -25,7 +25,6 @@ import org.apache.camel.builder.RouteBuilder; import org.apache.camel.spring.boot.CamelAutoConfiguration; import org.apache.camel.test.spring.junit5.CamelSpringBootTest; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -92,16 +91,15 @@ public class SpringBootPlatformHttpProxyTest { } @Test - @Disabled("Test is failing, work in progress") public void httpProxy() { final var proxyURI = "http://localhost:" + RestAssured.port; final var originURI = wiremockUrl; given() - .proxy(proxyURI) + .proxy(originURI) .contentType(ContentType.JSON) - .when().get(originURI) + .when().get(proxyURI) .then() .statusCode(200) .body(containsString("{\"message\": \"Hello World\"}")); diff --git a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpValidationTest.java b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpValidationTest.java index 4e3d1ed7983..cffcb8de9b3 100644 --- a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpValidationTest.java +++ b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpValidationTest.java @@ -19,7 +19,6 @@ package org.apache.camel.component.platform.http.springboot; import io.restassured.RestAssured; import io.restassured.http.ContentType; import io.restassured.response.ValidatableResponse; -import io.restassured.specification.RequestSpecification; import org.apache.camel.CamelContext; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.platform.http.spi.Method; @@ -29,7 +28,6 @@ import org.apache.camel.spring.boot.CamelAutoConfiguration; import org.apache.camel.test.spring.junit5.CamelSpringBootTest; import org.hamcrest.Matcher; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -45,7 +43,9 @@ import org.springframework.test.annotation.DirtiesContext; import java.util.List; import static io.restassured.RestAssured.given; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.emptyString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; @EnableAutoConfiguration(exclude = {OAuth2ClientAutoConfiguration.class, SecurityAutoConfiguration.class}) @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) @@ -88,7 +88,10 @@ public class SpringBootPlatformHttpValidationTest { from("platform-http:/echo") .setBody().simple("${body}"); - from("platform-http:/test") + from("platform-http:/test?useStreaming=true") + .process(exchange -> { + System.out.println("<<<<<<<<<<<<<< " + exchange.getIn().getBody(String.class) + " >>>>>>>>>>>>>"); + }) .setBody().simple("Hello ${body[method]}"); rest("/invalidContentTypeClientRequestValidation") @@ -132,9 +135,13 @@ public class SpringBootPlatformHttpValidationTest { } @Test - @Disabled("Test is failing, work in progress") public void requestBodyAllowed() { for (Method method : Method.values()) { + if (method == Method.TRACE || method == Method.CONNECT) { + // These methods are not supported OOB in tomcat + continue; + } + ValidatableResponse validatableResponse = given() .contentType(ContentType.JSON) .when() @@ -154,23 +161,29 @@ public class SpringBootPlatformHttpValidationTest { } @Test - @Disabled("Test is failing, work in progress") public void requestBodyAllowedFormUrlEncoded() { final List<Method> methodsWithBodyAllowed = List.of(Method.POST, Method.PUT, Method.PATCH, Method.DELETE); - RequestSpecification request = given() - .when() - .contentType(ContentType.URLENC); - for (Method method : Method.values()) { + if (method == Method.TRACE || method == Method.CONNECT) { + // These methods are not supported OOB in tomcat + continue; + } + if (methodsWithBodyAllowed.contains(method)) { - request.body("method=" + method) + given() + .when() + .contentType(ContentType.URLENC) + .body("method=" + method + "&test=value") .request(method.name(), "/test") .then() .statusCode(200) .body(equalTo("Hello " + method)); } else { - request.body(method) + given() + .when() + .contentType(ContentType.URLENC) + .body(method) .request(method.name(), "/test") .then() .statusCode(500);
