From 697bdf45ddca9f1550dea6b4a915e71fca652637 Mon Sep 17 00:00:00 2001 From: Neena Jacob Date: Fri, 14 Aug 2026 15:26:11 +0530 Subject: [PATCH] fix Invocation.Builder.property() not visible to HTTP transport --- .../client/spec/InvocationBuilderImpl.java | 4 ++ .../spec/InvocationBuilderImplTest.java | 61 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/InvocationBuilderImpl.java b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/InvocationBuilderImpl.java index 25c85abd825..5b062151f88 100644 --- a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/InvocationBuilderImpl.java +++ b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/InvocationBuilderImpl.java @@ -288,8 +288,12 @@ public Builder property(String name, Object value) { } if (value == null) { filterProps.remove(name); + contextProps.remove(name); } else { filterProps.put(name, value); + // CXF-9235: also write to the top-level context so that the transport layer + // can find the property via Message.getContextualProperty() + contextProps.put(name, value); } return this; } diff --git a/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/spec/InvocationBuilderImplTest.java b/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/spec/InvocationBuilderImplTest.java index 9569bfeddf1..6dcba9a8371 100644 --- a/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/spec/InvocationBuilderImplTest.java +++ b/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/spec/InvocationBuilderImplTest.java @@ -19,6 +19,7 @@ package org.apache.cxf.jaxrs.client.spec; import java.io.IOException; +import java.util.Map; import jakarta.ws.rs.client.Client; import jakarta.ws.rs.client.ClientBuilder; @@ -28,15 +29,21 @@ import jakarta.ws.rs.core.MultivaluedHashMap; import jakarta.ws.rs.core.MultivaluedMap; import jakarta.ws.rs.core.Response; +import org.apache.cxf.helpers.CastUtils; +import org.apache.cxf.jaxrs.client.WebClient; +import org.apache.cxf.transport.http.Headers; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; public class InvocationBuilderImplTest { + private static final String FILTER_PROPS_KEY = "jaxrs.filter.properties"; + public static class TestFilter implements ClientRequestFilter { /** {@inheritDoc}*/ @@ -83,4 +90,58 @@ public void testHeadersMethod() { response = builder.get(); assertEquals("", response.readEntity(String.class)); } + + /** + * CXF-9235: Invocation.Builder.property() must write the property value into BOTH: + * + * (a) the nested "jaxrs.filter.properties" sub-map inside the ClientConfiguration + * request context — this is what context.getProperty() reads inside a + * ClientRequestFilter (via MessagePropertyHolder / Exchange.get(PROPERTY_KEY)). + * + * (b) the flat top-level ClientConfiguration request context — this is what the + * HTTP transport reads via Message.getContextualProperty() in + * Headers.setProtocolHeadersInConnection(). + * + * Before the fix only (a) was written. The transport (b) path was silently missing, + * meaning a property like "set.content.type.for.empty.request" set on the + * Invocation.Builder had no effect on the conduit. + */ + @Test + public void testPropertyWrittenToBothFilterPropsAndFlatContext() { + Client client = ClientBuilder.newClient().register(TestFilter.class); + Builder builder = client.target("http://localhost:8080/notReal").request(); + + // ---- set ---- + builder.property(Headers.SET_EMPTY_REQUEST_CT_PROPERTY, Boolean.FALSE); + + InvocationBuilderImpl builderImpl = (InvocationBuilderImpl) builder; + Map requestContext = + WebClient.getConfig(builderImpl.getWebClient()).getRequestContext(); + + // (a) Must be in the nested filterProps sub-map that a ClientRequestFilter reads. + // This path was already written before the fix; we guard it stays working. + Map filterProps = + CastUtils.cast((Map) requestContext.get(FILTER_PROPS_KEY)); + assertTrue("jaxrs.filter.properties sub-map must exist after Builder.property()", + filterProps != null && filterProps.containsKey(Headers.SET_EMPTY_REQUEST_CT_PROPERTY)); + assertEquals("Value in filterProps must match what was set", + Boolean.FALSE, filterProps.get(Headers.SET_EMPTY_REQUEST_CT_PROPERTY)); + + // (b) Must also be present flat in the top-level context — the path the HTTP + // transport reads via Message.getContextualProperty(). This was the bug in + // CXF-9235: only (a) was written, so the transport never saw the property. + assertEquals("Flat request context must contain the property for the transport layer (CXF-9235)", + Boolean.FALSE, requestContext.get(Headers.SET_EMPTY_REQUEST_CT_PROPERTY)); + + // ---- remove (null value) ---- + builder.property(Headers.SET_EMPTY_REQUEST_CT_PROPERTY, null); + + // (a) removed from filterProps + assertNull("Null value must remove property from filterProps sub-map", + filterProps.get(Headers.SET_EMPTY_REQUEST_CT_PROPERTY)); + + // (b) removed from flat context + assertFalse("Null value must remove property from the flat transport context (CXF-9235)", + requestContext.containsKey(Headers.SET_EMPTY_REQUEST_CT_PROPERTY)); + } }