This is an automated email from the ASF dual-hosted git repository. reta pushed a commit to branch 3.6.x-fixes in repository https://gitbox.apache.org/repos/asf/cxf.git
commit 95195443cba6571adda2093f7200e97159044ec4 Author: Andriy Redko <drr...@gmail.com> AuthorDate: Thu Jan 18 19:47:13 2024 -0500 CXF-8812: Content-Type header created with collection containing 'null' value (#1653) --- .../org/apache/cxf/jaxrs/impl/HttpHeadersImpl.java | 44 ++++++++++++++++++++-- .../apache/cxf/jaxrs/impl/HttpHeadersImplTest.java | 31 +++++++++++++++ .../org/apache/cxf/systest/jaxrs/BookStore.java | 7 ++++ .../systest/jaxrs/JAXRS20ClientServerBookTest.java | 19 ++++++++++ 4 files changed, 98 insertions(+), 3 deletions(-) diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/HttpHeadersImpl.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/HttpHeadersImpl.java index 67fb41bb67..e3a2a57f41 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/HttpHeadersImpl.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/HttpHeadersImpl.java @@ -34,6 +34,7 @@ import java.util.Set; import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import javax.ws.rs.core.Cookie; import javax.ws.rs.core.HttpHeaders; @@ -87,9 +88,46 @@ public class HttpHeadersImpl implements HttpHeaders { private Map<String, List<String>> headers; public HttpHeadersImpl(Message message) { this.message = message; - this.headers = CastUtils.cast((Map<?, ?>)message.get(Message.PROTOCOL_HEADERS)); - if (headers == null) { - headers = Collections.emptyMap(); + this.headers = filter(CastUtils.cast((Map<?, ?>)message.get(Message.PROTOCOL_HEADERS))); + } + + /** + * Filters the protocol headers by excluding the headers that have {@code null} values. + * @param protocolHeaders protocol headers to filter + * @return filtered headers + */ + private static Map<String, List<String>> filter(Map<String, List<String>> protocolHeaders) { + if (protocolHeaders == null) { + return Collections.emptyMap(); + } else if (protocolHeaders.isEmpty()) { + return protocolHeaders; + } + + final Set<String> nullableHeader = protocolHeaders + .entrySet() + .stream() + .filter(e -> { + if (e.getValue() == null) { + return true; + } else if (e.getValue().size() == 1) { + return e.getValue().get(0) == null; + } else { + return false; + } + }) + .map(e -> e.getKey()) + .collect(Collectors.toSet()); + + if (nullableHeader.isEmpty()) { + return protocolHeaders; + } else { + final Map<String, List<String>> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + for (final Map.Entry<String, List<String>> entry: protocolHeaders.entrySet()) { + if (!nullableHeader.contains(entry.getKey())) { + headers.put(entry.getKey(), entry.getValue()); + } + } + return headers; } } diff --git a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/HttpHeadersImplTest.java b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/HttpHeadersImplTest.java index c0903057fe..423cb072a8 100644 --- a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/HttpHeadersImplTest.java +++ b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/HttpHeadersImplTest.java @@ -41,9 +41,12 @@ import org.apache.cxf.message.MessageImpl; import org.junit.Test; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; public class HttpHeadersImplTest { @@ -199,6 +202,34 @@ public class HttpHeadersImplTest { assertTrue(values.isEmpty()); } + @Test + public void testGetNullHeaderValue() throws Exception { + + Message m = new MessageImpl(); + // this is what happens at runtime and is tested in the system tests + Map<String, List<String>> headers = + new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + headers.put("A", Collections.<String>singletonList(null)); + m.put(Message.PROTOCOL_HEADERS, headers); + HttpHeaders h = new HttpHeadersImpl(m); + List<String> values = h.getRequestHeader("A"); + assertThat(values, is(nullValue())); + } + + @Test + public void testGetNullHeader() throws Exception { + + Message m = new MessageImpl(); + // this is what happens at runtime and is tested in the system tests + Map<String, List<String>> headers = + new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + headers.put("A", null); + m.put(Message.PROTOCOL_HEADERS, headers); + HttpHeaders h = new HttpHeadersImpl(m); + List<String> values = h.getRequestHeader("A"); + assertThat(values, is(nullValue())); + } + @Test public void testGetDate() throws Exception { diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java index 545f362b6e..bdbbeed451 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java @@ -1837,6 +1837,13 @@ public class BookStore { AnnotatedClass.class.getAnnotations()).build(); } + @GET + @Path("/headers") + @Produces("application/json") + public Map<String, List<String>> getHeaders() { + return httpHeaders.getRequestHeaders(); + } + public final String init() { books.clear(); cds.clear(); diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRS20ClientServerBookTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRS20ClientServerBookTest.java index 2f4bb40996..c66cfbe968 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRS20ClientServerBookTest.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRS20ClientServerBookTest.java @@ -29,6 +29,7 @@ import java.net.UnknownHostException; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -81,7 +82,9 @@ import org.junit.BeforeClass; import org.junit.Test; import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasKey; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -967,6 +970,22 @@ public class JAXRS20ClientServerBookTest extends AbstractBusClientServerTestBase } } + @Test + public void testGetHeaders() throws Exception { + final WebTarget target = ClientBuilder + .newClient() + .register(JacksonJaxbJsonProvider.class) + .target("http://localhost:" + PORT + "/bookstore/headers"); + + @SuppressWarnings("unchecked") + final Map<String, Object> headers = target + .request().accept("application/json") + .get(Map.class); + + assertThat(headers, hasKey("Accept")); + assertThat(headers, not(hasKey("Content-Type"))); + } + @Test public void testGetBookDateAnnotated() throws Exception { final String response = ClientBuilder