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:
Artem Bilan
2023-12-12 17:25:01 -05:00
parent 9bd8001a61
commit 6207bca3bd
3 changed files with 43 additions and 20 deletions

View File

@@ -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

View File

@@ -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<>();