GH-8806: Ignore HTTP body for DELETE & TRACE
Fixes: #8806 According to RFC 9110, the `TRACE` request must not contain the body and DELETE (like GET and HEAD) should not. * Fix `BaseHttpInboundEndpoint` adding `TRACE` & `DELETE` to the `NON_READABLE_BODY_HTTP_METHODS` list * Clean up typos and links in the `http/inbound.adoc`
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2022 the original author or authors.
|
||||
* Copyright 2017-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -62,7 +62,7 @@ public class BaseHttpInboundEndpoint extends MessagingGatewaySupport implements
|
||||
protected static final boolean ROME_TOOLS_PRESENT = ClassUtils.isPresent("com.rometools.rome.feed.atom.Feed", null);
|
||||
|
||||
protected static final List<HttpMethod> NON_READABLE_BODY_HTTP_METHODS =
|
||||
Arrays.asList(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.OPTIONS);
|
||||
Arrays.asList(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.DELETE, HttpMethod.OPTIONS, HttpMethod.TRACE);
|
||||
|
||||
protected final AtomicInteger activeCount = new AtomicInteger(); // NOSONAR
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -92,7 +92,7 @@ public class HttpRequestHandlingMessagingGatewayTests extends AbstractHttpInboun
|
||||
assertThat(message).isNotNull();
|
||||
assertThat(message.getPayload().getClass()).isEqualTo(LinkedMultiValueMap.class);
|
||||
LinkedMultiValueMap<String, String> map = (LinkedMultiValueMap<String, String>) message.getPayload();
|
||||
assertThat(map.get("foo").size()).isEqualTo(1);
|
||||
assertThat(map.get("foo")).hasSize(1);
|
||||
assertThat(map.getFirst("foo")).isEqualTo("bar");
|
||||
}
|
||||
|
||||
@@ -193,6 +193,7 @@ public class HttpRequestHandlingMessagingGatewayTests extends AbstractHttpInboun
|
||||
protected boolean doSend(Message<?> message, long timeout) {
|
||||
throw new RuntimeException("Planned");
|
||||
}
|
||||
|
||||
};
|
||||
HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(true);
|
||||
gateway.setBeanFactory(mock(BeanFactory.class));
|
||||
@@ -234,11 +235,8 @@ public class HttpRequestHandlingMessagingGatewayTests extends AbstractHttpInboun
|
||||
LinkedMultiValueMap<String, String> map = (LinkedMultiValueMap<String, String>) message.getPayload();
|
||||
List<String> fooValues = map.get("foo");
|
||||
List<String> barValues = map.get("bar");
|
||||
assertThat(fooValues.size()).isEqualTo(1);
|
||||
assertThat(fooValues.get(0)).isEqualTo("123");
|
||||
assertThat(barValues.size()).isEqualTo(2);
|
||||
assertThat(barValues.get(0)).isEqualTo("456");
|
||||
assertThat(barValues.get(1)).isEqualTo("789");
|
||||
assertThat(fooValues).containsExactly("123");
|
||||
assertThat(barValues).containsExactly("456", "789");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -281,8 +279,7 @@ public class HttpRequestHandlingMessagingGatewayTests extends AbstractHttpInboun
|
||||
HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(false);
|
||||
gateway.setBeanFactory(mock(BeanFactory.class));
|
||||
ParameterizedTypeReference<List<TestBean>> parameterizedTypeReference =
|
||||
new ParameterizedTypeReference<List<TestBean>>() {
|
||||
|
||||
new ParameterizedTypeReference<>() {
|
||||
};
|
||||
gateway.setRequestPayloadType(ResolvableType.forType(parameterizedTypeReference));
|
||||
gateway.setRequestChannel(channel);
|
||||
@@ -314,14 +311,12 @@ public class HttpRequestHandlingMessagingGatewayTests extends AbstractHttpInboun
|
||||
assertThat(bean).extracting(TestBean::getName).isEqualTo("T. Bean");
|
||||
assertThat(bean).extracting(TestBean::getAge).isEqualTo(42);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void INT2680DuplicateContentTypeHeader() throws Exception {
|
||||
|
||||
final DirectChannel requestChannel = new DirectChannel();
|
||||
DirectChannel requestChannel = new DirectChannel();
|
||||
requestChannel.subscribe(new AbstractReplyProducingMessageHandler() {
|
||||
|
||||
@Override
|
||||
@@ -478,7 +473,35 @@ public class HttpRequestHandlingMessagingGatewayTests extends AbstractHttpInboun
|
||||
verify(multipartResolver).isMultipart(any(HttpServletRequest.class));
|
||||
}
|
||||
|
||||
private class ContentTypeCheckingMockHttpServletResponse extends MockHttpServletResponse {
|
||||
@Test
|
||||
public void deleteRequestBodyIgnored() throws Exception {
|
||||
QueueChannel channel = new QueueChannel();
|
||||
HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(false);
|
||||
gateway.setBeanFactory(mock(BeanFactory.class));
|
||||
gateway.setRequestChannel(channel);
|
||||
gateway.afterPropertiesSet();
|
||||
gateway.start();
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("DELETE", "/delete");
|
||||
request.setContent("This content is ignored for DELETE".getBytes());
|
||||
request.setParameter("one", "1");
|
||||
request.addParameter("two", "2");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
gateway.handleRequest(request, response);
|
||||
Message<?> message = channel.receive(0);
|
||||
assertThat(message).isNotNull();
|
||||
assertThat(message.getPayload()).isNotNull();
|
||||
assertThat(message.getPayload().getClass()).isEqualTo(LinkedMultiValueMap.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
LinkedMultiValueMap<String, String> map = (LinkedMultiValueMap<String, String>) message.getPayload();
|
||||
List<String> oneValues = map.get("one");
|
||||
List<String> twoValues = map.get("two");
|
||||
assertThat(oneValues).containsExactly("1");
|
||||
assertThat(twoValues).containsExactly("2");
|
||||
}
|
||||
|
||||
|
||||
private static class ContentTypeCheckingMockHttpServletResponse extends MockHttpServletResponse {
|
||||
|
||||
private final List<String> contentTypeList = new ArrayList<>();
|
||||
|
||||
|
||||
@@ -14,12 +14,12 @@ The easiest way to do this is to use Spring's https://docs.spring.io/spring/docs
|
||||
----
|
||||
|
||||
Notice that the servlet name matches the bean name.
|
||||
For more information on using the `HttpRequestHandlerServlet`, see https://docs.spring.io/spring/docs/current/spring-framework-reference/html/remoting.html[Remoting and web services using Spring], which is part of the Spring Framework Reference documentation.
|
||||
For more information see the `HttpRequestHandlerServlet` Javadocs.
|
||||
|
||||
If you are running within a Spring MVC application, then the aforementioned explicit servlet definition is not necessary.
|
||||
In that case, the bean name for your gateway can be matched against the URL path as you would for a Spring MVC Controller bean.
|
||||
For more information, see
|
||||
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc[Web MVC framework], which is part of the Spring Framework Reference documentation.
|
||||
https://docs.spring.io/spring-framework/reference/web/webmvc.html[Web MVC framework], which is part of the Spring Framework Reference documentation.
|
||||
|
||||
TIP: For a sample application and the corresponding configuration, see the https://github.com/spring-projects/spring-integration-samples[Spring Integration Samples] repository.
|
||||
It contains the https://github.com/spring-projects/spring-integration-samples/tree/main/basic/http[HTTP sample] application, which demonstrates Spring Integration's HTTP support.
|
||||
@@ -52,7 +52,7 @@ If the request has been wrapped as a `MultipartHttpServletRequest`, when you use
|
||||
NOTE: The HTTP inbound endpoint locates a `MultipartResolver` in the context if one has a bean name of `multipartResolver` (the same name expected by Spring's `DispatcherServlet`).
|
||||
If it does locate that bean, the support for multipart files is enabled on the inbound request mapper.
|
||||
Otherwise, it fails when it tries to map a multipart file request to a Spring Integration `Message`.
|
||||
For more on Spring's support for `MultipartResolver`, see the https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-multipart[Spring Reference Manual].
|
||||
For more on Spring's support for `MultipartResolver`, see the https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-servlet/multipart.html[Spring Reference Manual].
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
@@ -113,6 +113,6 @@ By default, the key for that map entry is 'reply', but you can override this def
|
||||
== Payload Validation
|
||||
|
||||
Starting with version 5.2, the HTTP inbound endpoints can be supplied with a `Validator` to check a payload before sending into the channel.
|
||||
This payload is already a result of conversion and extraction after `payloadExpression` to narrow a validation scope in regards to the valuable data.
|
||||
The validation failure handling is fully the same what we have in Spring MVC https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-exceptionhandlers[Error Handling].
|
||||
This payload is already a result of conversion and extraction after `payloadExpression` to narrow a validation scope in regard to the valuable data.
|
||||
The validation failure handling is fully the same what we have in Spring MVC https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-servlet/exceptionhandlers.html[Error Handling].
|
||||
|
||||
|
||||
Reference in New Issue
Block a user